From 5632b2df9d5367128932175082f8d5e489749225 Mon Sep 17 00:00:00 2001 From: Yourz Date: Fri, 20 Feb 2026 11:00:26 +0800 Subject: [PATCH] feat: add essentials_category (#12357) * feat: add essentials_category field to node schema Amp-Thread-ID: https://ampcode.com/threads/T-019c2b25-cd90-7218-9071-03cb46b351b3 * feat: add ESSENTIALS_CATEGORY to core nodes Marked nodes: - Basic: LoadImage, SaveImage, LoadVideo, SaveVideo, Load3D, CLIPTextEncode - Image Tools: ImageScale, ImageInvert, ImageBatch, ImageCrop, ImageRotate, ImageBlur - Image Tools/Preprocessing: Canny - Image Generation: LoraLoader - Audio: LoadAudio, SaveAudio Amp-Thread-ID: https://ampcode.com/threads/T-019c2b25-cd90-7218-9071-03cb46b351b3 * Add ESSENTIALS_CATEGORY to more nodes - SaveGLB (Basic) - GetVideoComponents (Video Tools) - TencentTextToModelNode, TencentImageToModelNode (3D) - RecraftRemoveBackgroundNode (Image Tools) - KlingLipSyncAudioToVideoNode (Video Generation) - OpenAIChatNode (Text Generation) - StabilityTextToAudio (Audio) Amp-Thread-ID: https://ampcode.com/threads/T-019c2b69-81c1-71c3-8096-450a39e20910 * fix: correct essentials category for Canny node Amp-Thread-ID: https://ampcode.com/threads/T-019c7303-ab53-7341-be76-a5da1f7a657e Co-authored-by: Amp * refactor: replace essentials_category string literals with constants Amp-Thread-ID: https://ampcode.com/threads/T-019c7303-ab53-7341-be76-a5da1f7a657e Co-authored-by: Amp * refactor: revert constants, use string literals for essentials_category Amp-Thread-ID: https://ampcode.com/threads/T-019c7303-ab53-7341-be76-a5da1f7a657e Co-authored-by: Amp * fix: update basics --------- Co-authored-by: bymyself Co-authored-by: Amp Co-authored-by: Jedrzej Kosinski --- comfy_api/latest/_io.py | 4 ++++ comfy_api_nodes/nodes_hunyuan3d.py | 2 ++ comfy_api_nodes/nodes_kling.py | 1 + comfy_api_nodes/nodes_openai.py | 1 + comfy_api_nodes/nodes_recraft.py | 1 + comfy_api_nodes/nodes_stability.py | 1 + comfy_extras/nodes_audio.py | 2 ++ comfy_extras/nodes_canny.py | 1 + comfy_extras/nodes_hunyuan3d.py | 1 + comfy_extras/nodes_images.py | 2 ++ comfy_extras/nodes_load_3d.py | 1 + comfy_extras/nodes_post_processing.py | 1 + comfy_extras/nodes_video.py | 3 +++ nodes.py | 9 +++++++++ server.py | 4 ++++ 15 files changed, 34 insertions(+) diff --git a/comfy_api/latest/_io.py b/comfy_api/latest/_io.py index 312681249..dee487c92 100644 --- a/comfy_api/latest/_io.py +++ b/comfy_api/latest/_io.py @@ -1339,6 +1339,7 @@ class NodeInfoV1: api_node: bool=None price_badge: dict | None = None search_aliases: list[str]=None + essentials_category: str=None @dataclass @@ -1460,6 +1461,8 @@ class Schema: """Flags a node as expandable, allowing NodeOutput to include 'expand' property.""" accept_all_inputs: bool=False """When True, all inputs from the prompt will be passed to the node as kwargs, even if not defined in the schema.""" + essentials_category: str | None = None + """Optional category for the Essentials tab. Path-based like category field (e.g., 'Basic', 'Image Tools/Editing').""" def validate(self): '''Validate the schema: @@ -1566,6 +1569,7 @@ class Schema: python_module=getattr(cls, "RELATIVE_PYTHON_MODULE", "nodes"), price_badge=self.price_badge.as_dict(self.inputs) if self.price_badge is not None else None, search_aliases=self.search_aliases if self.search_aliases else None, + essentials_category=self.essentials_category, ) return info diff --git a/comfy_api_nodes/nodes_hunyuan3d.py b/comfy_api_nodes/nodes_hunyuan3d.py index ca002cc60..d1d9578ec 100644 --- a/comfy_api_nodes/nodes_hunyuan3d.py +++ b/comfy_api_nodes/nodes_hunyuan3d.py @@ -54,6 +54,7 @@ class TencentTextToModelNode(IO.ComfyNode): node_id="TencentTextToModelNode", display_name="Hunyuan3D: Text to Model", category="api node/3d/Tencent", + essentials_category="3D", inputs=[ IO.Combo.Input( "model", @@ -168,6 +169,7 @@ class TencentImageToModelNode(IO.ComfyNode): node_id="TencentImageToModelNode", display_name="Hunyuan3D: Image(s) to Model", category="api node/3d/Tencent", + essentials_category="3D", inputs=[ IO.Combo.Input( "model", diff --git a/comfy_api_nodes/nodes_kling.py b/comfy_api_nodes/nodes_kling.py index b89c85561..7d186e69e 100644 --- a/comfy_api_nodes/nodes_kling.py +++ b/comfy_api_nodes/nodes_kling.py @@ -2262,6 +2262,7 @@ class KlingLipSyncAudioToVideoNode(IO.ComfyNode): node_id="KlingLipSyncAudioToVideoNode", display_name="Kling Lip Sync Video with Audio", category="api node/video/Kling", + essentials_category="Video Generation", description="Kling Lip Sync Audio to Video Node. Syncs mouth movements in a video file to the audio content of an audio file. When using, ensure that the audio contains clearly distinguishable vocals and that the video contains a distinct face. The audio file should not be larger than 5MB. The video file should not be larger than 100MB, should have height/width between 720px and 1920px, and should be between 2s and 10s in length.", inputs=[ IO.Video.Input("video"), diff --git a/comfy_api_nodes/nodes_openai.py b/comfy_api_nodes/nodes_openai.py index 332107a82..323bd6227 100644 --- a/comfy_api_nodes/nodes_openai.py +++ b/comfy_api_nodes/nodes_openai.py @@ -575,6 +575,7 @@ class OpenAIChatNode(IO.ComfyNode): node_id="OpenAIChatNode", display_name="OpenAI ChatGPT", category="api node/text/OpenAI", + essentials_category="Text Generation", description="Generate text responses from an OpenAI model.", inputs=[ IO.String.Input( diff --git a/comfy_api_nodes/nodes_recraft.py b/comfy_api_nodes/nodes_recraft.py index 773cb7dbe..4d1d508fa 100644 --- a/comfy_api_nodes/nodes_recraft.py +++ b/comfy_api_nodes/nodes_recraft.py @@ -963,6 +963,7 @@ class RecraftRemoveBackgroundNode(IO.ComfyNode): node_id="RecraftRemoveBackgroundNode", display_name="Recraft Remove Background", category="api node/image/Recraft", + essentials_category="Image Tools", description="Remove background from image, and return processed image and mask.", inputs=[ IO.Image.Input("image"), diff --git a/comfy_api_nodes/nodes_stability.py b/comfy_api_nodes/nodes_stability.py index 5665109cf..998a20c17 100644 --- a/comfy_api_nodes/nodes_stability.py +++ b/comfy_api_nodes/nodes_stability.py @@ -624,6 +624,7 @@ class StabilityTextToAudio(IO.ComfyNode): node_id="StabilityTextToAudio", display_name="Stability AI Text To Audio", category="api node/audio/Stability AI", + essentials_category="Audio", description=cleandoc(cls.__doc__ or ""), inputs=[ IO.Combo.Input( diff --git a/comfy_extras/nodes_audio.py b/comfy_extras/nodes_audio.py index 7e74169f2..5aa39b332 100644 --- a/comfy_extras/nodes_audio.py +++ b/comfy_extras/nodes_audio.py @@ -159,6 +159,7 @@ class SaveAudio(IO.ComfyNode): search_aliases=["export flac"], display_name="Save Audio (FLAC)", category="audio", + essentials_category="Audio", inputs=[ IO.Audio.Input("audio"), IO.String.Input("filename_prefix", default="audio/ComfyUI"), @@ -300,6 +301,7 @@ class LoadAudio(IO.ComfyNode): search_aliases=["import audio", "open audio", "audio file"], display_name="Load Audio", category="audio", + essentials_category="Audio", inputs=[ IO.Combo.Input("audio", upload=IO.UploadType.audio, options=sorted(files)), ], diff --git a/comfy_extras/nodes_canny.py b/comfy_extras/nodes_canny.py index 6e0fadca5..956c1977c 100644 --- a/comfy_extras/nodes_canny.py +++ b/comfy_extras/nodes_canny.py @@ -12,6 +12,7 @@ class Canny(io.ComfyNode): node_id="Canny", search_aliases=["edge detection", "outline", "contour detection", "line art"], category="image/preprocessors", + essentials_category="Image Tools", inputs=[ io.Image.Input("image"), io.Float.Input("low_threshold", default=0.4, min=0.01, max=0.99, step=0.01), diff --git a/comfy_extras/nodes_hunyuan3d.py b/comfy_extras/nodes_hunyuan3d.py index c2df3e859..3512d3cf8 100644 --- a/comfy_extras/nodes_hunyuan3d.py +++ b/comfy_extras/nodes_hunyuan3d.py @@ -621,6 +621,7 @@ class SaveGLB(IO.ComfyNode): display_name="Save 3D Model", search_aliases=["export 3d model", "save mesh"], category="3d", + essentials_category="Basics", is_output_node=True, inputs=[ IO.MultiType.Input( diff --git a/comfy_extras/nodes_images.py b/comfy_extras/nodes_images.py index 23419a65d..88fe071e7 100644 --- a/comfy_extras/nodes_images.py +++ b/comfy_extras/nodes_images.py @@ -26,6 +26,7 @@ class ImageCrop(IO.ComfyNode): display_name="Image Crop (Deprecated)", category="image/transform", is_deprecated=True, + essentials_category="Image Tools", inputs=[ IO.Image.Input("image"), IO.Int.Input("width", default=512, min=1, max=nodes.MAX_RESOLUTION, step=1), @@ -589,6 +590,7 @@ class ImageRotate(IO.ComfyNode): node_id="ImageRotate", search_aliases=["turn", "flip orientation"], category="image/transform", + essentials_category="Image Tools", inputs=[ IO.Image.Input("image"), IO.Combo.Input("rotation", options=["none", "90 degrees", "180 degrees", "270 degrees"]), diff --git a/comfy_extras/nodes_load_3d.py b/comfy_extras/nodes_load_3d.py index edbb5cd40..3c88cbaa8 100644 --- a/comfy_extras/nodes_load_3d.py +++ b/comfy_extras/nodes_load_3d.py @@ -31,6 +31,7 @@ class Load3D(IO.ComfyNode): node_id="Load3D", display_name="Load 3D & Animation", category="3d", + essentials_category="Basics", is_experimental=True, inputs=[ IO.Combo.Input("model_file", options=sorted(files), upload=IO.UploadType.model), diff --git a/comfy_extras/nodes_post_processing.py b/comfy_extras/nodes_post_processing.py index 66dac10b1..f35819bc2 100644 --- a/comfy_extras/nodes_post_processing.py +++ b/comfy_extras/nodes_post_processing.py @@ -77,6 +77,7 @@ class Blur(io.ComfyNode): return io.Schema( node_id="ImageBlur", category="image/postprocessing", + essentials_category="Image Tools", inputs=[ io.Image.Input("image"), io.Int.Input("blur_radius", default=1, min=1, max=31, step=1), diff --git a/comfy_extras/nodes_video.py b/comfy_extras/nodes_video.py index cd765a7c1..db7c171a2 100644 --- a/comfy_extras/nodes_video.py +++ b/comfy_extras/nodes_video.py @@ -73,6 +73,7 @@ class SaveVideo(io.ComfyNode): search_aliases=["export video"], display_name="Save Video", category="image/video", + essentials_category="Basics", description="Saves the input images to your ComfyUI output directory.", inputs=[ io.Video.Input("video", tooltip="The video to save."), @@ -146,6 +147,7 @@ class GetVideoComponents(io.ComfyNode): search_aliases=["extract frames", "split video", "video to images", "demux"], display_name="Get Video Components", category="image/video", + essentials_category="Video Tools", description="Extracts all components from a video: frames, audio, and framerate.", inputs=[ io.Video.Input("video", tooltip="The video to extract components from."), @@ -174,6 +176,7 @@ class LoadVideo(io.ComfyNode): search_aliases=["import video", "open video", "video file"], display_name="Load Video", category="image/video", + essentials_category="Basics", inputs=[ io.Combo.Input("file", options=sorted(files), upload=io.UploadType.video), ], diff --git a/nodes.py b/nodes.py index d8e913065..330808cdc 100644 --- a/nodes.py +++ b/nodes.py @@ -8,6 +8,7 @@ import json import glob import hashlib import inspect + import traceback import math import time @@ -69,6 +70,7 @@ class CLIPTextEncode(ComfyNodeABC): FUNCTION = "encode" CATEGORY = "conditioning" + ESSENTIALS_CATEGORY = "Basics" DESCRIPTION = "Encodes a text prompt using a CLIP model into an embedding that can be used to guide the diffusion model towards generating specific images." SEARCH_ALIASES = ["text", "prompt", "text prompt", "positive prompt", "negative prompt", "encode text", "text encoder", "encode prompt"] @@ -667,6 +669,8 @@ class CLIPSetLastLayer: return (clip,) class LoraLoader: + ESSENTIALS_CATEGORY = "Image Generation" + def __init__(self): self.loaded_lora = None @@ -1648,6 +1652,7 @@ class SaveImage: OUTPUT_NODE = True CATEGORY = "image" + ESSENTIALS_CATEGORY = "Basics" DESCRIPTION = "Saves the input images to your ComfyUI output directory." SEARCH_ALIASES = ["save", "save image", "export image", "output image", "write image", "download"] @@ -1706,6 +1711,7 @@ class LoadImage: } CATEGORY = "image" + ESSENTIALS_CATEGORY = "Basics" SEARCH_ALIASES = ["load image", "open image", "import image", "image input", "upload image", "read image", "image loader"] RETURN_TYPES = ("IMAGE", "MASK") @@ -1863,6 +1869,7 @@ class ImageScale: FUNCTION = "upscale" CATEGORY = "image/upscaling" + ESSENTIALS_CATEGORY = "Image Tools" SEARCH_ALIASES = ["resize", "resize image", "scale image", "image resize", "zoom", "zoom in", "change size"] def upscale(self, image, upscale_method, width, height, crop): @@ -1902,6 +1909,7 @@ class ImageScaleBy: class ImageInvert: SEARCH_ALIASES = ["reverse colors"] + ESSENTIALS_CATEGORY = "Image Tools" @classmethod def INPUT_TYPES(s): @@ -1918,6 +1926,7 @@ class ImageInvert: class ImageBatch: SEARCH_ALIASES = ["combine images", "merge images", "stack images"] + ESSENTIALS_CATEGORY = "Image Tools" @classmethod def INPUT_TYPES(s): diff --git a/server.py b/server.py index 8882e43c4..275bce5a7 100644 --- a/server.py +++ b/server.py @@ -689,6 +689,10 @@ class PromptServer(): info['api_node'] = obj_class.API_NODE info['search_aliases'] = getattr(obj_class, 'SEARCH_ALIASES', []) + + if hasattr(obj_class, 'ESSENTIALS_CATEGORY'): + info['essentials_category'] = obj_class.ESSENTIALS_CATEGORY + return info @routes.get("/object_info")