"use client"; import { useEffect, useRef, useState } from "react"; import { useTheme } from "next-themes"; import { Bot, ClipboardList, Crop, FolderOpen, Frame, GitBranch, GitCompare, Image, ImageOff, Layers, LayoutPanelTop, MessageSquare, Moon, Package, Palette, Presentation, Repeat, Sparkles, Split, StickyNote, Sun, Type, Video, Wand2, type LucideIcon, } from "lucide-react"; import { useCanvasPlacement } from "@/components/canvas/canvas-placement-context"; import { useCenteredFlowNodePosition } from "@/hooks/use-centered-flow-node-position"; import { Command, CommandDialog, CommandEmpty, CommandGroup, CommandInput, CommandItem, CommandList, CommandSeparator, } from "@/components/ui/command"; import type { CanvasNodeTemplate } from "@/lib/canvas-node-templates"; import { NODE_CATEGORY_META, NODE_CATEGORIES_ORDERED, catalogEntriesByCategory, getTemplateForCatalogType, isNodePaletteEnabled, } from "@/lib/canvas-node-catalog"; const CATALOG_ICONS: Partial> = { image: Image, text: Type, prompt: Sparkles, "video-prompt": Video, color: Palette, video: Video, asset: Package, "ai-image": Sparkles, "ai-text": Type, "ai-video": Video, "agent-output": Bot, crop: Crop, "bg-remove": ImageOff, upscale: Wand2, "style-transfer": Wand2, "face-restore": Sparkles, curves: Sparkles, "color-adjust": Palette, "light-adjust": Sparkles, "detail-adjust": Wand2, render: Image, splitter: Split, loop: Repeat, agent: Bot, mixer: Layers, switch: GitBranch, group: FolderOpen, frame: Frame, note: StickyNote, "text-overlay": LayoutPanelTop, compare: GitCompare, comment: MessageSquare, presentation: Presentation, }; export function CanvasCommandPalette() { const [open, setOpen] = useState(false); const { createNodeWithIntersection } = useCanvasPlacement(); const getCenteredPosition = useCenteredFlowNodePosition(); const { setTheme } = useTheme(); const nodeCountRef = useRef(0); const byCategory = catalogEntriesByCategory(); useEffect(() => { const onKeyDown = (e: KeyboardEvent) => { if (!e.metaKey && !e.ctrlKey) return; if (e.key.toLowerCase() !== "k") return; e.preventDefault(); setOpen((prev) => !prev); }; document.addEventListener("keydown", onKeyDown); return () => document.removeEventListener("keydown", onKeyDown); }, []); const handleAddNode = (template: CanvasNodeTemplate) => { const stagger = (nodeCountRef.current % 8) * 24; nodeCountRef.current += 1; setOpen(false); void createNodeWithIntersection({ type: template.type, position: getCenteredPosition(template.width, template.height, stagger), width: template.width, height: template.height, data: template.defaultData, clientRequestId: crypto.randomUUID(), }).catch((error) => { console.error("[CanvasCommandPalette] createNode failed", error); }); }; return ( Keine Treffer. {NODE_CATEGORIES_ORDERED.map((categoryId) => { const entries = byCategory.get(categoryId) ?? []; if (entries.length === 0) return null; return ( {entries.map((entry) => { const template = getTemplateForCatalogType(entry.type); const enabled = isNodePaletteEnabled(entry) && Boolean(template); const Icon = CATALOG_ICONS[entry.type] ?? ClipboardList; return ( { if (!template) return; handleAddNode(template); }} > {entry.label} ); })} ); })} { setTheme("light"); setOpen(false); }} > Hell { setTheme("dark"); setOpen(false); }} > Dunkel
⌘K · Ctrl+K Palette umschalten
); }