import { AnimatePresence, motion } from "framer-motion"; import { Kanban, ListChecks, MagnifyingGlass, Target, TerminalWindow, UsersThree, } from "../components/ui/icons"; import type { AgentRecord, GoalRecord, ProjectRecord, RunRecord, SectionId, TaskRecord } from "@shared/types"; import { ListFrame, ListRow, StatusPill } from "../components/ui"; type NavItem = { id: SectionId; label: string; icon: typeof MagnifyingGlass }; type PaletteAction = { key: string; section: SectionId; entityId?: string; }; interface CommandPaletteViewProps { commandPaletteOpen: boolean; setCommandPaletteOpen: (value: boolean) => void; commandPaletteSearch: string; setCommandPaletteSearch: (value: string) => void; selectedPaletteIndex: number; setSelectedPaletteIndex: (value: number) => void; paletteTrapRef: React.RefObject; filteredNavItems: NavItem[]; matchedAgents: AgentRecord[]; matchedTasks: TaskRecord[]; matchedRuns: RunRecord[]; matchedGoals: GoalRecord[]; matchedProjects: ProjectRecord[]; hasEntityResults: boolean; paletteActions: PaletteAction[]; paletteActionIndex: Map; runs: RunRecord[]; navigateTo: (section: SectionId, entityId?: string) => void; panelTransition: { duration: number; ease?: readonly [number, number, number, number] } | { duration: number }; t: (key: string) => string; } interface PaletteResultSectionProps { label: string; items: T[]; keyPrefix: string; icon: typeof MagnifyingGlass; selectedPaletteIndex: number; paletteActionIndex: Map; onSelect: (item: T) => void; renderPrimary: (item: T) => React.ReactNode; renderRight: (item: T) => React.ReactNode; } function PaletteResultSection({ label, items, keyPrefix, icon: Icon, selectedPaletteIndex, paletteActionIndex, onSelect, renderPrimary, renderRight, }: PaletteResultSectionProps) { if (items.length === 0) return null; return ( <>
{label}
{items.map((item) => ( onSelect(item)} >
{renderPrimary(item)}
{renderRight(item)}
))}
); } export function CommandPaletteView({ commandPaletteOpen, setCommandPaletteOpen, commandPaletteSearch, setCommandPaletteSearch, selectedPaletteIndex, setSelectedPaletteIndex, paletteTrapRef, filteredNavItems, matchedAgents, matchedTasks, matchedRuns, matchedGoals, matchedProjects, hasEntityResults, paletteActionIndex, navigateTo, panelTransition, t, }: CommandPaletteViewProps) { const paletteQuery = commandPaletteSearch.trim().toLowerCase(); const closeAndReset = () => { setCommandPaletteOpen(false); setCommandPaletteSearch(""); setSelectedPaletteIndex(0); }; const selectAndClose = (section: SectionId, entityId?: string) => { navigateTo(section, entityId); closeAndReset(); }; return ( {commandPaletteOpen ? ( event.stopPropagation()} >
setCommandPaletteSearch(e.target.value)} autoFocus /> {commandPaletteSearch && ( )}
{filteredNavItems.length > 0 ? ( <>
{t("entity.sections")}
{filteredNavItems.map((item) => ( selectAndClose(item.id)} >
{item.label}
{item.id}
))}
) : null} {hasEntityResults ? (
selectAndClose("agents", agent.id)} renderPrimary={(agent) => ( <> {agent.name} {agent.role} )} renderRight={(agent) => } /> selectAndClose("tasks", task.id)} renderPrimary={(task) => ( {task.title} )} renderRight={(task) => } /> selectAndClose("runs", run.id)} renderPrimary={(run) => ( {run.summary || run.id.slice(0, 8)} )} renderRight={(run) => } /> selectAndClose("goals", goal.id)} renderPrimary={(goal) => ( {goal.title} )} renderRight={(goal) => } /> selectAndClose("projects", project.id)} renderPrimary={(project) => ( {project.name} )} renderRight={(project) => } />
) : null} {paletteQuery && filteredNavItems.length === 0 && !hasEntityResults ? (
{t("app.noResults")} "{commandPaletteSearch}"
) : null}
) : null}
); }