import { useState, useEffect, useMemo } from "react"; import { useNavigate } from "@/lib/router"; import { useQuery } from "@tanstack/react-query"; import { useCompany } from "../context/CompanyContext"; import { useDialog } from "../context/DialogContext"; import { useSidebar } from "../context/SidebarContext"; import { issuesApi } from "../api/issues"; import { agentsApi } from "../api/agents"; import { projectsApi } from "../api/projects"; import { queryKeys } from "../lib/queryKeys"; import { CommandDialog, CommandEmpty, CommandGroup, CommandInput, CommandItem, CommandList, CommandSeparator, } from "@/components/ui/command"; import { CircleDot, Bot, Hexagon, Target, LayoutDashboard, Inbox, DollarSign, History, SquarePen, Plus, } from "lucide-react"; import { Identity } from "./Identity"; import { agentUrl, projectUrl } from "../lib/utils"; export function CommandPalette() { const [open, setOpen] = useState(false); const [query, setQuery] = useState(""); const navigate = useNavigate(); const { selectedCompanyId } = useCompany(); const { openNewIssue, openNewAgent } = useDialog(); const { isMobile, setSidebarOpen } = useSidebar(); const searchQuery = query.trim(); useEffect(() => { function handleKeyDown(e: KeyboardEvent) { if (e.key === "k" && (e.metaKey || e.ctrlKey)) { e.preventDefault(); setOpen(true); if (isMobile) setSidebarOpen(false); } } document.addEventListener("keydown", handleKeyDown); return () => document.removeEventListener("keydown", handleKeyDown); }, [isMobile, setSidebarOpen]); useEffect(() => { if (!open) setQuery(""); }, [open]); const { data: issues = [] } = useQuery({ queryKey: queryKeys.issues.list(selectedCompanyId!), queryFn: () => issuesApi.list(selectedCompanyId!), enabled: !!selectedCompanyId && open, }); const { data: searchedIssues = [] } = useQuery({ queryKey: queryKeys.issues.search(selectedCompanyId!, searchQuery), queryFn: () => issuesApi.list(selectedCompanyId!, { q: searchQuery }), enabled: !!selectedCompanyId && open && searchQuery.length > 0, }); const { data: agents = [] } = useQuery({ queryKey: queryKeys.agents.list(selectedCompanyId!), queryFn: () => agentsApi.list(selectedCompanyId!), enabled: !!selectedCompanyId && open, }); const { data: allProjects = [] } = useQuery({ queryKey: queryKeys.projects.list(selectedCompanyId!), queryFn: () => projectsApi.list(selectedCompanyId!), enabled: !!selectedCompanyId && open, }); const projects = useMemo( () => allProjects.filter((p) => !p.archivedAt), [allProjects], ); function go(path: string) { setOpen(false); navigate(path); } const agentName = (id: string | null) => { if (!id) return null; return agents.find((a) => a.id === id)?.name ?? null; }; const visibleIssues = useMemo( () => (searchQuery.length > 0 ? searchedIssues : issues), [issues, searchedIssues, searchQuery], ); return ( { setOpen(v); if (v && isMobile) setSidebarOpen(false); }}> No results found. { setOpen(false); openNewIssue(); }} > Create new issue C { setOpen(false); openNewAgent(); }} > Create new agent go("/projects")}> Create new project go("/dashboard")}> Dashboard go("/inbox")}> Inbox go("/issues")}> Issues go("/projects")}> Projects go("/goals")}> Goals go("/agents")}> Agents go("/costs")}> Costs go("/activity")}> Activity {visibleIssues.length > 0 && ( <> {visibleIssues.slice(0, 10).map((issue) => ( 0 ? `${searchQuery} ${issue.identifier ?? ""} ${issue.title}` : undefined } onSelect={() => go(`/issues/${issue.identifier ?? issue.id}`)} > {issue.identifier ?? issue.id.slice(0, 8)} {issue.title} {issue.assigneeAgentId && (() => { const name = agentName(issue.assigneeAgentId); return name ? : null; })()} ))} )} {agents.length > 0 && ( <> {agents.slice(0, 10).map((agent) => ( go(agentUrl(agent))}> {agent.name} {agent.role} ))} )} {projects.length > 0 && ( <> {projects.slice(0, 10).map((project) => ( go(projectUrl(project))}> {project.name} ))} )} ); }