From 1521e6956f06eff276b129e5e2e17335a4e10922 Mon Sep 17 00:00:00 2001 From: bymyself Date: Tue, 5 May 2026 20:37:50 -0700 Subject: [PATCH] fix: painter widget preserves mask reference across WidgetPainter remount MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The painter's serializeValue closure captured hasStrokes and isDirty as local variables that reset on every WidgetPainter.vue mount. After a remount (e.g. NodeWidgets re-render driven by useProcessedWidgets, added in #10966), serializeValue saw isCanvasEmpty()=true and returned '' even when modelValue still held a valid mask reference from a workflow load or a prior upload — backend then received an empty mask, and on cloud this surfaced as ImageDownloadError. Reorder the short-circuits so a non-dirty painter returns its existing modelValue.value before the canvas-empty guard, and fall back to the prior modelValue.value if the canvas element is no longer mounted. --- src/composables/painter/usePainter.test.ts | 7 +++---- src/composables/painter/usePainter.ts | 6 +++--- 2 files changed, 6 insertions(+), 7 deletions(-) diff --git a/src/composables/painter/usePainter.test.ts b/src/composables/painter/usePainter.test.ts index 80a64fef2d..6146ebf66c 100644 --- a/src/composables/painter/usePainter.test.ts +++ b/src/composables/painter/usePainter.test.ts @@ -359,15 +359,14 @@ describe('usePainter', () => { expect(result).toBe('') }) - it('returns empty string when canvas has no strokes even if modelValue is set', async () => { + it('returns existing modelValue when not dirty (regression: WidgetPainter remount must not blank a workflow-restored mask reference)', async () => { const maskWidget = makeWidget('mask', '') mockWidgets.push(maskWidget) - const { modelValue } = mountPainter() - modelValue.value = 'painter/existing.png [temp]' + mountPainter('test-node', 'painter/existing.png [temp]') const result = await maskWidget.serializeValue!({} as LGraphNode, 0) - expect(result).toBe('') + expect(result).toBe('painter/existing.png [temp]') }) }) diff --git a/src/composables/painter/usePainter.ts b/src/composables/painter/usePainter.ts index 3c60ddfbf7..f0e0b0932a 100644 --- a/src/composables/painter/usePainter.ts +++ b/src/composables/painter/usePainter.ts @@ -624,13 +624,13 @@ export function usePainter(nodeId: string, options: UsePainterOptions) { } async function serializeValue(): Promise { + if (!isDirty.value) return modelValue.value + const el = canvasEl.value - if (!el) return '' + if (!el) return modelValue.value if (isCanvasEmpty()) return '' - if (!isDirty.value) return modelValue.value - const blob = await new Promise((resolve) => el.toBlob(resolve, 'image/png') )