Merge pull request #1 from traugdor/master
Added Prompt Editor to workflows
This commit is contained in:
+160
@@ -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
@@ -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
@@ -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)
|
||||
|
||||
@@ -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
@@ -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]);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user