Fix dynamic node inputs lost when loading workflows from images/videos (#64)
ComfyUI embeds a differently serialized copy of the workflow into the images/videos a prompt produces: graphToPrompt() runs compressWidgetInputSlots() on it, which removes every widget input slot that has no link and no label - a leftover from legacy widget conversion. For the dynamic nodes of this pack, key_1/value_1/key_2/... only exist because the JS created those slots, so they were stripped while their values stayed in widgets_values. On reload only the connected slots survived, the node was rebuilt as key_0/value_0/value_2 plus one fresh empty pair, and the positional widget restoration shifted every value into the wrong widget - the DICT came back with only its first key. Label dynamically created widget inputs with their own name, so they are kept in the compressed workflow. This is a visual no-op, as the widget shows its name anyway. Workflows saved before the label existed carry none, so onConfigure stamps it on every restored dynamic widget input. Renumbering now keeps input and widget labels in sync with the new name. Verified in ComfyUI 0.43 (frontend 1.57.4): loading the PNG produced by the minimal workflow of the report restores all key/value pairs, that recreated workflow runs, and clearing a pair still renumbers correctly.
This commit is contained in:
+24
-1
@@ -266,6 +266,11 @@ app.registerExtension({
|
||||
if (isWidgetFlag) {
|
||||
addStandardWidget.call(this, name, widgetGuiType, widgetDefaultVal, widgetConfOpts);
|
||||
this.addInput(name, comfyInputType, { shape, widget: { name } });
|
||||
// ComfyUI drops widget inputs that are not connected from the workflow it
|
||||
// embeds into saved images/videos (compressWidgetInputSlots) unless the slot
|
||||
// carries a label. For dynamic inputs the slot is what keeps the whole
|
||||
// key/value pair alive, so label it with its own name to survive the round trip.
|
||||
this.inputs[this.inputs.length - 1].label = name;
|
||||
} else {
|
||||
this.addInput(name, comfyInputType, { shape });
|
||||
}
|
||||
@@ -312,10 +317,12 @@ app.registerExtension({
|
||||
const widget = this.widgets.find(w => w.name === widgetName);
|
||||
if (widget) {
|
||||
widget.name = newName;
|
||||
if (widget.label) widget.label = newName;
|
||||
}
|
||||
if (inputRef.widget) inputRef.widget.name = newName;
|
||||
}
|
||||
inputRef.name = newName;
|
||||
if (inputRef.label) inputRef.label = newName;
|
||||
}
|
||||
updateSlotIndices(this);
|
||||
};
|
||||
@@ -500,7 +507,7 @@ app.registerExtension({
|
||||
this.setDirtyCanvas(true, true);
|
||||
};
|
||||
|
||||
// --- Event Handlers (onConnectionsChange, onConnectInput, onRemoved, onWidgetChanged) ---
|
||||
// --- Event Handlers (onConnectionsChange, onConnectInput, onConfigure, onRemoved, onWidgetChanged) ---
|
||||
const onConnectionsChange = nodeType.prototype.onConnectionsChange;
|
||||
nodeType.prototype.onConnectionsChange = function (type, slotIndex, isConnected, link, ioSlot) {
|
||||
const originalReturn = onConnectionsChange?.apply(this, arguments);
|
||||
@@ -532,6 +539,22 @@ app.registerExtension({
|
||||
return result;
|
||||
}
|
||||
|
||||
const onConfigure = nodeType.prototype.onConfigure;
|
||||
nodeType.prototype.onConfigure = function () {
|
||||
const originalReturn = onConfigure?.apply(this, arguments);
|
||||
// Inputs restored from a workflow may not carry the label yet, e.g. when the
|
||||
// workflow was saved before it was introduced. Without the label ComfyUI
|
||||
// drops the slot again when the workflow is embedded into an image/video, so
|
||||
// stamp it here for every dynamic widget input.
|
||||
for (const input of this.inputs ?? []) {
|
||||
if (!input.widget || input.label || !isDynamicInput(input.name)) continue;
|
||||
input.label = input.name;
|
||||
const widget = this.widgets?.find((w) => w.name === input.widget.name);
|
||||
if (widget && !widget.label) widget.label = input.name;
|
||||
}
|
||||
return originalReturn;
|
||||
};
|
||||
|
||||
const onRemoved = nodeType.prototype.onRemoved;
|
||||
nodeType.prototype.onRemoved = function () {
|
||||
if(!isProcessingConnection){
|
||||
|
||||
Reference in New Issue
Block a user