Fix dynamic outputs breaking Kijai Set/Get nodes on workflow load
Defer removal of the 32 default Python outputs from onNodeCreated using queueMicrotask so they remain available during graph loading. ComfyUI creates all nodes before configuring them, and nodes like Kijai's SetNode resolve links during their configure step — if outputs were already removed, the resolution failed with "node input undefined". The deferred cleanup only runs for new nodes; loaded workflows set _configured=true in onConfigure first. Also adds a fallback to sync widget values from serialized outputs when widget restoration fails. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -17,17 +17,31 @@ app.registerExtension({
|
|||||||
if (w) { w.type = "hidden"; w.computeSize = () => [0, -4]; }
|
if (w) { w.type = "hidden"; w.computeSize = () => [0, -4]; }
|
||||||
}
|
}
|
||||||
|
|
||||||
// Remove all 32 default outputs from Python RETURN_TYPES
|
// Do NOT remove default outputs synchronously here.
|
||||||
while (this.outputs.length > 0) {
|
// During graph loading, ComfyUI creates all nodes (firing onNodeCreated)
|
||||||
this.removeOutput(0);
|
// before configuring them. Other nodes (e.g. Kijai Set/Get) may resolve
|
||||||
}
|
// links to our outputs during their configure step. If we remove outputs
|
||||||
|
// here, those nodes find no output slot and error out.
|
||||||
|
//
|
||||||
|
// Instead, defer cleanup: for loaded workflows onConfigure sets _configured
|
||||||
|
// before this runs; for new nodes the defaults are cleaned up.
|
||||||
|
this._configured = false;
|
||||||
|
|
||||||
// Add Refresh button
|
// Add Refresh button
|
||||||
this.addWidget("button", "Refresh Outputs", null, () => {
|
this.addWidget("button", "Refresh Outputs", null, () => {
|
||||||
this.refreshDynamicOutputs();
|
this.refreshDynamicOutputs();
|
||||||
});
|
});
|
||||||
|
|
||||||
this.setSize(this.computeSize());
|
queueMicrotask(() => {
|
||||||
|
if (!this._configured) {
|
||||||
|
// New node (not loading) — remove the 32 Python default outputs
|
||||||
|
while (this.outputs.length > 0) {
|
||||||
|
this.removeOutput(0);
|
||||||
|
}
|
||||||
|
this.setSize(this.computeSize());
|
||||||
|
app.graph?.setDirtyCanvas(true, true);
|
||||||
|
}
|
||||||
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
nodeType.prototype.refreshDynamicOutputs = async function () {
|
nodeType.prototype.refreshDynamicOutputs = async function () {
|
||||||
@@ -111,6 +125,7 @@ app.registerExtension({
|
|||||||
const origOnConfigure = nodeType.prototype.onConfigure;
|
const origOnConfigure = nodeType.prototype.onConfigure;
|
||||||
nodeType.prototype.onConfigure = function (info) {
|
nodeType.prototype.onConfigure = function (info) {
|
||||||
origOnConfigure?.apply(this, arguments);
|
origOnConfigure?.apply(this, arguments);
|
||||||
|
this._configured = true;
|
||||||
|
|
||||||
// Hide internal widgets
|
// Hide internal widgets
|
||||||
for (const name of ["output_keys", "output_types"]) {
|
for (const name of ["output_keys", "output_types"]) {
|
||||||
@@ -128,16 +143,23 @@ app.registerExtension({
|
|||||||
? otWidget.value.split(",")
|
? otWidget.value.split(",")
|
||||||
: [];
|
: [];
|
||||||
|
|
||||||
// On load, LiteGraph already restored serialized outputs with links.
|
if (keys.length > 0) {
|
||||||
// Rename and set types to match stored state (preserves links).
|
// On load, LiteGraph already restored serialized outputs with links.
|
||||||
for (let i = 0; i < this.outputs.length && i < keys.length; i++) {
|
// Rename and set types to match stored state (preserves links).
|
||||||
this.outputs[i].name = keys[i].trim();
|
for (let i = 0; i < this.outputs.length && i < keys.length; i++) {
|
||||||
if (types[i]) this.outputs[i].type = types[i];
|
this.outputs[i].name = keys[i].trim();
|
||||||
}
|
if (types[i]) this.outputs[i].type = types[i];
|
||||||
|
}
|
||||||
|
|
||||||
// Remove any extra outputs beyond the key count
|
// Remove any extra outputs beyond the key count
|
||||||
while (this.outputs.length > keys.length) {
|
while (this.outputs.length > keys.length) {
|
||||||
this.removeOutput(this.outputs.length - 1);
|
this.removeOutput(this.outputs.length - 1);
|
||||||
|
}
|
||||||
|
} else if (this.outputs.length > 0) {
|
||||||
|
// Widget values empty but serialized outputs exist — sync widgets
|
||||||
|
// from the outputs LiteGraph already restored (fallback).
|
||||||
|
if (okWidget) okWidget.value = this.outputs.map(o => o.name).join(",");
|
||||||
|
if (otWidget) otWidget.value = this.outputs.map(o => o.type).join(",");
|
||||||
}
|
}
|
||||||
|
|
||||||
this.setSize(this.computeSize());
|
this.setSize(this.computeSize());
|
||||||
|
|||||||
Reference in New Issue
Block a user