From 7ba772155800ca247020e64b7dcc49f1f3fc1f4b Mon Sep 17 00:00:00 2001 From: Shaun Johnston Date: Tue, 16 Dec 2025 09:47:20 +1000 Subject: [PATCH] interactive --- js/multi_replace.js | 228 +++++++++++++++++++++----------------------- 1 file changed, 109 insertions(+), 119 deletions(-) diff --git a/js/multi_replace.js b/js/multi_replace.js index cc807ad..9623283 100644 --- a/js/multi_replace.js +++ b/js/multi_replace.js @@ -10,99 +10,114 @@ const MAX_PAIRS = 80; app.registerExtension({ name: "comfyui-multi-replace", + async nodeCreated(node) { + if (node.comfyClass !== "FindReplacePairs") return; + + node.pairCount = 1; + + // Find all the find/replace widgets and store references + const pairWidgets = []; + if (node.widgets) { + for (const widget of node.widgets) { + const match = widget.name?.match(/^(find|replace)_(\d+)$/); + if (match) { + pairWidgets.push(widget); + } + } + } + + // Function to update visibility + node.updatePairVisibility = function() { + if (!this.widgets) return; + + for (const widget of this.widgets) { + const match = widget.name?.match(/^(find|replace)_(\d+)$/); + if (match) { + const pairIndex = parseInt(match[2]); + const shouldShow = pairIndex <= this.pairCount; + + if (shouldShow) { + widget.type = widget._originalType || widget.type || "STRING"; + if (widget._originalComputeSize) { + widget.computeSize = widget._originalComputeSize; + } else { + delete widget.computeSize; + } + } else { + if (!widget._originalType && widget.type !== "hidden") { + widget._originalType = widget.type; + widget._originalComputeSize = widget.computeSize; + } + widget.type = "hidden"; + widget.computeSize = () => [0, -4]; + } + } + } + + this.setSize(this.computeSize()); + app.graph.setDirtyCanvas(true, true); + }; + + // Add pair function + node.addNewPair = function() { + if (this.pairCount >= MAX_PAIRS) return; + this.pairCount++; + this.updatePairVisibility(); + }; + + // Remove pair function + node.removeLastPair = function() { + if (this.pairCount <= 1) return; + + // Clear the values + const findWidget = this.widgets?.find(w => w.name === `find_${this.pairCount}`); + const replaceWidget = this.widgets?.find(w => w.name === `replace_${this.pairCount}`); + if (findWidget) findWidget.value = ""; + if (replaceWidget) replaceWidget.value = ""; + + this.pairCount--; + this.updatePairVisibility(); + }; + + // Add buttons AFTER all the pair widgets + // First, do initial hide of extra pairs + node.updatePairVisibility(); + + // Now add the buttons at the end + const addBtn = node.addWidget("button", "add_pair_btn", "➕ Add Pair", () => { + node.addNewPair(); + }); + addBtn.serializeValue = () => undefined; // Don't serialize button + + const removeBtn = node.addWidget("button", "remove_pair_btn", "➖ Remove Last Pair", () => { + node.removeLastPair(); + }); + removeBtn.serializeValue = () => undefined; // Don't serialize button + + // Override serialize to save pair count + const origSerialize = node.serialize; + node.serialize = function() { + const data = origSerialize ? origSerialize.call(this) : {}; + data.pairCount = this.pairCount; + return data; + }; + + // Handle loading saved pair count + const origConfigure = node.configure; + node.configure = function(data) { + if (origConfigure) { + origConfigure.call(this, data); + } + if (data.pairCount) { + this.pairCount = data.pairCount; + setTimeout(() => this.updatePairVisibility(), 10); + } + }; + }, + async beforeRegisterNodeDef(nodeType, nodeData, app) { if (nodeData.name === "FindReplacePairs") { - const origOnNodeCreated = nodeType.prototype.onNodeCreated; - - nodeType.prototype.onNodeCreated = function() { - if (origOnNodeCreated) { - origOnNodeCreated.apply(this, arguments); - } - - this.pairCount = 1; - - // Add the "Add Pair" button at the end - this.addWidget("button", "➕ Add Pair", null, () => { - this.addNewPair(); - }); - - // Add the "Remove Last Pair" button - this.addWidget("button", "➖ Remove Last Pair", null, () => { - this.removeLastPair(); - }); - - // Initial visibility update - setTimeout(() => { - this.updatePairVisibility(); - }, 50); - }; - - // Show the next pair - nodeType.prototype.addNewPair = function() { - if (this.pairCount >= MAX_PAIRS) return; - this.pairCount++; - this.updatePairVisibility(); - }; - - // Hide the last pair - nodeType.prototype.removeLastPair = function() { - if (this.pairCount <= 1) return; - - // Clear the values of the pair being removed - const findWidget = this.widgets?.find(w => w.name === `find_${this.pairCount}`); - const replaceWidget = this.widgets?.find(w => w.name === `replace_${this.pairCount}`); - if (findWidget) findWidget.value = ""; - if (replaceWidget) replaceWidget.value = ""; - - this.pairCount--; - this.updatePairVisibility(); - }; - - // Update visibility of pair widgets - nodeType.prototype.updatePairVisibility = function() { - if (!this.widgets) return; - - for (const widget of this.widgets) { - const match = widget.name?.match(/^(find|replace)_(\d+)$/); - if (match) { - const pairIndex = parseInt(match[2]); - const shouldShow = pairIndex <= this.pairCount; - - if (shouldShow) { - // Show widget - widget.type = widget._originalType || widget.type; - if (widget._originalComputeSize) { - widget.computeSize = widget._originalComputeSize; - } - } else { - // Hide widget - if (widget.type !== "hidden") { - widget._originalType = widget.type; - widget._originalComputeSize = widget.computeSize; - } - widget.type = "hidden"; - widget.computeSize = () => [0, -4]; - } - } - } - - // Also hide/show the corresponding inputs - if (this.inputs) { - for (const input of this.inputs) { - const match = input.name?.match(/^(find|replace)_(\d+)$/); - if (match) { - const pairIndex = parseInt(match[2]); - // We can't truly hide inputs, but we can mark them - input._hidden = pairIndex > this.pairCount; - } - } - } - - this.setSize(this.computeSize()); - app.graph.setDirtyCanvas(true, true); - }; - - // Override getExtraMenuOptions to add right-click options + // Add right-click menu options const origGetExtraMenuOptions = nodeType.prototype.getExtraMenuOptions; nodeType.prototype.getExtraMenuOptions = function(_, options) { if (origGetExtraMenuOptions) { @@ -113,41 +128,16 @@ app.registerExtension({ { content: "Add Find/Replace Pair", disabled: this.pairCount >= MAX_PAIRS, - callback: () => this.addNewPair() + callback: () => this.addNewPair?.() }, { content: "Remove Last Pair", disabled: this.pairCount <= 1, - callback: () => this.removeLastPair() + callback: () => this.removeLastPair?.() }, - null // separator + null ); }; - - // Save pair count - const origOnSerialize = nodeType.prototype.onSerialize; - nodeType.prototype.onSerialize = function(info) { - if (origOnSerialize) { - origOnSerialize.apply(this, arguments); - } - info.pairCount = this.pairCount; - }; - - // Restore pair count and visibility - const origOnConfigure = nodeType.prototype.onConfigure; - nodeType.prototype.onConfigure = function(info) { - if (origOnConfigure) { - origOnConfigure.apply(this, arguments); - } - - if (info.pairCount) { - this.pairCount = info.pairCount; - } - - setTimeout(() => { - this.updatePairVisibility(); - }, 50); - }; } } });