From ae7a4ebbf133786620c9d87041e40158a56246c0 Mon Sep 17 00:00:00 2001 From: Joel Trauger Date: Mon, 25 Dec 2023 23:45:28 -0600 Subject: [PATCH 1/3] 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/3] 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/3] 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]); } } });