Skip to content

Commit 9f1376d

Browse files
DrJKLampcode-com
andcommitted
refactor: extract addAfterConfigureHandler to graphConfigureUtil
Move private method from ComfyApp to a standalone exported utility function. Eliminates unsafe 'as any' cast in tests by making the function directly importable. Amp-Thread-ID: https://ampcode.com/threads/T-019c9bda-0293-7528-9b0a-f72444199cbe Co-authored-by: Amp <amp@ampcode.com>
1 parent 2686bec commit 9f1376d

File tree

4 files changed

+116
-75
lines changed

4 files changed

+116
-75
lines changed

src/scripts/app.test.ts

Lines changed: 2 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -6,11 +6,7 @@ import type {
66
LGraphNode
77
} from '@/lib/litegraph/src/litegraph'
88
import { ComfyApp } from './app'
9-
import {
10-
createNode,
11-
fixLinkInputSlots,
12-
hasLegacyLinkInputSlotMismatch
13-
} from '@/utils/litegraphUtil'
9+
import { createNode } from '@/utils/litegraphUtil'
1410
import {
1511
pasteAudioNode,
1612
pasteAudioNodes,
@@ -26,9 +22,7 @@ vi.mock('@/utils/litegraphUtil', () => ({
2622
isImageNode: vi.fn(),
2723
isVideoNode: vi.fn(),
2824
isAudioNode: vi.fn(),
29-
executeWidgetsCallback: vi.fn(),
30-
fixLinkInputSlots: vi.fn(),
31-
hasLegacyLinkInputSlotMismatch: vi.fn()
25+
executeWidgetsCallback: vi.fn()
3226
}))
3327

3428
vi.mock('@/composables/usePaste', () => ({
@@ -146,37 +140,6 @@ describe('ComfyApp', () => {
146140
})
147141
})
148142

149-
describe('addAfterConfigureHandler', () => {
150-
function createConfigureGraph() {
151-
return {
152-
nodes: [],
153-
onConfigure: vi.fn()
154-
} as unknown as LGraph
155-
}
156-
157-
it('runs legacy slot repair when mismatch is detected', () => {
158-
vi.mocked(hasLegacyLinkInputSlotMismatch).mockReturnValue(true)
159-
const graph = createConfigureGraph()
160-
161-
;(app as any).addAfterConfigureHandler(graph)
162-
graph.onConfigure?.({} as never)
163-
164-
expect(hasLegacyLinkInputSlotMismatch).toHaveBeenCalledWith(graph)
165-
expect(fixLinkInputSlots).toHaveBeenCalledWith(graph)
166-
})
167-
168-
it('skips legacy slot repair when no mismatch is present', () => {
169-
vi.mocked(hasLegacyLinkInputSlotMismatch).mockReturnValue(false)
170-
const graph = createConfigureGraph()
171-
172-
;(app as any).addAfterConfigureHandler(graph)
173-
graph.onConfigure?.({} as never)
174-
175-
expect(hasLegacyLinkInputSlotMismatch).toHaveBeenCalledWith(graph)
176-
expect(fixLinkInputSlots).not.toHaveBeenCalled()
177-
})
178-
})
179-
180143
describe('handleAudioFileList', () => {
181144
it('should create audio nodes and select them', async () => {
182145
const mockNode1 = createMockNode({ id: 1, type: 'LoadAudio' })

src/scripts/app.ts

Lines changed: 2 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,7 @@ import { reactive, unref } from 'vue'
55
import { shallowRef } from 'vue'
66

77
import { useCanvasPositionConversion } from '@/composables/element/useCanvasPositionConversion'
8-
import { layoutStore } from '@/renderer/core/layout/store/layoutStore'
9-
import { flushScheduledSlotLayoutSync } from '@/renderer/extensions/vueNodes/composables/useSlotElementTracking'
8+
import { addAfterConfigureHandler } from '@/utils/graphConfigureUtil'
109

1110
import { st, t } from '@/i18n'
1211
import type { IContextMenuValue } from '@/lib/litegraph/src/interfaces'
@@ -87,8 +86,6 @@ import {
8786
import {
8887
executeWidgetsCallback,
8988
createNode,
90-
fixLinkInputSlots,
91-
hasLegacyLinkInputSlotMismatch,
9289
isImageNode,
9390
isVideoNode
9491
} from '@/utils/litegraphUtil'
@@ -792,37 +789,6 @@ export class ComfyApp {
792789
}
793790
}
794791

795-
private addAfterConfigureHandler(graph: LGraph) {
796-
const { onConfigure } = graph
797-
graph.onConfigure = function (...args) {
798-
// Set pending sync flag to suppress link rendering until slots are synced
799-
if (LiteGraph.vueNodesMode) {
800-
layoutStore.setPendingSlotSync(true)
801-
}
802-
803-
try {
804-
if (hasLegacyLinkInputSlotMismatch(this)) fixLinkInputSlots(this)
805-
806-
// Fire callbacks before the onConfigure, this is used by widget inputs to setup the config
807-
triggerCallbackOnAllNodes(this, 'onGraphConfigured')
808-
809-
const r = onConfigure?.apply(this, args)
810-
811-
// Fire after onConfigure, used by primitives to generate widget using input nodes config
812-
triggerCallbackOnAllNodes(this, 'onAfterGraphConfigured')
813-
814-
return r
815-
} finally {
816-
// Flush pending slot layout syncs to fix link alignment after undo/redo
817-
// Using finally ensures links aren't permanently suppressed if an error occurs
818-
if (LiteGraph.vueNodesMode) {
819-
flushScheduledSlotLayoutSync()
820-
app.canvas?.setDirty(true, true)
821-
}
822-
}
823-
}
824-
}
825-
826792
/**
827793
* Set up the app on the page
828794
*/
@@ -861,7 +827,7 @@ export class ComfyApp {
861827
}
862828
})
863829

864-
this.addAfterConfigureHandler(graph)
830+
addAfterConfigureHandler(graph, () => this.canvas)
865831

866832
this.rootGraphInternal = graph
867833
this.canvas = new LGraphCanvas(canvasEl, graph)
Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
import { beforeEach, describe, expect, it, vi } from 'vitest'
2+
3+
import type { LGraph } from '@/lib/litegraph/src/litegraph'
4+
import {
5+
fixLinkInputSlots,
6+
hasLegacyLinkInputSlotMismatch
7+
} from '@/utils/litegraphUtil'
8+
9+
import { addAfterConfigureHandler } from './graphConfigureUtil'
10+
11+
vi.mock('@/utils/litegraphUtil', () => ({
12+
fixLinkInputSlots: vi.fn(),
13+
hasLegacyLinkInputSlotMismatch: vi.fn()
14+
}))
15+
16+
vi.mock('@/utils/graphTraversalUtil', () => ({
17+
triggerCallbackOnAllNodes: vi.fn()
18+
}))
19+
20+
vi.mock('@/renderer/core/layout/store/layoutStore', () => ({
21+
layoutStore: { setPendingSlotSync: vi.fn() }
22+
}))
23+
24+
vi.mock(
25+
'@/renderer/extensions/vueNodes/composables/useSlotElementTracking',
26+
() => ({
27+
flushScheduledSlotLayoutSync: vi.fn()
28+
})
29+
)
30+
31+
function createConfigureGraph(): LGraph {
32+
return {
33+
nodes: [],
34+
onConfigure: vi.fn()
35+
} as Partial<LGraph> as LGraph
36+
}
37+
38+
describe('addAfterConfigureHandler', () => {
39+
beforeEach(() => {
40+
vi.clearAllMocks()
41+
})
42+
43+
it('runs legacy slot repair when mismatch is detected', () => {
44+
vi.mocked(hasLegacyLinkInputSlotMismatch).mockReturnValue(true)
45+
const graph = createConfigureGraph()
46+
47+
addAfterConfigureHandler(graph, () => undefined)
48+
graph.onConfigure!.call(
49+
graph,
50+
{} as Parameters<NonNullable<LGraph['onConfigure']>>[0]
51+
)
52+
53+
expect(hasLegacyLinkInputSlotMismatch).toHaveBeenCalledWith(graph)
54+
expect(fixLinkInputSlots).toHaveBeenCalledWith(graph)
55+
})
56+
57+
it('skips legacy slot repair when no mismatch is present', () => {
58+
vi.mocked(hasLegacyLinkInputSlotMismatch).mockReturnValue(false)
59+
const graph = createConfigureGraph()
60+
61+
addAfterConfigureHandler(graph, () => undefined)
62+
graph.onConfigure!.call(
63+
graph,
64+
{} as Parameters<NonNullable<LGraph['onConfigure']>>[0]
65+
)
66+
67+
expect(hasLegacyLinkInputSlotMismatch).toHaveBeenCalledWith(graph)
68+
expect(fixLinkInputSlots).not.toHaveBeenCalled()
69+
})
70+
})

src/utils/graphConfigureUtil.ts

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
import type { LGraph, LGraphCanvas } from '@/lib/litegraph/src/litegraph'
2+
import { LiteGraph } from '@/lib/litegraph/src/litegraph'
3+
import { layoutStore } from '@/renderer/core/layout/store/layoutStore'
4+
import { flushScheduledSlotLayoutSync } from '@/renderer/extensions/vueNodes/composables/useSlotElementTracking'
5+
import { triggerCallbackOnAllNodes } from '@/utils/graphTraversalUtil'
6+
import {
7+
fixLinkInputSlots,
8+
hasLegacyLinkInputSlotMismatch
9+
} from '@/utils/litegraphUtil'
10+
11+
/**
12+
* Wraps graph.onConfigure to add legacy slot repair,
13+
* node configure callbacks, and layout sync flushing.
14+
*/
15+
export function addAfterConfigureHandler(
16+
graph: LGraph,
17+
getCanvas: () => LGraphCanvas | undefined
18+
) {
19+
const { onConfigure } = graph
20+
graph.onConfigure = function (...args) {
21+
if (LiteGraph.vueNodesMode) {
22+
layoutStore.setPendingSlotSync(true)
23+
}
24+
25+
try {
26+
if (hasLegacyLinkInputSlotMismatch(this)) fixLinkInputSlots(this)
27+
28+
triggerCallbackOnAllNodes(this, 'onGraphConfigured')
29+
30+
const r = onConfigure?.apply(this, args)
31+
32+
triggerCallbackOnAllNodes(this, 'onAfterGraphConfigured')
33+
34+
return r
35+
} finally {
36+
if (LiteGraph.vueNodesMode) {
37+
flushScheduledSlotLayoutSync()
38+
getCanvas()?.setDirty(true, true)
39+
}
40+
}
41+
}
42+
}

0 commit comments

Comments
 (0)