From 1ccbbc5a7bfd848dc0cc80f0be31bb6e383abf79 Mon Sep 17 00:00:00 2001 From: ddCat <2695092045@qq.com> Date: Wed, 26 Aug 2026 22:38:07 +0800 Subject: [PATCH] =?UTF-8?q?fix(canvas):=20=E5=88=86=E9=95=9C=E7=94=9F?= =?UTF-8?q?=E6=88=90=E4=B8=8A=E4=B8=8B=E6=96=87=20-=20=E5=BF=BD=E7=95=A5?= =?UTF-8?q?=E6=9C=AA=E7=BB=91=E5=AE=9A=E8=B5=84=E4=BA=A7=E7=9A=84=E8=A7=92?= =?UTF-8?q?=E8=89=B2=E8=AE=BE=E8=AE=A1=E5=B7=A5=E4=BD=9C=E6=B5=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../lib/canvas/canvas-storyboard-context.ts | 5 +- web/test/canvas-storyboard-context.test.ts | 65 +++++++++++++++++++ 2 files changed, 69 insertions(+), 1 deletion(-) create mode 100644 web/test/canvas-storyboard-context.test.ts diff --git a/web/src/lib/canvas/canvas-storyboard-context.ts b/web/src/lib/canvas/canvas-storyboard-context.ts index 560e474ca..066ce2354 100644 --- a/web/src/lib/canvas/canvas-storyboard-context.ts +++ b/web/src/lib/canvas/canvas-storyboard-context.ts @@ -22,7 +22,10 @@ export function resolveStoryboardGenerationContext(nodes: CanvasNodeData[]): Sto const stylePresetId = String(styleNode?.metadata?.stylePresetId || "").trim(); if (!styleNode || !stylePrompt || !stylePresetId) throw new Error("请先设置项目画风,再生成分镜"); - const characterNodes = nodes.filter((node) => node.metadata?.workflowKind === "character"); + // `workflowKind=character` is also used by standalone character-design + // image workflows. Only nodes linked to a project character asset are + // storyboard character cards and therefore participate in this check. + const characterNodes = nodes.filter((node) => node.metadata?.workflowKind === "character" && node.metadata?.characterAssetId?.trim()); const invalidCharacter = characterNodes.find((node) => !node.metadata?.characterAssetId?.trim() || !node.metadata?.characterVersionId?.trim() || !(node.metadata?.characterName || node.title).trim()); if (invalidCharacter) throw new Error(`角色卡“${invalidCharacter.metadata?.characterName || invalidCharacter.title || "未命名角色"}”版本未同步,请刷新角色资产后再生成分镜`); diff --git a/web/test/canvas-storyboard-context.test.ts b/web/test/canvas-storyboard-context.test.ts new file mode 100644 index 000000000..03033f023 --- /dev/null +++ b/web/test/canvas-storyboard-context.test.ts @@ -0,0 +1,65 @@ +import { describe, expect, it } from "bun:test"; + +import { resolveStoryboardGenerationContext } from "@/lib/canvas/canvas-storyboard-context"; +import { CanvasNodeType, type CanvasNodeData } from "@/types/canvas"; + +const node = (id: string, type: CanvasNodeType, metadata: CanvasNodeData["metadata"] = {}): CanvasNodeData => ({ + id, + type, + title: id, + position: { x: 0, y: 0 }, + width: 320, + height: 180, + metadata, +}); + +const styleNode = node("style", CanvasNodeType.Text, { + workflowKind: "styleboard", + stylePresetId: "style-v1", + content: "统一的项目画风", +}); + +describe("storyboard generation context", () => { + it("ignores standalone character-design workflows without a linked asset", () => { + const context = resolveStoryboardGenerationContext([ + styleNode, + node("character-turnaround", CanvasNodeType.Image, { + workflowKind: "character", + composerContent: "生成角色三视图", + }), + ]); + + expect(context.characters).toEqual([]); + }); + + it("keeps the version check for linked character cards", () => { + expect(() => resolveStoryboardGenerationContext([ + styleNode, + node("character-card", CanvasNodeType.Text, { + workflowKind: "character", + characterAssetId: "asset-1", + characterName: "李当歌", + }), + ])).toThrow("角色卡“李当歌”版本未同步"); + }); + + it("passes linked character cards with a current version", () => { + const context = resolveStoryboardGenerationContext([ + styleNode, + node("character-card", CanvasNodeType.Text, { + workflowKind: "character", + characterAssetId: "asset-1", + characterVersionId: "version-1", + characterName: "李当歌", + characterDefinition: { role: "镇国公世子" }, + }), + ]); + + expect(context.characters).toEqual([{ + assetId: "asset-1", + versionId: "version-1", + name: "李当歌", + definition: { role: "镇国公世子" }, + }]); + }); +});