feat: enhance canvas and layout components with new features and improvements

- Added remote image patterns to the Next.js configuration for enhanced image handling.
- Updated TypeScript configuration to exclude the 'implement' directory.
- Refactored layout component to fetch initial authentication token and pass it to Providers.
- Replaced CanvasToolbar with CanvasSidebar for improved UI layout and functionality.
- Enhanced Canvas component with new drag-and-drop file upload capabilities and batch node movement.
- Updated various node components to support new status handling and improved user interactions.
- Added debounced saving for note and prompt nodes to optimize performance.
This commit is contained in:
Matthias
2026-03-25 17:58:58 +01:00
parent d1834c5694
commit ca40f5cb13
27 changed files with 1363 additions and 207 deletions

View File

@@ -1,15 +1,68 @@
"use client";
import { type Node, type NodeProps } from "@xyflow/react";
import { useState, useCallback, useEffect } from "react";
import { type NodeProps, type Node } from "@xyflow/react";
import { useMutation } from "convex/react";
import { api } from "@/convex/_generated/api";
import type { Id } from "@/convex/_generated/dataModel";
import BaseNodeWrapper from "./base-node-wrapper";
export type GroupNode = Node<{ label?: string }, "group">;
type GroupNodeData = {
label?: string;
_status?: string;
_statusMessage?: string;
};
export type GroupNode = Node<GroupNodeData, "group">;
export default function GroupNode({ id, data, selected }: NodeProps<GroupNode>) {
const updateData = useMutation(api.nodes.updateData);
const [label, setLabel] = useState(data.label ?? "Gruppe");
const [isEditing, setIsEditing] = useState(false);
useEffect(() => {
if (!isEditing) {
setLabel(data.label ?? "Gruppe");
}
}, [data.label, isEditing]);
const handleBlur = useCallback(() => {
setIsEditing(false);
if (label !== data.label) {
updateData({
nodeId: id as Id<"nodes">,
data: {
...data,
label,
_status: undefined,
_statusMessage: undefined,
},
});
}
}, [label, data, id, updateData]);
export default function GroupNode({ data, selected }: NodeProps<GroupNode>) {
return (
<BaseNodeWrapper selected={selected} className="min-h-[150px] min-w-[200px] border-dashed p-3">
<div className="text-xs font-medium text-muted-foreground">{data.label || "Gruppe"}</div>
<BaseNodeWrapper
selected={selected}
className="min-w-[200px] min-h-[150px] p-3 border-dashed"
>
{isEditing ? (
<input
value={label}
onChange={(e) => setLabel(e.target.value)}
onBlur={handleBlur}
onKeyDown={(e) => e.key === "Enter" && handleBlur()}
autoFocus
className="nodrag text-xs font-medium text-muted-foreground bg-transparent border-0 outline-none w-full"
/>
) : (
<div
onDoubleClick={() => setIsEditing(true)}
className="text-xs font-medium text-muted-foreground cursor-text"
>
📁 {label}
</div>
)}
</BaseNodeWrapper>
);
}