219 lines · 7.6 KB · TypeScript
RawDownload
import { useCallback, useEffect, useMemo } 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 { PresetIcon } from "./preset-icon";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 integrations (priority, shown first)//   2. Add <Plugin> actions for each available integration plugin//   3. Popular integrations (plugin presets)// --------------------------------------------------------------------------- export function CommandPalette(props: { open: boolean; onOpenChange: (open: boolean) => void }) {  const { open, onOpenChange } = props;  const integrationPlugins = useIntegrationPlugins();  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();        onOpenChange(!open);      }    };    document.addEventListener("keydown", onKeyDown);    return () => document.removeEventListener("keydown", onKeyDown);  }, [onOpenChange, open]);   const connectedIntegrations = 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.name || 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;      presetFallbackIcon?: 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,          presetFallbackIcon: preset.fallbackIcon,        });      }    }    return entries;  }, [integrationPlugins]);   const close = useCallback(() => onOpenChange(false), [onOpenChange]);   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<string, string> = { preset: presetId };      if (presetUrl) search.url = presetUrl;      void navigate({        to: "/{-$orgSlug}/integrations/add/$pluginKey",        params: { pluginKey },        search,      });    },    [close, navigate],  );   if (!open) return null;   return (    <CommandDialog open={open} onOpenChange={onOpenChange}>      <CommandInput placeholder="Search integrations or jump to add…" />      <CommandList>        <CommandEmpty>No results found.</CommandEmpty>         {connectedIntegrations.length > 0 && (          <CommandGroup heading="Connected">            {connectedIntegrations.map(              (s: {                readonly id: string;                readonly name: string;                readonly kind: string;                readonly url?: string;              }) => (                <CommandItem                  key={`integration-${s.id}`}                  value={`connected ${s.name} ${s.id} ${s.kind}`}                  onSelect={() => goToIntegration(s.id)}                >                  <IntegrationFavicon                    icon={integrationPresetIconUrl(s, integrationPlugins)}                    url={s.url}                  />                  <span className="flex-1 truncate">{s.name}</span>                  <CommandShortcut>{s.kind}</CommandShortcut>                </CommandItem>              ),            )}          </CommandGroup>        )}         {connectedIntegrations.length > 0 && integrationPlugins.length > 0 && <CommandSeparator />}         {integrationPlugins.length > 0 && (          <CommandGroup heading="Add integration">            {integrationPlugins.map((plugin) => (              <CommandItem                key={`add-${plugin.key}`}                value={`add ${plugin.label} ${plugin.key}`}                onSelect={() => goToAdd(plugin.key)}              >                <PlusIcon />                <span className="flex-1 truncate">Add {plugin.label}</span>              </CommandItem>            ))}          </CommandGroup>        )}         {presetEntries.length > 0 && <CommandSeparator />}         {presetEntries.length > 0 && (          <CommandGroup heading="Popular integrations">            {presetEntries.map((e) => (              <CommandItem                key={`preset-${e.pluginKey}-${e.presetId}`}                value={`preset ${e.presetName} ${e.presetSummary ?? ""} ${e.pluginLabel}`}                onSelect={() => goToPreset(e.pluginKey, e.presetId, e.presetUrl)}              >                <PresetIcon                  {...(e.presetIcon ? { icon: e.presetIcon } : {})}                  {...(e.presetFallbackIcon ? { fallbackSrc: e.presetFallbackIcon } : {})}                  className="size-4 shrink-0 object-contain"                  fallback={                    <span                      aria-hidden                      className="size-4 shrink-0 rounded-sm bg-muted-foreground/20"                    />                  }                />                <span className="flex-1 truncate">{e.presetName}</span>                <CommandShortcut>{e.pluginLabel}</CommandShortcut>              </CommandItem>            ))}          </CommandGroup>        )}      </CommandList>    </CommandDialog>  );}