[backport] Fix cyclic prototype errors with subgraphNodes (#5650)

Cherry-pick of PR #5637: Fix cyclic prototype errors with subgraphNodes

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-5650-Hotfix-Fix-cyclic-prototype-errors-with-subgraphNodes-2736d73d365081238853d5b445d08e7f)
by [Unito](https://www.unito.io)

Co-authored-by: AustinMroz <austin@comfy.org>
This commit is contained in:
Christian Byrne
2025-09-18 18:59:11 -07:00
committed by GitHub
parent dac73dc7ed
commit 12f23dac4f

View File

@@ -4,11 +4,14 @@ import { LGraphCanvas } from '@/lib/litegraph/src/LGraphCanvas'
import { LGraphNode } from '@/lib/litegraph/src/LGraphNode'
import { LLink, type ResolvedConnection } from '@/lib/litegraph/src/LLink'
import { RecursionError } from '@/lib/litegraph/src/infrastructure/RecursionError'
import type { ISubgraphInput } from '@/lib/litegraph/src/interfaces'
import {
type INodeInputSlot,
type ISlotType,
type NodeId
import type {
ISubgraphInput,
IWidgetLocator
} from '@/lib/litegraph/src/interfaces'
import type {
INodeInputSlot,
ISlotType,
NodeId
} from '@/lib/litegraph/src/litegraph'
import { NodeInputSlot } from '@/lib/litegraph/src/node/NodeInputSlot'
import { NodeOutputSlot } from '@/lib/litegraph/src/node/NodeOutputSlot'
@@ -78,9 +81,10 @@ export class SubgraphNode extends LGraphNode implements BaseLGraph {
const existingInput = this.inputs.find((i) => i.name == name)
if (existingInput) {
const linkId = subgraphInput.linkIds[0]
const { inputNode } = subgraph.links[linkId].resolve(subgraph)
const { inputNode, input } = subgraph.links[linkId].resolve(subgraph)
const widget = inputNode?.widgets?.find?.((w) => w.name == name)
if (widget) this.#setWidget(subgraphInput, existingInput, widget)
if (widget)
this.#setWidget(subgraphInput, existingInput, widget, input?.widget)
return
}
const input = this.addInput(name, type)
@@ -185,13 +189,14 @@ export class SubgraphNode extends LGraphNode implements BaseLGraph {
subgraphInput.events.addEventListener(
'input-connected',
() => {
(e) => {
if (input._widget) return
const widget = subgraphInput._widget
if (!widget) return
this.#setWidget(subgraphInput, input, widget)
const widgetLocator = e.detail.input.widget
this.#setWidget(subgraphInput, input, widget, widgetLocator)
},
{ signal }
)
@@ -301,7 +306,7 @@ export class SubgraphNode extends LGraphNode implements BaseLGraph {
const widget = resolved.inputNode.getWidgetFromSlot(resolved.input)
if (!widget) continue
this.#setWidget(subgraphInput, input, widget)
this.#setWidget(subgraphInput, input, widget, resolved.input.widget)
break
}
}
@@ -310,11 +315,13 @@ export class SubgraphNode extends LGraphNode implements BaseLGraph {
#setWidget(
subgraphInput: Readonly<SubgraphInput>,
input: INodeInputSlot,
widget: Readonly<IBaseWidget>
widget: Readonly<IBaseWidget>,
inputWidget: IWidgetLocator | undefined
) {
// Use the first matching widget
const targetWidget = toConcreteWidget(widget, this)
const promotedWidget = targetWidget.createCopyForNode(this)
const promotedWidget = toConcreteWidget(widget, this).createCopyForNode(
this
)
Object.assign(promotedWidget, {
get name() {
@@ -372,11 +379,9 @@ export class SubgraphNode extends LGraphNode implements BaseLGraph {
// NOTE: This code creates linked chains of prototypes for passing across
// multiple levels of subgraphs. As part of this, it intentionally avoids
// creating new objects. Have care when making changes.
const backingInput =
targetWidget.node.findInputSlot(widget.name, true)?.widget ?? {}
input.widget ??= { name: subgraphInput.name }
input.widget.name = subgraphInput.name
Object.setPrototypeOf(input.widget, backingInput)
if (inputWidget) Object.setPrototypeOf(input.widget, inputWidget)
input._widget = promotedWidget
}