Files
ComfyUI_frontend/src/platform/assets/utils/clearNodePreviewCacheForValues.ts
Comfy Org PR Bot e6ead5631a [backport core/1.44] fix: Load Image preview retains deleted asset (FE-230) (#12129)
Backport of #11493 to `core/1.44`

Automatically created by backport workflow.

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-12129-backport-core-1-44-fix-Load-Image-preview-retains-deleted-asset-FE-230-35d6d73d36508157ada3db9f8416e113)
by [Unito](https://www.unito.io)

Co-authored-by: Dante <bunggl@naver.com>
2026-05-14 11:21:29 +09:00

66 lines
2.4 KiB
TypeScript

import type {
LGraph,
LGraphNode,
Subgraph
} from '@/lib/litegraph/src/litegraph'
import { collectAllNodes } from '@/utils/graphTraversalUtil'
/**
* Clear cached Load Image / Load Video preview state on any node whose widget
* value matches one of the given values. Covers:
* - the canvas renderer cache (`node.imgs`, `node.videoContainer`)
* - the Vue preview source — must be cleared via `removeOutputsForNode`
* so the Pinia reactive ref (`nodeOutputStore.nodeOutputs.value`) updates,
* not just the legacy `app.nodeOutputs` mirror
*
* Comparison is full-string against the widget value as stored — callers must
* provide the canonical widget-value variants for each deleted asset (e.g.
* `foo.png`, `foo.png [output]`, `sub/foo.png [output]`, `<asset_hash>`). This
* avoids false matches when two distinct assets share a basename across
* input/output sources.
*
* Walks the full graph hierarchy via `collectAllNodes`, so Load Image / Load
* Video nodes inside subgraphs are also matched.
*
* FE-230 — invoked after successful asset deletion so the Load Image / Load
* Video node preview does not keep displaying a thumbnail for an asset that
* no longer exists.
*/
export function clearNodePreviewCacheForValues(
rootGraph: LGraph | Subgraph,
deletedValues: ReadonlySet<string>,
removeOutputsForNode: (node: LGraphNode) => void
): void {
if (deletedValues.size === 0) return
for (const node of findNodesReferencingValues(rootGraph, deletedValues)) {
removeOutputsForNode(node)
node.imgs = undefined
node.videoContainer = undefined
node.graph?.setDirtyCanvas(true)
}
}
/**
* Walk the graph hierarchy and yield each leaf node whose widget value matches
* one of `deletedValues`. Used by both the preview-clearing path and the
* missing-media-marking path so the two stay in lockstep.
*
* Skips subgraph wrapper nodes — only their interior nodes are inspected.
*/
export function findNodesReferencingValues(
rootGraph: LGraph | Subgraph,
deletedValues: ReadonlySet<string>
): LGraphNode[] {
if (deletedValues.size === 0) return []
const matches: LGraphNode[] = []
for (const node of collectAllNodes(rootGraph)) {
if (!node.widgets?.length) continue
if (node.isSubgraphNode?.()) continue
const referencesDeleted = node.widgets.some(
(w) => typeof w.value === 'string' && deletedValues.has(w.value)
)
if (referencesDeleted) matches.push(node)
}
return matches
}