feat(canvas): 媒体转换节点 - 改进操作选择并支持完成后作为生成参考

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
ddCatandCursor committed 2026-09-09 09:46:18 +08:00
1 parent d79b323af0
commit 18244c213f
6 files changed
+296 -42

No files matched your search

@@ -232,6 +232,19 @@ export function buildImageGenerationMetadata(type: CanvasImageGenerationType, co
}
export function nodeReferenceImage(node: CanvasNodeData): ReferenceImage | null {
if (node.type === CanvasNodeType.MediaConversion) {
// 转换节点完成后才算图片素材:结果写在 storageKey / resultStorageKey,content 会被清空。
const conversion = node.metadata?.mediaConversion;
const storageKey = conversion?.resultStorageKey || node.metadata?.storageKey;
if (conversion?.status !== "completed" || !storageKey) return null;
return {
id: node.id,
name: node.title || `conversion-${node.id}.png`,
type: node.metadata?.mimeType || "image/png",
dataUrl: node.metadata?.content || "",
storageKey,
};
}
if (node.type !== CanvasNodeType.Image || (!node.metadata?.content && !node.metadata?.storageKey)) return null;
return {
id: node.id,
@@ -168,7 +168,7 @@ const BUILTIN_NODE_TRAITS = {
[CanvasNodeType.MediaConversion]: {
label: "转换",
icon: <WandSparkles />,
minSize: { width: 320, height: 360 },
minSize: { width: 400, height: 360 },
showInCreateMenu: true,
resourceKind: (node: CanvasNodeData) => {
const conversion = node.metadata?.mediaConversion;
@@ -11,9 +11,12 @@ function isVideo(ctx: ToolContext) { return ctx.node?.type === CanvasNodeType.Vi
function isAudio(ctx: ToolContext) { return ctx.node?.type === CanvasNodeType.Audio; }
function isText(ctx: ToolContext) { return ctx.node?.type === CanvasNodeType.Text; }
function isConfig(ctx: ToolContext) { return ctx.node?.type === CanvasNodeType.Config; }
function hasImage(ctx: ToolContext) { return isImage(ctx) && Boolean(ctx.nodeMetadata?.content); }
function hasVideo(ctx: ToolContext) { return isVideo(ctx) && Boolean(ctx.nodeMetadata?.content); }
function hasAudio(ctx: ToolContext) { return isAudio(ctx) && Boolean(ctx.nodeMetadata?.content); }
function hasMediaPayload(ctx: ToolContext) {
return Boolean(ctx.nodeMetadata?.content || ctx.nodeMetadata?.storageKey || ctx.nodeMetadata?.previewContent);
}
function hasImage(ctx: ToolContext) { return isImage(ctx) && hasMediaPayload(ctx); }
function hasVideo(ctx: ToolContext) { return isVideo(ctx) && hasMediaPayload(ctx); }
function hasAudio(ctx: ToolContext) { return isAudio(ctx) && Boolean(ctx.nodeMetadata?.content || ctx.nodeMetadata?.storageKey); }
function isCharacterReference(ctx: ToolContext) { return isText(ctx) && ctx.nodeMetadata?.workflowKind === "character" && Boolean(ctx.nodeMetadata?.characterAssetId); }
function isEditableText(ctx: ToolContext) { return isText(ctx) && !isCharacterReference(ctx); }
function canOpenDialog(ctx: ToolContext) { return isEditableText(ctx) || (isImage(ctx) && !isCanvasImageSourceNode(ctx.node)) || isVideo(ctx); }