From 4af3a0628bc4b65ca77c9ed7001d225978b688bf Mon Sep 17 00:00:00 2001 From: StableLlama Date: Tue, 3 Nov 2026 23:32:20 +0100 Subject: [PATCH 1/2] 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. --- web/js/dynamicnode.js | 25 ++++++++++++++++++++++++- 1 file changed, 24 insertions(+), 1 deletion(-) diff --git a/web/js/dynamicnode.js b/web/js/dynamicnode.js index fdb9aa4..7c8eae1 100644 --- a/web/js/dynamicnode.js +++ b/web/js/dynamicnode.js @@ -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){ From ed5efc2f91a2e8efc7bd2de6b6fb6849d1885726 Mon Sep 17 00:00:00 2001 From: StableLlama Date: Tue, 3 Nov 2026 23:35:16 +0100 Subject: [PATCH 2/2] Bump version to 2.2.1 and update changelog Patch release for the two dynamic input fixes that landed after 2.2.0: clearing a key/value pair corrupting the node, and workflows recreated from a produced image/video losing their extra pairs. The changelog section doubles as the release notes for the Comfy registry, which the publish workflow extracts from CHANGELOG.md. --- CHANGELOG.md | 16 ++++++++++++++++ pyproject.toml | 2 +- 2 files changed, 17 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ab6c4e8..6b2333d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,22 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## [2.2.1] - 2026-11-03 + +Fixed: +- Dynamic input nodes (e.g. "create DICT"): clearing a key/value pair could + leave the node with input names and widget names out of sync. The prompt was + then built from the stale widget names, so the container was created empty + and the connection was drawn next to the wrong row. Removed widgets are now + also removed from ComfyUI's widget value store, which keeps both in sync. +- Dynamic input nodes (e.g. "create DICT" and "create LIST") lost their extra + key/value pairs when the workflow was recreated by dragging a produced image + or video back into ComfyUI: the workflow that ComfyUI embeds into those files + drops unconnected widget inputs, and for these nodes that slot is what keeps + a pair alive, so the values ended up on the wrong rows and the container was + created with only its first entry. The dynamic inputs are now kept in that + embedded workflow, also for workflows that were saved before this fix. + ## [2.2.0] - 2026-10-16 Added: diff --git a/pyproject.toml b/pyproject.toml index 7b45cb8..6f8a935 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta" [project] name = "basic_data_handling" -version = "2.2.0" +version = "2.2.1" description = """Basic Python functions for manipulating data that every programmer is used to, lightweight with no additional dependencies. Supported data types: