From f314de62fe815d1a76a1fe16a12850086c306c7e Mon Sep 17 00:00:00 2001 From: yun Date: Mon, 6 Jul 2026 18:30:15 +0800 Subject: [PATCH] =?UTF-8?q?=E8=A3=81=E5=89=AA=E6=97=B6=E4=BD=BF=E7=94=A8?= =?UTF-8?q?=E5=8E=9F=E5=9B=BE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../__tests__/GenerateImageNode.test.tsx | 22 +++++++++++++++ .../__tests__/ImageInputNode.test.tsx | 28 +++++++++++++++++++ src/components/nodes/GenerateImageNode.tsx | 4 +-- src/components/nodes/ImageInputNode.tsx | 4 +-- 4 files changed, 54 insertions(+), 4 deletions(-) diff --git a/src/components/__tests__/GenerateImageNode.test.tsx b/src/components/__tests__/GenerateImageNode.test.tsx index b838dd6b..63aaf79d 100644 --- a/src/components/__tests__/GenerateImageNode.test.tsx +++ b/src/components/__tests__/GenerateImageNode.test.tsx @@ -379,6 +379,28 @@ describe("GenerateImageNode", () => { expect(mockDownloadMedia).toHaveBeenCalledWith("https://cdn.example.com/full.png", "image"); }); + it("should use the full output image while cropping and return to preview afterward", () => { + render( + + + + ); + + const img = screen.getByAltText("Generated"); + expect(img).toHaveAttribute("src", "https://cdn.example.com/full_thumb.webp"); + + fireEvent.click(screen.getByRole("button", { name: /Crop|裁剪/ })); + + expect(img).toHaveAttribute("src", "https://cdn.example.com/full.png"); + + fireEvent.click(screen.getByRole("button", { name: "关闭编辑工具" })); + + expect(img).toHaveAttribute("src", "https://cdn.example.com/full_thumb.webp"); + }); + it("should not resize the canvas node while rendering an output image", () => { render( diff --git a/src/components/__tests__/ImageInputNode.test.tsx b/src/components/__tests__/ImageInputNode.test.tsx index 01cf0f71..adcbfd3a 100644 --- a/src/components/__tests__/ImageInputNode.test.tsx +++ b/src/components/__tests__/ImageInputNode.test.tsx @@ -308,6 +308,34 @@ describe("ImageInputNode", () => { expect(img).toHaveClass("object-cover"); }); + it("should use the full image while cropping and return to preview afterward", () => { + mockNodes = [{ id: propsWithImage.id, selected: true }]; + render( + + + + ); + + const img = screen.getByAltText("test-image.png"); + expect(img).toHaveAttribute("src", "https://example.com/full_thumb.webp"); + + fireEvent.click(screen.getByRole("button", { name: "Crop" })); + + expect(img).toHaveAttribute("src", "https://example.com/full.png"); + + fireEvent.click(screen.getByRole("button", { name: "关闭编辑工具" })); + + expect(img).toHaveAttribute("src", "https://example.com/full_thumb.webp"); + }); + it("should show a load failure state when the image cannot be rendered", () => { render( diff --git a/src/components/nodes/GenerateImageNode.tsx b/src/components/nodes/GenerateImageNode.tsx index 49ca3242..78ef2165 100644 --- a/src/components/nodes/GenerateImageNode.tsx +++ b/src/components/nodes/GenerateImageNode.tsx @@ -375,7 +375,7 @@ export function GenerateImageNodeView({ id, data, selected }: GenerateImageNodeV }, [nodeData.status, displayError, t]); const selectedNodeCount = useSelectedNodeCount(); - const editableImage = displayImage; + const editableImage = nodeData.outputImage || displayImage; const downloadImage = nodeData.outputImage || editableImage; const overlayImageDimensions = cropImageDimensions || asImageDimensions(nodeData.dimensions); const showSelectedActions = Boolean(selected && selectedNodeCount === 1 && editableImage); @@ -576,7 +576,7 @@ export function GenerateImageNodeView({ id, data, selected }: GenerateImageNodeV onLoad={(event) => { setImageRetryNonce(0); const image = event.currentTarget; - if (image.naturalWidth > 0 && image.naturalHeight > 0) { + if (renderedImageSrc === nodeData.outputImage && image.naturalWidth > 0 && image.naturalHeight > 0) { const dimensions = { width: image.naturalWidth, height: image.naturalHeight }; setCropImageDimensions(dimensions); updateMediaNodeData(id, { dimensions }, dimensions); diff --git a/src/components/nodes/ImageInputNode.tsx b/src/components/nodes/ImageInputNode.tsx index 27560b72..a02b24cd 100644 --- a/src/components/nodes/ImageInputNode.tsx +++ b/src/components/nodes/ImageInputNode.tsx @@ -124,7 +124,7 @@ export function ImageInputNodeView({ id, data, selected, renderInputHandle = fal const [imageLoadFailed, setImageLoadFailed] = useState(false); const isUploading = nodeData.uploadStatus === "loading"; const displayImage = displayFallbackImage || preferredDisplayImage; - const imageSrc = displayImage; + const imageSrc = isCropping ? nodeData.image || displayImage : displayImage; useEffect(() => { setDisplayFallbackImage(null); @@ -576,7 +576,7 @@ export function ImageInputNodeView({ id, data, selected, renderInputHandle = fal onLoad={(event) => { setImageLoadFailed(false); const image = event.currentTarget; - if (image.naturalWidth > 0 && image.naturalHeight > 0) { + if (imageSrc === nodeData.image && image.naturalWidth > 0 && image.naturalHeight > 0) { const dimensions = { width: image.naturalWidth, height: image.naturalHeight }; if (nodeData.dimensions?.width !== dimensions.width || nodeData.dimensions?.height !== dimensions.height) { updateMediaNodeData(id, { dimensions }, dimensions);