feat: add cmdk dependency and enhance canvas node creation with edge splitting functionality
- Introduced the cmdk package for improved command palette capabilities. - Enhanced the canvas placement context to support creating nodes with edge splitting, allowing for more dynamic node interactions. - Updated the canvas inner component to utilize optimistic updates for node creation, improving user experience during interactions. - Refactored node handling logic to incorporate new mutation types and streamline data management.
This commit is contained in:
152
components/canvas/canvas-command-palette.tsx
Normal file
152
components/canvas/canvas-command-palette.tsx
Normal file
@@ -0,0 +1,152 @@
|
||||
"use client";
|
||||
|
||||
import { useEffect, useRef, useState } from "react";
|
||||
import { useTheme } from "next-themes";
|
||||
import {
|
||||
Frame,
|
||||
GitCompare,
|
||||
Image,
|
||||
Moon,
|
||||
Sparkles,
|
||||
StickyNote,
|
||||
Sun,
|
||||
Type,
|
||||
type LucideIcon,
|
||||
} from "lucide-react";
|
||||
|
||||
import { useCanvasPlacement } from "@/components/canvas/canvas-placement-context";
|
||||
import {
|
||||
Command,
|
||||
CommandDialog,
|
||||
CommandEmpty,
|
||||
CommandGroup,
|
||||
CommandInput,
|
||||
CommandItem,
|
||||
CommandList,
|
||||
CommandSeparator,
|
||||
} from "@/components/ui/command";
|
||||
import {
|
||||
CANVAS_NODE_TEMPLATES,
|
||||
type CanvasNodeTemplate,
|
||||
} from "@/lib/canvas-node-templates";
|
||||
|
||||
const NODE_ICONS: Record<CanvasNodeTemplate["type"], LucideIcon> = {
|
||||
image: Image,
|
||||
text: Type,
|
||||
prompt: Sparkles,
|
||||
note: StickyNote,
|
||||
frame: Frame,
|
||||
compare: GitCompare,
|
||||
};
|
||||
|
||||
const NODE_SEARCH_KEYWORDS: Partial<
|
||||
Record<CanvasNodeTemplate["type"], string[]>
|
||||
> = {
|
||||
image: ["image", "photo", "foto"],
|
||||
text: ["text", "typo"],
|
||||
prompt: ["prompt", "ai", "generate"],
|
||||
note: ["note", "sticky", "notiz"],
|
||||
frame: ["frame", "artboard"],
|
||||
compare: ["compare", "before", "after"],
|
||||
};
|
||||
|
||||
export function CanvasCommandPalette() {
|
||||
const [open, setOpen] = useState(false);
|
||||
const { createNodeWithIntersection } = useCanvasPlacement();
|
||||
const { setTheme } = useTheme();
|
||||
const nodeCountRef = useRef(0);
|
||||
|
||||
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 = async (
|
||||
type: CanvasNodeTemplate["type"],
|
||||
data: CanvasNodeTemplate["defaultData"],
|
||||
width: number,
|
||||
height: number,
|
||||
) => {
|
||||
const offset = (nodeCountRef.current % 8) * 24;
|
||||
nodeCountRef.current += 1;
|
||||
await createNodeWithIntersection({
|
||||
type,
|
||||
position: { x: 100 + offset, y: 100 + offset },
|
||||
width,
|
||||
height,
|
||||
data,
|
||||
});
|
||||
setOpen(false);
|
||||
};
|
||||
|
||||
return (
|
||||
<CommandDialog
|
||||
open={open}
|
||||
onOpenChange={setOpen}
|
||||
title="Befehle"
|
||||
description="Knoten hinzufuegen oder Erscheinungsbild aendern"
|
||||
>
|
||||
<Command>
|
||||
<CommandInput placeholder="Suchen …" />
|
||||
<CommandList>
|
||||
<CommandEmpty>Keine Treffer.</CommandEmpty>
|
||||
<CommandGroup heading="Knoten">
|
||||
{CANVAS_NODE_TEMPLATES.map((template) => {
|
||||
const Icon = NODE_ICONS[template.type];
|
||||
return (
|
||||
<CommandItem
|
||||
key={template.type}
|
||||
keywords={NODE_SEARCH_KEYWORDS[template.type] ?? []}
|
||||
onSelect={() =>
|
||||
void handleAddNode(
|
||||
template.type,
|
||||
template.defaultData,
|
||||
template.width,
|
||||
template.height,
|
||||
)
|
||||
}
|
||||
>
|
||||
<Icon className="size-4" />
|
||||
{template.label}
|
||||
</CommandItem>
|
||||
);
|
||||
})}
|
||||
</CommandGroup>
|
||||
<CommandSeparator />
|
||||
<CommandGroup heading="Erscheinungsbild">
|
||||
<CommandItem
|
||||
keywords={["light", "hell", "day"]}
|
||||
onSelect={() => {
|
||||
setTheme("light");
|
||||
setOpen(false);
|
||||
}}
|
||||
>
|
||||
<Sun className="size-4" />
|
||||
Hell
|
||||
</CommandItem>
|
||||
<CommandItem
|
||||
keywords={["dark", "dunkel", "night"]}
|
||||
onSelect={() => {
|
||||
setTheme("dark");
|
||||
setOpen(false);
|
||||
}}
|
||||
>
|
||||
<Moon className="size-4" />
|
||||
Dunkel
|
||||
</CommandItem>
|
||||
</CommandGroup>
|
||||
</CommandList>
|
||||
<div className="border-t px-3 py-2 text-xs text-muted-foreground">
|
||||
<span className="font-mono tracking-wide">⌘K · Ctrl+K</span>
|
||||
<span className="ml-2">Palette umschalten</span>
|
||||
</div>
|
||||
</Command>
|
||||
</CommandDialog>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user