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 0f29b3e..9866af5 100644 Binary files a/__pycache__/__init__.cpython-310.pyc and b/__pycache__/__init__.cpython-310.pyc differ diff --git a/__pycache__/harronode.cpython-310.pyc b/__pycache__/harronode.cpython-310.pyc index dba54b3..9a2c518 100644 Binary files a/__pycache__/harronode.cpython-310.pyc and b/__pycache__/harronode.cpython-310.pyc differ diff --git a/harronode.py b/harronode.py index 59ca277..ac288a4 100644 --- a/harronode.py +++ b/harronode.py @@ -1,6 +1,6 @@ import json from pathlib import Path -import torch +from server import PromptServer import server class Harronode: @@ -15,6 +15,7 @@ class Harronode: accents = data.get("accents", []) content = data.get("content", []) + # setup inputs for the node inputs = { "required": { "prompt": ("STRING", {"default": "", "multiline": True}), @@ -42,26 +43,76 @@ class Harronode: return inputs RETURN_TYPES = ("STRING",) - FUNCTION = "fuck_me_up_fam" + FUNCTION = "doTheThing" CATEGORY = "Harronode/PromptBuilder" - def fuck_me_up_fam(self, *args, **kwargs): + def doTheThing(self, *args, **kwargs): output = kwargs['prompt'] - return(output,) - + 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("impact-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}") @@ -76,13 +127,14 @@ def harroonprompt(json_data): class prompt_builder: def build_prompt(inputs): + # Initialize the prompt builder string text = "" - print(inputs) 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"] @@ -92,6 +144,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 +153,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 +163,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 +172,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..79454a7 100644 --- a/js/harronode.js +++ b/js/harronode.js @@ -1,7 +1,9 @@ import { app } from "../../scripts/app.js"; +import { api } from "../../scripts/api.js"; let origProps = {}; let initialized = false; +let previousPrompt = ""; const harroWidgetHandlers = { "Harronode": { @@ -10,8 +12,13 @@ const harroWidgetHandlers = { 'accent_count': handleAccentCount, 'content_count': handleContentCount }, + "PromptEditor": { + 'mode': handleMode + }, }; +// ------------------ HANDLERS + function handleColorCount(node, widget) { updateColorWidgets(node, widget.value); } @@ -28,6 +35,21 @@ 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---------// const findWidgetByName = (node, name) => { @@ -40,6 +62,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; @@ -63,6 +86,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 @@ -121,6 +145,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 @@ -132,8 +158,30 @@ function startupLogic (node, widget) //change name } +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; @@ -141,8 +189,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. @@ -165,12 +211,25 @@ 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") { + 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]); + } } });