diff --git a/README.md b/README.md
index 683ed63..ff710de 100644
--- a/README.md
+++ b/README.md
@@ -7,7 +7,7 @@
[](https://python.org)
[](https://github.com/comfyanonymous/ComfyUI)
[](LICENSE)
-[](https://github.com/DebugPadawan/DebugPadawans-ComfyUI-Essentials/releases)
+[](https://github.com/DebugPadawan/DebugPadawans-ComfyUI-Essentials/releases)
[](https://github.com/DebugPadawan/DebugPadawans-ComfyUI-Essentials/stargazers)
@@ -301,6 +301,95 @@ The "all" mode removes all whitespace and collapses multiple spaces into single
+
+๐ค Text Case Converter - Transform text case styles
+
+**Category:** `DebugPadawan/Text`
+
+Converts text to different case styles for formatting prompts, filenames, and display text.
+
+**๐ฅ Inputs:**
+- `text` *(String)*: The text to convert
+- `case_mode` *(List)*: Case style - UPPER, lower, Title Case, Sentence case, snake_case, kebab-case, camelCase, PascalCase
+
+**๐ค Outputs:**
+- `converted_text` *(String)*: The converted text
+
+**๐ก Example:**
+```
+Input: "hello world"
+Mode: "Title Case"
+Output: "Hello World"
+
+Input: "Hello World"
+Mode: "snake_case"
+Output: "hello_world"
+
+Input: "hello world"
+Mode: "PascalCase"
+Output: "HelloWorld"
+```
+
+Normalize prompts, format filenames, convert naming conventions between different systems.
+
+
+
+
+โ๏ธ Text Trimmer - Advanced text trimming options
+
+**Category:** `DebugPadawan/Text`
+
+Trims text with various whitespace removal options.
+
+**๐ฅ Inputs:**
+- `text` *(String)*: The text to trim
+- `mode` *(List)*: Trim mode - both, start, end, all_whitespace, collapse_spaces
+
+**๐ค Outputs:**
+- `trimmed_text` *(String)*: The trimmed text
+
+**๐ก Example:**
+```
+Input: " Hello World "
+Mode: "collapse_spaces"
+Output: "Hello World"
+
+Mode: "all_whitespace"
+Output: "HelloWorld"
+```
+
+Clean up prompt formatting, remove extra spaces, prepare text for further processing.
+
+
+
+
+๐ Text Prefix Suffix - Add prefix and/or suffix to text
+
+**Category:** `DebugPadawan/Text`
+
+Adds prefix and/or suffix to text with optional spacing.
+
+**๐ฅ Inputs:**
+- `text` *(String)*: The base text
+- `prefix` *(String, optional)*: Text to add at the beginning
+- `suffix` *(String, optional)*: Text to add at the end
+- `add_space` *(Boolean)*: Add space between parts (default: True)
+
+**๐ค Outputs:**
+- `result_text` *(String)*: The modified text
+
+**๐ก Example:**
+```
+Text: "beautiful"
+Prefix: "a"
+Suffix: "sunset"
+Output: "a beautiful sunset"
+```
+
+Build complex prompts from components, wrap text with markers, add modifiers to prompts.
+
+
+
---
### ๐ฆ Data Conversion
@@ -551,6 +640,115 @@ Get the first N images of a batch, skip the first item, or take a specific range
+
+๐ฒ Random List Selector - Pick random items from a list
+
+**Category:** `DebugPadawan/List`
+
+Randomly selects one or more items from a list with seed-based reproducibility.
+
+**๐ฅ Inputs:**
+- `input_list` *(List)*: The input list.
+- `seed` *(Integer)*: Seed for reproducible random selection.
+- `count` *(Integer)*: Number of items to select (default: 1).
+- `allow_duplicates` *(Boolean)*: Allow selecting the same item multiple times.
+
+**๐ค Outputs:**
+- `selected_items` *(List)*: The randomly selected items.
+- `count` *(Integer)*: Number of items selected.
+- `first_item` *(Any)*: The first selected item (convenient for single selections).
+
+**๐ก Example:**
+```
+Input List: ["style1", "style2", "style3", "style4", "style5"]
+Seed: 42
+Count: 2
+Output: ["style3", "style1"]
+```
+
+Perfect for randomizing prompt styles, selecting random seeds, or picking random colors/tags from lists.
+
+
+
+
+๐ List Shuffler - Shuffle list order deterministically
+
+**Category:** `DebugPadawan/List`
+
+Shuffles a list into random order using a seed for reproducibility.
+
+**๐ฅ Inputs:**
+- `input_list` *(List)*: The input list.
+- `seed` *(Integer)*: Seed for reproducible shuffling.
+
+**๐ค Outputs:**
+- `shuffled_list` *(List)*: The shuffled list.
+- `count` *(Integer)*: Number of items.
+
+**๐ก Example:**
+```
+Input: ["a", "b", "c", "d"]
+Seed: 123
+Output: ["c", "a", "d", "b"]
+```
+
+Randomize processing order, shuffle prompt components, or reorder batch elements.
+
+
+
+
+๐ List Merger - Combine multiple lists
+
+**Category:** `DebugPadawan/List`
+
+Merges two lists together with different modes.
+
+**๐ฅ Inputs:**
+- `list_a` *(List)*: First list.
+- `list_b` *(List)*: Second list.
+- `mode` *(List)*: Merge mode - "concatenate" or "interleave".
+
+**๐ค Outputs:**
+- `merged_list` *(List)*: The merged list.
+- `count` *(Integer)*: Number of items.
+
+**๐ก Example:**
+```
+List A: [1, 2, 3]
+List B: ["a", "b", "c"]
+Mode: "interleave"
+Output: [1, "a", 2, "b", 3, "c"]
+```
+
+Combine tag lists, merge processing pipelines, or interleave prompt components.
+
+
+
+
+๐งน List Deduplicator - Remove duplicates from a list
+
+**Category:** `DebugPadawan/List`
+
+Removes duplicate items from a list while preserving order.
+
+**๐ฅ Inputs:**
+- `input_list` *(List)*: The input list.
+
+**๐ค Outputs:**
+- `deduplicated_list` *(List)*: List with duplicates removed.
+- `count` *(Integer)*: Number of unique items.
+- `removed_count` *(Integer)*: Number of duplicates removed.
+
+**๐ก Example:**
+```
+Input: ["apple", "banana", "apple", "orange", "banana"]
+Output: ["apple", "banana", "orange"], count: 3, removed: 2
+```
+
+Clean up tag lists, remove redundant prompts, or filter unique values.
+
+
+
---
### ๐ Type Conversion
@@ -1012,7 +1210,7 @@ Data โ Wait (2.0s) โ Processing โ Wait (1.0s) โ Output
| Category | Nodes | Purpose |
|----------|-------|---------|
-| **DebugPadawan/Text** | Text Splitter, Text Joiner, Text Replace, Text Regex, String Formatter, Text Compare, Text Length, Text Case, Text Trim | Text manipulation and processing |
+| **DebugPadawan/Text** | Text Splitter, Text Joiner, Text Replace, Text Regex, String Formatter, Text Compare, Text Length, Text Case, Text Trim, Text Case Converter, Text Trimmer, Text Prefix Suffix | 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 |
@@ -1020,7 +1218,7 @@ Data โ Wait (2.0s) โ Processing โ Wait (1.0s) โ Output
| **DebugPadawan/Logic** | Conditional String, Logic Gate | Conditional operations |
| **DebugPadawan/Image** | Image Info, Color Palette Extractor | Image tensor analysis and color extraction |
| **DebugPadawan/Math** | Int/Float Math Operation, Random Generator | Basic arithmetic and random number generation |
-| **DebugPadawan/List** | Get List Item, List Slicer | List manipulation |
+| **DebugPadawan/List** | Get List Item, List Slicer, Random List Selector, List Shuffler, List Merger, List Deduplicator | List manipulation |
| **DebugPadawan/Conversion** | Type Converter, Int to Float, Float to Int, String to Int, String to Float, String to Boolean, Number to String | Type conversion utilities |
| **DebugPadawan/Number** | Number Clamp, Number Round, Number Range, Number Compare, Number Absolute, Number Sign, Number Remap, Number Lerp | Number formatting and utilities |
diff --git a/__init__.py b/__init__.py
index 174ecdc..4564547 100644
--- a/__init__.py
+++ b/__init__.py
@@ -67,5 +67,5 @@ NODE_DISPLAY_NAME_MAPPINGS = {
__all__ = ['NODE_CLASS_MAPPINGS', 'NODE_DISPLAY_NAME_MAPPINGS']
# Version info
-__version__ = "1.4.0"
+__version__ = "1.5.0"
__author__ = "DebugPadawan"
\ No newline at end of file
diff --git a/nodes/list_nodes.py b/nodes/list_nodes.py
index 9a20270..467549e 100644
--- a/nodes/list_nodes.py
+++ b/nodes/list_nodes.py
@@ -1,4 +1,6 @@
import torch
+import random
+
class GetListItem:
@classmethod
@@ -87,10 +89,160 @@ class ListInfo:
return (count, first_item, last_item)
+class RandomListSelector:
+ """
+ Node for randomly selecting one or more items from a list
+ Supports seeded randomness for reproducible selections
+ """
+
+ @classmethod
+ def INPUT_TYPES(cls):
+ return {
+ "required": {
+ "input_list": ("LIST",),
+ "seed": ("INT", {"default": 0, "min": 0, "max": 0xffffffffffffffff}),
+ "count": ("INT", {"default": 1, "min": 1}),
+ "allow_duplicates": ("BOOLEAN", {"default": False}),
+ }
+ }
+
+ RETURN_TYPES = ("LIST", "INT", "*")
+ RETURN_NAMES = ("selected_items", "count", "first_item")
+
+ FUNCTION = "select_random"
+ CATEGORY = "DebugPadawan/List"
+
+ def select_random(self, input_list, seed, count, allow_duplicates):
+ if not input_list:
+ raise ValueError("Input list is empty.")
+
+ rng = random.Random(seed)
+ list_len = len(input_list)
+
+ if count > list_len and not allow_duplicates:
+ count = list_len
+
+ if allow_duplicates:
+ selected = [rng.choice(input_list) for _ in range(count)]
+ else:
+ selected = rng.sample(input_list, min(count, list_len))
+
+ first_item = selected[0] if selected else None
+ return (selected, len(selected), first_item)
+
+
+class ListShuffler:
+ """
+ Node for shuffling a list with a deterministic seed
+ Useful for randomizing order while maintaining reproducibility
+ """
+
+ @classmethod
+ def INPUT_TYPES(cls):
+ return {
+ "required": {
+ "input_list": ("LIST",),
+ "seed": ("INT", {"default": 0, "min": 0, "max": 0xffffffffffffffff}),
+ }
+ }
+
+ RETURN_TYPES = ("LIST", "INT")
+ RETURN_NAMES = ("shuffled_list", "count")
+
+ FUNCTION = "shuffle_list"
+ CATEGORY = "DebugPadawan/List"
+
+ def shuffle_list(self, input_list, seed):
+ if not input_list:
+ return ([], 0)
+
+ # Create a copy to avoid modifying the original
+ shuffled = list(input_list)
+ rng = random.Random(seed)
+ rng.shuffle(shuffled)
+
+ return (shuffled, len(shuffled))
+
+
+class ListMerger:
+ """
+ Node for merging multiple lists together
+ Supports concatenation and interleaving modes
+ """
+
+ @classmethod
+ def INPUT_TYPES(cls):
+ return {
+ "required": {
+ "list_a": ("LIST",),
+ "list_b": ("LIST",),
+ "mode": (["concatenate", "interleave"],),
+ }
+ }
+
+ RETURN_TYPES = ("LIST", "INT")
+ RETURN_NAMES = ("merged_list", "count")
+
+ FUNCTION = "merge_lists"
+ CATEGORY = "DebugPadawan/List"
+
+ def merge_lists(self, list_a, list_b, mode):
+ if mode == "concatenate":
+ result = list_a + list_b
+ else: # interleave
+ result = []
+ max_len = max(len(list_a), len(list_b))
+ for i in range(max_len):
+ if i < len(list_a):
+ result.append(list_a[i])
+ if i < len(list_b):
+ result.append(list_b[i])
+
+ return (result, len(result))
+
+
+class ListDeduplicator:
+ """
+ Node for removing duplicate items from a list
+ Preserves original order
+ """
+
+ @classmethod
+ def INPUT_TYPES(cls):
+ return {
+ "required": {
+ "input_list": ("LIST",),
+ }
+ }
+
+ RETURN_TYPES = ("LIST", "INT", "INT")
+ RETURN_NAMES = ("deduplicated_list", "count", "removed_count")
+
+ FUNCTION = "deduplicate"
+ CATEGORY = "DebugPadawan/List"
+
+ def deduplicate(self, input_list):
+ seen = []
+ result = []
+ for item in input_list:
+ # Convert to string for comparison (handles non-hashable types)
+ item_key = str(item)
+ if item_key not in seen:
+ seen.append(item_key)
+ result.append(item)
+
+ removed = len(input_list) - len(result)
+ return (result, len(result), removed)
+
+
NODE_CLASS_MAPPINGS = {
"DebugPadawan_GetListItem": GetListItem,
"DebugPadawan_ListSlicer": ListSlicer,
"DebugPadawan_ListInfo": ListInfo,
+ "DebugPadawan_RandomListSelector": RandomListSelector,
+ "DebugPadawan_ListShuffler": ListShuffler,
+ "DebugPadawan_ListMerger": ListMerger,
+ "DebugPadawan_ListDeduplicator": ListDeduplicator,
# Alias for backward compatibility
"DP_GetListItem": GetListItem,
}
@@ -99,5 +251,9 @@ NODE_DISPLAY_NAME_MAPPINGS = {
"DebugPadawan_GetListItem": "Get List Item",
"DebugPadawan_ListSlicer": "List Slicer",
"DebugPadawan_ListInfo": "List Info",
+ "DebugPadawan_RandomListSelector": "Random List Selector",
+ "DebugPadawan_ListShuffler": "List Shuffler",
+ "DebugPadawan_ListMerger": "List Merger",
+ "DebugPadawan_ListDeduplicator": "List Deduplicator",
"DP_GetListItem": "Get List Item (Legacy)",
}
diff --git a/nodes/text_processing.py b/nodes/text_processing.py
index 6168f08..ad2d39c 100644
--- a/nodes/text_processing.py
+++ b/nodes/text_processing.py
@@ -6,7 +6,7 @@ class TextSplitter:
"""
Node for splitting text strings by a delimiter
"""
-
+
@classmethod
def INPUT_TYPES(cls):
return {
@@ -29,25 +29,25 @@ class TextSplitter:
}),
}
}
-
+
RETURN_TYPES = ("LIST", "INT")
RETURN_NAMES = ("text_list", "count")
-
+
FUNCTION = "split_text"
CATEGORY = "DebugPadawan/Text"
-
+
def split_text(self, text, delimiter, strip_whitespace=True, remove_empty=True):
"""
Split text by delimiter and return list with count
"""
result = text.split(delimiter)
-
+
if strip_whitespace:
result = [item.strip() for item in result]
-
+
if remove_empty:
result = [item for item in result if item]
-
+
return (result, len(result))
@@ -55,7 +55,7 @@ class TextJoiner:
"""
Node for joining a list of strings with a delimiter
"""
-
+
@classmethod
def INPUT_TYPES(cls):
return {
@@ -77,13 +77,13 @@ class TextJoiner:
}),
}
}
-
+
RETURN_TYPES = ("STRING",)
RETURN_NAMES = ("joined_text",)
-
+
FUNCTION = "join_text"
CATEGORY = "DebugPadawan/Text"
-
+
def join_text(self, text_list, delimiter, prefix="", suffix=""):
str_list = [str(item) for item in text_list]
result = delimiter.join(str_list)
@@ -95,7 +95,7 @@ class TextReplace:
"""
Node for replacing specific text in a string
"""
-
+
@classmethod
def INPUT_TYPES(cls):
return {
@@ -114,13 +114,13 @@ class TextReplace:
}),
}
}
-
+
RETURN_TYPES = ("STRING",)
RETURN_NAMES = ("text",)
-
+
FUNCTION = "replace_text"
CATEGORY = "DebugPadawan/Text"
-
+
def replace_text(self, text, find, replace):
if not find:
return (text,)
@@ -131,7 +131,7 @@ class TextRegex:
"""
Node for searching and replacing text using regular expressions
"""
-
+
@classmethod
def INPUT_TYPES(cls):
return {
@@ -150,28 +150,192 @@ class TextRegex:
}),
}
}
-
+
RETURN_TYPES = ("STRING", "LIST", "BOOLEAN")
RETURN_NAMES = ("text", "matches", "found")
-
+
FUNCTION = "regex_op"
CATEGORY = "DebugPadawan/Text"
-
+
def regex_op(self, text, pattern, replace):
if not pattern:
return (text, [], False)
-
+
matches = re.findall(pattern, text)
result = re.sub(pattern, replace, text)
-
+
return (result, matches, len(matches) > 0)
+class TextCaseConverter:
+ """
+ Node for converting text to different case styles
+ Useful for formatting prompts, filenames, and display text
+ """
+
+ @classmethod
+ def INPUT_TYPES(cls):
+ return {
+ "required": {
+ "text": ("STRING", {
+ "multiline": True,
+ "default": "hello world"
+ }),
+ "case_mode": (["UPPER", "lower", "Title Case", "Sentence case", "snake_case", "kebab-case", "camelCase", "PascalCase"],),
+ }
+ }
+
+ RETURN_TYPES = ("STRING",)
+ RETURN_NAMES = ("converted_text",)
+
+ FUNCTION = "convert_case"
+ CATEGORY = "DebugPadawan/Text"
+
+ def convert_case(self, text, case_mode):
+ """
+ Convert text to the specified case style
+ """
+ if case_mode == "UPPER":
+ return (text.upper(),)
+ elif case_mode == "lower":
+ return (text.lower(),)
+ elif case_mode == "Title Case":
+ return (text.title(),)
+ elif case_mode == "Sentence case":
+ # Capitalize first letter of each sentence
+ result = '. '.join(s.capitalize() for s in text.split('. '))
+ return (result,)
+ elif case_mode == "snake_case":
+ # Convert to snake_case
+ # First normalize spaces and existing separators
+ normalized = text.lower().replace('-', ' ').replace('_', ' ')
+ words = normalized.split()
+ return ('_'.join(words),)
+ elif case_mode == "kebab-case":
+ # Convert to kebab-case
+ normalized = text.lower().replace('_', ' ').replace('-', ' ')
+ words = normalized.split()
+ return ('-'.join(words),)
+ elif case_mode == "camelCase":
+ # Convert to camelCase
+ normalized = text.replace('-', ' ').replace('_', ' ')
+ words = normalized.split()
+ if not words:
+ return ("",)
+ result = words[0].lower() + ''.join(w.capitalize() for w in words[1:])
+ return (result,)
+ elif case_mode == "PascalCase":
+ # Convert to PascalCase
+ normalized = text.replace('-', ' ').replace('_', ' ')
+ words = normalized.split()
+ result = ''.join(w.capitalize() for w in words)
+ return (result,)
+ else:
+ return (text,)
+
+
+class TextTrimmer:
+ """
+ Node for trimming text with various options
+ """
+
+ @classmethod
+ def INPUT_TYPES(cls):
+ return {
+ "required": {
+ "text": ("STRING", {
+ "multiline": True,
+ "default": " hello world "
+ }),
+ "mode": (["both", "start", "end", "all_whitespace", "collapse_spaces"],),
+ }
+ }
+
+ RETURN_TYPES = ("STRING",)
+ RETURN_NAMES = ("trimmed_text",)
+
+ FUNCTION = "trim_text"
+ CATEGORY = "DebugPadawan/Text"
+
+ def trim_text(self, text, mode):
+ """
+ Trim text based on the specified mode
+ """
+ if mode == "both":
+ return (text.strip(),)
+ elif mode == "start":
+ return (text.lstrip(),)
+ elif mode == "end":
+ return (text.rstrip(),)
+ elif mode == "all_whitespace":
+ # Remove all whitespace characters
+ return (''.join(text.split()),)
+ elif mode == "collapse_spaces":
+ # Collapse multiple spaces into single spaces
+ return (' '.join(text.split()),)
+ else:
+ return (text,)
+
+
+class TextPrefixSuffix:
+ """
+ Node for adding prefix and/or suffix to text
+ """
+
+ @classmethod
+ def INPUT_TYPES(cls):
+ return {
+ "required": {
+ "text": ("STRING", {
+ "multiline": True,
+ "default": "beautiful"
+ }),
+ },
+ "optional": {
+ "prefix": ("STRING", {
+ "multiline": False,
+ "default": ""
+ }),
+ "suffix": ("STRING", {
+ "multiline": False,
+ "default": ""
+ }),
+ "add_space": ("BOOLEAN", {"default": True}),
+ }
+ }
+
+ RETURN_TYPES = ("STRING",)
+ RETURN_NAMES = ("result_text",)
+
+ FUNCTION = "add_prefix_suffix"
+ CATEGORY = "DebugPadawan/Text"
+
+ def add_prefix_suffix(self, text, prefix="", suffix="", add_space=True):
+ result = text
+
+ if prefix:
+ if add_space and text and not text.startswith(' '):
+ result = prefix + ' ' + result
+ else:
+ result = prefix + result
+
+ if suffix:
+ if add_space and text and not text.endswith(' '):
+ result = result + ' ' + suffix
+ else:
+ result = result + suffix
+
+ return (result,)
+
+
NODE_CLASS_MAPPINGS = {
"DebugPadawan_TextSplitter": TextSplitter,
- "DebugPadawan_TextJoiner": TextJoiner,
+ "DebugPadawan_TextJoiner": TextJoiner,
"DebugPadawan_TextReplace": TextReplace,
"DebugPadawan_TextRegex": TextRegex,
+ "DebugPadawan_TextCaseConverter": TextCaseConverter,
+ "DebugPadawan_TextTrimmer": TextTrimmer,
+ "DebugPadawan_TextPrefixSuffix": TextPrefixSuffix,
}
NODE_DISPLAY_NAME_MAPPINGS = {
@@ -179,4 +343,7 @@ NODE_DISPLAY_NAME_MAPPINGS = {
"DebugPadawan_TextJoiner": "Text Joiner",
"DebugPadawan_TextReplace": "Text Replace",
"DebugPadawan_TextRegex": "Text Regex (Search & Replace)",
+ "DebugPadawan_TextCaseConverter": "Text Case Converter",
+ "DebugPadawan_TextTrimmer": "Text Trimmer",
+ "DebugPadawan_TextPrefixSuffix": "Text Prefix Suffix",
}