• Modal dialog for displaying keyboard shortcuts with search functionality.

    Parameters

    • props: Readonly<ShortcutsPanelProps>

      Component properties.

      • isOpen

        Whether the panel is visible.

      • onClose

        Callback invoked when panel should close.

    Returns Element

    Shortcuts modal or null when closed.

    export function ShortcutsPanel({
    isOpen,
    onClose,
    }: Readonly<ShortcutsPanelProps>) {
    const [searchQuery, setSearchQuery] = useState("");
    const searchInputRef = useRef<HTMLInputElement>(null);

    // Manage autofocus: focus after a small delay when dialog opens
    useEffect(() => {
    if (isOpen) {
    const timer = globalThis.setTimeout(() => {
    if (searchInputRef.current) {
    searchInputRef.current.focus();
    }
    }, 100);
    return () => globalThis.clearTimeout(timer);
    }
    return undefined;
    }, [isOpen]);

    // Memoize categories to prevent recomputation
    const allCategories = useMemo(() => Object.values(ShortcutCategory), []);

    // Filter shortcuts by search query
    const filteredShortcuts = useMemo(() => {
    if (!searchQuery.trim()) {
    return SHORTCUTS;
    }

    // Precompute keyLabel for each shortcut to enable string-based search
    const shortcutsWithKeyLabels = SHORTCUTS.map((shortcut) => ({
    ...shortcut,
    keyLabel: formatShortcutKey(shortcut.keys),
    }));

    const fuse = buildFuse(shortcutsWithKeyLabels, [
    { name: "description", weight: 0.5 },
    { name: "action", weight: 0.3 },
    { name: "keyLabel", weight: 0.2 },
    ]);

    const results = fuse.search(searchQuery);
    return results.map((result) => result.item);
    }, [searchQuery]);

    // Get shortcuts filtered by category
    const getFilteredByCategory = (category: ShortcutCategory) => {
    return filteredShortcuts.filter((s) => s.category === category);
    };

    // Check if search has results
    const hasResults = filteredShortcuts.length > 0;

    // Handle Escape key to close panel
    const handleEscapeKeyDown = (e: React.KeyboardEvent) => {
    if (e.key === "Escape") {
    e.preventDefault();
    e.stopPropagation();
    onClose();
    }
    };

    return (
    <AnimatePresence>
    {isOpen && (
    <Dialog
    open={isOpen}
    onOpenChange={(open) => {
    if (!open) onClose();
    }}
    >
    <DialogContent
    className="max-h-[80vh] max-w-3xl overflow-hidden rounded-2xl border-white/10 bg-slate-950/90 p-6 shadow-2xl backdrop-blur-xl"
    onKeyDown={handleEscapeKeyDown}
    >
    <DialogHeader>
    <div className="flex items-center gap-3">
    <div className="rounded-full bg-blue-500/20 p-2">
    <Keyboard className="h-5 w-5 text-blue-400" />
    </div>
    <div>
    <DialogTitle className="text-2xl">
    Keyboard Shortcuts
    </DialogTitle>
    <DialogDescription>
    Navigate and control the app using your keyboard
    </DialogDescription>
    </div>
    </div>
    </DialogHeader>

    {/* Search Input */}
    <div className="relative">
    <Search className="text-muted-foreground absolute left-3 top-3 h-4 w-4" />
    <Input
    ref={searchInputRef}
    placeholder="Search shortcuts by name or key..."
    className="border-white/10 bg-white/5 pl-10 focus:bg-white/10"
    value={searchQuery}
    onChange={(e) => setSearchQuery(e.target.value)}
    aria-label="Search shortcuts"
    aria-describedby="shortcuts-search-status"
    aria-controls="shortcuts-list"
    />
    </div>

    {/* Shortcuts Display */}
    <ScrollArea className="h-[400px] w-full rounded-lg border border-white/10 bg-white/5 p-4">
    {/* Announcements for search results */}
    {searchQuery && (
    <output
    id="shortcuts-search-status"
    className="sr-only"
    aria-live="polite"
    aria-atomic="true"
    >
    {(() => {
    const countText =
    filteredShortcuts.length === 1 ? "shortcut" : "shortcuts";
    return hasResults
    ? `Found ${filteredShortcuts.length} ${countText}`
    : `No shortcuts found matching "${searchQuery}"`;
    })()}
    </output>
    )}

    <section id="shortcuts-list" aria-label="Search results">
    {hasResults ? (
    <div className="space-y-6 pr-4">
    {searchQuery ? (
    // Show all matching shortcuts when searching
    <div className="space-y-2">
    {filteredShortcuts.map((shortcut) => (
    <ShortcutCard
    key={shortcut.id}
    shortcutItem={shortcut}
    />
    ))}
    </div>
    ) : (
    // Show organized by category using memoized categories
    <>
    {allCategories.map((category) => (
    <ShortcutCategorySection
    key={category}
    category={category}
    categoryShortcuts={getFilteredByCategory(category)}
    />
    ))}
    </>
    )}
    </div>
    ) : (
    <div className="flex h-full items-center justify-center">
    <div className="text-center">
    <p className="text-muted-foreground">
    No shortcuts found matching &quot;{searchQuery}&quot;
    </p>
    </div>
    </div>
    )}
    </section>
    </ScrollArea>

    {/* Footer */}
    <motion.div
    initial={{ opacity: 0 }}
    animate={{ opacity: 1 }}
    transition={{ delay: 0.2 }}
    className="text-muted-foreground flex items-center justify-between border-t border-white/10 pt-4 text-xs"
    >
    <p>
    Press{" "}
    <kbd className="rounded bg-white/10 px-1.5 py-0.5 font-mono">
    ?
    </kbd>{" "}
    or{" "}
    <kbd className="rounded bg-white/10 px-1.5 py-0.5 font-mono">
    Ctrl+/
    </kbd>{" "}
    to toggle this panel
    </p>
    <Badge variant="outline">
    {SHORTCUTS.length} shortcuts total
    </Badge>
    </motion.div>
    </DialogContent>
    </Dialog>
    )}
    </AnimatePresence>
    );
    }