From 1e43e9e540da2ccb965e13f58d36a1fa4452d764 Mon Sep 17 00:00:00 2001 From: samuelxuliu Date: Tue, 11 Nov 2025 14:50:43 +0800 Subject: [PATCH] Add mergeSelector and numberTypeConverter nodes --- __init__.py | 6 ++ py/mergeSelector.py | 148 ++++++++++++++++++++++++++++++++++++++ py/numberTypeConverter.py | 77 ++++++++++++++++++++ 3 files changed, 231 insertions(+) create mode 100644 py/mergeSelector.py create mode 100644 py/numberTypeConverter.py diff --git a/__init__.py b/__init__.py index a5d8971..bc8c4f6 100644 --- a/__init__.py +++ b/__init__.py @@ -14,6 +14,8 @@ from .py.intSplit import PrepackIntSplit from .py.saveByFileName import PrepackSaveByFileName from .py.condArea import PrepackCondArea from .py.calculator import PrepackCalculator +from .py.mergeSelector import PrepackMergeSelector +from .py.numberTypeConverter import PrepackNumberTypeConverter # Frontend extension directory for virtual nodes WEB_DIRECTORY = "./js" @@ -35,6 +37,8 @@ NODE_CLASS_MAPPINGS = { "💀Prepack Save By File Name": PrepackSaveByFileName, "PrepackCondArea": PrepackCondArea, "💀Prepack Calculator": PrepackCalculator, + "💀Prepack Merge Selector": PrepackMergeSelector, + "💀Prepack Number Type Converter": PrepackNumberTypeConverter, } NODE_DISPLAY_NAME_MAPPINGS = { @@ -54,6 +58,8 @@ NODE_DISPLAY_NAME_MAPPINGS = { "💀Prepack Save By File Name": "💀Save By File Name", "PrepackCondArea": "💀Prepack Cond Area", "💀Prepack Calculator": "💀Prepack Calculator", + "💀Prepack Merge Selector": "💀Prepack Merge Selector", + "💀Prepack Number Type Converter": "💀Prepack Number Type Converter", } __all__ = [ diff --git a/py/mergeSelector.py b/py/mergeSelector.py new file mode 100644 index 0000000..416d40c --- /dev/null +++ b/py/mergeSelector.py @@ -0,0 +1,148 @@ +class AlwaysEqualProxy(str): + """Proxy class to accept any input type""" + def __eq__(self, _): + return True + + def __ne__(self, _): + return False + + +# ANSI color utilities for pretty logging +class _PrepackColors: + RED = '\033[31m' + GREEN = '\033[32m' + YELLOW = '\033[33m' + BLUE = '\033[34m' + MAGENTA = '\033[35m' + CYAN = '\033[36m' + WHITE = '\033[37m' + RESET = '\033[0m' + BOLD = '\033[1m' + DIM = '\033[2m' + +class PrepackLogger: + """Unified colorful logger for Prepack nodes""" + PREFIX = "Prepack" + + @staticmethod + def _fmt(level: str, node: str, message: str) -> str: + # Choose icon and color by level + if level == "ERROR": + icon, color = "❌", _PrepackColors.RED + elif level == "WARNING": + icon, color = "⚠️", _PrepackColors.YELLOW + elif level == "SUCCESS": + icon, color = "✅", _PrepackColors.GREEN + elif level == "INFO": + icon, color = "ℹ️", _PrepackColors.CYAN + elif level == "DEBUG": + icon, color = "🔧", _PrepackColors.DIM + else: + icon, color = "📝", _PrepackColors.WHITE + # Format: [Prepack] ICON Node: message (with color and bold prefix) + return f"{color}{_PrepackColors.BOLD}[{PrepackLogger.PREFIX}]{_PrepackColors.RESET} {icon} {color}{node}:{_PrepackColors.RESET} {message}" + + @staticmethod + def info(node: str, message: str): + print(PrepackLogger._fmt("INFO", node, message)) + + @staticmethod + def debug(node: str, message: str): + print(PrepackLogger._fmt("DEBUG", node, message)) + + @staticmethod + def warning(node: str, message: str): + print(PrepackLogger._fmt("WARNING", node, message)) + + @staticmethod + def error(node: str, message: str): + print(PrepackLogger._fmt("ERROR", node, message)) + + @staticmethod + def success(node: str, message: str): + print(PrepackLogger._fmt("SUCCESS", node, message)) + + +class PrepackMergeSelector: + """ + Merge Selector node that outputs one of 5 arbitrary type inputs based on selector number. + Only outputs the selected input, ignoring all others. + """ + + @classmethod + def INPUT_TYPES(cls): + """ + Define input types for the merge selector node. + """ + return { + "required": { + "selector": ("INT", {"default": 1, "min": 1, "max": 5}), + }, + "optional": { + "input_1": (AlwaysEqualProxy("*"), {}), + "input_2": (AlwaysEqualProxy("*"), {}), + "input_3": (AlwaysEqualProxy("*"), {}), + "input_4": (AlwaysEqualProxy("*"), {}), + "input_5": (AlwaysEqualProxy("*"), {}), + } + } + + RETURN_TYPES = (AlwaysEqualProxy("*"),) + RETURN_NAMES = ("output",) + FUNCTION = "select" + CATEGORY = "💀Prepack" + + def select(self, selector, input_1=None, input_2=None, input_3=None, input_4=None, input_5=None): + """ + Select and output one of the 5 inputs based on selector number. + + Args: + selector: Integer (1-5) indicating which input to output + input_1: Optional first input of any type + input_2: Optional second input of any type + input_3: Optional third input of any type + input_4: Optional fourth input of any type + input_5: Optional fifth input of any type + + Returns: + The selected input based on selector value + """ + + # Map selector to corresponding input + inputs = { + 1: input_1, + 2: input_2, + 3: input_3, + 4: input_4, + 5: input_5 + } + + # Get the selected input + selected_input = inputs.get(selector) + + # Check if the selected input is connected + if selected_input is None: + PrepackLogger.warning( + "MergeSelector", + f"Selector={selector} but input_{selector} is not connected • returning None" + ) + return (None,) + + # Log successful selection + input_type = type(selected_input).__name__ + PrepackLogger.success( + "MergeSelector", + f"Selector={selector} • Selected input_{selector} • Type={input_type}" + ) + + return (selected_input,) + + +# Node class mappings +NODE_CLASS_MAPPINGS = { + "💀Prepack Merge Selector": PrepackMergeSelector +} + +NODE_DISPLAY_NAME_MAPPINGS = { + "💀Prepack Merge Selector": "💀Prepack Merge Selector" +} diff --git a/py/numberTypeConverter.py b/py/numberTypeConverter.py new file mode 100644 index 0000000..5e7124f --- /dev/null +++ b/py/numberTypeConverter.py @@ -0,0 +1,77 @@ +"""Prepack Number Type Converter: convert between string, int, and float types.""" + + +class AlwaysEqualProxy(str): + """Proxy class to accept any input type""" + def __eq__(self, _): + return True + + def __ne__(self, _): + return False + + +class PrepackNumberTypeConverter: + """ + Number type converter node that accepts string, int, or float input + and outputs all three types. + """ + + @classmethod + def INPUT_TYPES(cls): + return { + "required": { + "value": (AlwaysEqualProxy("*"), { + "tooltip": "Input value (string, int, or float)" + }), + } + } + + RETURN_TYPES = ("STRING", "INT", "FLOAT") + RETURN_NAMES = ("string", "int", "float") + OUTPUT_TOOLTIPS = ( + "Value as string", + "Value as integer (rounded if needed)", + "Value as float" + ) + FUNCTION = "convert" + CATEGORY = "💀Prepack" + DESCRIPTION = "Convert between string, int, and float types. Accepts any of these types and outputs all three." + + def convert(self, value): + """ + Convert input value to string, int, and float. + + Args: + value: Input value (string, int, or float) + + Returns: + Tuple of (string, int, float) + """ + try: + # Convert to string + str_value = str(value).strip() + + # Convert to float + float_value = float(value) + + # Convert to int (rounded) + int_value = int(round(float_value)) + + print(f"[Prepack] 🔄 Number Type Converter: input={value} → string='{str_value}', int={int_value}, float={float_value}") + + return (str_value, int_value, float_value) + + except (ValueError, TypeError) as e: + print(f"[Prepack] ❌ Number Type Converter: Error converting value '{value}': {str(e)}") + # Return safe defaults on error + return (str(value) if value is not None else "0", 0, 0.0) + + +# Node class mappings +NODE_CLASS_MAPPINGS = { + "PrepackNumberTypeConverter": PrepackNumberTypeConverter +} + +NODE_DISPLAY_NAME_MAPPINGS = { + "PrepackNumberTypeConverter": "💀Prepack Number Type Converter" +}