diff --git a/README.md b/README.md
index 134eb2d..fffcc5d 100644
--- a/README.md
+++ b/README.md
@@ -33,8 +33,12 @@
Add delays and timing control to your workflows |
+| 🔀 Logic & Math |
+Boolean logic gates and mathematical operations |
+
+
| 🔧 Utilities |
-Essential helpers for workflow optimization |
+Essential helpers for workflow optimization and list management |
@@ -142,6 +146,54 @@ Useful for dynamically adjusting prompts, modifying paths, and cleaning up gener
+
+🔍 Text Regex (Search & Replace) - Pattern-based text manipulation
+
+**Category:** `DebugPadawan/Text`
+
+Searches for patterns using Regular Expressions and optionally replaces them.
+
+**📥 Inputs:**
+- `text` *(String)*: The text to process
+- `pattern` *(String)*: The regex pattern to search for
+- `replace` *(String)*: The string to replace matches with
+
+**📤 Outputs:**
+- `text` *(String)*: The modified text string
+- `matches` *(List)*: All strings that matched the pattern
+- `found` *(Boolean)*: True if at least one match was found
+
+Extract numbers, redact sensitive info, complex pattern replacement.
+
+
+
+
+💬 String Formatter - Create custom strings with placeholders
+
+**Category:** `DebugPadawan/Text`
+
+Formats a string using f-string-like syntax with up to 5 input strings.
+
+**📥 Inputs:**
+- `format_string` *(String)*: The template string with placeholders (e.g., "Hello {input1}, your number is {input2}").
+- `input1` - `input5` *(String, optional)*: Up to five input strings to replace placeholders in the `format_string`.
+
+**📤 Outputs:**
+- `formatted_string` *(String)*: The resulting formatted string.
+
+**💡 Example:**
+```
+Format String: "The result of {input1} plus {input2} is {input3}"
+Input1: "2"
+Input2: "3"
+Input3: "5"
+Output: "The result of 2 plus 3 is 5"
+```
+
+Useful for constructing dynamic prompts, filenames, messages, or URLs from multiple pieces of data.
+
+
+
---
### 📦 Data Conversion
@@ -169,6 +221,35 @@ Use this node to quickly transform JSON-formatted text into a usable data struct
+
+💬 JSONToText - Convert JSON to text
+
+**Category:** `DebugPadawan/JSON`
+
+Converts a JSON object back into a formatted JSON string.
+
+**📥 Inputs:**
+- `json_data` *(JSON/Object)*: The JSON object to convert.
+- `indent` *(Integer, optional)*: Number of spaces for indentation (0 for no indentation, default: `0`).
+
+**📤 Outputs:**
+- `text` *(String)*: The JSON object as a string.
+
+**💡 Example:**
+```
+Input: { "name": "Alice", "age": 30 }
+Indent: 2
+Output:
+{
+ "name": "Alice",
+ "age": 30
+}
+```
+
+Use this node to convert JSON data into a readable string for display, logging, or integration with nodes that expect string input.
+
+
+
---
### 🐛 Debug Tools
@@ -296,6 +377,55 @@ Generates a random float or integer between a min and max value, driven by a spe
---
+### 📝 List Manipulation
+
+
+📝 Get List Item - Retrieve an item from a list by index
+
+**Category:** `DebugPadawan/List`
+
+Retrieves a specific item from a list based on its index. Supports converting some iterable types to lists.
+
+**📥 Inputs:**
+- `input_list` *(Any, List force input)*: The input list or iterable.
+- `index` *(Integer)*: The zero-based index of the item to retrieve.
+
+**📤 Outputs:**
+- `item` *(Any)*: The item at the specified index.
+
+**💡 Example:**
+```
+Input List: ["apple", "banana", "orange"]
+Index: 1
+Output: "banana"
+```
+
+Useful for extracting specific elements from lists, such as getting a particular image from a batch, a specific tag from a list, or individual components from a parsed string list.
+
+
+
+
+✂️ List Slicer - Extract a range of items from a list
+
+**Category:** `DebugPadawan/List`
+
+Extracts a sub-list from a larger list using start and end indices.
+
+**📥 Inputs:**
+- `input_list` *(List)*: The input list.
+- `start` *(Integer)*: Starting index (inclusive).
+- `end` *(Integer)*: Ending index (exclusive, use 0 for "to the end").
+
+**📤 Outputs:**
+- `list_slice` *(List)*: The sub-list.
+- `count` *(Integer)*: Number of elements in the slice.
+
+Get the first N images of a batch, skip the first item, or take a specific range of values.
+
+
+
+---
+
### 🔧 Utility Nodes
@@ -336,6 +466,24 @@ Conditional prompt modification, dynamic filename generation, workflow branching
+
+🔌 Logic Gate - Boolean operations
+
+**Category:** `DebugPadawan/Logic`
+
+Performs AND, OR, NOT, or XOR operations on boolean inputs.
+
+**📥 Inputs:**
+- `a`, `b` *(Boolean)*: Boolean values.
+- `operation` *(List)*: AND, OR, NOT (A), XOR.
+
+**📤 Outputs:**
+- `result` *(Boolean)*: The resulting boolean value.
+
+Complex workflow branching and conditional logic.
+
+
+
---
## 🎨 Quick Start Examples
@@ -368,14 +516,15 @@ Data → Wait (2.0s) → Processing → Wait (1.0s) → Output
| Category | Nodes | Purpose |
|----------|-------|---------|
-| **DebugPadawan/Text** | Text Splitter, Text Joiner, Text Replace | Text manipulation and processing |
-| **DebugPadawan/JSON** | TextToJSON | Text to JSON conversion |
+| **DebugPadawan/Text** | Text Splitter, Text Joiner, Text Replace, Text Regex, String Formatter | Text manipulation and processing |
+| **DebugPadawan/JSON** | TextToJSON, JSONToText | Text to JSON conversion |
| **DebugPadawan/Debug** | Debug Print | Debugging and monitoring |
| **DebugPadawan/Timing** | Wait | Timing control and delays |
| **DebugPadawan/Utilities** | List Info | Data analysis helpers |
-| **DebugPadawan/Logic** | Conditional String | Conditional operations |
+| **DebugPadawan/Logic** | Conditional String, Logic Gate | Conditional operations |
| **DebugPadawan/Image** | Image Info | Image tensor analysis |
| **DebugPadawan/Math** | Int Math Operation, Float Math Operation, Random Generator | Basic arithmetic and random number generation |
+| **DebugPadawan/List** | Get List Item, List Slicer | List manipulation |
---
@@ -399,4 +548,4 @@ This project is licensed under the [MIT License](LICENSE).
**Made with ❤️ by [DebugPadawan](https://github.com/DebugPadawan)**
-
\ No newline at end of file
+
diff --git a/__init__.py b/__init__.py
index 61a548a..9f8d34d 100644
--- a/__init__.py
+++ b/__init__.py
@@ -15,6 +15,13 @@ from .nodes.image import NODE_CLASS_MAPPINGS as IMAGE_NODES
from .nodes.image import NODE_DISPLAY_NAME_MAPPINGS as IMAGE_DISPLAY_NAMES
from .nodes.math_nodes import NODE_CLASS_MAPPINGS as MATH_NODES
from .nodes.math_nodes import NODE_DISPLAY_NAME_MAPPINGS as MATH_DISPLAY_NAMES
+from .nodes.list_nodes import NODE_CLASS_MAPPINGS as LIST_NODES
+from .nodes.list_nodes import NODE_DISPLAY_NAME_MAPPINGS as LIST_DISPLAY_NAMES
+
+from .nodes.json_to_text import NODE_CLASS_MAPPINGS as JSON_TO_TEXT_NODES
+from .nodes.json_to_text import NODE_DISPLAY_NAME_MAPPINGS as JSON_TO_TEXT_DISPLAY_NAMES
+from .nodes.string_formatter import NODE_CLASS_MAPPINGS as STRING_FORMATTER_NODES
+from .nodes.string_formatter import NODE_DISPLAY_NAME_MAPPINGS as STRING_FORMATTER_DISPLAY_NAMES
# Combine all node mappings
NODE_CLASS_MAPPINGS = {
@@ -24,6 +31,9 @@ NODE_CLASS_MAPPINGS = {
**JSON_NODES,
**IMAGE_NODES,
**MATH_NODES,
+ **LIST_NODES,
+ **JSON_TO_TEXT_NODES,
+ **STRING_FORMATTER_NODES,
}
NODE_DISPLAY_NAME_MAPPINGS = {
@@ -33,10 +43,13 @@ NODE_DISPLAY_NAME_MAPPINGS = {
**JSON_DISPLAY_NAMES,
**IMAGE_DISPLAY_NAMES,
**MATH_DISPLAY_NAMES,
+ **LIST_DISPLAY_NAMES,
+ **JSON_TO_TEXT_DISPLAY_NAMES,
+ **STRING_FORMATTER_DISPLAY_NAMES,
}
__all__ = ['NODE_CLASS_MAPPINGS', 'NODE_DISPLAY_NAME_MAPPINGS']
# Version info
-__version__ = "1.0.0"
+__version__ = "1.1.0"
__author__ = "DebugPadawan"
\ No newline at end of file
diff --git a/nodes/__init__.py b/nodes/__init__.py
index cfda150..e69de29 100644
--- a/nodes/__init__.py
+++ b/nodes/__init__.py
@@ -1,3 +0,0 @@
-"""
-Nodes package for DebugPadawan's ComfyUI Essentials
-"""
\ No newline at end of file
diff --git a/nodes/json_to_text.py b/nodes/json_to_text.py
new file mode 100644
index 0000000..c5ebd52
--- /dev/null
+++ b/nodes/json_to_text.py
@@ -0,0 +1,32 @@
+import json
+
+class JSONToText:
+ @classmethod
+ def INPUT_TYPES(s):
+ return {
+ "required": {
+ "json_data": ("JSON", {"forceInput": True}),
+ "indent": ("INT", {"default": 0, "min": 0, "max": 10, "step": 1}),
+ }
+ }
+
+ RETURN_TYPES = ("STRING",)
+ FUNCTION = "json_to_text"
+ CATEGORY = "DebugPadawan/JSON"
+ DESCRIPTION = "Converts a JSON object back into a formatted string"
+
+ def json_to_text(self, json_data, indent):
+ if indent > 0:
+ return (json.dumps(json_data, indent=indent),)
+ else:
+ return (json.dumps(json_data),)
+
+# A dictionary that contains all nodes you want to export with their names
+NODE_CLASS_MAPPINGS = {
+ "DP_JSONToText": JSONToText
+}
+
+# A dictionary that contains the friendly names for the nodes
+NODE_DISPLAY_NAME_MAPPINGS = {
+ "DP_JSONToText": "JSON To Text"
+}
diff --git a/nodes/list_nodes.py b/nodes/list_nodes.py
new file mode 100644
index 0000000..9a20270
--- /dev/null
+++ b/nodes/list_nodes.py
@@ -0,0 +1,103 @@
+import torch
+
+class GetListItem:
+ @classmethod
+ def INPUT_TYPES(s):
+ return {
+ "required": {
+ "input_list": ("*", {"forceInput": True}),
+ "index": ("INT", {"default": 0, "min": 0, "max": 0xFFFFFFFFFFFFFFFF}),
+ }
+ }
+
+ RETURN_TYPES = ("*",)
+ RETURN_NAMES = ("item",)
+ FUNCTION = "get_item"
+ CATEGORY = "DebugPadawan/List"
+
+ def get_item(self, input_list, index):
+ if not isinstance(input_list, list):
+ # Attempt to convert to list if it's a ComfyUI tensor batch or similar
+ if isinstance(input_list, torch.Tensor):
+ input_list = input_list.tolist()
+ elif hasattr(input_list, '__iter__') and not isinstance(input_list, str):
+ input_list = list(input_list)
+ else:
+ # If it's a single item, wrap it in a list to allow indexing
+ input_list = [input_list]
+
+ if not input_list:
+ raise ValueError("Input list is empty.")
+ if index < 0 or index >= len(input_list):
+ raise IndexError(f"Index {index} out of bounds for list of length {len(input_list)}.")
+
+ return (input_list[index],)
+
+
+class ListSlicer:
+ """
+ Node for getting a slice of a list
+ """
+ @classmethod
+ def INPUT_TYPES(cls):
+ return {
+ "required": {
+ "input_list": ("LIST",),
+ "start": ("INT", {"default": 0, "min": 0}),
+ "end": ("INT", {"default": 0, "min": 0}),
+ }
+ }
+
+ RETURN_TYPES = ("LIST", "INT")
+ RETURN_NAMES = ("list_slice", "count")
+ FUNCTION = "slice_list"
+ CATEGORY = "DebugPadawan/List"
+
+ def slice_list(self, input_list, start, end):
+ if end == 0:
+ res = input_list[start:]
+ else:
+ res = input_list[start:end]
+ return (res, len(res))
+
+
+class ListInfo:
+ """
+ Node for getting information about a list
+ """
+ @classmethod
+ def INPUT_TYPES(cls):
+ return {
+ "required": {
+ "input_list": ("LIST",),
+ }
+ }
+
+ RETURN_TYPES = ("INT", "STRING", "STRING")
+ RETURN_NAMES = ("count", "first_item", "last_item")
+
+ FUNCTION = "get_list_info"
+ CATEGORY = "DebugPadawan/Utilities"
+
+ def get_list_info(self, input_list):
+ count = len(input_list)
+ first_item = str(input_list[0]) if input_list else ""
+ last_item = str(input_list[-1]) if input_list else ""
+
+ return (count, first_item, last_item)
+
+
+NODE_CLASS_MAPPINGS = {
+ "DebugPadawan_GetListItem": GetListItem,
+ "DebugPadawan_ListSlicer": ListSlicer,
+ "DebugPadawan_ListInfo": ListInfo,
+ # Alias for backward compatibility
+ "DP_GetListItem": GetListItem,
+}
+
+NODE_DISPLAY_NAME_MAPPINGS = {
+ "DebugPadawan_GetListItem": "Get List Item",
+ "DebugPadawan_ListSlicer": "List Slicer",
+ "DebugPadawan_ListInfo": "List Info",
+ "DP_GetListItem": "Get List Item (Legacy)",
+}
diff --git a/nodes/string_formatter.py b/nodes/string_formatter.py
new file mode 100644
index 0000000..c368099
--- /dev/null
+++ b/nodes/string_formatter.py
@@ -0,0 +1,54 @@
+class StringFormatter:
+ @classmethod
+ def INPUT_TYPES(s):
+ return {
+ "required": {
+ "format_string": ("STRING", {"multiline": True, "default": "Result: {input1}, {input2}"}),
+ },
+ "optional": {
+ "input1": ("STRING", {"multiline": True, "default": ""}),
+ "input2": ("STRING", {"multiline": True, "default": ""}),
+ "input3": ("STRING", {"multiline": True, "default": ""}),
+ "input4": ("STRING", {"multiline": True, "default": ""}),
+ "input5": ("STRING", {"multiline": True, "default": ""}),
+ }
+ }
+
+ RETURN_TYPES = ("STRING",)
+ FUNCTION = "format_text"
+ CATEGORY = "DebugPadawan/Text"
+ DESCRIPTION = "Formats a string using f-string-like syntax with multiple inputs."
+
+ def format_text(self, format_string, input1="", input2="", input3="", input4="", input5=""):
+ # Create a dictionary of inputs to be used in the format string
+ # Filter out empty inputs if necessary, though format() handles missing keys gracefully.
+ inputs = {
+ "input1": input1,
+ "input2": input2,
+ "input3": input3,
+ "input4": input4,
+ "input5": input5,
+ }
+
+ # Use str.format_map for f-string-like behavior
+ # It's important to provide a default value for any potential missing keys in the format_string
+ # For simplicity, we are assuming keys in format_string will match inputX,
+ # or that missing ones will not cause an error with a default empty string.
+ try:
+ formatted_string = format_string.format(**inputs)
+ except KeyError as e:
+ # Handle cases where format_string contains keys not provided by inputs
+ # This can happen if user uses {input6} but only input1-5 are available
+ formatted_string = f"Error: Missing input for key {e} in format string."
+ except Exception as e:
+ formatted_string = f"An unexpected error occurred during formatting: {e}"
+
+ return (formatted_string,)
+
+NODE_CLASS_MAPPINGS = {
+ "DP_StringFormatter": StringFormatter
+}
+
+NODE_DISPLAY_NAME_MAPPINGS = {
+ "DP_StringFormatter": "String Formatter"
+}
diff --git a/nodes/text_processing.py b/nodes/text_processing.py
index 088ba52..6168f08 100644
--- a/nodes/text_processing.py
+++ b/nodes/text_processing.py
@@ -1,4 +1,5 @@
import json
+import re
class TextSplitter:
@@ -126,43 +127,56 @@ class TextReplace:
return (text.replace(find, replace),)
-class ListInfo:
+class TextRegex:
"""
- Node for getting information about a list
+ Node for searching and replacing text using regular expressions
"""
@classmethod
def INPUT_TYPES(cls):
return {
"required": {
- "input_list": ("LIST",),
+ "text": ("STRING", {
+ "multiline": True,
+ "default": "My phone number is 123-456-7890"
+ }),
+ "pattern": ("STRING", {
+ "multiline": False,
+ "default": r"\d{3}-\d{3}-\d{4}"
+ }),
+ "replace": ("STRING", {
+ "multiline": False,
+ "default": "[REDACTED]"
+ }),
}
}
- RETURN_TYPES = ("INT", "STRING", "STRING")
- RETURN_NAMES = ("count", "first_item", "last_item")
+ RETURN_TYPES = ("STRING", "LIST", "BOOLEAN")
+ RETURN_NAMES = ("text", "matches", "found")
- FUNCTION = "get_list_info"
- CATEGORY = "DebugPadawan/Utilities"
+ FUNCTION = "regex_op"
+ CATEGORY = "DebugPadawan/Text"
- def get_list_info(self, input_list):
- count = len(input_list)
- first_item = str(input_list[0]) if input_list else ""
- last_item = str(input_list[-1]) if input_list else ""
+ def regex_op(self, text, pattern, replace):
+ if not pattern:
+ return (text, [], False)
- return (count, first_item, last_item)
+ matches = re.findall(pattern, text)
+ result = re.sub(pattern, replace, text)
+
+ return (result, matches, len(matches) > 0)
NODE_CLASS_MAPPINGS = {
"DebugPadawan_TextSplitter": TextSplitter,
"DebugPadawan_TextJoiner": TextJoiner,
"DebugPadawan_TextReplace": TextReplace,
- "DebugPadawan_ListInfo": ListInfo,
+ "DebugPadawan_TextRegex": TextRegex,
}
NODE_DISPLAY_NAME_MAPPINGS = {
"DebugPadawan_TextSplitter": "Text Splitter",
"DebugPadawan_TextJoiner": "Text Joiner",
"DebugPadawan_TextReplace": "Text Replace",
- "DebugPadawan_ListInfo": "List Info",
+ "DebugPadawan_TextRegex": "Text Regex (Search & Replace)",
}
diff --git a/nodes/utilities.py b/nodes/utilities.py
index 79ff114..8987ba4 100644
--- a/nodes/utilities.py
+++ b/nodes/utilities.py
@@ -90,12 +90,50 @@ class ConditionalString:
"""
return (true_string if condition else false_string,)
+
+class LogicGate:
+ """
+ Node for boolean logic operations (AND, OR, NOT, XOR)
+ """
+
+ @classmethod
+ def INPUT_TYPES(cls):
+ return {
+ "required": {
+ "a": ("BOOLEAN", {"default": True}),
+ "b": ("BOOLEAN", {"default": False}),
+ "operation": (["AND", "OR", "NOT (A)", "XOR"],),
+ }
+ }
+
+ RETURN_TYPES = ("BOOLEAN",)
+ RETURN_NAMES = ("result",)
+
+ FUNCTION = "logic_op"
+ CATEGORY = "DebugPadawan/Logic"
+
+ def logic_op(self, a, b, operation):
+ if operation == "AND":
+ res = a and b
+ elif operation == "OR":
+ res = a or b
+ elif operation == "NOT (A)":
+ res = not a
+ elif operation == "XOR":
+ res = a != b
+ else:
+ res = False
+ return (res,)
+
+
UTILITY_NODE_CLASS_MAPPINGS = {
"DebugPadawan_DebugPrint": DebugPrint,
"DebugPadawan_ConditionalString": ConditionalString,
+ "DebugPadawan_LogicGate": LogicGate,
}
UTILITY_NODE_DISPLAY_NAME_MAPPINGS = {
"DebugPadawan_DebugPrint": "Debug Print",
"DebugPadawan_ConditionalString": "Conditional String",
-}
\ No newline at end of file
+ "DebugPadawan_LogicGate": "Logic Gate",
+}