mirror of
https://github.com/Comfy-Org/ComfyUI_frontend.git
synced 2026-02-19 22:34:15 +00:00
fix: undo breaking Vue node image preview reactivity (#8839)
## Summary restoreOutputs was assigning the same object reference to both app.nodeOutputs and the Pinia reactive ref. This caused subsequent writes via setOutputsByLocatorId to mutate the reactive proxy's target through the raw reference before the proxy write, making Vue detect no change and skip reactivity updates permanently. Shallow-copy the outputs when assigning to the reactive ref so the proxy target remains a separate object from app.nodeOutputs. ## Screenshots before https://github.com/user-attachments/assets/98f2b17c-87b9-41e7-9caa-238e36c3c032 after https://github.com/user-attachments/assets/cb6e1d25-bd2e-41ed-a536-7b8250f858ec ┆Issue is synchronized with this [Notion page](https://www.notion.so/PR-8839-fix-undo-breaking-Vue-node-image-preview-reactivity-3056d73d365081d2a1c7d4d9553f30e0) by [Unito](https://www.unito.io)
This commit is contained in:
@@ -87,6 +87,47 @@ describe('imagePreviewStore setNodeOutputsByExecutionId with merge', () => {
|
||||
})
|
||||
})
|
||||
|
||||
describe('imagePreviewStore restoreOutputs', () => {
|
||||
beforeEach(() => {
|
||||
setActivePinia(createTestingPinia({ stubActions: false }))
|
||||
vi.clearAllMocks()
|
||||
app.nodeOutputs = {}
|
||||
app.nodePreviewImages = {}
|
||||
})
|
||||
|
||||
it('should keep reactivity after restoreOutputs followed by setNodeOutputsByExecutionId', () => {
|
||||
const store = useNodeOutputStore()
|
||||
|
||||
// Simulate execution: set outputs for node "4" (e.g., PreviewImage)
|
||||
const executionOutput = createMockOutputs([
|
||||
{ filename: 'ComfyUI_00001.png', subfolder: '', type: 'temp' }
|
||||
])
|
||||
const savedOutputs: Record<string, ExecutedWsMessage['output']> = {
|
||||
'4': executionOutput
|
||||
}
|
||||
|
||||
// Simulate undo: restoreOutputs makes app.nodeOutputs and the ref
|
||||
// share the same underlying object if not handled correctly.
|
||||
store.restoreOutputs(savedOutputs)
|
||||
|
||||
expect(store.nodeOutputs['4']).toStrictEqual(executionOutput)
|
||||
expect(store.nodeOutputs['3']).toBeUndefined()
|
||||
|
||||
// Simulate widget callback setting outputs for node "3" (e.g., LoadImage)
|
||||
const widgetOutput = createMockOutputs([
|
||||
{ filename: 'example.png', subfolder: '', type: 'input' }
|
||||
])
|
||||
store.setNodeOutputsByExecutionId('3', widgetOutput)
|
||||
|
||||
// The reactive store must reflect the new output.
|
||||
// Before the fix, the raw write to app.nodeOutputs would mutate the
|
||||
// proxy's target before the proxy write, causing Vue to skip the
|
||||
// reactivity update.
|
||||
expect(store.nodeOutputs['3']).toStrictEqual(widgetOutput)
|
||||
expect(app.nodeOutputs['3']).toStrictEqual(widgetOutput)
|
||||
})
|
||||
})
|
||||
|
||||
describe('imagePreviewStore getPreviewParam', () => {
|
||||
beforeEach(() => {
|
||||
setActivePinia(createTestingPinia({ stubActions: false }))
|
||||
|
||||
@@ -365,7 +365,7 @@ export const useNodeOutputStore = defineStore('nodeOutput', () => {
|
||||
outputs: Record<string, ExecutedWsMessage['output']>
|
||||
) {
|
||||
app.nodeOutputs = outputs
|
||||
nodeOutputs.value = outputs
|
||||
nodeOutputs.value = { ...outputs }
|
||||
}
|
||||
|
||||
function updateNodeImages(node: LGraphNode) {
|
||||
|
||||
Reference in New Issue
Block a user