feat: add Regex, Logic Gate, and List Slicer nodes; refactor project structure

This commit is contained in:
root
2026-03-24 19:45:07 +01:00
parent 8d418fa89c
commit c91536449c
8 changed files with 424 additions and 24 deletions
+154 -5
View File
@@ -33,8 +33,12 @@
<td>Add delays and timing control to your workflows</td>
</tr>
<tr>
<td><b>🔀 Logic & Math</b></td>
<td>Boolean logic gates and mathematical operations</td>
</tr>
<tr>
<td><b>🔧 Utilities</b></td>
<td>Essential helpers for workflow optimization</td>
<td>Essential helpers for workflow optimization and list management</td>
</tr>
</table>
@@ -142,6 +146,54 @@ Useful for dynamically adjusting prompts, modifying paths, and cleaning up gener
</details>
<details>
<summary><b>🔍 Text Regex (Search & Replace)</b> - Pattern-based text manipulation</summary>
**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.
</details>
<details>
<summary><b>💬 String Formatter</b> - Create custom strings with placeholders</summary>
**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.
</details>
---
### 📦 Data Conversion
@@ -169,6 +221,35 @@ Use this node to quickly transform JSON-formatted text into a usable data struct
</details>
<details>
<summary><b>💬 JSONToText</b> - Convert JSON to text</summary>
**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.
</details>
---
### 🐛 Debug Tools
@@ -296,6 +377,55 @@ Generates a random float or integer between a min and max value, driven by a spe
---
### 📝 List Manipulation
<details>
<summary><b>📝 Get List Item</b> - Retrieve an item from a list by index</summary>
**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.
</details>
<details>
<summary><b>✂️ List Slicer</b> - Extract a range of items from a list</summary>
**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.
</details>
---
### 🔧 Utility Nodes
<details>
@@ -336,6 +466,24 @@ Conditional prompt modification, dynamic filename generation, workflow branching
</details>
<details>
<summary><b>🔌 Logic Gate</b> - Boolean operations</summary>
**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.
</details>
---
## 🎨 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)**
</div>
</div>
+14 -1
View File
@@ -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"
-3
View File
@@ -1,3 +0,0 @@
"""
Nodes package for DebugPadawan's ComfyUI Essentials
"""
+32
View File
@@ -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"
}
+103
View File
@@ -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)",
}
+54
View File
@@ -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"
}
+28 -14
View File
@@ -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)",
}
+39 -1
View File
@@ -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",
}
"DebugPadawan_LogicGate": "Logic Gate",
}