diff --git a/components/canvas/nodes/ImageNode.tsx b/components/canvas/nodes/ImageNode.tsx index 565a427f8..0f58b6b9c 100644 --- a/components/canvas/nodes/ImageNode.tsx +++ b/components/canvas/nodes/ImageNode.tsx @@ -6607,7 +6607,9 @@ export function ImageNode({ return; } - setIsCreatingDetachedResultNode(true); + // The server creates the result node after completion. Keep the source-node + // processing overlay visible while that detached result is pending. + setIsCreatingDetachedResultNode(false); setIsProcessing(true); try { const command = createCanvasCapabilityClientCommand({ diff --git a/tests/routes/canvas-runtime-internal-endpoint.test.ts b/tests/routes/canvas-runtime-internal-endpoint.test.ts index f3404a0e6..2f74c66c0 100644 --- a/tests/routes/canvas-runtime-internal-endpoint.test.ts +++ b/tests/routes/canvas-runtime-internal-endpoint.test.ts @@ -1,4 +1,6 @@ import { expect, test } from "@playwright/test"; +import { readFileSync } from "node:fs"; +import { resolve } from "node:path"; import { resolveCanvasRuntimeEndpoint } from "@/lib/canvas/capabilities/internal-endpoint"; import { @@ -7,6 +9,25 @@ import { } from "@/lib/canvas/capabilities/task-queue"; test.describe("canvas runtime internal endpoints", () => { + test("keeps the image processing placeholder visible for shared toolbar actions", () => { + const source = readFileSync( + resolve(process.cwd(), "components/canvas/nodes/ImageNode.tsx"), + "utf8" + ); + const start = source.indexOf("const executeSharedToolbarCapability = async"); + const end = source.indexOf("// 处理图片编辑操作", start); + + expect(start).toBeGreaterThanOrEqual(0); + expect(end).toBeGreaterThan(start); + const sharedCapabilityFlow = source.slice(start, end); + expect(sharedCapabilityFlow).toContain( + "setIsCreatingDetachedResultNode(false);" + ); + expect(sharedCapabilityFlow).not.toContain( + "setIsCreatingDetachedResultNode(true);" + ); + }); + test("uses the local HTTP listener for server-internal capability calls", () => { expect( resolveCanvasRuntimeEndpoint(