diff --git a/README.md b/README.md index 5e4f134..2221c69 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # String Essentials - Custom nodes for ComfyUI -Simple string manipulation nodes for ComfyUI (strip/remove text strings, search and replace text strings, preview modified string outputs). Particularly useful for modifying text from LLM outputs (Ollama, Claude.ai, OpenAI ChatGPT), such as prompts or image-to-text description results. No bloat/additional Python dependencies or external modules required. Nodes use a small amount of basic Python and Javascript code. +Simple string manipulation nodes for ComfyUI (strip/remove text strings, search and replace text strings, preview modified string outputs). Useful for modifying text from LLM outputs (Ollama, Claude.ai, OpenAI ChatGPT), such as prompts or image-to-text description results. No additional Python dependencies or external modules required. Nodes use a small amount of basic Python and Javascript code. ## Nodes included: @@ -35,7 +35,7 @@ Removes specified strings from input text. Each line should contain the string t - **`remove_extra_spaces`**: Clean up extra spaces after removal ## String Replace -Replaces text strings with new strings (replace/swap words etc.). Each line should contain a search and replace pair separated by the replacement_delimiter (default is double colon `::`). Example to replace dog with cat `dog::cat`. Supports mulitple string replacements, just add one replacement per line. +Replaces text strings with new strings (replace/swap words etc.). Each line should contain a search and replace pair separated by the replacement_delimiter (default is double colon `::`). Example to replace dog with cat `dog::cat`. Supports mulitple string replacements, just add one replacement per line. You can also remove strings with this node just leave the right side of the delimiter blank. ![stringreplace](images/stringreplace.png) @@ -54,8 +54,9 @@ Simply displays a string output. Can be used to view the results/changes to the ## Example Usage -![exampleusage](images/exampleusage.png) - +![exampleusage](images/examplestrip.png) + +![exampleusage](images/examplereplace.png) ## Installation 1. Navigate to the `custom_nodes` directory in your ComfyUI installation path. @@ -67,5 +68,5 @@ git clone https://github.com/bradsec/ComfyUI_StringEssentials.git 4. Select node from the `Add Node` > `utils` menu. -### [If you found these nodes useful please ⭐ Star the repo at github.com/bradsec/ComfyUI_StringEssentials.](https://github.com/bradsec/ComfyUI_StringEssentials "BRADSEC ComfyUI_StringEssentials") +### [Found these nodes useful? ⭐ Star the repo github.com/bradsec/ComfyUI_StringEssentials.](https://github.com/bradsec/ComfyUI_StringEssentials "BRADSEC ComfyUI_StringEssentials") diff --git a/images/examplereplace.png b/images/examplereplace.png new file mode 100644 index 0000000..b91bdc1 Binary files /dev/null and b/images/examplereplace.png differ diff --git a/images/examplestrip.png b/images/examplestrip.png new file mode 100644 index 0000000..1b5075c Binary files /dev/null and b/images/examplestrip.png differ diff --git a/images/exampleusage.png b/images/exampleusage.png deleted file mode 100644 index c46082b..0000000 Binary files a/images/exampleusage.png and /dev/null differ diff --git a/pyproject.toml b/pyproject.toml index bd7d72c..7079278 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,7 +1,7 @@ [project] name = "comfyui_stringessentials" description = "Simple string manipulation nodes for ComfyUI (strip/remove text strings, search and replace text strings, preview modified string outputs). Useful for modifying text prompts or results from LLM outputs. Nodes will be located under standard Add Node > utils menu. Node names: String Textbox, String Strip, String Replace, and String Preview." -version = "1.0.3" +version = "1.0.4" license = {file = "LICENSE"} [project.urls] diff --git a/string_preview_node.py b/string_preview_node.py index 7472d04..70860f4 100644 --- a/string_preview_node.py +++ b/string_preview_node.py @@ -1,6 +1,6 @@ class StringPreviewNode: @classmethod - def INPUT_TYPES(s): + def INPUT_TYPES(cls): return { "required": {}, } diff --git a/string_replace_node.py b/string_replace_node.py index b762f98..6c19aed 100644 --- a/string_replace_node.py +++ b/string_replace_node.py @@ -2,7 +2,7 @@ import re class StringReplaceNode: @classmethod - def INPUT_TYPES(s): + def INPUT_TYPES(cls): return { "required": { "replacement_pairs": ("STRING", {"multiline": True, "default": "", @@ -23,10 +23,14 @@ class StringReplaceNode: FUNCTION = "string_replace" CATEGORY = "utils" - def string_replace(self, input_string, replacement_pairs, replacement_delimiter, match_case, match_whole_string, remove_extra_spaces): - result = input_string - flags = 0 if match_case else re.IGNORECASE + def cleanup_text(self, text): + text = re.sub(r'\s+([,;:.])', r'\1', text) # Remove spaces before punctuation + text = re.sub(r'\s{2,}', ' ', text) # Normalize multiple spaces + return text.strip() + def string_replace(self, input_string, replacement_pairs, replacement_delimiter, match_case, match_whole_string, remove_extra_spaces): + # Create a list of all replacements + replacements = [] for line in replacement_pairs.splitlines(): line = line.strip() if not line or replacement_delimiter not in line: @@ -38,17 +42,28 @@ class StringReplaceNode: if not search_str: continue + + replacements.append((len(search_str), search_str, replace_str)) + + # Sort by length in descending order + replacements.sort(reverse=True) + + result = input_string + flags = 0 if match_case else re.IGNORECASE + # Perform replacements + for _, search_str, replace_str in replacements: if match_whole_string: - pattern = re.escape(search_str) - pattern = r"(?everyone" + result = self.replace_node.string_replace( + input_string=input_string, + replacement_pairs=replacement_pairs, + replacement_delimiter="->", + match_case=False, + match_whole_string=True, + remove_extra_spaces=True + )[0] + self.assertEqual(result, "Hello everyone") + + def test_extra_spaces_handling(self): + # Test for StringStripNode + input_string = "This is a test string" + strings_to_remove = "test" + result1 = self.strip_node.string_strip( + input_string=input_string, + strings_to_remove=strings_to_remove, + match_case=False, + match_whole_string=True, + remove_extra_spaces=True + )[0] + self.assertEqual(result1, "This is a string") + + # Test for StringReplaceNode + replacement_pairs = "is::was" + result2 = self.replace_node.string_replace( + input_string=input_string, + replacement_pairs=replacement_pairs, + replacement_delimiter="::", + match_case=False, + match_whole_string=True, + remove_extra_spaces=True + )[0] + self.assertEqual(result2, "This was a test string") + +if __name__ == '__main__': + unittest.main() \ No newline at end of file