feat: add Regex, Logic Gate, and List Slicer nodes; refactor project structure
This commit is contained in:
@@ -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
@@ -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"
|
||||
@@ -1,3 +0,0 @@
|
||||
"""
|
||||
Nodes package for DebugPadawan's ComfyUI Essentials
|
||||
"""
|
||||
@@ -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"
|
||||
}
|
||||
@@ -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)",
|
||||
}
|
||||
@@ -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
@@ -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
@@ -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",
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user