Merge pull request #1 from traugdor/master

Added Prompt Editor to workflows
This commit is contained in:
traugdor
2023-12-26 03:05:56 -06:00
committed by GitHub
7 changed files with 302 additions and 17 deletions
+160
View File
@@ -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/
+6 -4
View File
@@ -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"
Binary file not shown.
Binary file not shown.
+64 -10
View File
@@ -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)
+10
View File
@@ -0,0 +1,10 @@
from server import PromptServer
from aiohttp import web
import time
class HarroServer:
@classmethod
def functionThatNeedsABetterName():
### do stuff
print("HELP")
+62 -3
View File
@@ -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]);
}
}
});