Files
kandev/apps/web/components/global-commands.tsx
T
2026-06-18 18:36:48 +01:00

224 lines
7.0 KiB
TypeScript

"use client";
import { useMemo, useCallback } from "react";
import { useRouter } from "@/lib/routing/client-router";
import { useTheme } from "@/components/theme/app-theme";
import {
IconHome,
IconList,
IconSettings,
IconChartBar,
IconSun,
IconMoon,
IconRobot,
IconCpu,
IconServer,
IconFolder,
IconMessageCircle,
IconSparkles,
IconBrandGithub,
} from "@tabler/icons-react";
import { useRegisterCommands } from "@/hooks/use-register-commands";
import { useKeyboardShortcut } from "@/hooks/use-keyboard-shortcut";
import { useAppShortcuts } from "@/hooks/use-app-shortcuts";
import { useAppStore } from "@/components/state-provider";
import { getShortcut } from "@/lib/keyboard/shortcut-overrides";
import type { CommandItem } from "@/lib/commands/types";
type PushFn = ReturnType<typeof useRouter>["push"];
function buildNavigationCommands(push: PushFn): CommandItem[] {
return [
{
id: "nav-home",
label: "Go to Home",
group: "Navigation",
icon: <IconHome className="size-3.5" />,
keywords: ["home", "kanban", "board"],
action: () => push("/"),
},
{
id: "nav-tasks",
label: "Go to All Tasks",
group: "Navigation",
icon: <IconList className="size-3.5" />,
keywords: ["tasks", "list", "all"],
action: () => push("/tasks"),
},
{
id: "nav-settings",
label: "Go to Settings",
group: "Navigation",
icon: <IconSettings className="size-3.5" />,
keywords: ["settings", "preferences", "config"],
action: () => push("/settings/general"),
},
{
id: "nav-stats",
label: "Go to Stats",
group: "Navigation",
icon: <IconChartBar className="size-3.5" />,
keywords: ["stats", "analytics", "metrics"],
action: () => push("/stats"),
},
{
id: "nav-github",
label: "Go to GitHub Dashboard",
group: "Navigation",
icon: <IconBrandGithub className="size-3.5" />,
keywords: ["github", "dashboard", "pr", "pull request", "review"],
action: () => push("/github"),
},
{
id: "settings-agents",
label: "Agents Settings",
group: "Settings",
icon: <IconRobot className="size-3.5" />,
keywords: ["agents", "ai", "claude"],
action: () => push("/settings/agents"),
},
{
id: "settings-executors",
label: "Executors Settings",
group: "Settings",
icon: <IconCpu className="size-3.5" />,
keywords: ["executors", "compute", "run"],
action: () => push("/settings/executors"),
},
{
id: "settings-environments",
label: "Environments Settings",
group: "Settings",
icon: <IconServer className="size-3.5" />,
keywords: ["environments", "env", "variables"],
action: () => push("/settings/environments"),
},
{
id: "settings-workspace",
label: "Workspace Settings",
group: "Settings",
icon: <IconFolder className="size-3.5" />,
keywords: ["workspace", "project"],
action: () => push("/settings/workspace"),
},
{
id: "settings-prompts",
label: "Prompts Settings",
group: "Settings",
icon: <IconMessageCircle className="size-3.5" />,
keywords: ["prompts", "templates", "message"],
action: () => push("/settings/prompts"),
},
];
}
function buildThemeCommand(
resolvedTheme: string | undefined,
setTheme: (theme: string) => void,
): CommandItem {
const isDark = resolvedTheme === "dark";
return {
id: "pref-theme",
label: isDark ? "Switch to Light Mode" : "Switch to Dark Mode",
group: "Preferences",
icon: isDark ? <IconSun className="size-3.5" /> : <IconMoon className="size-3.5" />,
keywords: ["theme", "dark", "light", "mode"],
action: () => setTheme(isDark ? "light" : "dark"),
};
}
export function GlobalCommands() {
const router = useRouter();
const { resolvedTheme, setTheme } = useTheme();
const activeWorkspaceId = useAppStore((s) => s.workspaces.activeId);
const quickChatSessions = useAppStore((s) => s.quickChat.sessions);
const openQuickChat = useAppStore((s) => s.openQuickChat);
const startNewConfigChat = useAppStore((s) => s.startNewConfigChat);
const openConfigChat = useAppStore((s) => s.openConfigChat);
const configChatSessions = useAppStore((s) => s.configChat.sessions);
const configChatActiveSessionId = useAppStore((s) => s.configChat.activeSessionId);
const configChatWorkspaceId = useAppStore((s) => s.configChat.workspaceId);
const handleOpenQuickChat = useCallback(() => {
if (!activeWorkspaceId) {
return;
}
// If there's an existing session for this workspace, open it
const existingSession = quickChatSessions.find((s) => s.workspaceId === activeWorkspaceId);
if (existingSession) {
openQuickChat(existingSession.sessionId, activeWorkspaceId);
} else {
// Open modal without a session - will show agent picker
openQuickChat("", activeWorkspaceId);
}
}, [activeWorkspaceId, quickChatSessions, openQuickChat]);
const handleOpenConfigChat = useCallback(() => {
if (!activeWorkspaceId) return;
// Reuse existing session if one is active for this workspace
if (configChatActiveSessionId && configChatWorkspaceId === activeWorkspaceId) {
openConfigChat(configChatActiveSessionId, activeWorkspaceId);
return;
}
// Check for a persisted session for this workspace
const existingSession = configChatSessions.find((s) => s.workspaceId === activeWorkspaceId);
if (existingSession) {
openConfigChat(existingSession.sessionId, activeWorkspaceId);
return;
}
startNewConfigChat(activeWorkspaceId);
}, [
activeWorkspaceId,
configChatSessions,
configChatActiveSessionId,
configChatWorkspaceId,
openConfigChat,
startNewConfigChat,
]);
const keyboardShortcuts = useAppStore((s) => s.userSettings.keyboardShortcuts);
const quickChatShortcut = getShortcut("QUICK_CHAT", keyboardShortcuts);
const quickChatCommand: CommandItem = useMemo(
() => ({
id: "quick-chat",
label: "Quick Chat",
group: "Actions",
icon: <IconMessageCircle className="size-3.5" />,
keywords: ["quick", "chat", "ai", "agent"],
shortcut: quickChatShortcut,
action: handleOpenQuickChat,
}),
[handleOpenQuickChat, quickChatShortcut],
);
const configChatCommand: CommandItem = useMemo(
() => ({
id: "config-chat",
label: "Configuration Chat",
group: "Actions",
icon: <IconSparkles className="size-3.5" />,
keywords: ["config", "configure", "settings", "chat", "ai"],
action: handleOpenConfigChat,
}),
[handleOpenConfigChat],
);
const commands = useMemo<CommandItem[]>(
() => [
...buildNavigationCommands(router.push),
buildThemeCommand(resolvedTheme, setTheme),
quickChatCommand,
configChatCommand,
],
[router.push, resolvedTheme, setTheme, quickChatCommand, configChatCommand],
);
useRegisterCommands(commands);
useKeyboardShortcut(quickChatShortcut, handleOpenQuickChat);
useAppShortcuts();
return null;
}