'use client'; import * as React from 'react'; import { Command } from 'lucide-react'; import { cn } from '@/lib/utils/cn'; export interface CommandAction { id: string; label: string; description?: string; shortcut?: string; category: 'edit' | 'playback' | 'file' | 'view' | 'effects' | 'tracks'; action: () => void; } export interface CommandPaletteProps { actions: CommandAction[]; className?: string; } export function CommandPalette({ actions, className }: CommandPaletteProps) { const [isOpen, setIsOpen] = React.useState(false); const [search, setSearch] = React.useState(''); const [selectedIndex, setSelectedIndex] = React.useState(0); const inputRef = React.useRef(null); const filteredActions = React.useMemo(() => { if (!search) return actions; const query = search.toLowerCase(); return actions.filter( (action) => action.label.toLowerCase().includes(query) || action.description?.toLowerCase().includes(query) || action.category.toLowerCase().includes(query) ); }, [actions, search]); const groupedActions = React.useMemo(() => { const groups: Record = {}; filteredActions.forEach((action) => { if (!groups[action.category]) { groups[action.category] = []; } groups[action.category].push(action); }); return groups; }, [filteredActions]); React.useEffect(() => { const handleKeyDown = (e: KeyboardEvent) => { // Ctrl+K or Cmd+K to open if ((e.ctrlKey || e.metaKey) && e.key === 'k') { e.preventDefault(); setIsOpen(true); } // Escape to close if (e.key === 'Escape') { setIsOpen(false); setSearch(''); setSelectedIndex(0); } }; window.addEventListener('keydown', handleKeyDown); return () => window.removeEventListener('keydown', handleKeyDown); }, []); React.useEffect(() => { if (isOpen && inputRef.current) { inputRef.current.focus(); } }, [isOpen]); const handleKeyDown = (e: React.KeyboardEvent) => { if (e.key === 'ArrowDown') { e.preventDefault(); setSelectedIndex((prev) => Math.min(prev + 1, filteredActions.length - 1)); } else if (e.key === 'ArrowUp') { e.preventDefault(); setSelectedIndex((prev) => Math.max(prev - 1, 0)); } else if (e.key === 'Enter') { e.preventDefault(); if (filteredActions[selectedIndex]) { filteredActions[selectedIndex].action(); setIsOpen(false); setSearch(''); setSelectedIndex(0); } } }; const executeAction = (action: CommandAction) => { action.action(); setIsOpen(false); setSearch(''); setSelectedIndex(0); }; if (!isOpen) { return ( ); } return (
{/* Search Input */}
{ setSearch(e.target.value); setSelectedIndex(0); }} onKeyDown={handleKeyDown} placeholder="Type a command or search..." className="flex-1 bg-transparent border-none outline-none text-foreground placeholder:text-muted-foreground" /> ESC
{/* Results */}
{Object.keys(groupedActions).length === 0 ? (
No commands found
) : ( Object.entries(groupedActions).map(([category, categoryActions]) => (
{category}
{categoryActions.map((action, index) => { const globalIndex = filteredActions.indexOf(action); return ( ); })}
)) )}
); }