diff --git a/__init__.py b/__init__.py index ebb1123..d7fbb3c 100644 --- a/__init__.py +++ b/__init__.py @@ -5,8 +5,12 @@ @description: A simple wildcard node for ComfyUI. Can also be used a style prompt node. """ -from .wildcard import Wildcard +from .wildcard import SimpleWildcard +from .concat import SimpleConcat -NODE_CLASS_MAPPINGS = {"SimpleWildcard": Wildcard} -NODE_DISPLAY_NAME_MAPPINGS = {"SimpleWildcard": "SimpleWildcard"} +NODE_CLASS_MAPPINGS = {"SimpleWildcard": SimpleWildcard, "SimpleConcat": SimpleConcat} +NODE_DISPLAY_NAME_MAPPINGS = { + "SimpleWildcard": "SimpleWildcard", + "SimpleConcat": "SimpleConcat", +} __all__ = ["NODE_CLASS_MAPPINGS", "NODE_DISPLAY_NAME_MAPPINGS"] diff --git a/concat.py b/concat.py new file mode 100644 index 0000000..f32ff85 --- /dev/null +++ b/concat.py @@ -0,0 +1,76 @@ +class SimpleConcat: + def __init__(self): + pass + + @classmethod + def INPUT_TYPES(s): + return { + "required": { + "num": ("INT", {"default": 2, "min": 2, "max": 10}), + "delimeter": ("STRING", {"default": ", ", "dynamicPrompts": False}), + "output_text": ( + "STRING", + {"multiline": True, "dynamicPrompts": False, "default": ""}, + ), + }, + "optional": { + "input_0": ( + "STRING", + {"default": "", "dynamicPrompts": False, "multiline": True}, + ), + "input_1": ( + "STRING", + {"default": "", "dynamicPrompts": False, "multiline": True}, + ), + "input_2": ( + "STRING", + {"default": "", "dynamicPrompts": False, "multiline": True}, + ), + "input_3": ( + "STRING", + {"default": "", "dynamicPrompts": False, "multiline": True}, + ), + "input_4": ( + "STRING", + {"default": "", "dynamicPrompts": False, "multiline": True}, + ), + "input_5": ( + "STRING", + {"default": "", "dynamicPrompts": False, "multiline": True}, + ), + "input_6": ( + "STRING", + {"default": "", "dynamicPrompts": False, "multiline": True}, + ), + "input_7": ( + "STRING", + {"default": "", "dynamicPrompts": False, "multiline": True}, + ), + "input_8": ( + "STRING", + {"default": "", "dynamicPrompts": False, "multiline": True}, + ), + "input_9": ( + "STRING", + {"default": "", "dynamicPrompts": False, "multiline": True}, + ), + }, + } + + RETURN_TYPES = ("STRING",) + FUNCTION = "func" + CATEGORY = "vanilla" + OUTPUT_NODE = True + + def func(self, *args, **kwargs): + output_text = kwargs["delimeter"].join( + [ + value + for key, value in kwargs.items() + if key.startswith("input") and value.strip() != "" + ] + ) + return { + "ui": {"output_text": output_text}, + "result": (output_text,), + } diff --git a/js/main.js b/js/main.js index 60fb44d..62a4cc6 100644 --- a/js/main.js +++ b/js/main.js @@ -1,27 +1,26 @@ -const NAME = "SimpleWildcard"; +const WILDCARD_NAME = "SimpleWildcard"; +const CONCAT_NAME = "SimpleConcat"; import { app } from "../../../scripts/app.js"; +import { ComfyWidgets } from "../../../scripts/widgets.js"; app.registerExtension({ - name: NAME, + name: WILDCARD_NAME, async nodeCreated(node, nodeData, app) { - if (node?.comfyClass !== NAME) return; + if (node?.comfyClass !== WILDCARD_NAME) return; const input_files_widget_index = node.widgets?.findIndex( (w) => w.name === "input_files", ); const input_files_widget = node.widgets?.[input_files_widget_index]; - const output_text_widget = node.widgets?.find( - (w) => w.name === "output_text", - ); const input_text_widget = node.widgets?.find( (w) => w.name === "input_text", ); - let value = input_files_widget.value; + let input_files = input_files_widget.value; let ac; Object.defineProperty(input_files_widget, "value", { get() { - return value; + return input_files; }, set(newValue) { - value = newValue; + input_files = newValue; ac?.abort(); ac = new AbortController(); fetch(`/simple-wildcards?path=${encodeURIComponent(newValue)}`, { @@ -37,11 +36,77 @@ app.registerExtension({ enumerable: true, configurable: true, }); + const output_text_widget = node.widgets?.find( + (w) => w.name === "output_text", + ); output_text_widget.inputEl.placeholder = "output_text (autogenerated)"; output_text_widget.inputEl.disabled = true; + output_text_widget.inputEl.opacity = 0.6; }, async beforeRegisterNodeDef(node, nodeData, app) { - if (node?.comfyClass !== NAME) return; + if (node?.comfyClass !== WILDCARD_NAME) return; + const onExecuted = node.prototype.onExecuted; + node.prototype.onExecuted = function (message) { + onExecuted?.apply(this, arguments); + const output_text_widget = this.widgets?.find( + (w) => w.name === "output_text", + ); + if (!output_text_widget) return; + output_text_widget.value = message.output_text.join(""); + this.onResize?.(this.size); // onResize redraws the node + }; + }, +}); + +app.registerExtension({ + name: CONCAT_NAME, + async nodeCreated(node, nodeData, app) { + if (node?.comfyClass !== CONCAT_NAME) return; + const num_widget = node.widgets?.find((w) => w.name === "num"); + let num = num_widget.value; + Object.defineProperty(num_widget, "value", { + get() { + return num; + }, + set(newValue) { + num = newValue; + for (let i = 0; i < 10; i++) { + const name = `input_${i}`; + if (i < num) { + let widget = node.widgets?.find((w) => w.name === name); + if (widget !== undefined) continue; + widget = ComfyWidgets["STRING"]( + node, + name, + ["STRING", { default: "", multiline: true }], + app, + ).widget; + node.onResize?.(node.size); + } else { + const widget_index = node.widgets?.findIndex( + (w) => w.name === name, + ); + if (widget_index >= 0) { + const wid = node.widgets[widget_index]; + node.widgets.splice(widget_index, 1); + wid?.onRemove(); + node.size = node.computeSize(); + node.setDirtyCanvas(true, true); + } + } + } + }, + enumerable: true, + configurable: true, + }); + const output_text_widget = node.widgets?.find( + (w) => w.name === "output_text", + ); + output_text_widget.inputEl.placeholder = "output_text (autogenerated)"; + output_text_widget.inputEl.disabled = true; + }, + async beforeRegisterNodeDef(node, nodeData, app) { + if (node?.comfyClass !== CONCAT_NAME) return; const onExecuted = node.prototype.onExecuted; node.prototype.onExecuted = function (message) { onExecuted?.apply(this, arguments); diff --git a/wildcard.py b/wildcard.py index bafe768..98a0c8e 100644 --- a/wildcard.py +++ b/wildcard.py @@ -1,12 +1,10 @@ from pipe import where, map, dedup, sort from .utils import flat_map -from pathlib import Path, PosixPath -import os -import folder_paths +from pathlib import Path import random +from .vars import application_root_directory -application_root_directory = os.path.dirname(folder_paths.__file__) -wildcards_directory = Path(application_root_directory) / "wildcards" +wildcards_directory = application_root_directory / "wildcards" if not wildcards_directory.is_dir(): wildcards_directory.mkdir() files = list( @@ -24,7 +22,7 @@ def get_items_for_wildcard_path(glob: str): ) -class Wildcard: +class SimpleWildcard: def __init__(self): pass