import { agentNativePath } from "@agent-native/core/client/api-path"; import { ChangelogDialog } from "@agent-native/core/client/changelog"; import { extensionPath } from "@agent-native/core/client/extensions"; import { callAction, useChangeVersions } from "@agent-native/core/client/hooks"; import { LanguagePicker, useT } from "@agent-native/core/client/i18n"; import { useOrgRole } from "@agent-native/core/client/org"; import { IconFlask, IconTool, IconChartBar, IconLayoutDashboard, IconSun, IconMoon, IconHistory, IconHierarchy2, IconLanguage, IconRefresh, IconSettings, } from "@tabler/icons-react"; import { useQuery } from "@tanstack/react-query"; import { useTheme } from "next-themes"; import { Children, cloneElement, Fragment, isValidElement, useCallback, useEffect, useMemo, useRef, useState, type ComponentProps, type ReactElement, type ReactNode, } from "react"; import { useNavigate } from "react-router"; import { CommandDialog, CommandInput, CommandList, CommandGroup, CommandItem, } from "@/components/ui/command"; import { Dialog, DialogContent, DialogHeader, DialogTitle, } from "@/components/ui/dialog"; import { Label } from "@/components/ui/label"; import { Skeleton } from "@/components/ui/skeleton"; import { useReplayStorageStatus } from "@/hooks/use-replay-storage-status"; import { dashboards } from "@/pages/adhoc/registry"; import { buildAnalyticsGeneralSettingsSearchEntries, buildAnalyticsSettingsCommandItems, } from "@/pages/settings/settings-search"; import changelog from "../../../CHANGELOG.md?raw"; import { commandPaletteKeywords, rankCommandPaletteEntries, uniqueCommandItems, } from "./command-palette-search"; interface SavedConfig { id: string; name: string; } interface ExplorerDashboard { id: string; name: string; hiddenAt?: string | null; } interface ExtensionSearchItem { id: string; name: string; description?: string; } const defaultTools = [ { id: "agents", nameKey: "navigation.admin", href: "/agents?view=dashboards", keywords: [ "agent monitoring", "observability", "evals", "feedback", "database", "db admin", "dashboard usage", "dashboard audit", "llm", ], }, { id: "feature-flags", nameKey: "agents.featureFlags", href: "/agents?view=flags", keywords: ["flags", "rollout", "release", "targeting"], }, { id: "explorer", nameKey: "commandPalette.toolExplorer", href: "/dashboards/explorer", keywords: [], }, { id: "customer-health", nameKey: "commandPalette.toolCustomerHealth", href: "/dashboards/customer-health", keywords: [], }, ]; const loadingRowWidths = ["w-[58%]", "w-[71%]", "w-[84%]"] as const; function CommandLoadingGroup({ heading, rows = 3, }: { heading: string; rows?: number; }) { return ( {Array.from({ length: rows }).map((_, index) => ( ))} ); } type CommandGroupElement = ReactElement>; type CommandItemElement = ReactElement>; function isCommandGroupElement(node: ReactNode): node is CommandGroupElement { return isValidElement(node) && node.type === CommandGroup; } function isCommandItemElement(node: ReactNode): node is CommandItemElement { return isValidElement(node) && node.type === CommandItem; } function RankedCommandGroups({ search, emptyLabel, showEmpty, children, }: { search: string; emptyLabel: string; showEmpty: boolean; children: ReactNode; }) { const groups = Children.toArray(children).filter(isCommandGroupElement); const query = search.trim(); if (!query) return <>{groups}; const rankedGroups = groups .map((group, index) => { const items = Children.toArray(group.props.children).filter( isCommandItemElement, ); const rankedItems = rankCommandPaletteEntries(items, query, (item) => ({ value: item.props.value ?? "", keywords: item.props.keywords, })); if (rankedItems.length === 0) return null; return { index, score: rankedItems[0].score, group: cloneElement( group, undefined, rankedItems.map(({ entry }) => entry), ), }; }) .filter((group): group is NonNullable => group !== null) .sort((a, b) => b.score - a.score || a.index - b.index); return ( {showEmpty && rankedGroups.length === 0 ? (
{emptyLabel}
) : null} {rankedGroups.map(({ group }) => group)}
); } async function fetchSavedConfigs(): Promise { const rows = await callAction("list-explorer-configs", {}, { method: "GET" }); return uniqueCommandItems((Array.isArray(rows) ? rows : []) as SavedConfig[]); } async function fetchExplorerDashboards(): Promise { const result = await callAction( "list-explorer-dashboards", { hidden: "all", }, { method: "GET" }, ); const dashboards = result && typeof result === "object" && "dashboards" in result ? (result as { dashboards: unknown[] }).dashboards : []; return uniqueCommandItems( (Array.isArray(dashboards) ? dashboards : []) .filter((d: any) => d && d.name) .map((d: any) => ({ id: d.id, name: d.name, hiddenAt: typeof d.hiddenAt === "string" ? d.hiddenAt : null, })), ); } async function fetchSqlDashboards( t: (key: string) => string, ): Promise<{ id: string; name: string; hiddenAt: string | null }[]> { const rows = await callAction( "list-sql-dashboards", { hidden: "all" }, { method: "GET" }, ); return uniqueCommandItems( (Array.isArray(rows) ? rows : []) .filter((d: any) => d && typeof d.id === "string" && d.id.length > 0) .map((d: any) => ({ id: d.id, name: typeof d.name === "string" && d.name.trim().length > 0 ? d.name : t("commandPalette.untitledDashboard"), hiddenAt: typeof d.hiddenAt === "string" ? d.hiddenAt : null, })), ); } async function fetchExtensions(): Promise { const res = await fetch(agentNativePath("/_agent-native/extensions")); if (!res.ok) throw new Error(`Failed to load extensions (${res.status})`); const data = await res.json(); return uniqueCommandItems( (Array.isArray(data) ? data : []) .filter((extension: any) => { return ( extension && typeof extension.id === "string" && extension.id.length > 0 && typeof extension.name === "string" && extension.name.trim().length > 0 ); }) .map((extension: any) => ({ id: extension.id, name: extension.name, description: typeof extension.description === "string" ? extension.description : undefined, })), ); } function persistThemePreference(theme: "light" | "dark") { callAction("set-theme", { theme }).catch(() => {}); } export function CommandPalette() { const t = useT(); const { canManageOrg } = useOrgRole(); const [open, setOpen] = useState(false); const [changelogOpen, setChangelogOpen] = useState(false); const [languageOpen, setLanguageOpen] = useState(false); const [searchQuery, setSearchQuery] = useState(""); const [selectedCommand, setSelectedCommand] = useState(""); const commandListRef = useRef(null); const navigate = useNavigate(); const { resolvedTheme, setTheme } = useTheme(); const isDark = resolvedTheme === "dark"; const replayStorageStatus = useReplayStorageStatus({ enabled: open }); const settingsCommands = useMemo(() => { const generalEntries = buildAnalyticsGeneralSettingsSearchEntries( t, !!replayStorageStatus.data?.configured, ); return buildAnalyticsSettingsCommandItems(t, generalEntries); }, [replayStorageStatus.data?.configured, t]); const savedChartsQuery = useQuery({ queryKey: ["explorer-configs-palette"], queryFn: fetchSavedConfigs, staleTime: 30_000, enabled: open, }); const dashboardsSync = useChangeVersions(["dashboards", "action"]); const explorerDashboardsQuery = useQuery({ queryKey: ["explorer-dashboards-palette", dashboardsSync], queryFn: fetchExplorerDashboards, staleTime: 30_000, enabled: open, placeholderData: (prev) => prev, }); const sqlDashboardsQuery = useQuery({ queryKey: ["sql-dashboards-palette", dashboardsSync], queryFn: () => fetchSqlDashboards(t), staleTime: 30_000, enabled: open, placeholderData: (prev) => prev, }); const extensionsQuery = useQuery({ queryKey: ["extensions"], queryFn: fetchExtensions, staleTime: 30_000, enabled: open, placeholderData: (prev) => prev, }); const savedCharts = savedChartsQuery.data ?? []; const explorerDashboards = explorerDashboardsQuery.data ?? []; const sqlDashboards = sqlDashboardsQuery.data ?? []; const extensions = extensionsQuery.data ?? []; const savedChartsLoading = savedChartsQuery.isLoading; const explorerDashboardsLoading = explorerDashboardsQuery.isLoading; const sqlDashboardsLoading = sqlDashboardsQuery.isLoading; const extensionsLoading = extensionsQuery.isLoading; const asyncGroupsErrored = savedChartsQuery.isError || explorerDashboardsQuery.isError || sqlDashboardsQuery.isError || extensionsQuery.isError; const retryAsyncGroups = () => { void Promise.all([ savedChartsQuery.refetch(), explorerDashboardsQuery.refetch(), sqlDashboardsQuery.refetch(), extensionsQuery.refetch(), ]); }; useEffect(() => { const handler = (e: KeyboardEvent) => { if ((e.metaKey || e.ctrlKey) && e.key === "k") { e.preventDefault(); setOpen((o) => !o); } }; const openHandler = () => setOpen(true); document.addEventListener("keydown", handler); window.addEventListener("analytics:open-command-palette", openHandler); window.addEventListener("agent-native:open-command-menu", openHandler); return () => { document.removeEventListener("keydown", handler); window.removeEventListener("analytics:open-command-palette", openHandler); window.removeEventListener("agent-native:open-command-menu", openHandler); }; }, []); useEffect(() => { const frame = requestAnimationFrame(() => { if (commandListRef.current) commandListRef.current.scrollTop = 0; }); return () => cancelAnimationFrame(frame); }, [searchQuery]); const go = useCallback( (href: string) => { navigate(href); setOpen(false); }, [navigate], ); const asyncGroupsLoading = (explorerDashboardsLoading && explorerDashboards.length === 0) || (sqlDashboardsLoading && sqlDashboards.length === 0) || (extensionsLoading && extensions.length === 0) || (savedChartsLoading && savedCharts.length === 0); const showHiddenResults = searchQuery.trim().length > 0; const visibleExplorerDashboards = showHiddenResults ? explorerDashboards : explorerDashboards.filter((dashboard) => !dashboard.hiddenAt); const visibleSqlDashboards = showHiddenResults ? sqlDashboards : sqlDashboards.filter((dashboard) => !dashboard.hiddenAt); return ( <> { setOpen(next); if (!next) { setSearchQuery(""); setSelectedCommand(""); } }} > { setSelectedCommand(""); setSearchQuery(nextQuery); }} /> {asyncGroupsErrored && ( {t("sidebar.retry")} )} {visibleExplorerDashboards.length > 0 && ( {visibleExplorerDashboards.map((d) => ( go(`/dashboards/explorer-dashboard?id=${d.id}`) } keywords={commandPaletteKeywords( d.name, "explorer dashboard", "dashboard", )} > {d.name} {d.hiddenAt ? ( {t("commandPalette.hidden")} ) : null} ))} )} {visibleSqlDashboards.length > 0 && ( {visibleSqlDashboards.map((d) => ( go(`/dashboards/${d.id}`)} keywords={commandPaletteKeywords( d.name, "sql dashboard", "dashboard", )} > {d.name} {d.hiddenAt ? ( {t("commandPalette.hidden")} ) : null} ))} )} {extensions.length > 0 && ( {extensions.map((extension) => ( go(extensionPath(extension.id, extension.name)) } keywords={commandPaletteKeywords( extension.name, extension.description, "extension", "tool", )} > {extension.name} ))} )} {dashboards.map((d) => ( go(`/dashboards/${d.id}`)} keywords={commandPaletteKeywords(d.name, "dashboard")} > {d.name} ))} {defaultTools .filter((tool) => ["agents", "feature-flags"].includes(tool.id) ? canManageOrg : true, ) .map((tool) => ( go(tool.href)} keywords={commandPaletteKeywords( t(tool.nameKey), "tool", ...tool.keywords, )} > {t(tool.nameKey)} ))} {showHiddenResults && ( go("/agent")} keywords={commandPaletteKeywords( t("settings.agentTitle"), "agent", "context", "files", "connections", "jobs", "access", )} > {t("settings.agentTitle")} {settingsCommands.map((setting) => ( go(setting.href)} keywords={commandPaletteKeywords( setting.label, setting.keywords, "settings", )} > {setting.label} ))} )} { setOpen(false); setLanguageOpen(true); }} keywords={commandPaletteKeywords( t("settings.languageTitle"), t("settings.languageLabel"), "language", "locale", "translation", "internationalization", "i18n", )} > {t("settings.languageTitle")} { const nextTheme = isDark ? "light" : "dark"; setTheme(nextTheme); persistThemePreference(nextTheme); }} keywords={commandPaletteKeywords( isDark ? t("commandPalette.toggleLightMode") : t("commandPalette.toggleDarkMode"), "theme", "dark", "light", "mode", )} > {isDark ? ( ) : ( )} {isDark ? t("commandPalette.toggleLightMode") : t("commandPalette.toggleDarkMode")} { setOpen(false); setChangelogOpen(true); }} keywords={commandPaletteKeywords( t("commandPalette.whatsNew"), "changelog", "updates", "release notes", "changes", )} > {t("commandPalette.whatsNew")} {savedCharts.length > 0 && ( {savedCharts.map((c) => ( go(`/dashboards/explorer?config=${c.id}`)} keywords={commandPaletteKeywords( c.name, "saved chart", "chart", )} > {c.name} ))} )} {explorerDashboardsLoading && explorerDashboards.length === 0 && ( )} {sqlDashboardsLoading && sqlDashboards.length === 0 && ( )} {extensionsLoading && extensions.length === 0 && ( )} {savedChartsLoading && savedCharts.length === 0 && ( )} {t("settings.languageTitle")}
); }