Files
ComfyUI_frontend/src/composables/node/useNodeCanvasImagePreview.ts
Christian Byrne f9b0f277bf fix: track nodePreviewImages in usePromotedPreviews (#10165)
## Summary

The `computed` in `usePromotedPreviews` only tracked `nodeOutputs` as a
reactive dependency. GLSL live previews (and other preview-only sources)
write to `nodePreviewImages` instead of `nodeOutputs`, so promoted
preview widgets on SubgraphNodes never re-evaluated when live previews
updated.

## Changes

**Production** (`usePromotedPreviews.ts` — 3-line fix):
- Add `nodePreviewImages[locatorId]` as a second reactive dependency
alongside `nodeOutputs[locatorId]`
- Guard now passes when *either* source has data, not just `nodeOutputs`

**Tests** (`usePromotedPreviews.test.ts`):
- Add `nodePreviewImages` to mock store type and factory
- Add `seedPreviewImages()` helper
- Add `getNodeImageUrls.mockReset()` in `beforeEach` for proper test
isolation
- Two new test cases:
- `returns preview when only nodePreviewImages exist (e.g. GLSL live
preview)`
- `recomputes when preview images are populated after first evaluation`
- Clean up existing tests to use hoisted `getNodeImageUrls` mock
directly instead of `vi.mocked(useNodeOutputStore().getNodeImageUrls)`

## What this supersedes

This is a minimal re-implementation of #9461. That PR also modified
`promotionStore.ts` with a `_version`/`_touch()` monotonic counter to
manually force reactivity — that approach is dropped here as it is an
anti-pattern (manually managing reactivity counters instead of using
Vue's built-in reactivity system). The promotionStore changes were not
needed for this fix.

## Related

- Supersedes #9461
- Prerequisite work: #9198 (add GLSLShader to canvas image preview node
types)
- Upstream feature: #9201 (useGLSLPreview composable)
- Adjacent: #9435 (centralize node image rendering state in
NodeImageStore)

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-10165-fix-track-nodePreviewImages-in-usePromotedPreviews-for-GLSL-live-preview-propagation-3266d73d365081cd87d0d12c4c041907)
by [Unito](https://www.unito.io)

---------

Co-authored-by: GitHub Action <action@github.com>
2026-03-17 12:54:21 -07:00

50 lines
1.3 KiB
TypeScript

import type { LGraphNode } from '@/lib/litegraph/src/litegraph'
import { useImagePreviewWidget } from '@/renderer/extensions/vueNodes/widgets/composables/useImagePreviewWidget'
import { CANVAS_IMAGE_PREVIEW_WIDGET } from '@/composables/node/canvasImagePreviewTypes'
/**
* Composable for handling canvas image previews in nodes
*/
export function useNodeCanvasImagePreview() {
const imagePreviewWidget = useImagePreviewWidget()
/**
* Shows canvas image preview for a node
* @param node The graph node to show the preview for
*/
function showCanvasImagePreview(node: LGraphNode) {
if (!node.imgs?.length) return
if (node.isSubgraphNode()) return
if (!node.widgets?.find((w) => w.name === CANVAS_IMAGE_PREVIEW_WIDGET)) {
imagePreviewWidget(node, {
type: 'IMAGE_PREVIEW',
name: CANVAS_IMAGE_PREVIEW_WIDGET
})
}
}
/**
* Removes canvas image preview from a node
* @param node The graph node to remove the preview from
*/
function removeCanvasImagePreview(node: LGraphNode) {
if (!node.widgets) return
const widgetIdx = node.widgets.findIndex(
(w) => w.name === CANVAS_IMAGE_PREVIEW_WIDGET
)
if (widgetIdx > -1) {
node.widgets[widgetIdx].onRemove?.()
node.widgets.splice(widgetIdx, 1)
}
}
return {
showCanvasImagePreview,
removeCanvasImagePreview
}
}