feat(plugin): implement canvas node plugin system with dynamic registration and remote installation

This commit is contained in:
HouYunFei
2026-07-15 11:47:25 +08:00
parent f0db29d8e3
commit eef4d96787
51 changed files with 4933 additions and 72 deletions
@@ -1,5 +1,6 @@
import { imageReferenceLabel } from "@/lib/image-reference-prompt";
import { seedanceReferenceLabel } from "@/lib/seedance-video";
import { getNodeDefinition } from "@/lib/canvas/node-registry";
import { CanvasNodeType, type CanvasConnection, type CanvasNodeData } from "@/types/canvas";
export type CanvasResourceKind = "image" | "video" | "audio" | "text";
@@ -71,7 +72,7 @@ function labelResourceNodes(nodes: CanvasNodeData[], active: boolean) {
label,
title: node.title || label,
previewUrl: node.metadata?.content,
text: node.type === CanvasNodeType.Text ? node.metadata?.content || node.metadata?.prompt : undefined,
text: resourceText(node),
active,
},
];
@@ -89,10 +90,17 @@ function isResourceNode(node: CanvasNodeData) {
return Boolean(resourceKind(node));
}
function resourceText(node: CanvasNodeData): string | undefined {
if (node.type === CanvasNodeType.Text) return node.metadata?.content || node.metadata?.prompt;
const resource = getNodeDefinition(node.type)?.resource?.(node);
return resource?.kind === "text" ? resource.text : undefined;
}
function resourceKind(node: CanvasNodeData): CanvasResourceKind | null {
if (node.type === CanvasNodeType.Image && node.metadata?.content) return "image";
if (node.type === CanvasNodeType.Video && node.metadata?.content) return "video";
if (node.type === CanvasNodeType.Audio && node.metadata?.content) return "audio";
if (node.type === CanvasNodeType.Text && (node.metadata?.content || node.metadata?.prompt)) return "text";
return null;
// 插件节点通过 definition.resource 声明可作为输入
return getNodeDefinition(node.type)?.resource?.(node)?.kind || null;
}