import { useQuery } from "@tanstack/react-query" import { useNavigate } from "@tanstack/react-router" import type { FlowSummary, NodeTypeInfo } from "@/client" import { CommandDialog, CommandEmpty, CommandGroup, CommandInput, CommandItem, CommandList, } from "@/components/ui/command" import { libraryQueryOptions } from "./queries" /** * ⌘P: add a node, jump to another flow, or run the current one, without * reaching for the dock. */ export function CommandPalette({ open, onOpenChange, nodeTypes, flows, onAddNode, onAddSharedNode, onRun, }: { open: boolean onOpenChange: (open: boolean) => void nodeTypes: NodeTypeInfo[] flows: FlowSummary[] onAddNode: (type: string) => void onAddSharedNode: (libName: string) => void onRun: () => void }) { const navigate = useNavigate() const { data: libraryData } = useQuery({ ...libraryQueryOptions(), enabled: open, }) // Picking an item re-renders the canvas, which can interrupt the dialog's // exit animation and leave its overlay swallowing clicks. Unmounting the // dialog outright is deterministic; the palette does not need to fade out. if (!open) return null const library = libraryData ?? [] const close = (action: () => void) => { onOpenChange(false) action() } return ( Nothing matches that. {nodeTypes.map((nodeType) => ( close(() => onAddNode(nodeType.type))} > {nodeType.title} {nodeType.description} ))} {library.length > 0 ? ( {library.map((entry) => ( close(() => onAddSharedNode(entry.name))} > {entry.name} {(entry.used_by ?? []).length === 1 ? "Used once" : `Used ${(entry.used_by ?? []).length} times`} , code shared with every flow using it ))} ) : null} {flows.length > 0 ? ( {flows.map((flow) => ( close(() => navigate({ to: "/flows/$flowName", params: { flowName: flow.name }, }), ) } > {flow.title || flow.name} ))} ) : null} close(onRun)}> Run this flow ) }