Files
2026-03-01 13:20:56 -05:00

129 lines
4.8 KiB
JavaScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import { app } from "../../scripts/app.js";
const NODE_NAME = "DynamicPromptComposer";
const MAX_SECTIONS = 20;
// ─── widget visibility ───────────────────────────────────────────────────────
function hideWidget(w) {
if (w.hidden) return;
w.hidden = true;
w.options.hidden = true;
}
function showWidget(w) {
if (!w.hidden) return;
w.hidden = false;
w.options.hidden = false;
}
function getSectionWidgets(node, i) {
return [
node.widgets?.find((w) => w.name === `section_${i}`),
node.widgets?.find((w) => w.name === `section_${i}_mode`),
node.widgets?.find((w) => w.name === `section_${i}_start_index`),
];
}
function applyVisibility(node, count) {
for (let i = 0; i < MAX_SECTIONS; i++) {
const [textW, modeW, fixedIdxW] = getSectionWidgets(node, i);
if (!textW || !modeW) continue;
const active = i < count;
(active ? showWidget : hideWidget)(textW);
(active ? showWidget : hideWidget)(modeW);
if (fixedIdxW) {
(active ? showWidget : hideWidget)(fixedIdxW);
}
}
node.size[1] = node.computeSize()[1];
app.graph?.setDirtyCanvas(true);
}
// ─── buttons ─────────────────────────────────────────────────────────────────
function refreshButtons(node) {
node.widgets = (node.widgets ?? []).filter((w) => !w._dpcButton);
const addBtn = node.addWidget(
"button",
"+ Add Section",
null,
() => {
const count = node.properties._sectionCount ?? 1;
if (count >= MAX_SECTIONS) return;
node.properties._sectionCount = count + 1;
applyVisibility(node, count + 1);
},
{ serialize: false }
);
addBtn._dpcButton = true;
const removeBtn = node.addWidget(
"button",
"- Remove Section",
null,
() => {
const count = node.properties._sectionCount ?? 1;
if (count <= 1) return;
// Clear the removed section so Python ignores it on future runs
const [textW] = getSectionWidgets(node, count - 1);
if (textW) {
textW.value = "";
if (textW.element) textW.element.value = "";
}
node.properties._sectionCount = count - 1;
applyVisibility(node, count - 1);
},
{ serialize: false }
);
removeBtn._dpcButton = true;
}
// ─── extension ───────────────────────────────────────────────────────────────
app.registerExtension({
name: "DynamicPromptComposer.Extension",
async beforeRegisterNodeDef(nodeType, nodeData) {
if (nodeData.name !== NODE_NAME) return;
// Fresh node dropped from the menu.
// setTimeout defers visibility setup until after ComfyUI finishes
// registering all widget objects, so section_9 is present when we
// try to hide it.
const origOnCreated = nodeType.prototype.onNodeCreated;
nodeType.prototype.onNodeCreated = function () {
origOnCreated?.apply(this, arguments);
this.properties ??= {};
this.properties._sectionCount ??= 3;
// Assume fresh until onConfigure proves otherwise (see below).
this._dpcIsNew = true;
const self = this;
setTimeout(() => {
applyVisibility(self, self.properties._sectionCount);
refreshButtons(self);
// Only double the width for truly new nodes. For nodes loaded
// from a saved workflow, onConfigure fires synchronously before
// this setTimeout and clears the flag, so we skip the doubling.
if (self._dpcIsNew) {
self.size[0] = Math.max(self.size[0], 400);
app.graph?.setDirtyCanvas(true);
}
}, 0);
};
// Node loaded from a saved workflow
const origOnConfigure = nodeType.prototype.onConfigure;
nodeType.prototype.onConfigure = function (config) {
// Let LiteGraph restore widget values and node.properties first.
// Clear the new-node flag so the setTimeout above skips width doubling.
origOnConfigure?.apply(this, arguments);
this._dpcIsNew = false;
const count = this.properties?._sectionCount ?? 1;
applyVisibility(this, count);
refreshButtons(this);
};
},
});