ai-chatbot/lib/canvas/layout-replace-source-selection.ts

75 lines
2.0 KiB
TypeScript

import { isCanvasImageReadyForAi } from "@/lib/canvas/local-upload-state";
import type { CanvasNode } from "@/types/canvas";
export type LayoutReplaceSource = {
id: string;
imageUrl: string;
kind: "canvas" | "upload";
nodeId?: string;
title: string;
};
function resolveCanvasImageUrl(node: CanvasNode) {
const data = (node.data || {}) as Record<string, any>;
return String(
data.src ||
data.previewImage ||
data.imageUrl ||
data.renderArtifacts?.screenshotUrl ||
""
).trim();
}
export function getLayoutReplaceSourceIdentity(source: LayoutReplaceSource) {
if (source.kind === "canvas" && source.nodeId) {
return `canvas:${source.nodeId}`;
}
return `${source.kind}:${source.imageUrl}`;
}
export function getReadyCanvasLayoutReplaceSource(
node: CanvasNode
): LayoutReplaceSource | null {
if (!isCanvasImageReadyForAi(node)) return null;
const data = (node.data || {}) as Record<string, any>;
const imageUrl = resolveCanvasImageUrl(node);
return {
id: `canvas:${node.id}`,
imageUrl,
kind: "canvas",
nodeId: node.id,
title: String(data.title || "画布图片").trim() || "画布图片",
};
}
export function mergeUniqueLayoutReplaceSources(
current: LayoutReplaceSource[],
incoming: LayoutReplaceSource[],
maxCount: number
) {
const next = [...current];
const identities = new Set(next.map(getLayoutReplaceSourceIdentity));
for (const source of incoming) {
const identity = getLayoutReplaceSourceIdentity(source);
if (next.length >= maxCount) break;
if (identities.has(identity)) continue;
identities.add(identity);
next.push(source);
}
return next;
}
export function getAvailableCanvasLayoutReplaceSources(
canvasSources: LayoutReplaceSource[],
currentSources: LayoutReplaceSource[]
) {
const existingIdentities = new Set(
currentSources.map(getLayoutReplaceSourceIdentity)
);
return canvasSources.filter(
(source) =>
Boolean(source.nodeId) &&
!existingIdentities.has(getLayoutReplaceSourceIdentity(source))
);
}