import { useState, useEffect, useRef } from "react"; import { useLocation } from "wouter"; import { useListTools, getListToolsQueryKey } from "@workspace/api-client-react"; import { CommandDialog, CommandInput, CommandList, CommandEmpty, CommandGroup, CommandItem, CommandSeparator, } from "@/components/ui/command"; import { useAuth } from "@/hooks/use-auth"; import { useTranslation } from "react-i18next"; import { getRecentTools } from "@/lib/recent-tools"; import { Compass, PlusCircle, BarChart3, Trash2, ShieldCheck, Wrench, Star, History, } from "lucide-react"; const openRequesters = new Set<() => void>(); export function requestOpenCommandPalette() { for (const request of openRequesters) request(); } export function CommandPalette() { const [, navigate] = useLocation(); const { t } = useTranslation(); const { isAuthenticated, isAdmin, hasFeature } = useAuth(); const [open, setOpen] = useState(false); const [search, setSearch] = useState(""); const [recent, setRecent] = useState(() => getRecentTools()); const searchTimer = useRef | null>(null); useEffect(() => { const request = () => setOpen(true); openRequesters.add(request); return () => { openRequesters.delete(request); }; }, []); useEffect(() => { const down = (e: KeyboardEvent) => { if (e.key.toLowerCase() === "k" && (e.metaKey || e.ctrlKey)) { e.preventDefault(); setOpen((o) => !o); } }; window.addEventListener("keydown", down); return () => window.removeEventListener("keydown", down); }, []); useEffect(() => { if (!open) { setSearch(""); } else { setRecent(getRecentTools()); } }, [open]); const { data: tools } = useListTools( search ? { search } : undefined, { query: { queryKey: getListToolsQueryKey(search ? { search } : undefined), enabled: open && search.trim().length > 0, staleTime: 30_000, }, }, ); function go(path: string) { setOpen(false); navigate(path); } const showTrash = hasFeature("trash") || isAdmin; const showWatchlist = (hasFeature("watchlist") || isAdmin) && isAuthenticated; const searching = search.trim().length > 0; return ( { setSearch(v); if (searchTimer.current) clearTimeout(searchTimer.current); }} /> {searching ? t("command.noResults", { query: search }) : t("command.startTyping")} {!searching && recent.length > 0 && ( {recent.map((tool) => ( go(`/tools/${tool.id}`)}> {tool.name} ))} )} go("/tools")}> {t("nav.browseTools")} go("/tools/new")}> {t("nav.addTool")} go("/analytics")}> {t("nav.analytics")} {showWatchlist && ( go("/watchlist")}> {t("nav.watchlist")} )} {showTrash && ( go("/trash")}> {t("nav.trash")} )} {isAdmin && ( go("/admin")}> {t("nav.admin")} )} {searching && ( <> {(tools ?? []).slice(0, 10).map((tool) => ( go(`/tools/${tool.id}`)}> {tool.name} {tool.avgCombined ? `${tool.avgCombined.toFixed(1)}★` : ""} ))} )} ); }