From a74e690ec7e6b94379176109dac1e3a49bddcbb4 Mon Sep 17 00:00:00 2001 From: yun Date: Mon, 6 Jul 2026 14:39:29 +0800 Subject: [PATCH] =?UTF-8?q?=E8=8A=82=E7=82=B9=E5=A4=A7=E5=B0=8F=E5=A4=84?= =?UTF-8?q?=E7=90=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/hooks/useCanvasContextMenuActions.tsx | 4 +++- .../__tests__/derivedImageExecutor.test.ts | 9 +++---- src/store/execution/derivedImageExecutor.ts | 9 +++++-- src/utils/canvasClipboard.ts | 24 +++++++++++++++---- 4 files changed, 35 insertions(+), 11 deletions(-) diff --git a/src/hooks/useCanvasContextMenuActions.tsx b/src/hooks/useCanvasContextMenuActions.tsx index 3f659343..353d5816 100644 --- a/src/hooks/useCanvasContextMenuActions.tsx +++ b/src/hooks/useCanvasContextMenuActions.tsx @@ -48,6 +48,7 @@ export function useCanvasContextMenuActions({ const addNode = useWorkflowStore((state) => state.addNode); const updateNodeData = useWorkflowStore((state) => state.updateNodeData); + const updateMediaNodeData = useWorkflowStore((state) => state.updateMediaNodeData); const copySelectedNodes = useWorkflowStore((state) => state.copySelectedNodes); const copyNodesById = useWorkflowStore((state) => state.copyNodesById); const pasteNodes = useWorkflowStore((state) => state.pasteNodes); @@ -67,9 +68,10 @@ export function useCanvasContextMenuActions({ clearClipboard, addNode: (type, position) => addNode(type, position), updateNodeData: (nodeId, data) => updateNodeData(nodeId, data as Partial), + updateMediaNodeData: (nodeId, data, dimensions) => updateMediaNodeData(nodeId, data as Partial, dimensions), onError: (message) => showToast(message, "error"), }); - }, [addNode, clearClipboard, clipboard, getCanvasFlowCenter, nodes, pasteNodes, showToast, updateNodeData]); + }, [addNode, clearClipboard, clipboard, getCanvasFlowCenter, nodes, pasteNodes, showToast, updateMediaNodeData, updateNodeData]); const handleKeyboardPaste = useCallback(() => { pasteAtPosition(); diff --git a/src/store/execution/__tests__/derivedImageExecutor.test.ts b/src/store/execution/__tests__/derivedImageExecutor.test.ts index e67203d9..f411b318 100644 --- a/src/store/execution/__tests__/derivedImageExecutor.test.ts +++ b/src/store/execution/__tests__/derivedImageExecutor.test.ts @@ -37,6 +37,7 @@ function makeCtx(node: WorkflowNode, images: string[] = ["source.png"]): NodeExe easeCurve: null, }), updateNodeData: vi.fn(), + updateMediaNodeData: vi.fn(), getFreshNode: vi.fn().mockReturnValue(node), getEdges: vi.fn().mockReturnValue([]), getNodes: vi.fn().mockReturnValue([]), @@ -86,13 +87,13 @@ describe("derivedImageExecutor", () => { expect(createCroppedImageBlob).toHaveBeenCalledWith("source.png", expect.objectContaining({ outputWidth: 100 })); expect(uploadBlobToGatewayMedia).toHaveBeenCalledWith(blob, "crop.png", "image/png", "image"); - expect(ctx.updateNodeData).toHaveBeenCalledWith("derived-1", expect.objectContaining({ + expect(ctx.updateMediaNodeData).toHaveBeenCalledWith("derived-1", expect.objectContaining({ image: "https://media.example/crop.png", previewImage: "https://media.example/crop-thumb.png", dimensions: { width: 100, height: 80 }, status: "complete", error: null, - })); + }), { width: 100, height: 80 }); }); it("sets an error when the source image is missing", async () => { @@ -151,10 +152,10 @@ describe("derivedImageExecutor", () => { expect(loadCropImage).toHaveBeenCalledWith("source.png"); expect(drawImage).toHaveBeenCalledWith(expect.anything(), 0, 20, 20, 20, 0, 0, 20, 20); - expect(ctx.updateNodeData).toHaveBeenCalledWith("derived-1", expect.objectContaining({ + expect(ctx.updateMediaNodeData).toHaveBeenCalledWith("derived-1", expect.objectContaining({ image: "https://media.example/grid.png", dimensions: { width: 20, height: 20 }, status: "complete", - })); + }), { width: 20, height: 20 }); }); }); diff --git a/src/store/execution/derivedImageExecutor.ts b/src/store/execution/derivedImageExecutor.ts index 46a0b808..bc8cea86 100644 --- a/src/store/execution/derivedImageExecutor.ts +++ b/src/store/execution/derivedImageExecutor.ts @@ -130,7 +130,7 @@ export async function executeDerivedImage(ctx: NodeExecutionContext): Promise void; addNode: (type: "smartImage" | "smartText", position: XYPosition) => string; updateNodeData: (nodeId: string, data: Partial) => void; + updateMediaNodeData?: ( + nodeId: string, + data: Partial, + dimensions: { width: number; height: number } | null | undefined + ) => void; onError?: (message: string) => void; } @@ -28,6 +33,7 @@ export function pasteClipboardAtPosition({ clearClipboard, addNode, updateNodeData, + updateMediaNodeData, onError, }: PasteClipboardAtPositionOptions): void { if (clipboard && clipboard.nodes.length > 0) { @@ -60,21 +66,31 @@ export function pasteClipboardAtPosition({ ); if (selectedSmartImageNode) { - updateNodeData(selectedSmartImageNode.id, { + const patch: Partial = { image, imageRef: undefined, filename, dimensions, - }); + }; + if (updateMediaNodeData) { + updateMediaNodeData(selectedSmartImageNode.id, patch, dimensions); + } else { + updateNodeData(selectedSmartImageNode.id, patch); + } } else { const center = getCanvasFlowCenter(); const position = flowPosition ?? { x: center.centerX, y: center.centerY }; const nodeId = addNode("smartImage", { x: position.x - 150, y: position.y - 140 }); - updateNodeData(nodeId, { + const patch: Partial = { image, filename, dimensions, - }); + }; + if (updateMediaNodeData) { + updateMediaNodeData(nodeId, patch, dimensions); + } else { + updateNodeData(nodeId, patch); + } } }; reader.readAsDataURL(blob);