mirror of
https://github.com/basketikun/infinite-canvas.git
synced 2026-08-05 17:04:27 +08:00
feat(canvas): enhance configuration node with new input handling and reference selection features
This commit is contained in:
@@ -1,4 +1,6 @@
|
||||
import type { ChatCompletionMessage } from "@/services/api/image";
|
||||
import { imageReferenceLabel } from "@/lib/image-reference-prompt";
|
||||
import { seedanceReferenceLabel } from "@/lib/seedance-video";
|
||||
import type { ReferenceImage } from "@/types/image";
|
||||
import type { ReferenceAudio, ReferenceVideo } from "@/types/media";
|
||||
import { CanvasNodeType, type CanvasConnection, type CanvasNodeData } from "../types";
|
||||
@@ -27,6 +29,11 @@ export type NodeGenerationInput = {
|
||||
|
||||
export function buildNodeGenerationContext(nodeId: string, nodes: CanvasNodeData[], connections: CanvasConnection[], prompt: string): NodeGenerationContext {
|
||||
const inputs = buildNodeGenerationInputs(nodeId, nodes, connections);
|
||||
const sourceNode = nodes.find((node) => node.id === nodeId);
|
||||
if (sourceNode?.type === CanvasNodeType.Config && Boolean(sourceNode.metadata?.composerContent?.trim())) {
|
||||
return buildComposerGenerationContext(inputs, prompt);
|
||||
}
|
||||
|
||||
const upstreamText = inputs
|
||||
.map((input) => input.text)
|
||||
.filter(Boolean)
|
||||
@@ -47,6 +54,65 @@ export function buildNodeGenerationContext(nodeId: string, nodes: CanvasNodeData
|
||||
};
|
||||
}
|
||||
|
||||
function buildComposerGenerationContext(inputs: NodeGenerationInput[], prompt: string): NodeGenerationContext {
|
||||
const inputByNodeId = new Map(inputs.map((input) => [input.nodeId, input]));
|
||||
const selectedInputs: NodeGenerationInput[] = [];
|
||||
const labelByNodeId = new Map<string, string>();
|
||||
const textBlocks: string[] = [];
|
||||
const counts = { image: 0, video: 0, audio: 0, text: 0 };
|
||||
let hasToken = false;
|
||||
let lastIndex = 0;
|
||||
let nextPrompt = "";
|
||||
|
||||
for (const match of prompt.matchAll(/@\[node:([^\]]+)\]/g)) {
|
||||
if (match.index === undefined) continue;
|
||||
hasToken = true;
|
||||
nextPrompt += prompt.slice(lastIndex, match.index);
|
||||
const input = inputByNodeId.get(match[1]);
|
||||
if (input) {
|
||||
let label = labelByNodeId.get(input.nodeId);
|
||||
if (!label) {
|
||||
label = generationLabel(input.type, counts[input.type]++);
|
||||
labelByNodeId.set(input.nodeId, label);
|
||||
if (input.type === "text") textBlocks.push(`【${label}】\n${input.text || ""}`);
|
||||
else selectedInputs.push(input);
|
||||
}
|
||||
nextPrompt += input.type === "text" ? `【${label}】` : label;
|
||||
}
|
||||
lastIndex = match.index + match[0].length;
|
||||
}
|
||||
|
||||
nextPrompt += prompt.slice(lastIndex);
|
||||
if (textBlocks.length) nextPrompt = `${nextPrompt.trim()}\n\n${textBlocks.join("\n\n")}`;
|
||||
const referenceImages = selectedInputs.map((input) => input.image).filter((image): image is ReferenceImage => Boolean(image));
|
||||
const referenceVideos = selectedInputs.map((input) => input.video).filter((video): video is ReferenceVideo => Boolean(video));
|
||||
const referenceAudios = selectedInputs.map((input) => input.audio).filter((audio): audio is ReferenceAudio => Boolean(audio));
|
||||
|
||||
if (!hasToken) {
|
||||
return {
|
||||
prompt,
|
||||
referenceImages: [],
|
||||
referenceVideos: [],
|
||||
referenceAudios: [],
|
||||
textCount: 0,
|
||||
imageCount: 0,
|
||||
videoCount: 0,
|
||||
audioCount: 0,
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
prompt: nextPrompt,
|
||||
referenceImages,
|
||||
referenceVideos,
|
||||
referenceAudios,
|
||||
textCount: counts.text,
|
||||
imageCount: referenceImages.length,
|
||||
videoCount: referenceVideos.length,
|
||||
audioCount: referenceAudios.length,
|
||||
};
|
||||
}
|
||||
|
||||
export function buildNodeGenerationInputs(nodeId: string, nodes: CanvasNodeData[], connections: CanvasConnection[]): NodeGenerationInput[] {
|
||||
return getGenerationResourceNodes(nodeId, nodes, connections).flatMap((node): NodeGenerationInput[] => {
|
||||
const image = readReferenceImage(node);
|
||||
@@ -84,6 +150,13 @@ function readNodeTextInput(node: CanvasNodeData) {
|
||||
return node.metadata?.prompt || "";
|
||||
}
|
||||
|
||||
function generationLabel(type: NodeGenerationInput["type"], index: number) {
|
||||
if (type === "image") return imageReferenceLabel(index);
|
||||
if (type === "video") return seedanceReferenceLabel("video", index);
|
||||
if (type === "audio") return seedanceReferenceLabel("audio", index);
|
||||
return `文本${index + 1}`;
|
||||
}
|
||||
|
||||
function readReferenceImage(node: CanvasNodeData): ReferenceImage | null {
|
||||
if (node.type !== CanvasNodeType.Image || !node.metadata?.content) return null;
|
||||
return {
|
||||
|
||||
Reference in New Issue
Block a user