diff --git a/__init__.py b/__init__.py new file mode 100644 index 0000000..947db62 --- /dev/null +++ b/__init__.py @@ -0,0 +1,22 @@ +""" +@author: HarroweD and quadmoon (https://github.com/traugdor) +@title: Harrlogos Prompt Builder Node +@nickname: Harronode +@description: This extension to ComfyUI will build a prompt for the Harrlogos LoRA for SDXL. +""" + + +from .harronode import Harronode + +NODE_CLASS_MAPPINGS = { + "Harronode": Harronode + +} + +NODE_DISPLAY_NAME_MAPPINGS = { + "Harronode": "Harronode - Prompt Builder" +} + +WEB_DIRECTORY = "js" + +__all__ = ['NODE_CLASS_MAPPINGS', 'NODE_DISPLAY_NAME_MAPPINGS'] diff --git a/__pycache__/__init__.cpython-310.pyc b/__pycache__/__init__.cpython-310.pyc new file mode 100644 index 0000000..0f29b3e Binary files /dev/null and b/__pycache__/__init__.cpython-310.pyc differ diff --git a/__pycache__/harronode.cpython-310.pyc b/__pycache__/harronode.cpython-310.pyc new file mode 100644 index 0000000..dba54b3 Binary files /dev/null and b/__pycache__/harronode.cpython-310.pyc differ diff --git a/harronode.py b/harronode.py new file mode 100644 index 0000000..59ca277 --- /dev/null +++ b/harronode.py @@ -0,0 +1,123 @@ +import json +from pathlib import Path +import torch +import server + +class Harronode: + + @classmethod + def INPUT_TYPES(cls): + words_path = Path(__file__).parent / "words.json" + with open(words_path, "r") as file: + data = json.load(file) + colors = data.get("colors", []) + styles = data.get("styles", []) + accents = data.get("accents", []) + content = data.get("content", []) + + inputs = { + "required": { + "prompt": ("STRING", {"default": "", "multiline": True}), + "text_on_image": ("STRING", {"default": "", "multiline": True}), + "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}), + "accent_count": ("INT", {"default": 1, "min": 0, "max": 3, "step": 1}), + "content_count": ("INT", {"default": 1, "min": 0, "max": 3, "step": 1}) + } + } + + for i in range (1, 4): + inputs["required"][f"color_{i}"] = (colors, {"default": colors[0] if colors else None}) + + for i in range (1, 3): + inputs["required"][f"style_{i}"] = (styles, {"default": styles[0] if styles else None}) + + for i in range (1, 4): + inputs["required"][f"accent_{i}"] = (accents, {"default": accents[0] if accents else None}) + + for i in range (1, 4): + inputs["required"][f"content_{i}"] = (content, {"default": content[0] if content else None}) + + return inputs + + RETURN_TYPES = ("STRING",) + FUNCTION = "fuck_me_up_fam" + CATEGORY = "Harronode/PromptBuilder" + + def fuck_me_up_fam(self, *args, **kwargs): + output = kwargs['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']}) + 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): + 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"] + 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"] + 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"] + 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"] + 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 new file mode 100644 index 0000000..045eac2 --- /dev/null +++ b/js/harronode.js @@ -0,0 +1,176 @@ +import { app } from "../../scripts/app.js"; + +let origProps = {}; +let initialized = false; + +const harroWidgetHandlers = { + "Harronode": { + 'color_count': handleColorCount, + 'style_count': handleStyleCount, + 'accent_count': handleAccentCount, + 'content_count': handleContentCount + }, +}; + +function handleColorCount(node, widget) { + updateColorWidgets(node, widget.value); +} + +function handleStyleCount(node, widget) { + updateStyleWidgets(node, widget.value); +} + +function handleAccentCount(node, widget) { + updateAccentWidgets(node, widget.value); +} + +function handleContentCount(node, widget) { + updateContentWidgets(node, widget.value); +} + +//-------function credit to efficiency nodes for comfyUI---------// + +const findWidgetByName = (node, name) => { + return node.widgets ? node.widgets.find((w) => w.name === name) : null; +}; + +const doesInputWithNameExist = (node, name) => { + return node.inputs ? node.inputs.some((input) => input.name === name) : false; +}; + +const HIDDEN_TAG = "harrohide"; + +function toggleWidget(node, widget, show = false, suffix = "") { + if (!widget || doesInputWithNameExist(node, widget.name)) return; + + // Store the original properties of the widget if not already stored + if (!origProps[widget.name]) { + origProps[widget.name] = { origType: widget.type, origComputeSize: widget.computeSize }; + } + + const origSize = node.size; + + // Set the widget type and computeSize based on the show flag + widget.type = show ? origProps[widget.name].origType : HIDDEN_TAG + 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)); + + // Calculate the new height for the node based on its computeSize method + const newHeight = node.computeSize()[1]; + node.setSize([node.size[0], newHeight]); +} +//--------------------------END CREDIT---------------------------// + +function updateColorWidgets(node, 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) + } + else { + //hide and set not required + toggleWidget(node, color_picker, false) + } + } +} + +function updateStyleWidgets(node, color_count){ + for (let i = 1; i <= 3; i++) { + //find widget + const style_picker = findWidgetByName(node, `style_${i}`) + if(i <= color_count){ + //set visible and required + toggleWidget(node, style_picker, true) + } + else { + //hide and set not required + toggleWidget(node, style_picker, false) + } + } +} +function updateAccentWidgets(node, color_count){ + for (let i = 1; i <= 3; i++) { + //find widget + const accent_picker = findWidgetByName(node, `accent_${i}`) + if(i <= color_count){ + //set visible and required + toggleWidget(node, accent_picker, true) + } + else { + //hide and set not required + toggleWidget(node, accent_picker, false) + } + } +} +function updateContentWidgets(node, color_count){ + for (let i = 1; i <= 3; i++) { + //find widget + const content_picker = findWidgetByName(node, `content_${i}`) + if(i <= color_count){ + //set visible and required + toggleWidget(node, content_picker, true) + } + else { + //hide and set not required + toggleWidget(node, content_picker, false) + } + } +} + +function startupLogic (node, widget) //change name +{ + // Retrieve the handler for the current node title and widget name + const handler = harroWidgetHandlers[node.comfyClass]?.[widget.name]; + if (handler) { + handler(node, widget); + } + prompt = findWidgetByName(node, 'prompt'); + +} + +app.registerExtension({ + name: "harronode.harronode", + 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. + let valueToReturn = originalDescriptor && originalDescriptor.get + ? originalDescriptor.get.call(w) + : widgetValue; + + return valueToReturn; + }, + set(newVal) { + // If there's an original setter, use it. Otherwise, set widgetValue. + if (originalDescriptor && originalDescriptor.set) { + originalDescriptor.set.call(w, newVal); + } else { + widgetValue = newVal; + } + + startupLogic(node, w); + } + }); + } + setTimeout(() => { initialized = true; }, 500); + + 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; + } + } +}); diff --git a/prompt_builder.py b/prompt_builder.py new file mode 100644 index 0000000..47d87cc --- /dev/null +++ b/prompt_builder.py @@ -0,0 +1,13 @@ +import re +import random +import os +import nodes +import folder_paths +import yaml +import threading + +def build_prompt(inputs): + print(inputs) + text = "WIP" + + return text \ No newline at end of file diff --git a/words.json b/words.json new file mode 100644 index 0000000..79af422 --- /dev/null +++ b/words.json @@ -0,0 +1,6 @@ +{ + "colors": ["blue", "teal", "gold", "rainbow", "red", "orange", "white", "cyan", "purple", "green", "yellow", "grey", "silver", "black"], + "styles": ["dripping", "colorful", "graffiti", "tattoo", "anime", "pixel art", "8-bit", "16-bit", "32-bit", "metal", "metallic", "spikey", "stone", "splattered", "comic book", "80s", "neon", "3D"], + "accents": ["smoke", "fire", "flames", "tentacles", "hell", "glow", "horns", "wings", "halo", "roots", "embossed", "blood", "digital", "ice", "frozen", "japanese", "chrome", "pastel", "robotic", "hearts", "cute", "egyptian", "viking"], + "content": ["cat", "sword", "owl", "cat ears", "cthulu", "sun", "roses", "clouds", "space", "stars", "skeletons", "demons", "fog", "trees", "moon", "skulls", "bones", "planet", "earth", "cherry blossom", "pentagram", "crosses", "lightning", "bolts", "crown", "circle", "moth"] +}