import { useEffect, useRef, useMemo } from 'react' import { useCommandStore } from '@/stores/useCommandStore' function SearchIcon() { return ( ) } function Kbd({ keys }: { keys: string }) { return ( {keys.split('+').map((k) => ( {k === 'Ctrl' ? '⌃' : k === 'Shift' ? '⇧' : k === 'Cmd' ? '⌘' : k} ))} ) } function CommandGroup({ category, children }: { category: string; children: React.ReactNode }) { return (
{category}
{children}
) } export default function CommandPalette() { const isOpen = useCommandStore((s) => s.isOpen) const query = useCommandStore((s) => s.query) const selectedIndex = useCommandStore((s) => s.selectedIndex) const commands = useCommandStore((s) => s.commands) const setQuery = useCommandStore((s) => s.setQuery) const setSelectedIndex = useCommandStore((s) => s.setSelectedIndex) const executeSelected = useCommandStore((s) => s.executeSelected) const close = useCommandStore((s) => s.close) const inputRef = useRef(null) const filtered = useMemo( () => commands.filter( (c) => c.label.toLowerCase().includes(query.toLowerCase()) || c.category.toLowerCase().includes(query.toLowerCase()) ), [commands, query] ) const grouped = useMemo(() => { const map = new Map() filtered.forEach((c) => { const group = map.get(c.category) ?? [] group.push(c) map.set(c.category, group) }) return Array.from(map.entries()) }, [filtered]) useEffect(() => { if (isOpen) { inputRef.current?.focus() } }, [isOpen]) useEffect(() => { if (!isOpen) return function handleKeyDown(e: KeyboardEvent) { if (e.key === 'Escape') { e.preventDefault() close() return } if (e.key === 'Enter') { e.preventDefault() executeSelected() return } if (e.key === 'ArrowDown') { e.preventDefault() setSelectedIndex(Math.min(selectedIndex + 1, filtered.length - 1)) return } if (e.key === 'ArrowUp') { e.preventDefault() setSelectedIndex(Math.max(selectedIndex - 1, 0)) return } } window.addEventListener('keydown', handleKeyDown) return () => window.removeEventListener('keydown', handleKeyDown) }, [isOpen, filtered.length, selectedIndex, setSelectedIndex, executeSelected, close]) if (!isOpen) return null return (
e.stopPropagation()} >
setQuery(e.target.value)} placeholder="Type a command..." className="flex-1 bg-transparent text-ui-base text-text-primary outline-none placeholder:text-text-muted" />
{filtered.length === 0 && (
No results found
)} {grouped.map(([category, items]) => ( {items.map((cmd) => { const idx = filtered.indexOf(cmd) const isSelected = idx === selectedIndex return ( ) })} ))}
navigate select close
) }