diff --git a/src/core/graph/subgraph/promotedWidgetTypes.ts b/src/core/graph/subgraph/promotedWidgetTypes.ts index b68e177438..637e367695 100644 --- a/src/core/graph/subgraph/promotedWidgetTypes.ts +++ b/src/core/graph/subgraph/promotedWidgetTypes.ts @@ -31,6 +31,12 @@ export interface PromotedWidgetView extends IBaseWidget { */ readonly sourceNodeId: string readonly sourceWidgetName: string + + /** + * Per-instance value hydration that writes only to host widget state, never + * cascading into the shared interior widget. Used during configure/clone. + */ + hydrateHostValue(value: IBaseWidget['value']): void } export function isPromotedWidgetView( diff --git a/src/core/graph/subgraph/promotedWidgetView.ts b/src/core/graph/subgraph/promotedWidgetView.ts index 9ddec2df21..e30d6492d6 100644 --- a/src/core/graph/subgraph/promotedWidgetView.ts +++ b/src/core/graph/subgraph/promotedWidgetView.ts @@ -268,6 +268,16 @@ class PromotedWidgetView implements IPromotedWidgetView { if (state) state.label = value } + /** + * Write a value into this host's widget store entry without cascading into + * the shared interior widget — the only safe path for per-instance hydration + * during `configure()` and clone, where multiple SubgraphNode instances + * reference the same shared interior nodes. + */ + hydrateHostValue(value: IBaseWidget['value']): void { + this.setHostWidgetState(value) + } + /** * Returns the cached bound subgraph slot reference, refreshing only when * the subgraph node's input list has changed (length mismatch). diff --git a/src/lib/litegraph/src/subgraph/SubgraphNode.ts b/src/lib/litegraph/src/subgraph/SubgraphNode.ts index 482577cc28..aad7f9e22d 100644 --- a/src/lib/litegraph/src/subgraph/SubgraphNode.ts +++ b/src/lib/litegraph/src/subgraph/SubgraphNode.ts @@ -706,6 +706,12 @@ export class SubgraphNode extends LGraphNode implements BaseLGraph { this._applyPromotedWidgetValues(info.widgets_values) } + /** + * Hydrate per-instance promoted widget values into this host's widget value + * store entry. Routing through `PromotedWidgetView.set value` would cascade + * into the shared interior widget, stomping every other SubgraphNode + * instance that references the same shared interior. + */ private _applyPromotedWidgetValues( widgetValues: ExportedSubgraphInstance['widgets_values'] ): void { @@ -713,10 +719,10 @@ export class SubgraphNode extends LGraphNode implements BaseLGraph { let valueIndex = 0 for (const input of this.inputs) { - const widget = input._widget - if (!widget || !isPromotedWidgetView(widget)) continue + const view = input._widget + if (!view || !isPromotedWidgetView(view)) continue if (valueIndex >= widgetValues.length) return - widget.value = widgetValues[valueIndex] + view.hydrateHostValue(widgetValues[valueIndex]) valueIndex += 1 } }