Files
moteva/frontend/src/ui/pages/CanvasWorkspace/canvas/nodeReferenceImages.ts
T

53 lines
1.9 KiB
TypeScript
Raw Normal View History

import type { CanvasNode } from "@/domain/design";
import type { UploadedReferenceImage } from "@/ui/components/PromptComposer";
const canvasNodeReferencePrefix = "canvas-node:";
export function canvasNodeToReferenceImage(node: CanvasNode): UploadedReferenceImage | null {
const content = node.content.trim();
if (node.type !== "image" || node.layerRole === "pen-stroke" || node.layerRole === "image-generator" || node.status === "generating" || node.status === "error" || !isImageReferenceUrl(content)) {
return null;
}
return {
id: `${canvasNodeReferencePrefix}${node.id}`,
name: node.title.trim() || "Image",
publicUrl: content,
previewUrl: content,
semanticContext: node.semanticContext
};
}
export function isCanvasNodeReferenceImage(reference: UploadedReferenceImage) {
return reference.id.startsWith(canvasNodeReferencePrefix);
}
export function isAssetReferencedByCanvas(publicUrl: string, nodes: CanvasNode[]) {
const targetKeys = assetReferenceKeys(publicUrl);
if (targetKeys.size === 0) return false;
return nodes.some((node) => {
const contentKeys = assetReferenceKeys(node.content);
return Array.from(contentKeys).some((key) => targetKeys.has(key));
});
}
function isImageReferenceUrl(value: string) {
return /^(https?:\/\/|data:image\/|blob:|\/)/.test(value);
}
function assetReferenceKeys(value: string) {
const trimmed = value.trim();
const keys = new Set<string>();
if (!trimmed || trimmed.startsWith("data:image/") || trimmed.startsWith("blob:")) return keys;
keys.add(trimmed);
const queryIndex = trimmed.indexOf("?");
if (queryIndex > 0) keys.add(trimmed.slice(0, queryIndex));
try {
const url = new URL(trimmed, typeof window === "undefined" ? "http://localhost" : window.location.origin);
keys.add(url.pathname);
keys.add(`${url.pathname}${url.search}`);
} catch {
// Keep exact/path keys above for non-URL storage identifiers.
}
return keys;
}