mirror of
https://github.com/Comfy-Org/ComfyUI_frontend.git
synced 2026-04-20 06:20:11 +00:00
fix: prevent nested SubgraphNode input slots from doubling on reload (#10187)
## Summary - Fix nested SubgraphNode input slots doubling on each page reload - Root cause: during configure, `_configureSubgraph` recreates `SubgraphInput` objects with new references, and the `input-added` event handler used `===` identity check which failed for these new objects, causing `addInput()` to duplicate inputs - Add `id`-based fallback matching in the `input-added` handler and rebind `_subgraphSlot` with re-registered listeners ## Changes **`SubgraphNode.ts:614-622`**: Add UUID `id` fallback to the `===` reference check in the `input-added` event handler. When a stale reference is matched by id, call `_addSubgraphInputListeners()` to update `_subgraphSlot` and re-register listeners on the new `SubgraphInput` object. **`SubgraphNode.test.ts`**: 2 regression tests for nested subgraph reconfigure scenarios. ## Test plan - [x] Existing SubgraphNode tests pass (6 passed, 34 skipped) - [x] New tests verify inputs don't duplicate after single and repeated reconfigure cycles - [x] Manual: create a subgraph containing another subgraph node, save, reload — input slots should remain unchanged ┆Issue is synchronized with this [Notion page](https://www.notion.so/PR-10187-fix-prevent-nested-SubgraphNode-input-slots-from-doubling-on-reload-3266d73d3650817286abea52365a626e) by [Unito](https://www.unito.io)
This commit is contained in:
@@ -698,6 +698,55 @@ describe('SubgraphNode duplicate input pruning (#9977)', () => {
|
||||
})
|
||||
})
|
||||
|
||||
describe('Nested SubgraphNode duplicate input prevention', () => {
|
||||
it('should not duplicate inputs when the referenced subgraph is reconfigured', () => {
|
||||
setActivePinia(createTestingPinia({ stubActions: false }))
|
||||
|
||||
const subgraph = createTestSubgraph({
|
||||
inputs: [
|
||||
{ name: 'a', type: 'STRING' },
|
||||
{ name: 'b', type: 'NUMBER' }
|
||||
]
|
||||
})
|
||||
|
||||
const node = createTestSubgraphNode(subgraph)
|
||||
expect(node.inputs).toHaveLength(2)
|
||||
|
||||
// Simulate what happens during nested subgraph configure:
|
||||
// B.configure() calls _configureSubgraph(), which recreates SubgraphInput
|
||||
// objects and dispatches 'input-added' events with new references.
|
||||
const serialized = subgraph.asSerialisable()
|
||||
subgraph.configure(serialized)
|
||||
|
||||
// The SubgraphNode's event listener should recognize existing inputs
|
||||
// by ID and NOT add duplicates.
|
||||
expect(node.inputs).toHaveLength(2)
|
||||
expect(node.inputs.every((i) => i._subgraphSlot)).toBe(true)
|
||||
})
|
||||
|
||||
it('should not accumulate inputs across multiple reconfigure cycles', () => {
|
||||
setActivePinia(createTestingPinia({ stubActions: false }))
|
||||
|
||||
const subgraph = createTestSubgraph({
|
||||
inputs: [
|
||||
{ name: 'x', type: 'IMAGE' },
|
||||
{ name: 'y', type: 'VAE' }
|
||||
]
|
||||
})
|
||||
|
||||
const node = createTestSubgraphNode(subgraph)
|
||||
expect(node.inputs).toHaveLength(2)
|
||||
|
||||
for (let i = 0; i < 5; i++) {
|
||||
const serialized = subgraph.asSerialisable()
|
||||
subgraph.configure(serialized)
|
||||
}
|
||||
|
||||
expect(node.inputs).toHaveLength(2)
|
||||
expect(node.inputs.map((i) => i.name)).toEqual(['x', 'y'])
|
||||
})
|
||||
})
|
||||
|
||||
describe('SubgraphNode promotion view keys', () => {
|
||||
it('distinguishes tuples that differ only by colon placement', () => {
|
||||
setActivePinia(createTestingPinia({ stubActions: false }))
|
||||
|
||||
@@ -612,9 +612,14 @@ export class SubgraphNode extends LGraphNode implements BaseLGraph {
|
||||
const subgraphInput = e.detail.input
|
||||
const { name, type } = subgraphInput
|
||||
const existingInput = this.inputs.find(
|
||||
(input) => input._subgraphSlot === subgraphInput
|
||||
(input) =>
|
||||
input._subgraphSlot === subgraphInput ||
|
||||
(input._subgraphSlot && input._subgraphSlot.id === subgraphInput.id)
|
||||
)
|
||||
if (existingInput) {
|
||||
// Rebind to the new SubgraphInput object and re-register listeners
|
||||
// (configure recreates SubgraphInput objects with the same id)
|
||||
this._addSubgraphInputListeners(subgraphInput, existingInput)
|
||||
const linkId = subgraphInput.linkIds[0]
|
||||
if (linkId === undefined) return
|
||||
|
||||
|
||||
Reference in New Issue
Block a user