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:
@@ -1,66 +1,78 @@
|
||||
"use client";
|
||||
|
||||
import { Handle, Position, type Node, type NodeProps } from "@xyflow/react";
|
||||
|
||||
import { Handle, Position, type NodeProps, type Node } from "@xyflow/react";
|
||||
import BaseNodeWrapper from "./base-node-wrapper";
|
||||
|
||||
export type AiImageNodeData = {
|
||||
type AiImageNodeData = {
|
||||
url?: string;
|
||||
prompt?: string;
|
||||
model?: string;
|
||||
status?: "idle" | "executing" | "done" | "error";
|
||||
errorMessage?: string;
|
||||
_status?: string;
|
||||
_statusMessage?: string;
|
||||
};
|
||||
|
||||
export type AiImageNode = Node<AiImageNodeData, "ai-image">;
|
||||
|
||||
export default function AiImageNode({ data, selected }: NodeProps<AiImageNode>) {
|
||||
const status = data.status ?? "idle";
|
||||
export default function AiImageNode({
|
||||
data,
|
||||
selected,
|
||||
}: NodeProps<AiImageNode>) {
|
||||
const status = data._status ?? "idle";
|
||||
|
||||
return (
|
||||
<BaseNodeWrapper selected={selected} status={status} className="p-2">
|
||||
<div className="mb-1 text-xs font-medium text-emerald-500">KI-Bild</div>
|
||||
|
||||
{status === "executing" ? (
|
||||
<div className="flex h-36 w-56 items-center justify-center rounded-lg bg-muted">
|
||||
<div className="h-6 w-6 animate-spin rounded-full border-2 border-primary border-t-transparent" />
|
||||
<BaseNodeWrapper
|
||||
selected={selected}
|
||||
status={status}
|
||||
statusMessage={data._statusMessage}
|
||||
>
|
||||
<div className="p-2">
|
||||
<div className="text-xs font-medium text-emerald-500 mb-1">
|
||||
🤖 KI-Bild
|
||||
</div>
|
||||
) : null}
|
||||
|
||||
{status === "done" && data.url ? (
|
||||
<img
|
||||
src={data.url}
|
||||
alt={data.prompt ?? "KI-generiertes Bild"}
|
||||
className="max-w-[280px] rounded-lg object-cover"
|
||||
draggable={false}
|
||||
/>
|
||||
) : null}
|
||||
{status === "executing" && (
|
||||
<div className="flex h-36 w-56 items-center justify-center rounded-lg bg-muted">
|
||||
<div className="h-6 w-6 animate-spin rounded-full border-2 border-primary border-t-transparent" />
|
||||
</div>
|
||||
)}
|
||||
|
||||
{status === "error" ? (
|
||||
<div className="flex h-36 w-56 items-center justify-center rounded-lg bg-red-50 text-sm text-red-600 dark:bg-red-950/20">
|
||||
{data.errorMessage ?? "Fehler bei der Generierung"}
|
||||
</div>
|
||||
) : null}
|
||||
{status === "done" && data.url && (
|
||||
<img
|
||||
src={data.url}
|
||||
alt={data.prompt ?? "KI-generiertes Bild"}
|
||||
className="rounded-lg object-cover max-w-[260px]"
|
||||
draggable={false}
|
||||
/>
|
||||
)}
|
||||
|
||||
{status === "idle" ? (
|
||||
<div className="flex h-36 w-56 items-center justify-center rounded-lg border-2 border-dashed text-sm text-muted-foreground">
|
||||
Prompt verbinden
|
||||
</div>
|
||||
) : null}
|
||||
{status === "error" && (
|
||||
<div className="flex h-36 w-56 items-center justify-center rounded-lg bg-red-50 dark:bg-red-950/20 text-sm text-red-600">
|
||||
{data._statusMessage ?? "Fehler bei der Generierung"}
|
||||
</div>
|
||||
)}
|
||||
|
||||
{data.prompt && status === "done" ? (
|
||||
<p className="mt-1 max-w-[280px] truncate text-xs text-muted-foreground">{data.prompt}</p>
|
||||
) : null}
|
||||
{status === "idle" && (
|
||||
<div className="flex h-36 w-56 items-center justify-center rounded-lg border-2 border-dashed text-sm text-muted-foreground">
|
||||
Prompt verbinden
|
||||
</div>
|
||||
)}
|
||||
|
||||
{data.prompt && status === "done" && (
|
||||
<p className="mt-1 text-xs text-muted-foreground truncate max-w-[260px]">
|
||||
{data.prompt}
|
||||
</p>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<Handle
|
||||
type="target"
|
||||
position={Position.Left}
|
||||
className="!h-3 !w-3 !border-2 !border-background !bg-emerald-500"
|
||||
className="!h-3 !w-3 !bg-emerald-500 !border-2 !border-background"
|
||||
/>
|
||||
<Handle
|
||||
type="source"
|
||||
position={Position.Right}
|
||||
className="!h-3 !w-3 !border-2 !border-background !bg-primary"
|
||||
className="!h-3 !w-3 !bg-primary !border-2 !border-background"
|
||||
/>
|
||||
</BaseNodeWrapper>
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user