""" Masslevel Nodes - Variable Text Processor Processes a multiline text template, replacing {variable} placeholders with dynamically wired input values. """ class AnyType(str): """ A special type that matches any ComfyUI type for wildcard inputs. By overriding __ne__ to always return False, it satisfies ComfyUI's type compatibility checks for any connected output type. """ def __ne__(self, other: object) -> bool: return False # Singleton wildcard type instance any_type = AnyType("*") class VariableTextProcessor: """ Processes a multiline text template by replacing {variable_name} placeholders with values from dynamically named input pins. Dynamic inputs are managed entirely from the JS frontend — users add/remove/rename variable pins via the node's UI. Each pin name corresponds directly to a {placeholder} in the template. Unresolved placeholders (no connected pin) pass through unchanged. INT and FLOAT inputs are automatically converted to string. """ @classmethod def INPUT_TYPES(cls) -> dict: return { "required": { "template": ("STRING", { "multiline": True, "default": "Enter your template here.\nUse {variable_name} as placeholders.", }), }, # Dynamic variable inputs are added by the JS frontend. # They arrive here as **kwargs at execution time. } @classmethod def VALIDATE_INPUTS(cls, **kwargs) -> bool: """ Bypass ComfyUI's strict input validation so dynamically added variable pins (not declared in INPUT_TYPES) are accepted without type errors. """ return True RETURN_TYPES = ("STRING",) RETURN_NAMES = ("text",) FUNCTION = "process" CATEGORY = "masslevel/Text" def process(self, template: str, **kwargs) -> tuple[str]: """ Replace all {key} placeholders in the template with the corresponding kwarg values. Unmatched placeholders are left as-is. Args: template: The multiline template string containing {placeholders}. **kwargs: Named variable inputs wired from the frontend pins. Returns: A tuple containing the processed string. """ result = template for key, value in kwargs.items(): placeholder = f"{{{key}}}" if placeholder in result: result = result.replace(placeholder, str(value)) return (result,) # --------------------------------------------------------------------------- # Node registration mappings # --------------------------------------------------------------------------- NODE_CLASS_MAPPINGS = { "VariableTextProcessor": VariableTextProcessor, } NODE_DISPLAY_NAME_MAPPINGS = { "VariableTextProcessor": "Variable Text Processor (masslevel)", }