Compare commits

...

5 Commits

Author SHA1 Message Date
bymyself
1a84f3ddbf fix: remove debug console.warn statements from preview hot paths 2026-05-03 21:56:00 -07:00
bymyself
8052cf7b60 fix: dirty root canvas for subgraph LoadImage preview + add debug logging 2026-05-03 21:56:00 -07:00
bymyself
727cc3ed87 fix: stabilize LoadImage subgraph E2E test for CI
Load default workflow and pan to node before context menu
interaction, matching the pattern used by other subgraph tests.
2026-05-03 21:56:00 -07:00
bymyself
1d14fd7230 test: add Playwright test for LoadImage subgraph preview promotion
Verifies that converting a LoadImage node to a subgraph produces the
$$canvas-image-preview pseudo-widget in the subgraph node's
proxyWidgets, confirming the fix in canvasImagePreviewTypes.ts.
2026-05-03 21:55:37 -07:00
bymyself
b64aec26a2 fix: add LoadImage and LoadVideo to canvas preview node types
LoadImage and LoadVideo were missing from CANVAS_IMAGE_PREVIEW_NODE_TYPES,
so their previews were never promoted to parent subgraph nodes. This caused
promoted preview images to not display on subgraph nodes in graph mode and
app mode when the interior node was a LoadImage or LoadVideo.
2026-05-03 21:53:31 -07:00
4 changed files with 81 additions and 2 deletions

View File

@@ -6,7 +6,8 @@ import { TestIds } from '@e2e/fixtures/selectors'
import { fitToViewInstant } from '@e2e/fixtures/utils/fitToView'
import {
getPromotedWidgetNames,
getPromotedWidgetCount
getPromotedWidgetCount,
getPseudoPreviewWidgets
} from '@e2e/fixtures/utils/promotedWidgets'
async function expectPromotedWidgetNamesToContain(
@@ -93,6 +94,34 @@ test.describe(
'filename_prefix'
)
})
test('LoadImage node gets $$canvas-image-preview pseudo-widget promoted', async ({
comfyPage
}) => {
await comfyPage.workflow.loadWorkflow('default')
// Add a LoadImage node and convert to subgraph programmatically
const subgraphNodeId = await comfyPage.page.evaluate(() => {
const graph = window.app!.graph!
const node = window.LiteGraph!.createNode('LoadImage')!
node.pos = [300, 300]
graph.add(node)
const { node: subgraphNode } = graph.convertToSubgraph(
new Set([node])
)
return String(subgraphNode.id)
})
await comfyPage.nextFrame()
const pseudoWidgets = await getPseudoPreviewWidgets(
comfyPage,
subgraphNodeId
)
expect(pseudoWidgets.length).toBeGreaterThan(0)
expect(
pseudoWidgets.some(([, name]) => name === '$$canvas-image-preview')
).toBe(true)
})
})
test.describe('Promoted Widget Visibility in LiteGraph Mode', () => {

View File

@@ -4,7 +4,9 @@ export const CANVAS_IMAGE_PREVIEW_WIDGET = '$$canvas-image-preview'
const CANVAS_IMAGE_PREVIEW_NODE_TYPES = new Set([
'PreviewImage',
'SaveImage',
'GLSLShader'
'GLSLShader',
'LoadImage',
'LoadVideo'
])
export function supportsVirtualCanvasImagePreview(node: LGraphNode): boolean {

View File

@@ -203,6 +203,28 @@ describe('getPromotableWidgets', () => {
).toBe(true)
})
it('adds virtual canvas preview widget for LoadImage nodes', () => {
const node = new LGraphNode('LoadImage')
node.type = 'LoadImage'
const widgets = getPromotableWidgets(node)
expect(
widgets.some((widget) => widget.name === CANVAS_IMAGE_PREVIEW_WIDGET)
).toBe(true)
})
it('adds virtual canvas preview widget for LoadVideo nodes', () => {
const node = new LGraphNode('LoadVideo')
node.type = 'LoadVideo'
const widgets = getPromotableWidgets(node)
expect(
widgets.some((widget) => widget.name === CANVAS_IMAGE_PREVIEW_WIDGET)
).toBe(true)
})
it('does not add virtual canvas preview widget for non-image nodes', () => {
const node = new LGraphNode('TextNode')
node.addOutput('TEXT', 'STRING')
@@ -271,6 +293,25 @@ describe('promoteRecommendedWidgets', () => {
expect(updatePreviewsMock).not.toHaveBeenCalled()
})
it('eagerly promotes virtual preview widget for LoadImage nodes', () => {
const subgraph = createTestSubgraph()
const subgraphNode = createTestSubgraphNode(subgraph)
const loadImageNode = new LGraphNode('LoadImage')
loadImageNode.type = 'LoadImage'
subgraph.add(loadImageNode)
promoteRecommendedWidgets(subgraphNode)
const store = usePromotionStore()
expect(
store.isPromoted(subgraphNode.rootGraph.id, subgraphNode.id, {
sourceNodeId: String(loadImageNode.id),
sourceWidgetName: CANVAS_IMAGE_PREVIEW_WIDGET
})
).toBe(true)
expect(updatePreviewsMock).not.toHaveBeenCalled()
})
it('registers $$canvas-image-preview on configure for GLSLShader in saved workflow', () => {
// Simulate loading a saved workflow where proxyWidgets does NOT contain
// the $$canvas-image-preview entry (e.g. blueprint authored before the

View File

@@ -110,6 +110,13 @@ export const useImageUploadWidget = () => {
isAnimated
})
node.graph?.setDirtyCanvas(true)
// When this node is inside a subgraph, also dirty the root canvas so
// the parent SubgraphNode redraws and picks up the new preview via
// its onDrawBackground → updatePreviews loop.
const rootGraph = node.graph?.rootGraph
if (rootGraph && rootGraph !== node.graph) {
rootGraph.setDirtyCanvas(true)
}
}
// On load if we have a value then render the image