From ae7a4ebbf133786620c9d87041e40158a56246c0 Mon Sep 17 00:00:00 2001 From: Joel Trauger Date: Mon, 25 Dec 2023 23:45:28 -0600 Subject: [PATCH 1/5] added some stuff. --- .gitignore | 160 ++++++++++++++++++++++++++ __init__.py | 10 +- __pycache__/__init__.cpython-310.pyc | Bin 611 -> 697 bytes __pycache__/harronode.cpython-310.pyc | Bin 3898 -> 4903 bytes harronode.py | 66 +++++++++-- harroserver.py | 10 ++ js/harronode.js | 53 ++++++++- 7 files changed, 283 insertions(+), 16 deletions(-) create mode 100644 .gitignore create mode 100644 harroserver.py diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..6769e21 --- /dev/null +++ b/.gitignore @@ -0,0 +1,160 @@ +# Byte-compiled / optimized / DLL files +__pycache__/ +*.py[cod] +*$py.class + +# C extensions +*.so + +# Distribution / packaging +.Python +build/ +develop-eggs/ +dist/ +downloads/ +eggs/ +.eggs/ +lib/ +lib64/ +parts/ +sdist/ +var/ +wheels/ +share/python-wheels/ +*.egg-info/ +.installed.cfg +*.egg +MANIFEST + +# PyInstaller +# Usually these files are written by a python script from a template +# before PyInstaller builds the exe, so as to inject date/other infos into it. +*.manifest +*.spec + +# Installer logs +pip-log.txt +pip-delete-this-directory.txt + +# Unit test / coverage reports +htmlcov/ +.tox/ +.nox/ +.coverage +.coverage.* +.cache +nosetests.xml +coverage.xml +*.cover +*.py,cover +.hypothesis/ +.pytest_cache/ +cover/ + +# Translations +*.mo +*.pot + +# Django stuff: +*.log +local_settings.py +db.sqlite3 +db.sqlite3-journal + +# Flask stuff: +instance/ +.webassets-cache + +# Scrapy stuff: +.scrapy + +# Sphinx documentation +docs/_build/ + +# PyBuilder +.pybuilder/ +target/ + +# Jupyter Notebook +.ipynb_checkpoints + +# IPython +profile_default/ +ipython_config.py + +# pyenv +# For a library or package, you might want to ignore these files since the code is +# intended to run in multiple environments; otherwise, check them in: +# .python-version + +# pipenv +# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. +# However, in case of collaboration, if having platform-specific dependencies or dependencies +# having no cross-platform support, pipenv may install dependencies that don't work, or not +# install all needed dependencies. +#Pipfile.lock + +# poetry +# Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control. +# This is especially recommended for binary packages to ensure reproducibility, and is more +# commonly ignored for libraries. +# https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control +#poetry.lock + +# pdm +# Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control. +#pdm.lock +# pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it +# in version control. +# https://pdm.fming.dev/#use-with-ide +.pdm.toml + +# PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm +__pypackages__/ + +# Celery stuff +celerybeat-schedule +celerybeat.pid + +# SageMath parsed files +*.sage.py + +# Environments +.env +.venv +env/ +venv/ +ENV/ +env.bak/ +venv.bak/ + +# Spyder project settings +.spyderproject +.spyproject + +# Rope project settings +.ropeproject + +# mkdocs documentation +/site + +# mypy +.mypy_cache/ +.dmypy.json +dmypy.json + +# Pyre type checker +.pyre/ + +# pytype static type analyzer +.pytype/ + +# Cython debug symbols +cython_debug/ + +# PyCharm +# JetBrains specific template is maintained in a separate JetBrains.gitignore that can +# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore +# and can be added to the global gitignore or merged into this file. For a more nuclear +# option (not recommended) you can uncomment the following to ignore the entire idea folder. +#.idea/ \ No newline at end of file diff --git a/__init__.py b/__init__.py index 947db62..b27c522 100644 --- a/__init__.py +++ b/__init__.py @@ -6,15 +6,17 @@ """ -from .harronode import Harronode +from .harronode import Harronode,PromptEditor +from .harroserver import HarroServer NODE_CLASS_MAPPINGS = { - "Harronode": Harronode - + "Harronode": Harronode, + "PromptEditor": PromptEditor } NODE_DISPLAY_NAME_MAPPINGS = { - "Harronode": "Harronode - Prompt Builder" + "Harronode": "Harronode - Prompt Builder", + "PromptEditor": "Harronode - Prompt Editor" } WEB_DIRECTORY = "js" diff --git a/__pycache__/__init__.cpython-310.pyc b/__pycache__/__init__.cpython-310.pyc index 0f29b3eaa889e664d20574775f1141664eeb4c62..9866af537fa2da96f8b21e34922e91882afb5ea1 100644 GIT binary patch delta 357 zcmYk1ze)o^5XN`s{^c$S2!g0s*j{I+qzdLDrivztxEw5JW@DmvS=n3!)1PY><$iVOz#6bp3K#l_t7aIVH6ox2<6vh;$9L8LxC?-aR z6y{W>OvWhY6y_8bAkC7(8pWEzmcpLS6vdXp5zL^;IkDuPAmc4gkHn&){Ji{>)FP01 zldBnJCU0UiXJnuJicyM<1t?U+I$4oPPKOi31QEYNoULM#^K;WGW1K-`sAqh6W?o8u md2xI}eo;wcQcmjRSSBYC0iaqI1|AL;W|sdfY(UJy!vp~DhcH$E diff --git a/__pycache__/harronode.cpython-310.pyc b/__pycache__/harronode.cpython-310.pyc index dba54b389cbb4d68da9cc57f4f75baae362241f2..9a2c5185275126cbe76b155180816964bbdaef4f 100644 GIT binary patch delta 2062 zcmZuy&2JM&6yF)|u6NhFjtL)*NkRkSqvj(J1eA}I@?qn;MJ6SV08_AxXB`a2>&&i! zMC+z%a_AvymCm6DQd-nQRrOHyP^qP--rN2Gs~*a!e?V_Y(7w0%z@YAGfA77S_c8Cy zo7u-(zS$XfEX$DKdGhne_KDPDe1NnT_Z&WzlbFKPd5NiWw4|~q)8?g8ls=;iG znJ{a@tZvJ1DZm4KVohXSL* zcNRYP2mm9DvPhlOD3h5oN$V1DgsCjTqDvHbZ7G7@S!{_ibBT6{AC{oNp|G=3VALRj zoo}|x6uG|=14`c&I4*I8iM_dQ-Ix0;jnX3a@cuA{?Q^=6$hyBXFG26tk_G(uOw zP}~QA*}A;|hGGzt$j_)siTqrBD#vE}x(ha(FN{qT%f%~W*>OG$27!TjEvppMq{t7z zV4UA2Y|{4J^4&_K zZM%FErEqvu!A`_D)0O`Lbl}Q|j6_sj4B{RvF_Vh)XX<8_x zWBf42!Jgo6ARI-&HsQw*jspa-3ll}`gu;bPHi)kX`SJ4k{CJU{0F4tISJt)KaNXfK z9)$@^CG0p&qSPRAj|?ltH%|Ge_*#9E43C9TZNPCUd($nON6=qdJc!OK95WW%wc-Z; zE^z5+px<(xW|>u>WJ&RfwsizsJs@`10x)*a=QNkkSh?l2+Kq~5mzU+5_)ANZ8^X|k z9lrsZ{2D^&o$~9*R}fyVeqi0KaPF-7ybBs`0)P@oNsd43BQn)Kq|bO&Y%z|KOX89- zs6B*Bhmj5u^TwO~IbUK1^t=o-22=rRE}sGMr_q0m%t0H2toy{*lM?GmN*ywF2(%I~ zYWN1zZ_9iN*5713O6F`$d z*MXOXSpTk7FTLQUcg@QJXr(}Fy}v&6^1JvWmU>5G7RV55j>0+Zg>yUTM;}OEP&~0K zxg^04Z0|}gkHbrIOCh`yXXxv@cpnxzB}U8vvRM?&?fW;tK%i8ehQnv*-XY!(aw+P1 z(+#^kvdSC`lyHO(h)3pbGAMpCcWAvs=3}g?l%oK*%EC#IsvRfRt zhDg7-YHcM0qGRm?^Y5&Yp_g}?Uj_@j*W8N$GSR4bZaHUOIW4Hv)h3~fuVu`=}fN*mv-G+D#u{}(aP fM&Ym0ZrCUBH|Js%F)UsXaT*`i4r+P6UT8J$DwJ`G}V|GZ3+dotP=kvR!~8MY7%gXK$e?lXp0-C&n~Q$ zBM|9<1KNP^u){P2LSn~>0~!p3#Bs+RcCobc&Ri!U_@1+MIbiGkzVCbA_xs-G=ikq} ze;YIWd|o5?1h4P7YU#c)N*C|%nVn$@OK*{8nq`=ZHZxCHmT6mrX+5c_y7EePMSGNU zGmj}HjGiZr+_Oh_CE9?g)Ew1eE%iMjl^>2zV())0&Kj2HT$&Sj@r-?Axy}3= zf!*N{27E!$u~~t^|JDW=`nv zGW7JFr3ul1y8XlZ6Tq)}ozB9adV`*W5Bl5l=S0tW0jCif5V2&1nh!1Z)1RAA;a2c+(Skc8KtO{F#dp7g=(F$JjU3!C!Hjh zt!{?Zqs))wC#kE361*!6Ro+BGJ7WtxhZnIaO^owlba(~E%u!l}Bj&DR2{&S;<$FHg zl=gncN1$WYX$`(JC+R4>G$(JSlAGtdL}A+VTaFhrDm?jja_D{=@`~tLP0~r9={TL( znP%S};0;VR^Vc1|EW~KHomID)OKR-vj<@de5mAs1!VS-z{@2blb4d)Biy559El%M) zP@GOAKcLsIkh*{?xPTy26<-~f1v#q*Worb+tl670nBxZod?`S12ITX<_)lO$C@zo@ zRH9QZb{ab0i6+i;oN&ooUgVO9l+fp`B|e9KU-^kt#V84hpA?>uVLY_pFlM9n`_i 0: color1 = inputs["color_1"] color2 = inputs["color_2"] @@ -92,6 +136,7 @@ class prompt_builder: color_value = locals()[variable_name] text = text + color_value + ", " style_count = inputs["style_count"] + # gather styles if style_count > 0: style1 = inputs["style_1"] style2 = inputs["style_2"] @@ -100,6 +145,7 @@ class prompt_builder: style_value = locals()[variable_name] text = text + style_value + ", " accent_count = inputs["accent_count"] + # gather accents if accent_count > 0: accent1 = inputs["accent_1"] accent2 = inputs["accent_2"] @@ -109,6 +155,7 @@ class prompt_builder: accent_value = locals()[variable_name] text = text + accent_value + ", " content_count = inputs["content_count"] + # gather contents if content_count > 0: content1 = inputs["content_1"] content2 = inputs["content_2"] @@ -117,7 +164,6 @@ class prompt_builder: variable_name = f"content{i}" content_value = locals()[variable_name] text = text + content_value + ", " - return text server.PromptServer.instance.add_on_prompt_handler(harroonprompt) diff --git a/harroserver.py b/harroserver.py new file mode 100644 index 0000000..04943d4 --- /dev/null +++ b/harroserver.py @@ -0,0 +1,10 @@ +from server import PromptServer +from aiohttp import web +import time + +class HarroServer: + + @classmethod + def functionThatNeedsABetterName(): + ### do stuff + print("HELP") \ No newline at end of file diff --git a/js/harronode.js b/js/harronode.js index 045eac2..12979ff 100644 --- a/js/harronode.js +++ b/js/harronode.js @@ -1,4 +1,5 @@ import { app } from "../../scripts/app.js"; +import { api } from "../../scripts/api.js"; let origProps = {}; let initialized = false; @@ -10,6 +11,9 @@ const harroWidgetHandlers = { 'accent_count': handleAccentCount, 'content_count': handleContentCount }, + "PromptEditor": { + 'mode': handleMode + }, }; function handleColorCount(node, widget) { @@ -28,6 +32,22 @@ function handleContentCount(node, widget) { updateContentWidgets(node, widget.value); } +function handleMode (node, widget) { + const goButton = findWidgetByName(node, "Prompt looks good (GO)"); + const stopButton = findWidgetByName(node, "I need to adjust something (STOP)"); + const promptEditor = findWidgetByName(node, "promptEditor"); + if (widget.value == "Bypass"){ + goButton.disabled = 1==1; + stopButton.disabled = 1==1; + promptEditor.inputEl.disabled = 1==1; + } + else { + goButton.disabled = 1==0; + stopButton.disabled = 1==0; + promptEditor.inputEl.disabled = 1==0; + } +} + //-------function credit to efficiency nodes for comfyUI---------// const findWidgetByName = (node, name) => { @@ -132,6 +152,25 @@ function startupLogic (node, widget) //change name } +/* +Comfy uses 'clicked' to make the button flash; so just disable that. +This *doesn't* stop the callback, it's totally cosmetic! +*/ +function enable_disabling(button) { + Object.defineProperty(button, 'clicked', { + get : function() { return this._clicked; }, + set : function(v) { this._clicked = (v && this.name!=''); } + }) +} + +function cancelButtonPressed (){ + +} + +function progressButtonPressed (){ + +} + app.registerExtension({ name: "harronode.harronode", nodeCreated(node) { @@ -141,8 +180,6 @@ app.registerExtension({ // Store the original descriptor if it exists let originalDescriptor = Object.getOwnPropertyDescriptor(w, 'value'); - startupLogic(node, w); - Object.defineProperty(w, 'value', { get() { // If there's an original getter, use it. Otherwise, return widgetValue. @@ -172,5 +209,17 @@ app.registerExtension({ const prompt = node.widgets.find((w) => w.name == 'prompt'); prompt.inputEl.disabled = 1 == 1; } + if(node.comfyClass == "PromptEditor") { + console.log (node.widgets); + const mode = node.widgets.find((w) => w.name === "mode"); + node.cancel_button_widget = node.addWidget("button", "cancel_button", "", cancelButtonPressed); + node.send_button_widget = node.addWidget("button", "progress_button", "", progressButtonPressed); + node.send_button_widget.name = "Prompt looks good (GO)"; + node.cancel_button_widget.name = "I need to adjust something (STOP)"; + enable_disabling(node.cancel_button_widget); + enable_disabling(node.send_button_widget); + startupLogic(node, node.cancel_button_widget); + startupLogic(node, node.send_button_widget); + } } }); From 9a1da5f7448d2603a5dca3c270b0112dc69dd89f Mon Sep 17 00:00:00 2001 From: Joel Trauger Date: Tue, 26 Dec 2023 00:08:20 -0600 Subject: [PATCH 2/5] updated some documentation --- js/harronode.js | 34 +++++++++++----------------------- 1 file changed, 11 insertions(+), 23 deletions(-) diff --git a/js/harronode.js b/js/harronode.js index 12979ff..473e769 100644 --- a/js/harronode.js +++ b/js/harronode.js @@ -16,6 +16,8 @@ const harroWidgetHandlers = { }, }; +// ------------------ HANDLERS + function handleColorCount(node, widget) { updateColorWidgets(node, widget.value); } @@ -34,20 +36,19 @@ function handleContentCount(node, widget) { function handleMode (node, widget) { const goButton = findWidgetByName(node, "Prompt looks good (GO)"); - const stopButton = findWidgetByName(node, "I need to adjust something (STOP)"); const promptEditor = findWidgetByName(node, "promptEditor"); if (widget.value == "Bypass"){ goButton.disabled = 1==1; - stopButton.disabled = 1==1; promptEditor.inputEl.disabled = 1==1; } else { goButton.disabled = 1==0; - stopButton.disabled = 1==0; promptEditor.inputEl.disabled = 1==0; } } +//----------------------END HANDLERS + //-------function credit to efficiency nodes for comfyUI---------// const findWidgetByName = (node, name) => { @@ -60,6 +61,7 @@ const doesInputWithNameExist = (node, name) => { const HIDDEN_TAG = "harrohide"; +// function to hide a widget from view function toggleWidget(node, widget, show = false, suffix = "") { if (!widget || doesInputWithNameExist(node, widget.name)) return; @@ -83,6 +85,7 @@ function toggleWidget(node, widget, show = false, suffix = "") { } //--------------------------END CREDIT---------------------------// +// -- BASE CODE NEEDED BY PROMPT BUILDER function updateColorWidgets(node, color_count){ for (let i = 1; i <= 3; i++) { //find widget @@ -141,6 +144,8 @@ function updateContentWidgets(node, color_count){ } } +// -- END BASE CODE NEEDED BY PROMPT BUILDER + function startupLogic (node, widget) //change name { // Retrieve the handler for the current node title and widget name @@ -152,21 +157,6 @@ function startupLogic (node, widget) //change name } -/* -Comfy uses 'clicked' to make the button flash; so just disable that. -This *doesn't* stop the callback, it's totally cosmetic! -*/ -function enable_disabling(button) { - Object.defineProperty(button, 'clicked', { - get : function() { return this._clicked; }, - set : function(v) { this._clicked = (v && this.name!=''); } - }) -} - -function cancelButtonPressed (){ - -} - function progressButtonPressed (){ } @@ -202,23 +192,21 @@ app.registerExtension({ }); } setTimeout(() => { initialized = true; }, 500); - + //do this if we're messing with the prompt builder if(node.comfyClass == "Harronode"){ node.widgets[0].inputEl.placeholder = "Populated Prompt (Will be generated automatically)"; node.widgets[1].inputEl.placeholder = "Input Text to Display on Image"; const prompt = node.widgets.find((w) => w.name == 'prompt'); prompt.inputEl.disabled = 1 == 1; } + //do this if we're messing with the prompt editor if(node.comfyClass == "PromptEditor") { console.log (node.widgets); const mode = node.widgets.find((w) => w.name === "mode"); - node.cancel_button_widget = node.addWidget("button", "cancel_button", "", cancelButtonPressed); node.send_button_widget = node.addWidget("button", "progress_button", "", progressButtonPressed); node.send_button_widget.name = "Prompt looks good (GO)"; - node.cancel_button_widget.name = "I need to adjust something (STOP)"; - enable_disabling(node.cancel_button_widget); enable_disabling(node.send_button_widget); - startupLogic(node, node.cancel_button_widget); + //need to call one last time after adding the button startupLogic(node, node.send_button_widget); } } From 1892d771e238ada674927f693dd10143dcc2009c Mon Sep 17 00:00:00 2001 From: Joel Trauger Date: Tue, 26 Dec 2023 03:02:07 -0600 Subject: [PATCH 3/5] Prompt Editor prototype finished --- harronode.py | 28 ++++++++++++++++++---------- js/harronode.js | 38 ++++++++++++++++++++++++++++++-------- 2 files changed, 48 insertions(+), 18 deletions(-) diff --git a/harronode.py b/harronode.py index 48fc3f9..ac288a4 100644 --- a/harronode.py +++ b/harronode.py @@ -1,5 +1,6 @@ import json from pathlib import Path +from server import PromptServer import server class Harronode: @@ -55,26 +56,34 @@ class PromptEditor: def INPUT_TYPES(cls): inputs = { + "optional": { + "prompt": ("STRING", {"forceInput": True}) # forceInput makes it an input node + }, "required": { - "prompt": ("STRING", {"forceInput": True}), # forceInput makes it an input node "mode": (["Edit", "Bypass"],{}), "promptEditor": ("STRING", {"default": "", "multiline": True}) }, "hidden": { "unique_id": "UNIQUE_ID", - "extra_pnginfo": "EXTRA_PNGINFO", + "extra_pnginfo": "EXTRA_PNGINFO" }, } return inputs RETURN_TYPES = ("STRING",) FUNCTION = "EditPrompt" - OUTPUT_NODE = True - OUTPUT_IS_LIST = (True,) CATEGORY = "Harronode/PromptBuilder" - def EditPrompt(self, prompt, mode, promptEditor, unique_id = None, extra_pnginfo=None): - print("Enter EditPrompt") + def EditPrompt(self, *args, **kwargs): + print("EditPrompt") + output = '' + prompt = '' + unique_id = kwargs["unique_id"] + extra_pnginfo = kwargs["extra_pnginfo"] + mode = kwargs["mode"] + promptEditor = kwargs["promptEditor"] + if "prompt" in kwargs: + prompt = kwargs["prompt"] if unique_id and extra_pnginfo and "workflow" in extra_pnginfo: workflow = extra_pnginfo["workflow"] target_id = unique_id @@ -85,10 +94,9 @@ class PromptEditor: break # if the node is found, set the value of the textbox (3rd input, 0-indexed) to the prompt if node: - node["widgets_values"][2] = [prompt] + print(node) if mode == "Edit": - # edit the prompt and wait for user to click GO - # set output + server.PromptServer.instance.send_sync("harronode-populate-promptEditor", {"node_id": unique_id, "value": prompt,}) output = promptEditor, else: # just set the output to the string we input @@ -104,7 +112,7 @@ def onprompt_populate_prompt(json_data): inputs = v['inputs'] if isinstance(inputs['prompt'], str): inputs['prompt'] = prompt_builder.build_prompt(inputs) - server.PromptServer.instance.send_sync("harronode-node-feedback", {"node_id": k, "widget_name": "prompt", "type": "STRING", "value": inputs['prompt']}) + # server.PromptServer.instance.send_sync("harronode-node-feedback", {"node_id": k, "widget_name": "prompt", "type": "STRING", "value": inputs['prompt']}) updated_widget_values[k] = inputs['prompt'] except Exception as e: print(f"[WARN] ComfyUI-Harronode: Error on prompt\n{e}") diff --git a/js/harronode.js b/js/harronode.js index 473e769..79454a7 100644 --- a/js/harronode.js +++ b/js/harronode.js @@ -3,6 +3,7 @@ import { api } from "../../scripts/api.js"; let origProps = {}; let initialized = false; +let previousPrompt = ""; const harroWidgetHandlers = { "Harronode": { @@ -35,14 +36,14 @@ function handleContentCount(node, widget) { } function handleMode (node, widget) { - const goButton = findWidgetByName(node, "Prompt looks good (GO)"); + //const goButton = findWidgetByName(node, "Prompt looks good (GO)"); const promptEditor = findWidgetByName(node, "promptEditor"); if (widget.value == "Bypass"){ - goButton.disabled = 1==1; + //goButton.disabled = 1==1; promptEditor.inputEl.disabled = 1==1; } else { - goButton.disabled = 1==0; + //goButton.disabled = 1==0; promptEditor.inputEl.disabled = 1==0; } } @@ -163,6 +164,24 @@ function progressButtonPressed (){ app.registerExtension({ name: "harronode.harronode", + setup(){ + function harronode_populate_promptEditor(event) + { + const node = app.graph._nodes_by_id[event.detail.node_id]; + if (node) { + if(node.comfyClass == "PromptEditor") { + let prompt = event.detail.value; + if (prompt != previousPrompt){ + previousPrompt = prompt; + node.widgets[1].inputEl.value = event.detail.value; + } + } + } else { + console.log(`Image Chooser Preview - failed to find ${event.detail.id}`) + } + } + api.addEventListener("harronode-populate-promptEditor", harronode_populate_promptEditor); + }, nodeCreated(node) { for (const w of node.widgets || []) { let widgetValue = w.value; @@ -201,13 +220,16 @@ app.registerExtension({ } //do this if we're messing with the prompt editor if(node.comfyClass == "PromptEditor") { - console.log (node.widgets); const mode = node.widgets.find((w) => w.name === "mode"); - node.send_button_widget = node.addWidget("button", "progress_button", "", progressButtonPressed); - node.send_button_widget.name = "Prompt looks good (GO)"; - enable_disabling(node.send_button_widget); + //node.send_button_widget = node.addWidget("button", "progress_button", "", progressButtonPressed); + //node.send_button_widget.name = "Prompt looks good (GO)"; + node.widgets[1].inputEl.placeholder = "Populated Prompt (Will be generated automatically)"; //need to call one last time after adding the button - startupLogic(node, node.send_button_widget); + //startupLogic(node, node.send_button_widget); + // set height after we're done. :) + console.log("setting size"); + const newHeight = node.computeSize()[1]; + node.setSize([node.size[0], newHeight]); } } }); From 66cc05f069393bb4ff7148d3de19497e2ac254a9 Mon Sep 17 00:00:00 2001 From: Joel Trauger Date: Tue, 26 Dec 2023 14:16:22 -0600 Subject: [PATCH 4/5] nodes generate at correct size; prompt builder autopopulates again --- harronode.py | 2 +- js/harronode.js | 49 ++++++++++++++++++++++++++++++------------------- 2 files changed, 31 insertions(+), 20 deletions(-) diff --git a/harronode.py b/harronode.py index ac288a4..ea9a993 100644 --- a/harronode.py +++ b/harronode.py @@ -112,7 +112,7 @@ def onprompt_populate_prompt(json_data): inputs = v['inputs'] if isinstance(inputs['prompt'], str): inputs['prompt'] = prompt_builder.build_prompt(inputs) - # server.PromptServer.instance.send_sync("harronode-node-feedback", {"node_id": k, "widget_name": "prompt", "type": "STRING", "value": inputs['prompt']}) + server.PromptServer.instance.send_sync("harronode-node-feedback", {"node_id": k, "widget_name": "prompt", "type": "STRING", "value": inputs['prompt']}) updated_widget_values[k] = inputs['prompt'] except Exception as e: print(f"[WARN] ComfyUI-Harronode: Error on prompt\n{e}") diff --git a/js/harronode.js b/js/harronode.js index 79454a7..a94b44d 100644 --- a/js/harronode.js +++ b/js/harronode.js @@ -63,7 +63,7 @@ const doesInputWithNameExist = (node, name) => { const HIDDEN_TAG = "harrohide"; // function to hide a widget from view -function toggleWidget(node, widget, show = false, suffix = "") { +function harrToggleWidget(node, widget, show = false, suffix = "") { if (!widget || doesInputWithNameExist(node, widget.name)) return; // Store the original properties of the widget if not already stored @@ -78,7 +78,7 @@ function toggleWidget(node, widget, show = false, suffix = "") { widget.computeSize = show ? origProps[widget.name].origComputeSize : () => [0, -4]; // Recursively handle linked widgets if they exist - widget.linkedWidgets?.forEach(w => toggleWidget(node, w, ":" + widget.name, show)); + widget.linkedWidgets?.forEach(w => harrToggleWidget(node, w, ":" + widget.name, show)); // Calculate the new height for the node based on its computeSize method const newHeight = node.computeSize()[1]; @@ -88,16 +88,17 @@ function toggleWidget(node, widget, show = false, suffix = "") { // -- BASE CODE NEEDED BY PROMPT BUILDER function updateColorWidgets(node, color_count){ + console.log("colors", color_count) for (let i = 1; i <= 3; i++) { //find widget const color_picker = findWidgetByName(node, `color_${i}`) if(i <= color_count){ //set visible and required - toggleWidget(node, color_picker, true) + harrToggleWidget(node, color_picker, true) } else { //hide and set not required - toggleWidget(node, color_picker, false) + harrToggleWidget(node, color_picker, false) } } } @@ -108,11 +109,11 @@ function updateStyleWidgets(node, color_count){ const style_picker = findWidgetByName(node, `style_${i}`) if(i <= color_count){ //set visible and required - toggleWidget(node, style_picker, true) + harrToggleWidget(node, style_picker, true) } else { //hide and set not required - toggleWidget(node, style_picker, false) + harrToggleWidget(node, style_picker, false) } } } @@ -122,11 +123,11 @@ function updateAccentWidgets(node, color_count){ const accent_picker = findWidgetByName(node, `accent_${i}`) if(i <= color_count){ //set visible and required - toggleWidget(node, accent_picker, true) + harrToggleWidget(node, accent_picker, true) } else { //hide and set not required - toggleWidget(node, accent_picker, false) + harrToggleWidget(node, accent_picker, false) } } } @@ -136,11 +137,11 @@ function updateContentWidgets(node, color_count){ const content_picker = findWidgetByName(node, `content_${i}`) if(i <= color_count){ //set visible and required - toggleWidget(node, content_picker, true) + harrToggleWidget(node, content_picker, true) } else { //hide and set not required - toggleWidget(node, content_picker, false) + harrToggleWidget(node, content_picker, false) } } } @@ -177,18 +178,33 @@ app.registerExtension({ } } } else { - console.log(`Image Chooser Preview - failed to find ${event.detail.id}`) + console.log(`Harronode Prompt Editor - failed to find ${event.detail.node_id}`) } } + + function harronode_node_feedback(event){ + const node = app.graph._nodes_by_id[event.detail.node_id]; + if (node) { + if(node.comfyClass == "Harronode"){ + let prompt = event.detail.value; + node.widgets[0].inputEl.value = prompt; + } + } + else { + console.log(`Harronode Prompt Builder - failed to find ${event.detail.node_id}`); + } + } + api.addEventListener("harronode-populate-promptEditor", harronode_populate_promptEditor); + api.addEventListener("harronode-node-feedback", harronode_node_feedback); }, - nodeCreated(node) { + async nodeCreated(node) { for (const w of node.widgets || []) { let widgetValue = w.value; // Store the original descriptor if it exists let originalDescriptor = Object.getOwnPropertyDescriptor(w, 'value'); - + startupLogic(node, w); Object.defineProperty(w, 'value', { get() { // If there's an original getter, use it. Otherwise, return widgetValue. @@ -210,7 +226,6 @@ app.registerExtension({ } }); } - setTimeout(() => { initialized = true; }, 500); //do this if we're messing with the prompt builder if(node.comfyClass == "Harronode"){ node.widgets[0].inputEl.placeholder = "Populated Prompt (Will be generated automatically)"; @@ -221,15 +236,11 @@ app.registerExtension({ //do this if we're messing with the prompt editor if(node.comfyClass == "PromptEditor") { const mode = node.widgets.find((w) => w.name === "mode"); - //node.send_button_widget = node.addWidget("button", "progress_button", "", progressButtonPressed); - //node.send_button_widget.name = "Prompt looks good (GO)"; node.widgets[1].inputEl.placeholder = "Populated Prompt (Will be generated automatically)"; - //need to call one last time after adding the button - //startupLogic(node, node.send_button_widget); - // set height after we're done. :) console.log("setting size"); const newHeight = node.computeSize()[1]; node.setSize([node.size[0], newHeight]); } + setTimeout(() => { initialized = true; }, 500); } }); From 8519e28da248c57cbeedca4f24fbbfdb7371bd75 Mon Sep 17 00:00:00 2001 From: Joel Trauger Date: Tue, 26 Dec 2023 23:06:25 -0600 Subject: [PATCH 5/5] Removed Prompt Editor and rolled into Prompt Builder --- __init__.py | 4 +- harronode.py | 127 +--------------------------- js/harronode.js | 215 +++++++++++++++++++++++++++++++++++------------- 3 files changed, 160 insertions(+), 186 deletions(-) diff --git a/__init__.py b/__init__.py index b27c522..2227a2d 100644 --- a/__init__.py +++ b/__init__.py @@ -6,17 +6,15 @@ """ -from .harronode import Harronode,PromptEditor +from .harronode import Harronode from .harroserver import HarroServer NODE_CLASS_MAPPINGS = { "Harronode": Harronode, - "PromptEditor": PromptEditor } NODE_DISPLAY_NAME_MAPPINGS = { "Harronode": "Harronode - Prompt Builder", - "PromptEditor": "Harronode - Prompt Editor" } WEB_DIRECTORY = "js" diff --git a/harronode.py b/harronode.py index ea9a993..72d5236 100644 --- a/harronode.py +++ b/harronode.py @@ -20,6 +20,7 @@ class Harronode: "required": { "prompt": ("STRING", {"default": "", "multiline": True}), "text_on_image": ("STRING", {"default": "", "multiline": True}), + "mode": ("BOOLEAN", {"default": True, "label_on": "Editable", "label_off": "Fixed"}), "text_weight": ("FLOAT", {"default": 1.0, "min":0.5, "max":1.5, "step": 0.01}), "color_count": ("INT", {"default": 1, "min": 0, "max": 3, "step": 1}), "style_count": ("INT", {"default": 1, "min": 1, "max": 2, "step": 1}), @@ -49,129 +50,3 @@ class Harronode: def doTheThing(self, *args, **kwargs): output = kwargs['prompt'] return(output,) # comma outputs a tuple with a single record instead of a bunch of records all containing the prompt - -class PromptEditor: - - @classmethod - def INPUT_TYPES(cls): - - inputs = { - "optional": { - "prompt": ("STRING", {"forceInput": True}) # forceInput makes it an input node - }, - "required": { - "mode": (["Edit", "Bypass"],{}), - "promptEditor": ("STRING", {"default": "", "multiline": True}) - }, - "hidden": { - "unique_id": "UNIQUE_ID", - "extra_pnginfo": "EXTRA_PNGINFO" - }, - } - return inputs - - RETURN_TYPES = ("STRING",) - FUNCTION = "EditPrompt" - CATEGORY = "Harronode/PromptBuilder" - - def EditPrompt(self, *args, **kwargs): - print("EditPrompt") - output = '' - prompt = '' - unique_id = kwargs["unique_id"] - extra_pnginfo = kwargs["extra_pnginfo"] - mode = kwargs["mode"] - promptEditor = kwargs["promptEditor"] - if "prompt" in kwargs: - prompt = kwargs["prompt"] - if unique_id and extra_pnginfo and "workflow" in extra_pnginfo: - workflow = extra_pnginfo["workflow"] - target_id = unique_id - node = None - # try to find the node in the workflow so we can get all the values of it - for node in workflow["nodes"]: - if str(node["id"]) == target_id: - break - # if the node is found, set the value of the textbox (3rd input, 0-indexed) to the prompt - if node: - print(node) - if mode == "Edit": - server.PromptServer.instance.send_sync("harronode-populate-promptEditor", {"node_id": unique_id, "value": prompt,}) - output = promptEditor, - else: - # just set the output to the string we input - output = prompt, - return (output,) - -def onprompt_populate_prompt(json_data): - try: - prompt = json_data['prompt'] - updated_widget_values = {} - for k, v in prompt.items(): - if 'class_type' in v and (v['class_type'] == 'Harronode'): - inputs = v['inputs'] - if isinstance(inputs['prompt'], str): - inputs['prompt'] = prompt_builder.build_prompt(inputs) - server.PromptServer.instance.send_sync("harronode-node-feedback", {"node_id": k, "widget_name": "prompt", "type": "STRING", "value": inputs['prompt']}) - updated_widget_values[k] = inputs['prompt'] - except Exception as e: - print(f"[WARN] ComfyUI-Harronode: Error on prompt\n{e}") - -def harroonprompt(json_data): - try: - onprompt_populate_prompt(json_data) - except Exception as e: - print(f"[WARN] ComfyUI-Harronode: Error on prompt - autopopulation of prompt will not work.\n{e}") - - return json_data - -class prompt_builder: - def build_prompt(inputs): - # Initialize the prompt builder string - text = "" - text = text + "('" - text = text + inputs["text_on_image"] - text_weight = inputs["text_weight"] - text = text + f"':{text_weight:.2f}) text logo, " - color_count = inputs["color_count"] - # gather colors - if color_count > 0: - color1 = inputs["color_1"] - color2 = inputs["color_2"] - color3 = inputs["color_3"] - for i in range(1, color_count + 1): - variable_name = f"color{i}" - color_value = locals()[variable_name] - text = text + color_value + ", " - style_count = inputs["style_count"] - # gather styles - if style_count > 0: - style1 = inputs["style_1"] - style2 = inputs["style_2"] - for i in range(1, style_count + 1): - variable_name = f"style{i}" - style_value = locals()[variable_name] - text = text + style_value + ", " - accent_count = inputs["accent_count"] - # gather accents - if accent_count > 0: - accent1 = inputs["accent_1"] - accent2 = inputs["accent_2"] - accent3 = inputs["accent_3"] - for i in range(1, accent_count + 1): - variable_name = f"accent{i}" - accent_value = locals()[variable_name] - text = text + accent_value + ", " - content_count = inputs["content_count"] - # gather contents - if content_count > 0: - content1 = inputs["content_1"] - content2 = inputs["content_2"] - content3 = inputs["content_3"] - for i in range(1, content_count + 1): - variable_name = f"content{i}" - content_value = locals()[variable_name] - text = text + content_value + ", " - return text - -server.PromptServer.instance.add_on_prompt_handler(harroonprompt) diff --git a/js/harronode.js b/js/harronode.js index a94b44d..4c15b52 100644 --- a/js/harronode.js +++ b/js/harronode.js @@ -1,23 +1,88 @@ import { app } from "../../scripts/app.js"; import { api } from "../../scripts/api.js"; +import "../../scripts/widgets.js"; +import { ComfyWidgets } from "../../scripts/widgets.js"; let origProps = {}; let initialized = false; let previousPrompt = ""; +let tokenDict = { + "text": { + "location": 0, + "currentValue":"" + }, + "color_1":{ + "location": 0, + "currentValue":"" + }, + "color_2":{ + "location": 0, + "currentValue":"" + }, + "color_3":{ + "location": 0, + "currentValue":"" + }, + "style_1":{ + "location": 0, + "currentValue":"" + }, + "style_2":{ + "location": 0, + "currentValue":"" + }, + "accent_1":{ + "location": 0, + "currentValue":"" + }, + "accent_2":{ + "location": 0, + "currentValue":"" + }, + "accent_3":{ + "location": 0, + "currentValue":"" + }, + "content_1":{ + "location": 0, + "currentValue":"" + }, + "content_2":{ + "location": 0, + "currentValue":"" + }, + "content_3":{ + "location": 0, + "currentValue":"" + }, + "tokens": [] +} const harroWidgetHandlers = { "Harronode": { 'color_count': handleColorCount, 'style_count': handleStyleCount, 'accent_count': handleAccentCount, - 'content_count': handleContentCount - }, - "PromptEditor": { - 'mode': handleMode + 'content_count': handleContentCount, + 'color_1': handlePromptChange, + 'color_2': handlePromptChange, + 'color_3': handlePromptChange, + 'style_1': handlePromptChange, + 'style_2': handlePromptChange, + 'accent_1': handlePromptChange, + 'accent_2': handlePromptChange, + 'accent_3': handlePromptChange, + 'content_1': handlePromptChange, + 'content_2': handlePromptChange, + 'content_3': handlePromptChange, + 'text_weight': handlePromptChange, }, }; // ------------------ HANDLERS +function handlePromptChange(node, widget) { + rebuildPrompt(node); +} function handleColorCount(node, widget) { updateColorWidgets(node, widget.value); @@ -35,19 +100,6 @@ function handleContentCount(node, widget) { updateContentWidgets(node, widget.value); } -function handleMode (node, widget) { - //const goButton = findWidgetByName(node, "Prompt looks good (GO)"); - const promptEditor = findWidgetByName(node, "promptEditor"); - if (widget.value == "Bypass"){ - //goButton.disabled = 1==1; - promptEditor.inputEl.disabled = 1==1; - } - else { - //goButton.disabled = 1==0; - promptEditor.inputEl.disabled = 1==0; - } -} - //----------------------END HANDLERS //-------function credit to efficiency nodes for comfyUI---------// @@ -88,7 +140,6 @@ function harrToggleWidget(node, widget, show = false, suffix = "") { // -- BASE CODE NEEDED BY PROMPT BUILDER function updateColorWidgets(node, color_count){ - console.log("colors", color_count) for (let i = 1; i <= 3; i++) { //find widget const color_picker = findWidgetByName(node, `color_${i}`) @@ -101,6 +152,7 @@ function updateColorWidgets(node, color_count){ harrToggleWidget(node, color_picker, false) } } + rebuildPrompt(node); } function updateStyleWidgets(node, color_count){ @@ -116,6 +168,7 @@ function updateStyleWidgets(node, color_count){ harrToggleWidget(node, style_picker, false) } } + rebuildPrompt(node); } function updateAccentWidgets(node, color_count){ for (let i = 1; i <= 3; i++) { @@ -130,6 +183,7 @@ function updateAccentWidgets(node, color_count){ harrToggleWidget(node, accent_picker, false) } } + rebuildPrompt(node); } function updateContentWidgets(node, color_count){ for (let i = 1; i <= 3; i++) { @@ -144,6 +198,56 @@ function updateContentWidgets(node, color_count){ harrToggleWidget(node, content_picker, false) } } + rebuildPrompt(node); +} + +function buildPrompt(node, widgets = null, event = null){ + var prompt = ""; + var _widgets; + if(widgets != null) + { + _widgets = widgets; + + } + //get widget values from node + else { + if(node.comfyClass == "Harronode"){ + _widgets = node.widgets; + } + } + prompt += '("' + findWidgetByName(node, "text_on_image").value+ '":' + parseFloat(findWidgetByName(node, "text_weight").value).toFixed(2) + ") text logo, "; + //foreach color + var colors = findWidgetByName(node, "color_count").value; + for(var i = 1; i <= colors; i++){ + var color = findWidgetByName(node, `color_${i}`).value; + prompt += color + ", "; + } + //foreach style + var styles = findWidgetByName(node, "style_count").value; + for(var i = 1; i <= styles; i++){ + var style = findWidgetByName(node, `style_${i}`).value; + prompt += style + ", "; + } + //foreach accent + var accents = findWidgetByName(node, "accent_count").value; + for(var i = 1; i <= accents; i++){ + var accent = findWidgetByName(node, `accent_${i}`).value; + prompt += accent + ", "; + } + //foreach content + var contents = findWidgetByName(node, "content_count").value; + for(var i = 1; i <= contents; i++){ + var content = findWidgetByName(node, `content_${i}`).value; + prompt += content + ", "; + } + return prompt; +} + +function rebuildPrompt(node){ + // build prompt + var promptOutput = ""; + promptOutput = buildPrompt(node); + node.widgets[0].inputEl.value = promptOutput; } // -- END BASE CODE NEEDED BY PROMPT BUILDER @@ -155,39 +259,21 @@ function startupLogic (node, widget) //change name if (handler) { handler(node, widget); } - prompt = findWidgetByName(node, 'prompt'); - -} - -function progressButtonPressed (){ - } app.registerExtension({ name: "harronode.harronode", - setup(){ - function harronode_populate_promptEditor(event) - { - const node = app.graph._nodes_by_id[event.detail.node_id]; - if (node) { - if(node.comfyClass == "PromptEditor") { - let prompt = event.detail.value; - if (prompt != previousPrompt){ - previousPrompt = prompt; - node.widgets[1].inputEl.value = event.detail.value; - } - } - } else { - console.log(`Harronode Prompt Editor - failed to find ${event.detail.node_id}`) - } - } - + setup() + { function harronode_node_feedback(event){ const node = app.graph._nodes_by_id[event.detail.node_id]; + let promptOutput = ""; + let editedPrompt = node.widgets[0].inputEl.value; if (node) { if(node.comfyClass == "Harronode"){ - let prompt = event.detail.value; - node.widgets[0].inputEl.value = prompt; + let widgets = event.detail.value; + console.log(widgets); + rebuildPrompt(node); } } else { @@ -195,10 +281,10 @@ app.registerExtension({ } } - api.addEventListener("harronode-populate-promptEditor", harronode_populate_promptEditor); api.addEventListener("harronode-node-feedback", harronode_node_feedback); }, - async nodeCreated(node) { + async nodeCreated(node) + { for (const w of node.widgets || []) { let widgetValue = w.value; @@ -225,22 +311,37 @@ app.registerExtension({ startupLogic(node, w); } }); + if(node.comfyClass == "Harronode"){ + const editablePrompt = node.widgets.find((w) => w.name == 'prompt'); + const mode_widget = node.widgets.find((w) => w.name == 'mode'); + Object.defineProperty(mode_widget, "value", { + set: (value) => { + node._mode_value = value == true || value == "Editable"; + editablePrompt.inputEl.disabled = value == false || value == "Editable"; + }, + get: () => { + if(node._mode_value != undefined) + return node._mode_value; + else + return true; + } + }); + } } //do this if we're messing with the prompt builder if(node.comfyClass == "Harronode"){ - node.widgets[0].inputEl.placeholder = "Populated Prompt (Will be generated automatically)"; + node.widgets[0].inputEl.placeholder = "Generated Prompt - Changing a parameter may overwrite what is in this box."; node.widgets[1].inputEl.placeholder = "Input Text to Display on Image"; - const prompt = node.widgets.find((w) => w.name == 'prompt'); - prompt.inputEl.disabled = 1 == 1; - } - //do this if we're messing with the prompt editor - if(node.comfyClass == "PromptEditor") { - const mode = node.widgets.find((w) => w.name === "mode"); - node.widgets[1].inputEl.placeholder = "Populated Prompt (Will be generated automatically)"; - console.log("setting size"); - const newHeight = node.computeSize()[1]; - node.setSize([node.size[0], newHeight]); + //janky af but I don't care at this point + if(!initialized) { + console.log(node.widgets); + const thebutton = node.addWidget("button", "CLICK to Rebuild Prompt (Erases edits)", "", () => { + rebuildPrompt(node); + }); + initialized = true; + console.log(node.widgets); + } } setTimeout(() => { initialized = true; }, 500); } -}); +}); \ No newline at end of file