import { useCallback, useEffect, useMemo, useState } from "react"; import { useNavigate } from "@tanstack/react-router"; import { useAtomValue } from "@effect/atom-react"; import * as AsyncResult from "effect/unstable/reactivity/AsyncResult"; import { PlusIcon } from "lucide-react"; import { trackEvent } from "../api/analytics"; import type { Integration } from "@executor-js/sdk/shared"; import { IntegrationFavicon, integrationPresetIconUrl } from "./integration-favicon"; import { integrationsOptimisticAtom } from "../api/atoms"; import { useIntegrationPlugins } from "@executor-js/sdk/client"; import { CommandDialog, CommandEmpty, CommandGroup, CommandInput, CommandItem, CommandList, CommandSeparator, CommandShortcut, } from "./command"; // --------------------------------------------------------------------------- // CommandPalette — global ⌘K navigator. // // Order of entries: // 1. Connected sources (priority, shown first) // 2. Add actions for each available source plugin // 3. Popular integrations (plugin presets) // --------------------------------------------------------------------------- export function CommandPalette() { const integrationPlugins = useIntegrationPlugins(); const [open, setOpen] = useState(false); const navigate = useNavigate(); const integrationsResult = useAtomValue(integrationsOptimisticAtom); // Toggle with ⌘K / Ctrl+K useEffect(() => { const onKeyDown = (e: KeyboardEvent) => { if (e.key === "k" && (e.metaKey || e.ctrlKey)) { e.preventDefault(); setOpen((o) => !o); } }; document.addEventListener("keydown", onKeyDown); return () => document.removeEventListener("keydown", onKeyDown); }, []); const connectedSources = useMemo( () => AsyncResult.match(integrationsResult, { onInitial: () => [] as Array<{ id: string; name: string; kind: string; url?: string }>, onFailure: () => [] as Array<{ id: string; name: string; kind: string; url?: string }>, onSuccess: ({ value }) => value.map((integration: Integration) => ({ id: String(integration.slug), name: integration.description || String(integration.slug), kind: integration.kind, url: integration.displayUrl, })), }), [integrationsResult], ); const presetEntries = useMemo(() => { const entries: Array<{ pluginKey: string; pluginLabel: string; presetId: string; presetName: string; presetSummary?: string; presetUrl?: string; presetIcon?: string; }> = []; for (const plugin of integrationPlugins) { for (const preset of plugin.presets ?? []) { entries.push({ pluginKey: plugin.key, pluginLabel: plugin.label, presetId: preset.id, presetName: preset.name, presetSummary: preset.summary, presetUrl: preset.url, presetIcon: preset.icon, }); } } return entries; }, [integrationPlugins]); const close = useCallback(() => setOpen(false), []); const goToIntegration = useCallback( (id: string) => { close(); trackEvent("command_palette_navigated", { kind: "integration", plugin_key: id }); void navigate({ to: "/{-$orgSlug}/integrations/$namespace", params: { namespace: id } }); }, [close, navigate], ); const goToAdd = useCallback( (pluginKey: string) => { close(); trackEvent("command_palette_navigated", { kind: "add_integration", plugin_key: pluginKey }); trackEvent("integration_add_started", { plugin_key: pluginKey, via: "command_palette" }); void navigate({ to: "/{-$orgSlug}/integrations/add/$pluginKey", params: { pluginKey }, }); }, [close, navigate], ); const goToPreset = useCallback( (pluginKey: string, presetId: string, presetUrl?: string) => { close(); trackEvent("command_palette_navigated", { kind: "preset", plugin_key: pluginKey }); trackEvent("integration_add_started", { plugin_key: pluginKey, via: "command_palette", preset_id: presetId, }); const search: Record = { preset: presetId }; if (presetUrl) search.url = presetUrl; void navigate({ to: "/{-$orgSlug}/integrations/add/$pluginKey", params: { pluginKey }, search, }); }, [close, navigate], ); return ( No results found. {connectedSources.length > 0 && ( {connectedSources.map( (s: { readonly id: string; readonly name: string; readonly kind: string; readonly url?: string; }) => ( goToIntegration(s.id)} > {s.name} {s.kind} ), )} )} {connectedSources.length > 0 && integrationPlugins.length > 0 && } {integrationPlugins.length > 0 && ( {integrationPlugins.map((plugin) => ( goToAdd(plugin.key)} > Add {plugin.label} ))} )} {presetEntries.length > 0 && } {presetEntries.length > 0 && ( {presetEntries.map((e) => ( goToPreset(e.pluginKey, e.presetId, e.presetUrl)} > {e.presetIcon ? ( ) : ( )} {e.presetName} {e.pluginLabel} ))} )} ); }