mirror of
https://github.com/Comfy-Org/ComfyUI_frontend.git
synced 2026-05-24 14:45:36 +00:00
fix(subgraph): isolate duplicated host widget values via host-only hydration
Cloning a SubgraphNode triggers serialize -> create -> configure on the new instance. The previous _applyPromotedWidgetValues went through PromotedWidgetView.set value, which cascades into getLinkedInputWidgets and resolveAtHost?.widget.value writes — stomping the shared interior widget state across every other SubgraphNode instance referencing the same shared interior. The DOM widget then races for ownership and the duplicated subgraph's textarea becomes unreachable. Add PromotedWidgetView.hydrateHostValue(value) that writes only to the host's widget value store entry (keyed on subgraphNode.id), and rewrite _applyPromotedWidgetValues to call it. Per-instance values stay isolated; interior widget state is no longer touched by the configure round-trip. Amp-Thread-ID: https://ampcode.com/threads/T-019e0e0d-1937-758e-8a9b-4f54716b0aa2 Co-authored-by: Amp <amp@ampcode.com>
This commit is contained in:
@@ -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(
|
||||
|
||||
@@ -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).
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user