// CRITICAL "use client"; import { useEffect, useMemo, useRef, useState } from "react"; import { Search, CornerDownLeft } from "lucide-react"; export type CommandPaletteAction = { id: string; label: string; hint?: string; keywords?: string[]; run: () => void | Promise; }; export function CommandPalette({ open, onClose, actions, statusText, }: { open: boolean; onClose: () => void; actions: CommandPaletteAction[]; statusText?: string; }) { const inputRef = useRef(null); const [query, setQuery] = useState(""); const [activeIndex, setActiveIndex] = useState(0); useEffect(() => { if (!open) return; setQuery(""); setActiveIndex(0); const t = window.setTimeout(() => inputRef.current?.focus(), 0); return () => window.clearTimeout(t); }, [open]); const filtered = useMemo(() => { const q = query.trim().toLowerCase(); if (!q) return actions; return actions.filter((a) => { const hay = [a.label, a.hint, ...(a.keywords || [])].filter(Boolean).join(" ").toLowerCase(); return hay.includes(q); }); }, [actions, query]); const runActive = async () => { const action = filtered[activeIndex]; if (!action) return; try { await action.run(); } finally { onClose(); } }; if (!open) return null; return (
e.stopPropagation()} >
setQuery(e.target.value)} placeholder="Search commands…" className="flex-1 bg-transparent outline-none text-sm" onKeyDown={(e) => { if (e.key === "Escape") { e.preventDefault(); onClose(); return; } if (e.key === "ArrowDown") { e.preventDefault(); setActiveIndex((i) => Math.min(i + 1, Math.max(filtered.length - 1, 0))); return; } if (e.key === "ArrowUp") { e.preventDefault(); setActiveIndex((i) => Math.max(i - 1, 0)); return; } if (e.key === "Enter") { e.preventDefault(); runActive(); } }} />
Enter
{filtered.length === 0 ? (
No matching commands.
) : ( filtered.map((a, idx) => ( )) )}
{statusText || "Ctrl/⌘K to open"}
); }