Enhance canvas connection validation and image resolution handling

- Introduced new functions for validating canvas connections, ensuring proper source and target node types.
- Updated edge and node mutation logic to enforce connection policies and improve error handling.
- Enhanced image resolution handling by integrating a new image source resolution function for better URL retrieval.
- Refactored existing validation logic to streamline connection checks and improve maintainability.
This commit is contained in:
Matthias
2026-04-02 22:27:05 +02:00
parent 3fa686d60d
commit 519caefae2
7 changed files with 356 additions and 82 deletions

View File

@@ -0,0 +1,87 @@
import { isAdjustmentNodeType } from "@/lib/canvas-node-types";
export const CANVAS_NODE_DND_MIME = "application/lemonspace-node-type";
const ADJUSTMENT_ALLOWED_SOURCE_TYPES = new Set<string>([
"image",
"asset",
"ai-image",
"curves",
"color-adjust",
"light-adjust",
"detail-adjust",
]);
const RENDER_ALLOWED_SOURCE_TYPES = new Set<string>([
"image",
"asset",
"ai-image",
"curves",
"color-adjust",
"light-adjust",
"detail-adjust",
]);
const ADJUSTMENT_DISALLOWED_TARGET_TYPES = new Set<string>(["prompt", "ai-image"]);
export type CanvasConnectionValidationReason =
| "incomplete"
| "self-loop"
| "unknown-node"
| "adjustment-source-invalid"
| "adjustment-incoming-limit"
| "adjustment-target-forbidden"
| "render-source-invalid";
export function validateCanvasConnectionPolicy(args: {
sourceType: string;
targetType: string;
targetIncomingCount: number;
}): CanvasConnectionValidationReason | null {
const { sourceType, targetType, targetIncomingCount } = args;
if (isAdjustmentNodeType(targetType)) {
if (!ADJUSTMENT_ALLOWED_SOURCE_TYPES.has(sourceType)) {
return "adjustment-source-invalid";
}
if (targetIncomingCount >= 1) {
return "adjustment-incoming-limit";
}
}
if (targetType === "render" && !RENDER_ALLOWED_SOURCE_TYPES.has(sourceType)) {
return "render-source-invalid";
}
if (
isAdjustmentNodeType(sourceType) &&
ADJUSTMENT_DISALLOWED_TARGET_TYPES.has(targetType)
) {
return "adjustment-target-forbidden";
}
return null;
}
export function getCanvasConnectionValidationMessage(
reason: CanvasConnectionValidationReason,
): string {
switch (reason) {
case "incomplete":
return "Unvollstaendige Verbindung.";
case "self-loop":
return "Node kann nicht mit sich selbst verbunden werden.";
case "unknown-node":
return "Verbindung enthaelt unbekannte Nodes.";
case "adjustment-source-invalid":
return "Adjustment-Nodes akzeptieren nur Bild-, Asset-, KI-Bild- oder Adjustment-Input.";
case "adjustment-incoming-limit":
return "Adjustment-Nodes erlauben genau eine eingehende Verbindung.";
case "adjustment-target-forbidden":
return "Adjustment-Ausgaben koennen nicht an Prompt- oder KI-Bild-Nodes angeschlossen werden.";
case "render-source-invalid":
return "Render akzeptiert nur Bild-, Asset-, KI-Bild- oder Adjustment-Input.";
default:
return "Verbindung ist fuer diese Node-Typen nicht erlaubt.";
}
}

View File

@@ -3,6 +3,10 @@
import { useTranslations } from 'next-intl';
import { toast, type ToastDurationOverrides } from './toast';
import type { CanvasNodeDeleteBlockReason } from './toast';
import {
getCanvasConnectionValidationMessage,
type CanvasConnectionValidationReason,
} from '@/lib/canvas-connection-policy';
const DURATION = {
success: 4000,
@@ -77,6 +81,13 @@ export const msg = {
desc: `${why.desc} ${suffix}`,
};
},
connectionRejected: (
_t: ToastTranslations,
reason: CanvasConnectionValidationReason,
) => ({
title: 'Verbindung abgelehnt',
desc: getCanvasConnectionValidationMessage(reason),
}),
},
ai: {
generating: (t: ToastTranslations) => ({ title: t('ai.generating') }),
@@ -205,3 +216,12 @@ export const msg = {
deleteFailed: (t: ToastTranslations) => ({ title: t('dashboard.deleteFailed') }),
},
} as const;
export function showCanvasConnectionRejectedToast(
t: ToastTranslations,
reason: CanvasConnectionValidationReason,
duration?: ToastDurationOverrides,
): void {
const payload = msg.canvas.connectionRejected(t, reason);
toast.warning(payload.title, payload.desc, duration ?? { duration: DURATION.warning });
}