mirror of
https://github.com/Comfy-Org/ComfyUI_frontend.git
synced 2026-01-26 10:59:53 +00:00
[refactor] reorganize devtools test nodes into modules (#6020)
## Summary
Refactored monolithic devtools node definitions into organized module
structure for better maintainability and separation of concerns.
## Changes
- **What**: Split 700+ line `dev_nodes.py` into modular structure under
`tools/devtools/nodes/` with categorized files: `errors.py`,
`inputs.py`, `models.py`, `remote.py`
- **Dependencies**: None
## Review Focus
Module import structure and ensure all node registrations are properly
preserved in the consolidated mappings.
**Before:**
```
tools/devtools/
├── __init__.py
└── dev_nodes.py (738 lines)
```
**After:**
```
tools/devtools/
├── __init__.py
├── dev_nodes.py (65 lines - imports only)
└── nodes/
├── __init__.py (consolidated mappings)
├── errors.py (error/debug nodes)
├── inputs.py (input/widget nodes)
├── models.py (model/patch nodes)
└── remote.py (remote/combo nodes)
```
┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6020-refactor-reorganize-devtools-test-nodes-into-modules-2896d73d365081e89efef7e88ca8fee3)
by [Unito](https://www.unito.io)
This commit is contained in:
@@ -97,4 +97,4 @@ async def set_settings(request: Request):
|
|||||||
return web.Response(status=500, text=f"Error: {str(e)}")
|
return web.Response(status=500, text=f"Error: {str(e)}")
|
||||||
|
|
||||||
|
|
||||||
__all__ = ["NODE_CLASS_MAPPINGS", "NODE_DISPLAY_NAME_MAPPINGS"]
|
__all__ = ["NODE_CLASS_MAPPINGS", "NODE_DISPLAY_NAME_MAPPINGS"]
|
||||||
|
|||||||
@@ -1,673 +1,67 @@
|
|||||||
import torch
|
from __future__ import annotations
|
||||||
import comfy.utils as utils
|
|
||||||
from comfy.model_patcher import ModelPatcher
|
from .nodes import (
|
||||||
import nodes
|
DeprecatedNode,
|
||||||
import time
|
DummyPatch,
|
||||||
import os
|
ErrorRaiseNode,
|
||||||
import folder_paths
|
ErrorRaiseNodeWithMessage,
|
||||||
|
ExperimentalNode,
|
||||||
|
LoadAnimatedImageTest,
|
||||||
class ErrorRaiseNode:
|
LongComboDropdown,
|
||||||
@classmethod
|
MultiSelectNode,
|
||||||
def INPUT_TYPES(cls):
|
NodeWithBooleanInput,
|
||||||
return {"required": {}}
|
NodeWithDefaultInput,
|
||||||
|
NodeWithForceInput,
|
||||||
RETURN_TYPES = ("IMAGE",)
|
NodeWithOptionalComboInput,
|
||||||
FUNCTION = "raise_error"
|
NodeWithOptionalInput,
|
||||||
CATEGORY = "DevTools"
|
NodeWithOnlyOptionalInput,
|
||||||
DESCRIPTION = "Raise an error for development purposes"
|
NodeWithOutputCombo,
|
||||||
|
NodeWithOutputList,
|
||||||
def raise_error(self):
|
NodeWithSeedInput,
|
||||||
raise Exception("Error node was called!")
|
NodeWithStringInput,
|
||||||
|
NodeWithUnionInput,
|
||||||
|
NodeWithValidation,
|
||||||
class ErrorRaiseNodeWithMessage:
|
NodeWithV2ComboInput,
|
||||||
@classmethod
|
ObjectPatchNode,
|
||||||
def INPUT_TYPES(cls):
|
RemoteWidgetNode,
|
||||||
return {"required": {"message": ("STRING", {"multiline": True})}}
|
RemoteWidgetNodeWithControlAfterRefresh,
|
||||||
|
RemoteWidgetNodeWithParams,
|
||||||
RETURN_TYPES = ()
|
RemoteWidgetNodeWithRefresh,
|
||||||
OUTPUT_NODE = True
|
RemoteWidgetNodeWithRefreshButton,
|
||||||
|
SimpleSlider,
|
||||||
FUNCTION = "raise_error"
|
NODE_CLASS_MAPPINGS,
|
||||||
CATEGORY = "DevTools"
|
NODE_DISPLAY_NAME_MAPPINGS,
|
||||||
DESCRIPTION = "Raise an error with message for development purposes"
|
)
|
||||||
|
|
||||||
def raise_error(self, message: str):
|
__all__ = [
|
||||||
raise Exception(message)
|
"DeprecatedNode",
|
||||||
|
"DummyPatch",
|
||||||
|
"ErrorRaiseNode",
|
||||||
class ExperimentalNode:
|
"ErrorRaiseNodeWithMessage",
|
||||||
@classmethod
|
"ExperimentalNode",
|
||||||
def INPUT_TYPES(cls):
|
"LoadAnimatedImageTest",
|
||||||
return {"required": {}}
|
"LongComboDropdown",
|
||||||
|
"MultiSelectNode",
|
||||||
RETURN_TYPES = ()
|
"NodeWithBooleanInput",
|
||||||
OUTPUT_NODE = True
|
"NodeWithDefaultInput",
|
||||||
FUNCTION = "experimental_function"
|
"NodeWithForceInput",
|
||||||
CATEGORY = "DevTools"
|
"NodeWithOptionalComboInput",
|
||||||
DESCRIPTION = "A experimental node"
|
"NodeWithOptionalInput",
|
||||||
|
"NodeWithOnlyOptionalInput",
|
||||||
EXPERIMENTAL = True
|
"NodeWithOutputCombo",
|
||||||
|
"NodeWithOutputList",
|
||||||
def experimental_function(self):
|
"NodeWithSeedInput",
|
||||||
print("Experimental node was called!")
|
"NodeWithStringInput",
|
||||||
|
"NodeWithUnionInput",
|
||||||
|
"NodeWithValidation",
|
||||||
class DeprecatedNode:
|
"NodeWithV2ComboInput",
|
||||||
@classmethod
|
"ObjectPatchNode",
|
||||||
def INPUT_TYPES(cls):
|
"RemoteWidgetNode",
|
||||||
return {"required": {}}
|
"RemoteWidgetNodeWithControlAfterRefresh",
|
||||||
|
"RemoteWidgetNodeWithParams",
|
||||||
RETURN_TYPES = ()
|
"RemoteWidgetNodeWithRefresh",
|
||||||
OUTPUT_NODE = True
|
"RemoteWidgetNodeWithRefreshButton",
|
||||||
FUNCTION = "deprecated_function"
|
"SimpleSlider",
|
||||||
CATEGORY = "DevTools"
|
"NODE_CLASS_MAPPINGS",
|
||||||
DESCRIPTION = "A deprecated node"
|
"NODE_DISPLAY_NAME_MAPPINGS",
|
||||||
|
]
|
||||||
DEPRECATED = True
|
|
||||||
|
|
||||||
def deprecated_function(self):
|
|
||||||
print("Deprecated node was called!")
|
|
||||||
|
|
||||||
|
|
||||||
class LongComboDropdown:
|
|
||||||
@classmethod
|
|
||||||
def INPUT_TYPES(cls):
|
|
||||||
return {"required": {"option": ([f"Option {i}" for i in range(1_000)],)}}
|
|
||||||
|
|
||||||
RETURN_TYPES = ()
|
|
||||||
OUTPUT_NODE = True
|
|
||||||
FUNCTION = "long_combo_dropdown"
|
|
||||||
CATEGORY = "DevTools"
|
|
||||||
DESCRIPTION = "A long combo dropdown"
|
|
||||||
|
|
||||||
def long_combo_dropdown(self, option: str):
|
|
||||||
print(option)
|
|
||||||
|
|
||||||
|
|
||||||
class NodeWithOptionalInput:
|
|
||||||
@classmethod
|
|
||||||
def INPUT_TYPES(cls):
|
|
||||||
return {
|
|
||||||
"required": {"required_input": ("IMAGE",)},
|
|
||||||
"optional": {"optional_input": ("IMAGE", {"default": None})},
|
|
||||||
}
|
|
||||||
|
|
||||||
RETURN_TYPES = ("IMAGE",)
|
|
||||||
FUNCTION = "node_with_optional_input"
|
|
||||||
CATEGORY = "DevTools"
|
|
||||||
DESCRIPTION = "A node with an optional input"
|
|
||||||
|
|
||||||
def node_with_optional_input(self, required_input, optional_input=None):
|
|
||||||
print(
|
|
||||||
f"Calling node with required_input: {required_input} and optional_input: {optional_input}"
|
|
||||||
)
|
|
||||||
return (required_input,)
|
|
||||||
|
|
||||||
|
|
||||||
class NodeWithOptionalComboInput:
|
|
||||||
@classmethod
|
|
||||||
def INPUT_TYPES(cls):
|
|
||||||
return {
|
|
||||||
"optional": {
|
|
||||||
"optional_combo_input": (
|
|
||||||
[f"Random Unique Option {time.time()}" for _ in range(8)],
|
|
||||||
{"default": None},
|
|
||||||
)
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
RETURN_TYPES = ("STRING",)
|
|
||||||
FUNCTION = "node_with_optional_combo_input"
|
|
||||||
CATEGORY = "DevTools"
|
|
||||||
DESCRIPTION = "A node with an optional combo input that returns unique values every time INPUT_TYPES is called"
|
|
||||||
|
|
||||||
def node_with_optional_combo_input(self, optional_combo_input=None):
|
|
||||||
print(f"Calling node with optional_combo_input: {optional_combo_input}")
|
|
||||||
return (optional_combo_input,)
|
|
||||||
|
|
||||||
|
|
||||||
class NodeWithOnlyOptionalInput:
|
|
||||||
@classmethod
|
|
||||||
def INPUT_TYPES(s):
|
|
||||||
return {
|
|
||||||
"optional": {
|
|
||||||
"text": ("STRING", {"multiline": True, "dynamicPrompts": True}),
|
|
||||||
"clip": ("CLIP", {}),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
RETURN_TYPES = ()
|
|
||||||
FUNCTION = "node_with_only_optional_input"
|
|
||||||
CATEGORY = "DevTools"
|
|
||||||
DESCRIPTION = "A node with only optional input"
|
|
||||||
|
|
||||||
def node_with_only_optional_input(self, clip=None, text=None):
|
|
||||||
pass
|
|
||||||
|
|
||||||
|
|
||||||
class NodeWithOutputList:
|
|
||||||
@classmethod
|
|
||||||
def INPUT_TYPES(cls):
|
|
||||||
return {"required": {}}
|
|
||||||
|
|
||||||
RETURN_TYPES = (
|
|
||||||
"INT",
|
|
||||||
"INT",
|
|
||||||
)
|
|
||||||
RETURN_NAMES = (
|
|
||||||
"INTEGER OUTPUT",
|
|
||||||
"INTEGER LIST OUTPUT",
|
|
||||||
)
|
|
||||||
OUTPUT_IS_LIST = (
|
|
||||||
False,
|
|
||||||
True,
|
|
||||||
)
|
|
||||||
FUNCTION = "node_with_output_list"
|
|
||||||
CATEGORY = "DevTools"
|
|
||||||
DESCRIPTION = "A node with an output list"
|
|
||||||
|
|
||||||
def node_with_output_list(self):
|
|
||||||
return (1, [1, 2, 3])
|
|
||||||
|
|
||||||
|
|
||||||
class NodeWithForceInput:
|
|
||||||
@classmethod
|
|
||||||
def INPUT_TYPES(cls):
|
|
||||||
return {
|
|
||||||
"required": {
|
|
||||||
"int_input": ("INT", {"forceInput": True}),
|
|
||||||
"int_input_widget": ("INT", {"default": 1}),
|
|
||||||
},
|
|
||||||
"optional": {"float_input": ("FLOAT", {"forceInput": True})},
|
|
||||||
}
|
|
||||||
|
|
||||||
RETURN_TYPES = ()
|
|
||||||
OUTPUT_NODE = True
|
|
||||||
FUNCTION = "node_with_force_input"
|
|
||||||
CATEGORY = "DevTools"
|
|
||||||
DESCRIPTION = "A node with a forced input"
|
|
||||||
|
|
||||||
def node_with_force_input(
|
|
||||||
self, int_input: int, int_input_widget: int, float_input: float = 0.0
|
|
||||||
):
|
|
||||||
print(
|
|
||||||
f"int_input: {int_input}, int_input_widget: {int_input_widget}, float_input: {float_input}"
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
class NodeWithDefaultInput:
|
|
||||||
@classmethod
|
|
||||||
def INPUT_TYPES(cls):
|
|
||||||
return {
|
|
||||||
"required": {
|
|
||||||
"int_input": ("INT", {"defaultInput": True}),
|
|
||||||
"int_input_widget": ("INT", {"default": 1}),
|
|
||||||
},
|
|
||||||
"optional": {"float_input": ("FLOAT", {"defaultInput": True})},
|
|
||||||
}
|
|
||||||
|
|
||||||
RETURN_TYPES = ()
|
|
||||||
OUTPUT_NODE = True
|
|
||||||
FUNCTION = "node_with_default_input"
|
|
||||||
CATEGORY = "DevTools"
|
|
||||||
DESCRIPTION = "A node with a default input"
|
|
||||||
|
|
||||||
def node_with_default_input(
|
|
||||||
self, int_input: int, int_input_widget: int, float_input: float = 0.0
|
|
||||||
):
|
|
||||||
print(
|
|
||||||
f"int_input: {int_input}, int_input_widget: {int_input_widget}, float_input: {float_input}"
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
class NodeWithStringInput:
|
|
||||||
@classmethod
|
|
||||||
def INPUT_TYPES(cls):
|
|
||||||
return {"required": {"string_input": ("STRING",)}}
|
|
||||||
|
|
||||||
RETURN_TYPES = ()
|
|
||||||
FUNCTION = "node_with_string_input"
|
|
||||||
CATEGORY = "DevTools"
|
|
||||||
DESCRIPTION = "A node with a string input"
|
|
||||||
|
|
||||||
def node_with_string_input(self, string_input: str):
|
|
||||||
print(f"string_input: {string_input}")
|
|
||||||
|
|
||||||
|
|
||||||
class NodeWithUnionInput:
|
|
||||||
@classmethod
|
|
||||||
def INPUT_TYPES(cls):
|
|
||||||
return {
|
|
||||||
"optional": {
|
|
||||||
"string_or_int_input": ("STRING,INT",),
|
|
||||||
"string_input": ("STRING", {"forceInput": True}),
|
|
||||||
"int_input": ("INT", {"forceInput": True}),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
RETURN_TYPES = ()
|
|
||||||
OUTPUT_NODE = True
|
|
||||||
FUNCTION = "node_with_union_input"
|
|
||||||
CATEGORY = "DevTools"
|
|
||||||
DESCRIPTION = "A node with a union input"
|
|
||||||
|
|
||||||
def node_with_union_input(
|
|
||||||
self,
|
|
||||||
string_or_int_input: str | int = "",
|
|
||||||
string_input: str = "",
|
|
||||||
int_input: int = 0,
|
|
||||||
):
|
|
||||||
print(
|
|
||||||
f"string_or_int_input: {string_or_int_input}, string_input: {string_input}, int_input: {int_input}"
|
|
||||||
)
|
|
||||||
return {
|
|
||||||
"ui": {
|
|
||||||
"text": string_or_int_input,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
class NodeWithBooleanInput:
|
|
||||||
@classmethod
|
|
||||||
def INPUT_TYPES(cls):
|
|
||||||
return {"required": {"boolean_input": ("BOOLEAN",)}}
|
|
||||||
|
|
||||||
RETURN_TYPES = ()
|
|
||||||
FUNCTION = "node_with_boolean_input"
|
|
||||||
CATEGORY = "DevTools"
|
|
||||||
DESCRIPTION = "A node with a boolean input"
|
|
||||||
|
|
||||||
def node_with_boolean_input(self, boolean_input: bool):
|
|
||||||
print(f"boolean_input: {boolean_input}")
|
|
||||||
|
|
||||||
|
|
||||||
class SimpleSlider:
|
|
||||||
@classmethod
|
|
||||||
def INPUT_TYPES(cls):
|
|
||||||
return {
|
|
||||||
"required": {
|
|
||||||
"value": (
|
|
||||||
"FLOAT",
|
|
||||||
{
|
|
||||||
"display": "slider",
|
|
||||||
"default": 0.5,
|
|
||||||
"min": 0.0,
|
|
||||||
"max": 1.0,
|
|
||||||
"step": 0.001,
|
|
||||||
},
|
|
||||||
),
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
RETURN_TYPES = ("FLOAT",)
|
|
||||||
FUNCTION = "execute"
|
|
||||||
CATEGORY = "DevTools"
|
|
||||||
|
|
||||||
def execute(self, value):
|
|
||||||
return (value,)
|
|
||||||
|
|
||||||
|
|
||||||
class NodeWithSeedInput:
|
|
||||||
@classmethod
|
|
||||||
def INPUT_TYPES(cls):
|
|
||||||
return {"required": {"seed": ("INT", {"default": 0})}}
|
|
||||||
|
|
||||||
RETURN_TYPES = ()
|
|
||||||
FUNCTION = "node_with_seed_input"
|
|
||||||
CATEGORY = "DevTools"
|
|
||||||
DESCRIPTION = "A node with a seed input"
|
|
||||||
OUTPUT_NODE = True
|
|
||||||
|
|
||||||
def node_with_seed_input(self, seed: int):
|
|
||||||
print(f"seed: {seed}")
|
|
||||||
|
|
||||||
|
|
||||||
class DummyPatch(torch.nn.Module):
|
|
||||||
def __init__(self, module: torch.nn.Module, dummy_float: float = 0.0):
|
|
||||||
super().__init__()
|
|
||||||
self.module = module
|
|
||||||
self.dummy_float = dummy_float
|
|
||||||
|
|
||||||
def forward(self, *args, **kwargs):
|
|
||||||
if isinstance(self.module, DummyPatch):
|
|
||||||
raise Exception(f"Calling nested dummy patch! {self.dummy_float}")
|
|
||||||
|
|
||||||
return self.module(*args, **kwargs)
|
|
||||||
|
|
||||||
|
|
||||||
class ObjectPatchNode:
|
|
||||||
@classmethod
|
|
||||||
def INPUT_TYPES(cls):
|
|
||||||
return {
|
|
||||||
"required": {
|
|
||||||
"model": ("MODEL",),
|
|
||||||
"target_module": ("STRING", {"multiline": True}),
|
|
||||||
},
|
|
||||||
"optional": {
|
|
||||||
"dummy_float": ("FLOAT", {"default": 0.0}),
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
RETURN_TYPES = ("MODEL",)
|
|
||||||
FUNCTION = "apply_patch"
|
|
||||||
CATEGORY = "DevTools"
|
|
||||||
DESCRIPTION = "A node that applies an object patch"
|
|
||||||
|
|
||||||
def apply_patch(
|
|
||||||
self, model: ModelPatcher, target_module: str, dummy_float: float = 0.0
|
|
||||||
) -> ModelPatcher:
|
|
||||||
module = utils.get_attr(model.model, target_module)
|
|
||||||
work_model = model.clone()
|
|
||||||
work_model.add_object_patch(target_module, DummyPatch(module, dummy_float))
|
|
||||||
return (work_model,)
|
|
||||||
|
|
||||||
|
|
||||||
class RemoteWidgetNode:
|
|
||||||
@classmethod
|
|
||||||
def INPUT_TYPES(cls):
|
|
||||||
return {
|
|
||||||
"required": {
|
|
||||||
"remote_widget_value": (
|
|
||||||
"COMBO",
|
|
||||||
{
|
|
||||||
"remote": {
|
|
||||||
"route": "/api/models/checkpoints",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
),
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
FUNCTION = "remote_widget"
|
|
||||||
CATEGORY = "DevTools"
|
|
||||||
DESCRIPTION = "A node that lazily fetches options from a remote endpoint"
|
|
||||||
RETURN_TYPES = ("STRING",)
|
|
||||||
|
|
||||||
def remote_widget(self, remote_widget_value: str):
|
|
||||||
return (remote_widget_value,)
|
|
||||||
|
|
||||||
|
|
||||||
class RemoteWidgetNodeWithParams:
|
|
||||||
@classmethod
|
|
||||||
def INPUT_TYPES(cls):
|
|
||||||
return {
|
|
||||||
"required": {
|
|
||||||
"remote_widget_value": (
|
|
||||||
"COMBO",
|
|
||||||
{
|
|
||||||
"remote": {
|
|
||||||
"route": "/api/models/checkpoints",
|
|
||||||
"query_params": {
|
|
||||||
"sort": "true",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
),
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
FUNCTION = "remote_widget"
|
|
||||||
CATEGORY = "DevTools"
|
|
||||||
DESCRIPTION = (
|
|
||||||
"A node that lazily fetches options from a remote endpoint with query params"
|
|
||||||
)
|
|
||||||
RETURN_TYPES = ("STRING",)
|
|
||||||
|
|
||||||
def remote_widget(self, remote_widget_value: str):
|
|
||||||
return (remote_widget_value,)
|
|
||||||
|
|
||||||
|
|
||||||
class RemoteWidgetNodeWithRefresh:
|
|
||||||
@classmethod
|
|
||||||
def INPUT_TYPES(cls):
|
|
||||||
return {
|
|
||||||
"required": {
|
|
||||||
"remote_widget_value": (
|
|
||||||
"COMBO",
|
|
||||||
{
|
|
||||||
"remote": {
|
|
||||||
"route": "/api/models/checkpoints",
|
|
||||||
"refresh": 300,
|
|
||||||
"max_retries": 10,
|
|
||||||
"timeout": 256,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
),
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
FUNCTION = "remote_widget"
|
|
||||||
CATEGORY = "DevTools"
|
|
||||||
DESCRIPTION = "A node that lazily fetches options from a remote endpoint and refresh the options every 300 ms"
|
|
||||||
RETURN_TYPES = ("STRING",)
|
|
||||||
|
|
||||||
def remote_widget(self, remote_widget_value: str):
|
|
||||||
return (remote_widget_value,)
|
|
||||||
|
|
||||||
|
|
||||||
class RemoteWidgetNodeWithRefreshButton:
|
|
||||||
@classmethod
|
|
||||||
def INPUT_TYPES(cls):
|
|
||||||
return {
|
|
||||||
"required": {
|
|
||||||
"remote_widget_value": (
|
|
||||||
"COMBO",
|
|
||||||
{
|
|
||||||
"remote": {
|
|
||||||
"route": "/api/models/checkpoints",
|
|
||||||
"refresh_button": True,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
),
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
FUNCTION = "remote_widget"
|
|
||||||
CATEGORY = "DevTools"
|
|
||||||
DESCRIPTION = "A node that lazily fetches options from a remote endpoint and has a refresh button to manually reload options"
|
|
||||||
RETURN_TYPES = ("STRING",)
|
|
||||||
|
|
||||||
def remote_widget(self, remote_widget_value: str):
|
|
||||||
return (remote_widget_value,)
|
|
||||||
|
|
||||||
|
|
||||||
class RemoteWidgetNodeWithControlAfterRefresh:
|
|
||||||
@classmethod
|
|
||||||
def INPUT_TYPES(cls):
|
|
||||||
return {
|
|
||||||
"required": {
|
|
||||||
"remote_widget_value": (
|
|
||||||
"COMBO",
|
|
||||||
{
|
|
||||||
"remote": {
|
|
||||||
"route": "/api/models/checkpoints",
|
|
||||||
"refresh_button": True,
|
|
||||||
"control_after_refresh": "first",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
),
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
FUNCTION = "remote_widget"
|
|
||||||
CATEGORY = "DevTools"
|
|
||||||
DESCRIPTION = "A node that lazily fetches options from a remote endpoint and has a refresh button to manually reload options and select the first option on refresh"
|
|
||||||
RETURN_TYPES = ("STRING",)
|
|
||||||
|
|
||||||
def remote_widget(self, remote_widget_value: str):
|
|
||||||
return (remote_widget_value,)
|
|
||||||
|
|
||||||
|
|
||||||
class NodeWithOutputCombo:
|
|
||||||
@classmethod
|
|
||||||
def INPUT_TYPES(cls):
|
|
||||||
return {
|
|
||||||
"required": {
|
|
||||||
"subset_options": (["A", "B"], {"forceInput": True}),
|
|
||||||
"subset_options_v2": (
|
|
||||||
"COMBO",
|
|
||||||
{"options": ["A", "B"], "forceInput": True},
|
|
||||||
),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
RETURN_TYPES = (["A", "B", "C"],)
|
|
||||||
FUNCTION = "node_with_output_combo"
|
|
||||||
CATEGORY = "DevTools"
|
|
||||||
DESCRIPTION = "A node that outputs a combo type"
|
|
||||||
|
|
||||||
def node_with_output_combo(self, subset_options: str):
|
|
||||||
return (subset_options,)
|
|
||||||
|
|
||||||
|
|
||||||
class MultiSelectNode:
|
|
||||||
@classmethod
|
|
||||||
def INPUT_TYPES(cls):
|
|
||||||
return {
|
|
||||||
"required": {
|
|
||||||
"foo": (
|
|
||||||
"COMBO",
|
|
||||||
{
|
|
||||||
"options": ["A", "B", "C"],
|
|
||||||
"multi_select": {
|
|
||||||
"placeholder": "Choose foos",
|
|
||||||
"chip": True,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
RETURN_TYPES = ("STRING",)
|
|
||||||
OUTPUT_IS_LIST = [True]
|
|
||||||
FUNCTION = "multi_select_node"
|
|
||||||
CATEGORY = "DevTools"
|
|
||||||
DESCRIPTION = "A node that outputs a multi select type"
|
|
||||||
|
|
||||||
def multi_select_node(self, foo: list[str]) -> list[str]:
|
|
||||||
return (foo,)
|
|
||||||
|
|
||||||
|
|
||||||
class LoadAnimatedImageTest(nodes.LoadImage):
|
|
||||||
@classmethod
|
|
||||||
def INPUT_TYPES(s):
|
|
||||||
input_dir = folder_paths.get_input_directory()
|
|
||||||
files = [
|
|
||||||
f
|
|
||||||
for f in os.listdir(input_dir)
|
|
||||||
if os.path.isfile(os.path.join(input_dir, f)) and f.endswith(".webp")
|
|
||||||
]
|
|
||||||
files = folder_paths.filter_files_content_types(files, ["image"])
|
|
||||||
return {
|
|
||||||
"required": {"image": (sorted(files), {"animated_image_upload": True})},
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
class NodeWithValidation:
|
|
||||||
@classmethod
|
|
||||||
def INPUT_TYPES(cls):
|
|
||||||
return {
|
|
||||||
"required": {"int_input": ("INT",)},
|
|
||||||
}
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def VALIDATE_INPUTS(cls, int_input: int):
|
|
||||||
if int_input < 0:
|
|
||||||
raise ValueError("int_input must be greater than 0")
|
|
||||||
return True
|
|
||||||
|
|
||||||
RETURN_TYPES = ()
|
|
||||||
FUNCTION = "execute"
|
|
||||||
CATEGORY = "DevTools"
|
|
||||||
DESCRIPTION = "A node that validates an input"
|
|
||||||
OUTPUT_NODE = True
|
|
||||||
|
|
||||||
def execute(self, int_input: int):
|
|
||||||
print(f"int_input: {int_input}")
|
|
||||||
return tuple()
|
|
||||||
|
|
||||||
class NodeWithV2ComboInput:
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def INPUT_TYPES(cls):
|
|
||||||
return {
|
|
||||||
"required": {
|
|
||||||
"combo_input": (
|
|
||||||
"COMBO",
|
|
||||||
{"options": ["A", "B"]},
|
|
||||||
),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
RETURN_TYPES = ("COMBO",)
|
|
||||||
FUNCTION = "node_with_v2_combo_input"
|
|
||||||
CATEGORY = "DevTools"
|
|
||||||
DESCRIPTION = (
|
|
||||||
"A node that outputs a combo type that adheres to the v2 combo input spec"
|
|
||||||
)
|
|
||||||
|
|
||||||
def node_with_v2_combo_input(self, combo_input: str):
|
|
||||||
return (combo_input,)
|
|
||||||
|
|
||||||
|
|
||||||
NODE_CLASS_MAPPINGS = {
|
|
||||||
"DevToolsErrorRaiseNode": ErrorRaiseNode,
|
|
||||||
"DevToolsErrorRaiseNodeWithMessage": ErrorRaiseNodeWithMessage,
|
|
||||||
"DevToolsExperimentalNode": ExperimentalNode,
|
|
||||||
"DevToolsDeprecatedNode": DeprecatedNode,
|
|
||||||
"DevToolsLongComboDropdown": LongComboDropdown,
|
|
||||||
"DevToolsNodeWithOptionalInput": NodeWithOptionalInput,
|
|
||||||
"DevToolsNodeWithOptionalComboInput": NodeWithOptionalComboInput,
|
|
||||||
"DevToolsNodeWithOnlyOptionalInput": NodeWithOnlyOptionalInput,
|
|
||||||
"DevToolsNodeWithOutputList": NodeWithOutputList,
|
|
||||||
"DevToolsNodeWithForceInput": NodeWithForceInput,
|
|
||||||
"DevToolsNodeWithDefaultInput": NodeWithDefaultInput,
|
|
||||||
"DevToolsNodeWithStringInput": NodeWithStringInput,
|
|
||||||
"DevToolsNodeWithUnionInput": NodeWithUnionInput,
|
|
||||||
"DevToolsSimpleSlider": SimpleSlider,
|
|
||||||
"DevToolsNodeWithSeedInput": NodeWithSeedInput,
|
|
||||||
"DevToolsObjectPatchNode": ObjectPatchNode,
|
|
||||||
"DevToolsNodeWithBooleanInput": NodeWithBooleanInput,
|
|
||||||
"DevToolsRemoteWidgetNode": RemoteWidgetNode,
|
|
||||||
"DevToolsRemoteWidgetNodeWithParams": RemoteWidgetNodeWithParams,
|
|
||||||
"DevToolsRemoteWidgetNodeWithRefresh": RemoteWidgetNodeWithRefresh,
|
|
||||||
"DevToolsRemoteWidgetNodeWithRefreshButton": RemoteWidgetNodeWithRefreshButton,
|
|
||||||
"DevToolsRemoteWidgetNodeWithControlAfterRefresh": RemoteWidgetNodeWithControlAfterRefresh,
|
|
||||||
"DevToolsNodeWithOutputCombo": NodeWithOutputCombo,
|
|
||||||
"DevToolsMultiSelectNode": MultiSelectNode,
|
|
||||||
"DevToolsLoadAnimatedImageTest": LoadAnimatedImageTest,
|
|
||||||
"DevToolsNodeWithValidation": NodeWithValidation,
|
|
||||||
"DevToolsNodeWithV2ComboInput": NodeWithV2ComboInput,
|
|
||||||
}
|
|
||||||
|
|
||||||
NODE_DISPLAY_NAME_MAPPINGS = {
|
|
||||||
"DevToolsErrorRaiseNode": "Raise Error",
|
|
||||||
"DevToolsErrorRaiseNodeWithMessage": "Raise Error with Message",
|
|
||||||
"DevToolsExperimentalNode": "Experimental Node",
|
|
||||||
"DevToolsDeprecatedNode": "Deprecated Node",
|
|
||||||
"DevToolsLongComboDropdown": "Long Combo Dropdown",
|
|
||||||
"DevToolsNodeWithOptionalInput": "Node With Optional Input",
|
|
||||||
"DevToolsNodeWithOptionalComboInput": "Node With Optional Combo Input",
|
|
||||||
"DevToolsNodeWithOnlyOptionalInput": "Node With Only Optional Input",
|
|
||||||
"DevToolsNodeWithOutputList": "Node With Output List",
|
|
||||||
"DevToolsNodeWithForceInput": "Node With Force Input",
|
|
||||||
"DevToolsNodeWithDefaultInput": "Node With Default Input",
|
|
||||||
"DevToolsNodeWithStringInput": "Node With String Input",
|
|
||||||
"DevToolsNodeWithUnionInput": "Node With Union Input",
|
|
||||||
"DevToolsSimpleSlider": "Simple Slider",
|
|
||||||
"DevToolsNodeWithSeedInput": "Node With Seed Input",
|
|
||||||
"DevToolsObjectPatchNode": "Object Patch Node",
|
|
||||||
"DevToolsNodeWithBooleanInput": "Node With Boolean Input",
|
|
||||||
"DevToolsRemoteWidgetNode": "Remote Widget Node",
|
|
||||||
"DevToolsRemoteWidgetNodeWithParams": "Remote Widget Node With Sort Query Param",
|
|
||||||
"DevToolsRemoteWidgetNodeWithRefresh": "Remote Widget Node With 300ms Refresh",
|
|
||||||
"DevToolsRemoteWidgetNodeWithRefreshButton": "Remote Widget Node With Refresh Button",
|
|
||||||
"DevToolsRemoteWidgetNodeWithControlAfterRefresh": "Remote Widget Node With Refresh Button and Control After Refresh",
|
|
||||||
"DevToolsNodeWithOutputCombo": "Node With Output Combo",
|
|
||||||
"DevToolsMultiSelectNode": "Multi Select Node",
|
|
||||||
"DevToolsLoadAnimatedImageTest": "Load Animated Image",
|
|
||||||
"DevToolsNodeWithValidation": "Node With Validation",
|
|
||||||
"DevToolsNodeWithV2ComboInput": "Node With V2 Combo Input",
|
|
||||||
}
|
|
||||||
|
|||||||
93
tools/devtools/nodes/__init__.py
Normal file
93
tools/devtools/nodes/__init__.py
Normal file
@@ -0,0 +1,93 @@
|
|||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
from .errors import (
|
||||||
|
DeprecatedNode,
|
||||||
|
ErrorRaiseNode,
|
||||||
|
ErrorRaiseNodeWithMessage,
|
||||||
|
ExperimentalNode,
|
||||||
|
NODE_CLASS_MAPPINGS as errors_class_mappings,
|
||||||
|
NODE_DISPLAY_NAME_MAPPINGS as errors_display_name_mappings,
|
||||||
|
)
|
||||||
|
from .inputs import (
|
||||||
|
LongComboDropdown,
|
||||||
|
NodeWithBooleanInput,
|
||||||
|
NodeWithDefaultInput,
|
||||||
|
NodeWithForceInput,
|
||||||
|
NodeWithOptionalComboInput,
|
||||||
|
NodeWithOptionalInput,
|
||||||
|
NodeWithOnlyOptionalInput,
|
||||||
|
NodeWithOutputList,
|
||||||
|
NodeWithSeedInput,
|
||||||
|
NodeWithStringInput,
|
||||||
|
NodeWithUnionInput,
|
||||||
|
NodeWithValidation,
|
||||||
|
NodeWithV2ComboInput,
|
||||||
|
SimpleSlider,
|
||||||
|
NODE_CLASS_MAPPINGS as inputs_class_mappings,
|
||||||
|
NODE_DISPLAY_NAME_MAPPINGS as inputs_display_name_mappings,
|
||||||
|
)
|
||||||
|
from .models import (
|
||||||
|
DummyPatch,
|
||||||
|
LoadAnimatedImageTest,
|
||||||
|
ObjectPatchNode,
|
||||||
|
NODE_CLASS_MAPPINGS as models_class_mappings,
|
||||||
|
NODE_DISPLAY_NAME_MAPPINGS as models_display_name_mappings,
|
||||||
|
)
|
||||||
|
from .remote import (
|
||||||
|
MultiSelectNode,
|
||||||
|
NodeWithOutputCombo,
|
||||||
|
RemoteWidgetNode,
|
||||||
|
RemoteWidgetNodeWithControlAfterRefresh,
|
||||||
|
RemoteWidgetNodeWithParams,
|
||||||
|
RemoteWidgetNodeWithRefresh,
|
||||||
|
RemoteWidgetNodeWithRefreshButton,
|
||||||
|
NODE_CLASS_MAPPINGS as remote_class_mappings,
|
||||||
|
NODE_DISPLAY_NAME_MAPPINGS as remote_display_name_mappings,
|
||||||
|
)
|
||||||
|
|
||||||
|
NODE_CLASS_MAPPINGS = {
|
||||||
|
**errors_class_mappings,
|
||||||
|
**inputs_class_mappings,
|
||||||
|
**remote_class_mappings,
|
||||||
|
**models_class_mappings,
|
||||||
|
}
|
||||||
|
|
||||||
|
NODE_DISPLAY_NAME_MAPPINGS = {
|
||||||
|
**errors_display_name_mappings,
|
||||||
|
**inputs_display_name_mappings,
|
||||||
|
**remote_display_name_mappings,
|
||||||
|
**models_display_name_mappings,
|
||||||
|
}
|
||||||
|
|
||||||
|
__all__ = [
|
||||||
|
"DeprecatedNode",
|
||||||
|
"DummyPatch",
|
||||||
|
"ErrorRaiseNode",
|
||||||
|
"ErrorRaiseNodeWithMessage",
|
||||||
|
"ExperimentalNode",
|
||||||
|
"LoadAnimatedImageTest",
|
||||||
|
"LongComboDropdown",
|
||||||
|
"MultiSelectNode",
|
||||||
|
"NodeWithBooleanInput",
|
||||||
|
"NodeWithDefaultInput",
|
||||||
|
"NodeWithForceInput",
|
||||||
|
"NodeWithOptionalComboInput",
|
||||||
|
"NodeWithOptionalInput",
|
||||||
|
"NodeWithOnlyOptionalInput",
|
||||||
|
"NodeWithOutputCombo",
|
||||||
|
"NodeWithOutputList",
|
||||||
|
"NodeWithSeedInput",
|
||||||
|
"NodeWithStringInput",
|
||||||
|
"NodeWithUnionInput",
|
||||||
|
"NodeWithValidation",
|
||||||
|
"NodeWithV2ComboInput",
|
||||||
|
"ObjectPatchNode",
|
||||||
|
"RemoteWidgetNode",
|
||||||
|
"RemoteWidgetNodeWithControlAfterRefresh",
|
||||||
|
"RemoteWidgetNodeWithParams",
|
||||||
|
"RemoteWidgetNodeWithRefresh",
|
||||||
|
"RemoteWidgetNodeWithRefreshButton",
|
||||||
|
"SimpleSlider",
|
||||||
|
"NODE_CLASS_MAPPINGS",
|
||||||
|
"NODE_DISPLAY_NAME_MAPPINGS",
|
||||||
|
]
|
||||||
89
tools/devtools/nodes/errors.py
Normal file
89
tools/devtools/nodes/errors.py
Normal file
@@ -0,0 +1,89 @@
|
|||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
|
||||||
|
class ErrorRaiseNode:
|
||||||
|
@classmethod
|
||||||
|
def INPUT_TYPES(cls):
|
||||||
|
return {"required": {}}
|
||||||
|
|
||||||
|
RETURN_TYPES = ("IMAGE",)
|
||||||
|
FUNCTION = "raise_error"
|
||||||
|
CATEGORY = "DevTools"
|
||||||
|
DESCRIPTION = "Raise an error for development purposes"
|
||||||
|
|
||||||
|
def raise_error(self):
|
||||||
|
raise Exception("Error node was called!")
|
||||||
|
|
||||||
|
|
||||||
|
class ErrorRaiseNodeWithMessage:
|
||||||
|
@classmethod
|
||||||
|
def INPUT_TYPES(cls):
|
||||||
|
return {"required": {"message": ("STRING", {"multiline": True})}}
|
||||||
|
|
||||||
|
RETURN_TYPES = ()
|
||||||
|
OUTPUT_NODE = True
|
||||||
|
|
||||||
|
FUNCTION = "raise_error"
|
||||||
|
CATEGORY = "DevTools"
|
||||||
|
DESCRIPTION = "Raise an error with message for development purposes"
|
||||||
|
|
||||||
|
def raise_error(self, message: str):
|
||||||
|
raise Exception(message)
|
||||||
|
|
||||||
|
|
||||||
|
class ExperimentalNode:
|
||||||
|
@classmethod
|
||||||
|
def INPUT_TYPES(cls):
|
||||||
|
return {"required": {}}
|
||||||
|
|
||||||
|
RETURN_TYPES = ()
|
||||||
|
OUTPUT_NODE = True
|
||||||
|
FUNCTION = "experimental_function"
|
||||||
|
CATEGORY = "DevTools"
|
||||||
|
DESCRIPTION = "A experimental node"
|
||||||
|
|
||||||
|
EXPERIMENTAL = True
|
||||||
|
|
||||||
|
def experimental_function(self):
|
||||||
|
print("Experimental node was called!")
|
||||||
|
|
||||||
|
|
||||||
|
class DeprecatedNode:
|
||||||
|
@classmethod
|
||||||
|
def INPUT_TYPES(cls):
|
||||||
|
return {"required": {}}
|
||||||
|
|
||||||
|
RETURN_TYPES = ()
|
||||||
|
OUTPUT_NODE = True
|
||||||
|
FUNCTION = "deprecated_function"
|
||||||
|
CATEGORY = "DevTools"
|
||||||
|
DESCRIPTION = "A deprecated node"
|
||||||
|
|
||||||
|
DEPRECATED = True
|
||||||
|
|
||||||
|
def deprecated_function(self):
|
||||||
|
print("Deprecated node was called!")
|
||||||
|
|
||||||
|
|
||||||
|
NODE_CLASS_MAPPINGS = {
|
||||||
|
"DevToolsErrorRaiseNode": ErrorRaiseNode,
|
||||||
|
"DevToolsErrorRaiseNodeWithMessage": ErrorRaiseNodeWithMessage,
|
||||||
|
"DevToolsExperimentalNode": ExperimentalNode,
|
||||||
|
"DevToolsDeprecatedNode": DeprecatedNode,
|
||||||
|
}
|
||||||
|
|
||||||
|
NODE_DISPLAY_NAME_MAPPINGS = {
|
||||||
|
"DevToolsErrorRaiseNode": "Raise Error",
|
||||||
|
"DevToolsErrorRaiseNodeWithMessage": "Raise Error with Message",
|
||||||
|
"DevToolsExperimentalNode": "Experimental Node",
|
||||||
|
"DevToolsDeprecatedNode": "Deprecated Node",
|
||||||
|
}
|
||||||
|
|
||||||
|
__all__ = [
|
||||||
|
"ErrorRaiseNode",
|
||||||
|
"ErrorRaiseNodeWithMessage",
|
||||||
|
"ExperimentalNode",
|
||||||
|
"DeprecatedNode",
|
||||||
|
"NODE_CLASS_MAPPINGS",
|
||||||
|
"NODE_DISPLAY_NAME_MAPPINGS",
|
||||||
|
]
|
||||||
357
tools/devtools/nodes/inputs.py
Normal file
357
tools/devtools/nodes/inputs.py
Normal file
@@ -0,0 +1,357 @@
|
|||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
import time
|
||||||
|
|
||||||
|
|
||||||
|
class LongComboDropdown:
|
||||||
|
@classmethod
|
||||||
|
def INPUT_TYPES(cls):
|
||||||
|
return {"required": {"option": ([f"Option {i}" for i in range(1_000)],)}}
|
||||||
|
|
||||||
|
RETURN_TYPES = ()
|
||||||
|
OUTPUT_NODE = True
|
||||||
|
FUNCTION = "long_combo_dropdown"
|
||||||
|
CATEGORY = "DevTools"
|
||||||
|
DESCRIPTION = "A long combo dropdown"
|
||||||
|
|
||||||
|
def long_combo_dropdown(self, option: str):
|
||||||
|
print(option)
|
||||||
|
|
||||||
|
|
||||||
|
class NodeWithOptionalInput:
|
||||||
|
@classmethod
|
||||||
|
def INPUT_TYPES(cls):
|
||||||
|
return {
|
||||||
|
"required": {"required_input": ("IMAGE",)},
|
||||||
|
"optional": {"optional_input": ("IMAGE", {"default": None})},
|
||||||
|
}
|
||||||
|
|
||||||
|
RETURN_TYPES = ("IMAGE",)
|
||||||
|
FUNCTION = "node_with_optional_input"
|
||||||
|
CATEGORY = "DevTools"
|
||||||
|
DESCRIPTION = "A node with an optional input"
|
||||||
|
|
||||||
|
def node_with_optional_input(self, required_input, optional_input=None):
|
||||||
|
print(
|
||||||
|
f"Calling node with required_input: {required_input} and optional_input: {optional_input}"
|
||||||
|
)
|
||||||
|
return (required_input,)
|
||||||
|
|
||||||
|
|
||||||
|
class NodeWithOptionalComboInput:
|
||||||
|
@classmethod
|
||||||
|
def INPUT_TYPES(cls):
|
||||||
|
return {
|
||||||
|
"optional": {
|
||||||
|
"optional_combo_input": (
|
||||||
|
[f"Random Unique Option {time.time()}" for _ in range(8)],
|
||||||
|
{"default": None},
|
||||||
|
)
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
RETURN_TYPES = ("STRING",)
|
||||||
|
FUNCTION = "node_with_optional_combo_input"
|
||||||
|
CATEGORY = "DevTools"
|
||||||
|
DESCRIPTION = "A node with an optional combo input that returns unique values every time INPUT_TYPES is called"
|
||||||
|
|
||||||
|
def node_with_optional_combo_input(self, optional_combo_input=None):
|
||||||
|
print(f"Calling node with optional_combo_input: {optional_combo_input}")
|
||||||
|
return (optional_combo_input,)
|
||||||
|
|
||||||
|
|
||||||
|
class NodeWithOnlyOptionalInput:
|
||||||
|
@classmethod
|
||||||
|
def INPUT_TYPES(s):
|
||||||
|
return {
|
||||||
|
"optional": {
|
||||||
|
"text": ("STRING", {"multiline": True, "dynamicPrompts": True}),
|
||||||
|
"clip": ("CLIP", {}),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
RETURN_TYPES = ()
|
||||||
|
FUNCTION = "node_with_only_optional_input"
|
||||||
|
CATEGORY = "DevTools"
|
||||||
|
DESCRIPTION = "A node with only optional input"
|
||||||
|
|
||||||
|
def node_with_only_optional_input(self, clip=None, text=None):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class NodeWithOutputList:
|
||||||
|
@classmethod
|
||||||
|
def INPUT_TYPES(cls):
|
||||||
|
return {"required": {}}
|
||||||
|
|
||||||
|
RETURN_TYPES = (
|
||||||
|
"INT",
|
||||||
|
"INT",
|
||||||
|
)
|
||||||
|
RETURN_NAMES = (
|
||||||
|
"INTEGER OUTPUT",
|
||||||
|
"INTEGER LIST OUTPUT",
|
||||||
|
)
|
||||||
|
OUTPUT_IS_LIST = (
|
||||||
|
False,
|
||||||
|
True,
|
||||||
|
)
|
||||||
|
FUNCTION = "node_with_output_list"
|
||||||
|
CATEGORY = "DevTools"
|
||||||
|
DESCRIPTION = "A node with an output list"
|
||||||
|
|
||||||
|
def node_with_output_list(self):
|
||||||
|
return (1, [1, 2, 3])
|
||||||
|
|
||||||
|
|
||||||
|
class NodeWithForceInput:
|
||||||
|
@classmethod
|
||||||
|
def INPUT_TYPES(cls):
|
||||||
|
return {
|
||||||
|
"required": {
|
||||||
|
"int_input": ("INT", {"forceInput": True}),
|
||||||
|
"int_input_widget": ("INT", {"default": 1}),
|
||||||
|
},
|
||||||
|
"optional": {"float_input": ("FLOAT", {"forceInput": True})},
|
||||||
|
}
|
||||||
|
|
||||||
|
RETURN_TYPES = ()
|
||||||
|
OUTPUT_NODE = True
|
||||||
|
FUNCTION = "node_with_force_input"
|
||||||
|
CATEGORY = "DevTools"
|
||||||
|
DESCRIPTION = "A node with a forced input"
|
||||||
|
|
||||||
|
def node_with_force_input(
|
||||||
|
self, int_input: int, int_input_widget: int, float_input: float = 0.0
|
||||||
|
):
|
||||||
|
print(
|
||||||
|
f"int_input: {int_input}, int_input_widget: {int_input_widget}, float_input: {float_input}"
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class NodeWithDefaultInput:
|
||||||
|
@classmethod
|
||||||
|
def INPUT_TYPES(cls):
|
||||||
|
return {
|
||||||
|
"required": {
|
||||||
|
"int_input": ("INT", {"defaultInput": True}),
|
||||||
|
"int_input_widget": ("INT", {"default": 1}),
|
||||||
|
},
|
||||||
|
"optional": {"float_input": ("FLOAT", {"defaultInput": True})},
|
||||||
|
}
|
||||||
|
|
||||||
|
RETURN_TYPES = ()
|
||||||
|
OUTPUT_NODE = True
|
||||||
|
FUNCTION = "node_with_default_input"
|
||||||
|
CATEGORY = "DevTools"
|
||||||
|
DESCRIPTION = "A node with a default input"
|
||||||
|
|
||||||
|
def node_with_default_input(
|
||||||
|
self, int_input: int, int_input_widget: int, float_input: float = 0.0
|
||||||
|
):
|
||||||
|
print(
|
||||||
|
f"int_input: {int_input}, int_input_widget: {int_input_widget}, float_input: {float_input}"
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class NodeWithStringInput:
|
||||||
|
@classmethod
|
||||||
|
def INPUT_TYPES(cls):
|
||||||
|
return {"required": {"string_input": ("STRING",)}}
|
||||||
|
|
||||||
|
RETURN_TYPES = ()
|
||||||
|
FUNCTION = "node_with_string_input"
|
||||||
|
CATEGORY = "DevTools"
|
||||||
|
DESCRIPTION = "A node with a string input"
|
||||||
|
|
||||||
|
def node_with_string_input(self, string_input: str):
|
||||||
|
print(f"string_input: {string_input}")
|
||||||
|
|
||||||
|
|
||||||
|
class NodeWithUnionInput:
|
||||||
|
@classmethod
|
||||||
|
def INPUT_TYPES(cls):
|
||||||
|
return {
|
||||||
|
"optional": {
|
||||||
|
"string_or_int_input": ("STRING,INT",),
|
||||||
|
"string_input": ("STRING", {"forceInput": True}),
|
||||||
|
"int_input": ("INT", {"forceInput": True}),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
RETURN_TYPES = ()
|
||||||
|
OUTPUT_NODE = True
|
||||||
|
FUNCTION = "node_with_union_input"
|
||||||
|
CATEGORY = "DevTools"
|
||||||
|
DESCRIPTION = "A node with a union input"
|
||||||
|
|
||||||
|
def node_with_union_input(
|
||||||
|
self,
|
||||||
|
string_or_int_input: str | int = "",
|
||||||
|
string_input: str = "",
|
||||||
|
int_input: int = 0,
|
||||||
|
):
|
||||||
|
print(
|
||||||
|
f"string_or_int_input: {string_or_int_input}, string_input: {string_input}, int_input: {int_input}"
|
||||||
|
)
|
||||||
|
return {
|
||||||
|
"ui": {
|
||||||
|
"text": string_or_int_input,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
class NodeWithBooleanInput:
|
||||||
|
@classmethod
|
||||||
|
def INPUT_TYPES(cls):
|
||||||
|
return {"required": {"boolean_input": ("BOOLEAN",)}}
|
||||||
|
|
||||||
|
RETURN_TYPES = ()
|
||||||
|
FUNCTION = "node_with_boolean_input"
|
||||||
|
CATEGORY = "DevTools"
|
||||||
|
DESCRIPTION = "A node with a boolean input"
|
||||||
|
|
||||||
|
def node_with_boolean_input(self, boolean_input: bool):
|
||||||
|
print(f"boolean_input: {boolean_input}")
|
||||||
|
|
||||||
|
|
||||||
|
class SimpleSlider:
|
||||||
|
@classmethod
|
||||||
|
def INPUT_TYPES(cls):
|
||||||
|
return {
|
||||||
|
"required": {
|
||||||
|
"value": (
|
||||||
|
"FLOAT",
|
||||||
|
{
|
||||||
|
"display": "slider",
|
||||||
|
"default": 0.5,
|
||||||
|
"min": 0.0,
|
||||||
|
"max": 1.0,
|
||||||
|
"step": 0.001,
|
||||||
|
},
|
||||||
|
),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
RETURN_TYPES = ("FLOAT",)
|
||||||
|
FUNCTION = "execute"
|
||||||
|
CATEGORY = "DevTools"
|
||||||
|
|
||||||
|
def execute(self, value):
|
||||||
|
return (value,)
|
||||||
|
|
||||||
|
|
||||||
|
class NodeWithSeedInput:
|
||||||
|
@classmethod
|
||||||
|
def INPUT_TYPES(cls):
|
||||||
|
return {"required": {"seed": ("INT", {"default": 0})}}
|
||||||
|
|
||||||
|
RETURN_TYPES = ()
|
||||||
|
FUNCTION = "node_with_seed_input"
|
||||||
|
CATEGORY = "DevTools"
|
||||||
|
DESCRIPTION = "A node with a seed input"
|
||||||
|
OUTPUT_NODE = True
|
||||||
|
|
||||||
|
def node_with_seed_input(self, seed: int):
|
||||||
|
print(f"seed: {seed}")
|
||||||
|
|
||||||
|
|
||||||
|
class NodeWithValidation:
|
||||||
|
@classmethod
|
||||||
|
def INPUT_TYPES(cls):
|
||||||
|
return {
|
||||||
|
"required": {"int_input": ("INT",)},
|
||||||
|
}
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def VALIDATE_INPUTS(cls, int_input: int):
|
||||||
|
if int_input < 0:
|
||||||
|
raise ValueError("int_input must be greater than 0")
|
||||||
|
return True
|
||||||
|
|
||||||
|
RETURN_TYPES = ()
|
||||||
|
FUNCTION = "execute"
|
||||||
|
CATEGORY = "DevTools"
|
||||||
|
DESCRIPTION = "A node that validates an input"
|
||||||
|
OUTPUT_NODE = True
|
||||||
|
|
||||||
|
def execute(self, int_input: int):
|
||||||
|
print(f"int_input: {int_input}")
|
||||||
|
return tuple()
|
||||||
|
|
||||||
|
|
||||||
|
class NodeWithV2ComboInput:
|
||||||
|
@classmethod
|
||||||
|
def INPUT_TYPES(cls):
|
||||||
|
return {
|
||||||
|
"required": {
|
||||||
|
"combo_input": (
|
||||||
|
"COMBO",
|
||||||
|
{"options": ["A", "B"]},
|
||||||
|
),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
RETURN_TYPES = ("COMBO",)
|
||||||
|
FUNCTION = "node_with_v2_combo_input"
|
||||||
|
CATEGORY = "DevTools"
|
||||||
|
DESCRIPTION = (
|
||||||
|
"A node that outputs a combo type that adheres to the v2 combo input spec"
|
||||||
|
)
|
||||||
|
|
||||||
|
def node_with_v2_combo_input(self, combo_input: str):
|
||||||
|
return (combo_input,)
|
||||||
|
|
||||||
|
|
||||||
|
NODE_CLASS_MAPPINGS = {
|
||||||
|
"DevToolsLongComboDropdown": LongComboDropdown,
|
||||||
|
"DevToolsNodeWithOptionalInput": NodeWithOptionalInput,
|
||||||
|
"DevToolsNodeWithOptionalComboInput": NodeWithOptionalComboInput,
|
||||||
|
"DevToolsNodeWithOnlyOptionalInput": NodeWithOnlyOptionalInput,
|
||||||
|
"DevToolsNodeWithOutputList": NodeWithOutputList,
|
||||||
|
"DevToolsNodeWithForceInput": NodeWithForceInput,
|
||||||
|
"DevToolsNodeWithDefaultInput": NodeWithDefaultInput,
|
||||||
|
"DevToolsNodeWithStringInput": NodeWithStringInput,
|
||||||
|
"DevToolsNodeWithUnionInput": NodeWithUnionInput,
|
||||||
|
"DevToolsNodeWithBooleanInput": NodeWithBooleanInput,
|
||||||
|
"DevToolsSimpleSlider": SimpleSlider,
|
||||||
|
"DevToolsNodeWithSeedInput": NodeWithSeedInput,
|
||||||
|
"DevToolsNodeWithValidation": NodeWithValidation,
|
||||||
|
"DevToolsNodeWithV2ComboInput": NodeWithV2ComboInput,
|
||||||
|
}
|
||||||
|
|
||||||
|
NODE_DISPLAY_NAME_MAPPINGS = {
|
||||||
|
"DevToolsLongComboDropdown": "Long Combo Dropdown",
|
||||||
|
"DevToolsNodeWithOptionalInput": "Node With Optional Input",
|
||||||
|
"DevToolsNodeWithOptionalComboInput": "Node With Optional Combo Input",
|
||||||
|
"DevToolsNodeWithOnlyOptionalInput": "Node With Only Optional Input",
|
||||||
|
"DevToolsNodeWithOutputList": "Node With Output List",
|
||||||
|
"DevToolsNodeWithForceInput": "Node With Force Input",
|
||||||
|
"DevToolsNodeWithDefaultInput": "Node With Default Input",
|
||||||
|
"DevToolsNodeWithStringInput": "Node With String Input",
|
||||||
|
"DevToolsNodeWithUnionInput": "Node With Union Input",
|
||||||
|
"DevToolsNodeWithBooleanInput": "Node With Boolean Input",
|
||||||
|
"DevToolsSimpleSlider": "Simple Slider",
|
||||||
|
"DevToolsNodeWithSeedInput": "Node With Seed Input",
|
||||||
|
"DevToolsNodeWithValidation": "Node With Validation",
|
||||||
|
"DevToolsNodeWithV2ComboInput": "Node With V2 Combo Input",
|
||||||
|
}
|
||||||
|
|
||||||
|
__all__ = [
|
||||||
|
"LongComboDropdown",
|
||||||
|
"NodeWithOptionalInput",
|
||||||
|
"NodeWithOptionalComboInput",
|
||||||
|
"NodeWithOnlyOptionalInput",
|
||||||
|
"NodeWithOutputList",
|
||||||
|
"NodeWithForceInput",
|
||||||
|
"NodeWithDefaultInput",
|
||||||
|
"NodeWithStringInput",
|
||||||
|
"NodeWithUnionInput",
|
||||||
|
"NodeWithBooleanInput",
|
||||||
|
"SimpleSlider",
|
||||||
|
"NodeWithSeedInput",
|
||||||
|
"NodeWithValidation",
|
||||||
|
"NodeWithV2ComboInput",
|
||||||
|
"NODE_CLASS_MAPPINGS",
|
||||||
|
"NODE_DISPLAY_NAME_MAPPINGS",
|
||||||
|
]
|
||||||
84
tools/devtools/nodes/models.py
Normal file
84
tools/devtools/nodes/models.py
Normal file
@@ -0,0 +1,84 @@
|
|||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
import os
|
||||||
|
|
||||||
|
import torch
|
||||||
|
|
||||||
|
import comfy.utils as utils
|
||||||
|
from comfy.model_patcher import ModelPatcher
|
||||||
|
import nodes
|
||||||
|
import folder_paths
|
||||||
|
|
||||||
|
|
||||||
|
class DummyPatch(torch.nn.Module):
|
||||||
|
def __init__(self, module: torch.nn.Module, dummy_float: float = 0.0):
|
||||||
|
super().__init__()
|
||||||
|
self.module = module
|
||||||
|
self.dummy_float = dummy_float
|
||||||
|
|
||||||
|
def forward(self, *args, **kwargs):
|
||||||
|
if isinstance(self.module, DummyPatch):
|
||||||
|
raise Exception(f"Calling nested dummy patch! {self.dummy_float}")
|
||||||
|
|
||||||
|
return self.module(*args, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
|
class ObjectPatchNode:
|
||||||
|
@classmethod
|
||||||
|
def INPUT_TYPES(cls):
|
||||||
|
return {
|
||||||
|
"required": {
|
||||||
|
"model": ("MODEL",),
|
||||||
|
"target_module": ("STRING", {"multiline": True}),
|
||||||
|
},
|
||||||
|
"optional": {
|
||||||
|
"dummy_float": ("FLOAT", {"default": 0.0}),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
RETURN_TYPES = ("MODEL",)
|
||||||
|
FUNCTION = "apply_patch"
|
||||||
|
CATEGORY = "DevTools"
|
||||||
|
DESCRIPTION = "A node that applies an object patch"
|
||||||
|
|
||||||
|
def apply_patch(
|
||||||
|
self, model: ModelPatcher, target_module: str, dummy_float: float = 0.0
|
||||||
|
) -> ModelPatcher:
|
||||||
|
module = utils.get_attr(model.model, target_module)
|
||||||
|
work_model = model.clone()
|
||||||
|
work_model.add_object_patch(target_module, DummyPatch(module, dummy_float))
|
||||||
|
return (work_model,)
|
||||||
|
|
||||||
|
|
||||||
|
class LoadAnimatedImageTest(nodes.LoadImage):
|
||||||
|
@classmethod
|
||||||
|
def INPUT_TYPES(s):
|
||||||
|
input_dir = folder_paths.get_input_directory()
|
||||||
|
files = [
|
||||||
|
f
|
||||||
|
for f in os.listdir(input_dir)
|
||||||
|
if os.path.isfile(os.path.join(input_dir, f)) and f.endswith(".webp")
|
||||||
|
]
|
||||||
|
files = folder_paths.filter_files_content_types(files, ["image"])
|
||||||
|
return {
|
||||||
|
"required": {"image": (sorted(files), {"animated_image_upload": True})},
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
NODE_CLASS_MAPPINGS = {
|
||||||
|
"DevToolsObjectPatchNode": ObjectPatchNode,
|
||||||
|
"DevToolsLoadAnimatedImageTest": LoadAnimatedImageTest,
|
||||||
|
}
|
||||||
|
|
||||||
|
NODE_DISPLAY_NAME_MAPPINGS = {
|
||||||
|
"DevToolsObjectPatchNode": "Object Patch Node",
|
||||||
|
"DevToolsLoadAnimatedImageTest": "Load Animated Image",
|
||||||
|
}
|
||||||
|
|
||||||
|
__all__ = [
|
||||||
|
"DummyPatch",
|
||||||
|
"ObjectPatchNode",
|
||||||
|
"LoadAnimatedImageTest",
|
||||||
|
"NODE_CLASS_MAPPINGS",
|
||||||
|
"NODE_DISPLAY_NAME_MAPPINGS",
|
||||||
|
]
|
||||||
220
tools/devtools/nodes/remote.py
Normal file
220
tools/devtools/nodes/remote.py
Normal file
@@ -0,0 +1,220 @@
|
|||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
|
||||||
|
class RemoteWidgetNode:
|
||||||
|
@classmethod
|
||||||
|
def INPUT_TYPES(cls):
|
||||||
|
return {
|
||||||
|
"required": {
|
||||||
|
"remote_widget_value": (
|
||||||
|
"COMBO",
|
||||||
|
{
|
||||||
|
"remote": {
|
||||||
|
"route": "/api/models/checkpoints",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
FUNCTION = "remote_widget"
|
||||||
|
CATEGORY = "DevTools"
|
||||||
|
DESCRIPTION = "A node that lazily fetches options from a remote endpoint"
|
||||||
|
RETURN_TYPES = ("STRING",)
|
||||||
|
|
||||||
|
def remote_widget(self, remote_widget_value: str):
|
||||||
|
return (remote_widget_value,)
|
||||||
|
|
||||||
|
|
||||||
|
class RemoteWidgetNodeWithParams:
|
||||||
|
@classmethod
|
||||||
|
def INPUT_TYPES(cls):
|
||||||
|
return {
|
||||||
|
"required": {
|
||||||
|
"remote_widget_value": (
|
||||||
|
"COMBO",
|
||||||
|
{
|
||||||
|
"remote": {
|
||||||
|
"route": "/api/models/checkpoints",
|
||||||
|
"query_params": {
|
||||||
|
"sort": "true",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
FUNCTION = "remote_widget"
|
||||||
|
CATEGORY = "DevTools"
|
||||||
|
DESCRIPTION = (
|
||||||
|
"A node that lazily fetches options from a remote endpoint with query params"
|
||||||
|
)
|
||||||
|
RETURN_TYPES = ("STRING",)
|
||||||
|
|
||||||
|
def remote_widget(self, remote_widget_value: str):
|
||||||
|
return (remote_widget_value,)
|
||||||
|
|
||||||
|
|
||||||
|
class RemoteWidgetNodeWithRefresh:
|
||||||
|
@classmethod
|
||||||
|
def INPUT_TYPES(cls):
|
||||||
|
return {
|
||||||
|
"required": {
|
||||||
|
"remote_widget_value": (
|
||||||
|
"COMBO",
|
||||||
|
{
|
||||||
|
"remote": {
|
||||||
|
"route": "/api/models/checkpoints",
|
||||||
|
"refresh": 300,
|
||||||
|
"max_retries": 10,
|
||||||
|
"timeout": 256,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
FUNCTION = "remote_widget"
|
||||||
|
CATEGORY = "DevTools"
|
||||||
|
DESCRIPTION = "A node that lazily fetches options from a remote endpoint and refresh the options every 300 ms"
|
||||||
|
RETURN_TYPES = ("STRING",)
|
||||||
|
|
||||||
|
def remote_widget(self, remote_widget_value: str):
|
||||||
|
return (remote_widget_value,)
|
||||||
|
|
||||||
|
|
||||||
|
class RemoteWidgetNodeWithRefreshButton:
|
||||||
|
@classmethod
|
||||||
|
def INPUT_TYPES(cls):
|
||||||
|
return {
|
||||||
|
"required": {
|
||||||
|
"remote_widget_value": (
|
||||||
|
"COMBO",
|
||||||
|
{
|
||||||
|
"remote": {
|
||||||
|
"route": "/api/models/checkpoints",
|
||||||
|
"refresh_button": True,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
FUNCTION = "remote_widget"
|
||||||
|
CATEGORY = "DevTools"
|
||||||
|
DESCRIPTION = "A node that lazily fetches options from a remote endpoint and has a refresh button to manually reload options"
|
||||||
|
RETURN_TYPES = ("STRING",)
|
||||||
|
|
||||||
|
def remote_widget(self, remote_widget_value: str):
|
||||||
|
return (remote_widget_value,)
|
||||||
|
|
||||||
|
|
||||||
|
class RemoteWidgetNodeWithControlAfterRefresh:
|
||||||
|
@classmethod
|
||||||
|
def INPUT_TYPES(cls):
|
||||||
|
return {
|
||||||
|
"required": {
|
||||||
|
"remote_widget_value": (
|
||||||
|
"COMBO",
|
||||||
|
{
|
||||||
|
"remote": {
|
||||||
|
"route": "/api/models/checkpoints",
|
||||||
|
"refresh_button": True,
|
||||||
|
"control_after_refresh": "first",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
FUNCTION = "remote_widget"
|
||||||
|
CATEGORY = "DevTools"
|
||||||
|
DESCRIPTION = "A node that lazily fetches options from a remote endpoint and has a refresh button to manually reload options and select the first option on refresh"
|
||||||
|
RETURN_TYPES = ("STRING",)
|
||||||
|
|
||||||
|
def remote_widget(self, remote_widget_value: str):
|
||||||
|
return (remote_widget_value,)
|
||||||
|
|
||||||
|
|
||||||
|
class NodeWithOutputCombo:
|
||||||
|
@classmethod
|
||||||
|
def INPUT_TYPES(cls):
|
||||||
|
return {
|
||||||
|
"required": {
|
||||||
|
"subset_options": (["A", "B"], {"forceInput": True}),
|
||||||
|
"subset_options_v2": (
|
||||||
|
"COMBO",
|
||||||
|
{"options": ["A", "B"], "forceInput": True},
|
||||||
|
),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
RETURN_TYPES = (["A", "B", "C"],)
|
||||||
|
FUNCTION = "node_with_output_combo"
|
||||||
|
CATEGORY = "DevTools"
|
||||||
|
DESCRIPTION = "A node that outputs a combo type"
|
||||||
|
|
||||||
|
def node_with_output_combo(self, subset_options: str):
|
||||||
|
return (subset_options,)
|
||||||
|
|
||||||
|
|
||||||
|
class MultiSelectNode:
|
||||||
|
@classmethod
|
||||||
|
def INPUT_TYPES(cls):
|
||||||
|
return {
|
||||||
|
"required": {
|
||||||
|
"foo": (
|
||||||
|
"COMBO",
|
||||||
|
{
|
||||||
|
"options": ["A", "B", "C"],
|
||||||
|
"multi_select": {
|
||||||
|
"placeholder": "Choose foos",
|
||||||
|
"chip": True,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
RETURN_TYPES = ("STRING",)
|
||||||
|
OUTPUT_IS_LIST = [True]
|
||||||
|
FUNCTION = "multi_select_node"
|
||||||
|
CATEGORY = "DevTools"
|
||||||
|
DESCRIPTION = "A node that outputs a multi select type"
|
||||||
|
|
||||||
|
def multi_select_node(self, foo: list[str]) -> list[str]:
|
||||||
|
return (foo,)
|
||||||
|
|
||||||
|
|
||||||
|
NODE_CLASS_MAPPINGS = {
|
||||||
|
"DevToolsRemoteWidgetNode": RemoteWidgetNode,
|
||||||
|
"DevToolsRemoteWidgetNodeWithParams": RemoteWidgetNodeWithParams,
|
||||||
|
"DevToolsRemoteWidgetNodeWithRefresh": RemoteWidgetNodeWithRefresh,
|
||||||
|
"DevToolsRemoteWidgetNodeWithRefreshButton": RemoteWidgetNodeWithRefreshButton,
|
||||||
|
"DevToolsRemoteWidgetNodeWithControlAfterRefresh": RemoteWidgetNodeWithControlAfterRefresh,
|
||||||
|
"DevToolsNodeWithOutputCombo": NodeWithOutputCombo,
|
||||||
|
"DevToolsMultiSelectNode": MultiSelectNode,
|
||||||
|
}
|
||||||
|
|
||||||
|
NODE_DISPLAY_NAME_MAPPINGS = {
|
||||||
|
"DevToolsRemoteWidgetNode": "Remote Widget Node",
|
||||||
|
"DevToolsRemoteWidgetNodeWithParams": "Remote Widget Node With Sort Query Param",
|
||||||
|
"DevToolsRemoteWidgetNodeWithRefresh": "Remote Widget Node With 300ms Refresh",
|
||||||
|
"DevToolsRemoteWidgetNodeWithRefreshButton": "Remote Widget Node With Refresh Button",
|
||||||
|
"DevToolsRemoteWidgetNodeWithControlAfterRefresh": "Remote Widget Node With Refresh Button and Control After Refresh",
|
||||||
|
"DevToolsNodeWithOutputCombo": "Node With Output Combo",
|
||||||
|
"DevToolsMultiSelectNode": "Multi Select Node",
|
||||||
|
}
|
||||||
|
|
||||||
|
__all__ = [
|
||||||
|
"RemoteWidgetNode",
|
||||||
|
"RemoteWidgetNodeWithParams",
|
||||||
|
"RemoteWidgetNodeWithRefresh",
|
||||||
|
"RemoteWidgetNodeWithRefreshButton",
|
||||||
|
"RemoteWidgetNodeWithControlAfterRefresh",
|
||||||
|
"NodeWithOutputCombo",
|
||||||
|
"MultiSelectNode",
|
||||||
|
"NODE_CLASS_MAPPINGS",
|
||||||
|
"NODE_DISPLAY_NAME_MAPPINGS",
|
||||||
|
]
|
||||||
Reference in New Issue
Block a user