From bfb4ac2a6497c07a727c2fe64db371c0e7e7507e Mon Sep 17 00:00:00 2001 From: yun Date: Mon, 6 Jul 2026 16:24:43 +0800 Subject: [PATCH] =?UTF-8?q?=E9=BB=98=E8=AE=A4=E5=B0=BA=E5=AF=B8=E4=BF=AE?= =?UTF-8?q?=E6=94=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../__tests__/GenerationComposer.test.tsx | 14 ++---- src/constants/nodeDimensions.ts | 44 +++++++++---------- .../generationPreferenceStore.test.ts | 8 ++-- .../workflowStore.integration.test.ts | 26 +++++------ src/utils/__tests__/nodeSizing.test.ts | 4 +- src/utils/nodeDimensions.ts | 26 ++++------- 6 files changed, 54 insertions(+), 68 deletions(-) diff --git a/src/components/__tests__/GenerationComposer.test.tsx b/src/components/__tests__/GenerationComposer.test.tsx index 120a72bf..e48d6360 100644 --- a/src/components/__tests__/GenerationComposer.test.tsx +++ b/src/components/__tests__/GenerationComposer.test.tsx @@ -1500,9 +1500,7 @@ describe("GenerationComposer", () => { nodes: [ { ...imageNode("img-1", true, { aspectRatio: "16:9", parameters: {} }), - width: 360, - height: 203, - measured: { width: 360, height: 203 }, + style: { width: 615, height: 346 }, } as WorkflowNode, ], }); @@ -1516,8 +1514,7 @@ describe("GenerationComposer", () => { const data = node.data as NanoBananaNodeData; expect(data.aspectRatio).toBe("9:16"); expect(data.parameters).toEqual({ ratio: "9:16" }); - expect(node.width).toBe(203); - expect(node.height).toBe(360); + expect(node.style).toMatchObject({ width: 346, height: 615 }); }); }); @@ -1546,9 +1543,7 @@ describe("GenerationComposer", () => { nodes: [ { ...imageNode("img-1", true, { aspectRatio: "16:9", parameters: {} }), - width: 360, - height: 203, - measured: { width: 360, height: 203 }, + style: { width: 615, height: 346 }, } as WorkflowNode, ], }); @@ -1560,8 +1555,7 @@ describe("GenerationComposer", () => { const data = node.data as NanoBananaNodeData; expect(data.parameters).toEqual({ ratio: "9:16" }); expect(data.aspectRatio).toBe("9:16"); - expect(node.width).toBe(203); - expect(node.height).toBe(360); + expect(node.style).toMatchObject({ width: 346, height: 615 }); }); }); diff --git a/src/constants/nodeDimensions.ts b/src/constants/nodeDimensions.ts index 13a58fa8..26ed2aac 100644 --- a/src/constants/nodeDimensions.ts +++ b/src/constants/nodeDimensions.ts @@ -3,34 +3,34 @@ import type { NodeType } from "@/types"; export type NodeDimensions = { width: number; height: number }; export const defaultNodeDimensions: Record = { - group: { width: 400, height: 300 }, + group: { width: 420, height: 300 }, imageInput: { width: 300, height: 280 }, smartImage: { width: 300, height: 280 }, - audioInput: { width: 300, height: 280 }, - smartAudio: { width: 300, height: 280 }, + audioInput: { width: 320, height: 180 }, + smartAudio: { width: 360, height: 180 }, videoInput: { width: 300, height: 280 }, - annotation: { width: 300, height: 280 }, + annotation: { width: 520, height: 360 }, smartVideo: { width: 300, height: 280 }, - prompt: { width: 300, height: 200 }, - smartText: { width: 300, height: 200 }, - array: { width: 300, height: 280 }, - promptConstructor: { width: 300, height: 280 }, + prompt: { width: 320, height: 180 }, + smartText: { width: 360, height: 220 }, + array: { width: 320, height: 220 }, + promptConstructor: { width: 420, height: 280 }, nanoBanana: { width: 300, height: 280 }, generateVideo: { width: 300, height: 280 }, - generate3d: { width: 300, height: 300 }, - generateAudio: { width: 300, height: 280 }, - llmGenerate: { width: 300, height: 200 }, - output: { width: 300, height: 280 }, - outputGallery: { width: 300, height: 280 }, - imageCompare: { width: 400, height: 360 }, - videoStitch: { width: 400, height: 280 }, - easeCurve: { width: 340, height: 280 }, - videoTrim: { width: 360, height: 360 }, - videoFrameGrab: { width: 320, height: 320 }, - router: { width: 200, height: 80 }, - switch: { width: 220, height: 120 }, - conditionalSwitch: { width: 260, height: 180 }, - glbViewer: { width: 360, height: 380 }, + generate3d: { width: 360, height: 300 }, + generateAudio: { width: 360, height: 180 }, + llmGenerate: { width: 360, height: 240 }, + output: { width: 360, height: 260 }, + outputGallery: { width: 420, height: 320 }, + imageCompare: { width: 520, height: 360 }, + videoStitch: { width: 420, height: 280 }, + easeCurve: { width: 340, height: 260 }, + videoTrim: { width: 420, height: 280 }, + videoFrameGrab: { width: 360, height: 240 }, + router: { width: 280, height: 180 }, + switch: { width: 280, height: 180 }, + conditionalSwitch: { width: 340, height: 240 }, + glbViewer: { width: 420, height: 360 }, }; export const DEFAULT_NODE_DIMENSIONS: NodeDimensions = { width: 300, height: 280 }; diff --git a/src/store/__tests__/generationPreferenceStore.test.ts b/src/store/__tests__/generationPreferenceStore.test.ts index 179ec164..82d84444 100644 --- a/src/store/__tests__/generationPreferenceStore.test.ts +++ b/src/store/__tests__/generationPreferenceStore.test.ts @@ -128,7 +128,7 @@ describe("generationPreferenceStore", () => { await expect(buildDefaultGenerationNodeCreation("nanoBanana")).resolves.toEqual({ data: expect.any(Object), - size: { width: 360, height: 203 }, + size: { width: 615, height: 346 }, }); }); @@ -147,7 +147,7 @@ describe("generationPreferenceStore", () => { await expect(buildDefaultGenerationNodeCreation("generateVideo")).resolves.toEqual({ data: expect.any(Object), - size: { width: 360, height: 203 }, + size: { width: 615, height: 346 }, }); }); @@ -166,7 +166,7 @@ describe("generationPreferenceStore", () => { aspectRatio: "4:5", parameters: { ratio: "4:5" }, }), - size: { width: 256, height: 320 }, + size: { width: 346, height: 432 }, }); }); @@ -245,7 +245,7 @@ describe("generationPreferenceStore", () => { parameters: { ratio: "9:16" }, selectedModel: expect.objectContaining({ modelId: "44", billingMethod: 2 }), }), - size: { width: 203, height: 360 }, + size: { width: 346, height: 615 }, }); }); diff --git a/src/store/__tests__/workflowStore.integration.test.ts b/src/store/__tests__/workflowStore.integration.test.ts index 5cc2f9be..412f43c8 100644 --- a/src/store/__tests__/workflowStore.integration.test.ts +++ b/src/store/__tests__/workflowStore.integration.test.ts @@ -634,7 +634,7 @@ describe("workflowStore integration tests", () => { }); const node = useWorkflowStore.getState().nodes[0]; - expect(node).toMatchObject({ style: { width: 203, height: 360 } }); + expect(node).toMatchObject({ style: { width: 346, height: 615 } }); expect(useWorkflowStore.getState().hasUnsavedChanges).toBe(true); }); @@ -685,7 +685,7 @@ describe("workflowStore integration tests", () => { ); const node = useWorkflowStore.getState().nodes.find((candidate) => candidate.id === nodeId); - expect(node?.style).toMatchObject({ width: 203, height: 360 }); + expect(node?.style).toMatchObject({ width: 346, height: 615 }); }); it("infers generation node dimensions from model parameter ratio", () => { @@ -696,7 +696,7 @@ describe("workflowStore integration tests", () => { ); const node = useWorkflowStore.getState().nodes.find((candidate) => candidate.id === nodeId); - expect(node?.style).toMatchObject({ width: 360, height: 203 }); + expect(node?.style).toMatchObject({ width: 615, height: 346 }); }); it("infers media node dimensions from initial media dimensions", () => { @@ -2130,7 +2130,7 @@ describe("workflowStore integration tests", () => { }); describe("createGroup bounding box calculation", () => { - it("should correctly calculate bounding box for easeCurve nodes (340x280)", () => { + it("should correctly calculate bounding box for easeCurve nodes (340x260)", () => { useWorkflowStore.setState({ nodes: [ createTestNode("ease-1", "easeCurve", { @@ -2173,13 +2173,13 @@ describe("workflowStore integration tests", () => { expect(groupId).toBeTruthy(); const group = useWorkflowStore.getState().groups[groupId]; - // easeCurve is 340x280, so maxX = 500 + 340 = 840, maxY = 100 + 280 = 380 + // easeCurve is 340x260, so maxX = 500 + 340 = 840, maxY = 100 + 260 = 360 // With padding=20: position.x = 100-20=80, width = 840-100+40=780 expect(group.size.width).toBeGreaterThanOrEqual(740); // Must account for 340px wide nodes - expect(group.size.height).toBeGreaterThanOrEqual(280); // Must account for 280px tall nodes + expect(group.size.height).toBeGreaterThanOrEqual(260); // Must account for 260px tall nodes }); - it("should correctly calculate bounding box for videoStitch nodes (400x280)", () => { + it("should correctly calculate bounding box for videoStitch nodes (420x280)", () => { useWorkflowStore.setState({ nodes: [ createTestNode("vs-1", "videoStitch", { @@ -2208,8 +2208,8 @@ describe("workflowStore integration tests", () => { const groupId = store.createGroup(["vs-1"]); const group = useWorkflowStore.getState().groups[groupId]; - // videoStitch is 400x280, with padding=20: width = 400+40=440 - expect(group.size.width).toBeGreaterThanOrEqual(400); // Must account for 400px wide node + // videoStitch is 420x280, with padding=20: width = 420+40=460 + expect(group.size.width).toBeGreaterThanOrEqual(420); // Must account for 420px wide node expect(group.size.height).toBeGreaterThanOrEqual(280); // Must account for 280px tall node }); @@ -2241,7 +2241,7 @@ describe("workflowStore integration tests", () => { expect(imageNode?.position).toEqual({ x: 0, y: 0 }); }); - it("should correctly calculate bounding box for audioInput nodes (300x200)", () => { + it("should correctly calculate bounding box for audioInput nodes (320x180)", () => { useWorkflowStore.setState({ nodes: [ createTestNode("audio-1", "audioInput", { @@ -2259,9 +2259,9 @@ describe("workflowStore integration tests", () => { const groupId = store.createGroup(["audio-1"]); const group = useWorkflowStore.getState().groups[groupId]; - // audioInput is 300x200, with padding=20: width = 300+40=340 - expect(group.size.width).toBeGreaterThanOrEqual(300); - expect(group.size.height).toBeGreaterThanOrEqual(200); + // audioInput is 320x180, with padding=20: width = 320+40=360 + expect(group.size.width).toBeGreaterThanOrEqual(320); + expect(group.size.height).toBeGreaterThanOrEqual(180); }); it("should replace selected existing groups when creating a new group", () => { diff --git a/src/utils/__tests__/nodeSizing.test.ts b/src/utils/__tests__/nodeSizing.test.ts index 81b7212b..61a25d64 100644 --- a/src/utils/__tests__/nodeSizing.test.ts +++ b/src/utils/__tests__/nodeSizing.test.ts @@ -30,7 +30,7 @@ describe("resolveNodeAutoSize", () => { aspectRatio: "9:16", dimensions: null, }) - ).toEqual({ width: 203, height: 360 }); + ).toEqual({ width: 346, height: 615 }); }); it("reads model ratio from parameters when no media url exists", () => { @@ -39,7 +39,7 @@ describe("resolveNodeAutoSize", () => { outputVideo: null, parameters: { ratio: "16:9" }, }) - ).toEqual({ width: 360, height: 203 }); + ).toEqual({ width: 615, height: 346 }); }); }); diff --git a/src/utils/nodeDimensions.ts b/src/utils/nodeDimensions.ts index d5814bbf..6f96cdb9 100644 --- a/src/utils/nodeDimensions.ts +++ b/src/utils/nodeDimensions.ts @@ -193,28 +193,20 @@ export function calculateMediaNodeSizeByShortSide( }; } -const GENERATION_NODE_SIZE_BY_ASPECT_RATIO: Record = { - "1:1": { width: 300, height: 300 }, - "16:9": { width: 360, height: 203 }, - "9:16": { width: 203, height: 360 }, - "21:9": { width: 420, height: 180 }, - "2:3": { width: 227, height: 340 }, - "3:2": { width: 340, height: 227 }, - "3:4": { width: 255, height: 340 }, - "4:3": { width: 340, height: 255 }, - "4:5": { width: 256, height: 320 }, - "5:4": { width: 320, height: 256 }, - "1:4": { width: 140, height: 420 }, - "4:1": { width: 420, height: 140 }, - "1:8": { width: 100, height: 420 }, - "8:1": { width: 420, height: 100 }, -}; +function parseAspectRatioValue(value: string): number | null { + const [widthText, heightText] = value.trim().split(":"); + const width = Number(widthText); + const height = Number(heightText); + if (!Number.isFinite(width) || !Number.isFinite(height) || width <= 0 || height <= 0) return null; + return width / height; +} export function getGenerationNodeSizeForAspectRatio( value: unknown ): { width: number; height: number } | null { if (typeof value !== "string") return null; - return GENERATION_NODE_SIZE_BY_ASPECT_RATIO[value.trim()] ?? null; + const aspectRatio = parseAspectRatioValue(value); + return aspectRatio ? calculateMediaNodeSizeByShortSide(aspectRatio) : null; } // Node sizing constraints