import { CommandDialog, CommandEmpty, CommandGroup, CommandInput, CommandItem, CommandList, } from "@components/UI/Command.tsx"; import { useAppStore } from "@core/stores/appStore.ts"; import { useDevice, useDeviceStore } from "@core/stores/deviceStore.ts"; import { useCommandState } from "cmdk"; import { ArrowLeftRightIcon, BoxSelectIcon, BugIcon, EraserIcon, FactoryIcon, LayersIcon, LinkIcon, MapIcon, MessageSquareIcon, PlusIcon, PowerIcon, QrCodeIcon, RefreshCwIcon, SettingsIcon, SmartphoneIcon, TrashIcon, UsersIcon, Pin, type LucideIcon, } from "lucide-react"; import { useEffect } from "react"; import { Avatar } from "@components/UI/Avatar.tsx"; import { cn } from "@core/utils/cn.ts"; import { usePinnedItems } from "@core/hooks/usePinnedItems.ts"; export interface Group { label: string; icon: LucideIcon; commands: Command[]; } export interface Command { label: string; icon: LucideIcon; action?: () => void; subItems?: SubItem[]; tags?: string[]; } export interface SubItem { label: string; icon: React.ReactNode; action: () => void; } export const CommandPalette = () => { const { commandPaletteOpen, setCommandPaletteOpen, setSelectedDevice, } = useAppStore(); const { getDevices } = useDeviceStore(); const { setDialogOpen, setActivePage, connection } = useDevice(); const { pinnedItems, togglePinnedItem } = usePinnedItems({ storageName: 'pinnedCommandMenuGroups' }); const groups: Group[] = [ { label: "Goto", icon: LinkIcon, commands: [ { label: "Messages", icon: MessageSquareIcon, action() { setActivePage("messages"); }, }, { label: "Map", icon: MapIcon, action() { setActivePage("map"); }, }, { label: "Config", icon: SettingsIcon, action() { setActivePage("config"); }, tags: ["settings"], }, { label: "Channels", icon: LayersIcon, action() { setActivePage("channels"); }, }, { label: "Nodes", icon: UsersIcon, action() { setActivePage("nodes"); }, }, ], }, { label: "Manage", icon: SmartphoneIcon, commands: [ { label: "Switch Node", icon: ArrowLeftRightIcon, subItems: getDevices().map((device) => ({ label: device.nodes.get(device.hardware.myNodeNum)?.user?.longName ?? device.hardware.myNodeNum.toString(), icon: ( ), action() { setSelectedDevice(device.id); }, })), }, { label: "Connect New Node", icon: PlusIcon, action() { setSelectedDevice(0); }, }, ], }, { label: "Contextual", icon: BoxSelectIcon, commands: [ { label: "QR Code", icon: QrCodeIcon, subItems: [ { label: "Generator", icon: , action() { setDialogOpen("QR", true); }, }, { label: "Import", icon: , action() { setDialogOpen("import", true); }, }, ], }, { label: "Schedule Shutdown", icon: PowerIcon, action() { setDialogOpen("shutdown", true); }, }, { label: "Schedule Reboot", icon: RefreshCwIcon, action() { setDialogOpen("reboot", true); }, }, { label: "Reboot To OTA Mode", icon: RefreshCwIcon, action() { setDialogOpen("rebootOTA", true); }, }, { label: "Reset Nodes", icon: TrashIcon, action() { connection?.resetNodes(); }, }, { label: "Factory Reset Device", icon: FactoryIcon, action() { connection?.factoryResetDevice(); }, }, { label: "Factory Reset Config", icon: FactoryIcon, action() { connection?.factoryResetConfig(); }, }, ], }, { label: "Debug", icon: BugIcon, commands: [ { label: "Reconfigure", icon: RefreshCwIcon, action() { void connection?.configure(); }, }, { label: "Clear All Stored Message", icon: EraserIcon, action() { setDialogOpen("clearMessages", true); }, }, ], }, ]; const sortedGroups = [...groups].sort((a, b) => { const aPinned = pinnedItems.includes(a.label) ? 1 : 0; const bPinned = pinnedItems.includes(b.label) ? 1 : 0; return bPinned - aPinned; }); useEffect(() => { const handleKeydown = (e: KeyboardEvent) => { if (e.key === "k" && (e.metaKey || e.ctrlKey)) { e.preventDefault(); setCommandPaletteOpen(true); } }; globalThis.addEventListener("keydown", handleKeydown); return () => globalThis.removeEventListener("keydown", handleKeydown); }, [setCommandPaletteOpen]); return ( No results found. {sortedGroups.map((group) => ( {group.label} } > {group.commands.map((command) => (
{ command.action?.(); setCommandPaletteOpen(false); }} > {command.label} {command.subItems?.map((subItem) => ( ))}
))}
))}
); }; const SubItem = ({ label, icon, action, }: { label: string; icon: React.ReactNode; action: () => void; }) => { const search = useCommandState((state) => state.search); if (!search) return null; return ( {icon} {label} ); };