Compare commits

...

29 Commits

Author SHA1 Message Date
jfcantu
e4be9eda6c
Merge 1bdcd1bdbf into 6147ed790b 2025-12-31 17:00:59 +01:00
Dr.Lt.Data
6147ed790b update DB
Some checks failed
Python Linting / Run Ruff (push) Has been cancelled
2025-12-30 12:50:31 +09:00
Dr.Lt.Data
e730af2ae5 update DB
Some checks are pending
Python Linting / Run Ruff (push) Waiting to run
2025-12-29 12:42:49 +09:00
Dr.Lt.Data
8662f6e527 update DB
Some checks are pending
Python Linting / Run Ruff (push) Waiting to run
2025-12-29 00:28:43 +09:00
Dr.Lt.Data
3103fc9864 update DB
Some checks are pending
Python Linting / Run Ruff (push) Waiting to run
2025-12-28 08:35:32 +09:00
Shobhit Gupta
637678db20
Custom nodes for Google's Genmedia models (#2433)
Adding a suite of experimental custom nodes that allows access to Google's 1P models like Veo, Imagen, Nano Banana, Gemini, Virtual-try-on, Lyria
2025-12-28 08:34:56 +09:00
Dr.Lt.Data
e97407a286 update DB 2025-12-28 08:34:44 +09:00
mrm987
e494abb779
Update custom-node-list.json (#2439)
Add ComfyUI-Multi-Prompt-Generator
2025-12-28 08:33:59 +09:00
Dr.Lt.Data
44093a42fa update DB 2025-12-28 08:33:12 +09:00
Dr.Lt.Data
8e1481ae78 update DB 2025-12-28 08:18:23 +09:00
chrishill197724-gif
9c59e7498f
Update custom-node-list.json - Add Wan2.2 Storyboard LowVRAM Node (#2434)
* Update custom-node-list.json

* Update custom-node-list.json

---------

Co-authored-by: Dr.Lt.Data <128333288+ltdrdata@users.noreply.github.com>
2025-12-28 08:17:28 +09:00
Dr.Lt.Data
0a202dd506 update DB 2025-12-28 07:47:42 +09:00
room3dev
7eb4a3f961
Add ComfyUI-FrameIO node to custom-node-list (#2435) 2025-12-28 07:46:36 +09:00
Dr.Lt.Data
1ce5603379 update DB 2025-12-28 07:46:24 +09:00
Lucas
97b86b02ad
Add "Image MetaHub Save Node" to custom-node-list (#2432) 2025-12-28 07:44:29 +09:00
CornmeisterNL
f2da1635f2
Add CornmeisterNL PowerPack (#2425)
Co-authored-by: CornmeisterNL <cornmeister@gmail.com>
2025-12-28 07:25:47 +09:00
Dr.Lt.Data
f0ed5c3433 update DB
Some checks are pending
Python Linting / Run Ruff (push) Waiting to run
2025-12-27 05:37:29 +09:00
Eric Rollei
aca5925e57
Add Qwen Layers Diffuser Pipeline integration (#2420)
Added a new integration for Qwen Layers Diffuser Pipeline with detailed description and reference.
2025-12-27 05:10:01 +09:00
Dr.Lt.Data
b8d78174a5 update DB 2025-12-27 04:48:18 +09:00
Wakapedia
edf2a43122
Add WanVideo Wakawave - Advanced LoRA & Prompt Tools (#2431)
* Add WanVideo Wakawave nodes

Added a new custom node entry for 'WanVideo Wakawave' with details on its features and installation.

* Fix typo in custom-node-list.json
2025-12-27 04:47:22 +09:00
Dr.Lt.Data
21de993546 update DB 2025-12-27 04:46:42 +09:00
ConstantlyGrowup
49bc24b66e
Update custom-node-list.json (#2428) 2025-12-27 04:44:29 +09:00
Dr.Lt.Data
771d627c5a update DB 2025-12-27 04:43:53 +09:00
hkun
98967de31b
Add 4 downloader plugins (LoRA, UNet, Plugin, Upscaler) to ComfyUI Manager (#2426)
new nodepacks: UNet Downloader for ComfyUI, Plugin Downloader for ComfyUI, Upscaler Downloader for ComfyUI
2025-12-27 04:35:55 +09:00
ArtsticH
c87c07dbd5
Update the main info for my plugin EasyKitHT NodeAlignPro. Cheers! (#2422)
* Update the main info for my plugin EasyKitHT NodeAlignPro. Cheers!

* Update custom-node-list.json

---------

Co-authored-by: Dr.Lt.Data <128333288+ltdrdata@users.noreply.github.com>
2025-12-27 04:27:35 +09:00
John Cantu
1bdcd1bdbf Merge branch 'main' of https://github.com/jfcantu/ComfyUI-Manager 2025-12-14 18:11:34 -08:00
John Cantu
e04ed0eda7 update README.md for node usage analyzeer 2025-09-14 23:10:10 -07:00
John Cantu
02aa67b541 Merge branch 'main' of https://github.com/jfcantu/ComfyUI-Manager 2025-09-14 22:52:41 -07:00
John Cantu
67d03530a3 Changes and new code for Node Usage Analyzer 2025-09-14 22:49:54 -07:00
19 changed files with 19980 additions and 15136 deletions

View File

@ -440,6 +440,16 @@
"install_type": "git-clone",
"description": "LM Studio calls with image support and easy task directions."
},
{
"author": "WASasquatch",
"title": "ComfyUI_RetroArch_Player",
"reference": "https://github.com/WASasquatch/ComfyUI_RetroArch_Player",
"files": [
"https://github.com/WASasquatch/ComfyUI_RetroArch_Player"
],
"install_type": "git-clone",
"description": "Simple ComfyUI RetroArch Web Player embed"
},
{
"author": "omar92",
"title": "Quality of life Suit:V2",
@ -1503,6 +1513,16 @@
"install_type": "git-clone",
"description": "A very barebones mostly-copypaste implementation of [a/https://github.com/xie-lab-ml/Golden-Noise-for-Diffusion-Models](https://github.com/xie-lab-ml/Golden-Noise-for-Diffusion-Models)"
},
{
"author": "asagi4",
"title": "comfyui-dynamic-anynode",
"reference": "https://github.com/asagi4/comfyui-dynamic-anynode",
"files": [
"https://github.com/asagi4/comfyui-dynamic-anynode"
],
"install_type": "git-clone",
"description": "Dynamic All-In-One Guider/Scheduler/Sampler nodes for custom sampling"
},
{
"author": "jamesWalker55",
"title": "ComfyUI - P2LDGAN Node",
@ -5121,6 +5141,16 @@
"install_type": "git-clone",
"description": "FL CosyVoice3 - Advanced Text-to-Speech nodes for ComfyUI. Features zero-shot voice cloning, cross-lingual synthesis, instruction-based control, and voice conversion using the CosyVoice3 model family. Supports 9 languages and 18+ Chinese dialects with automatic model downloading and caching."
},
{
"author": "filliptm",
"title": "ComfyUI_FL-ClearVoice",
"reference": "https://github.com/filliptm/ComfyUI_FL-ClearVoice",
"files": [
"https://github.com/filliptm/ComfyUI_FL-ClearVoice"
],
"install_type": "git-clone",
"description": "FL ClearVoice - Audio enhancement nodes for ComfyUI. Features speech enhancement, super-resolution, denoising, and audio restoration using ClearVoice, Resemble-Enhance, and VoiceFixer models. Supports multiple backends with automatic model downloading."
},
{
"author": "zfkun",
"title": "ComfyUI_zfkun",
@ -5757,6 +5787,16 @@
"install_type": "git-clone",
"description": "Noise, conditioning, and perturbation utility nodes for ComfyUI (procedural noise, mesh drag, latent channel stats preview)."
},
{
"author": "ttulttul",
"title": "Better-Gemini",
"reference": "https://github.com/ttulttul/Better-Gemini",
"files": [
"https://github.com/ttulttul/Better-Gemini"
],
"install_type": "git-clone",
"description": "A better node for using Google Gemini models, supporting the latest API endpoints with a v3 ComfyUI node interface."
},
{
"author": "jitcoder",
"title": "LoraInfo",
@ -13705,6 +13745,16 @@
"install_type": "git-clone",
"description": "Custom ComfyUI nodes for integrating RunningHub's AI image and video generation services into workflows. (Description by CC)"
},
{
"author": "marduk191",
"title": "ComfyUI_LFM2-350M",
"reference": "https://github.com/marduk191/ComfyUI_LFM2-350M",
"files": [
"https://github.com/marduk191/ComfyUI_LFM2-350M"
],
"install_type": "git-clone",
"description": "A custom node for ComfyUI to load and use the LFM2-350M model trained to work as a prompt enhancer for z-image turbo or any other long token model."
},
{
"author": "haohaocreates",
"title": "ComfyUI-HH-Image-Selector",
@ -18681,6 +18731,16 @@
"install_type": "git-clone",
"description": "This is a toolset designed for ComfyUI by LAOGOU-666, providing a series of practical image processing and operation nodes, making our operation more intuitive and convenient"
},
{
"author": "LAOGOU-666",
"title": "ComfyUI-LG_SamplingUtils",
"reference": "https://github.com/LAOGOU-666/ComfyUI-LG_SamplingUtils",
"files": [
"https://github.com/LAOGOU-666/ComfyUI-LG_SamplingUtils"
],
"install_type": "git-clone",
"description": "This is a toolset designed for ComfyUI by LAOGOU-666, providing a series of practical sampling nodes, making our operation more intuitive and convenient"
},
{
"author": "VertexStudio",
"title": "roblox-comfyui-nodes",
@ -20180,6 +20240,16 @@
"install_type": "git-clone",
"description": "ComfyUI toolkit with custom nodes for BBox canvas visualization, image brush mask drawing, and coordinate annotation for debugging and external data bridging. (Description by CC)"
},
{
"author": "yichengup",
"title": "ComfyUI-gaussian_preview",
"reference": "https://github.com/yichengup/ComfyUI-gaussian_preview",
"files": [
"https://github.com/yichengup/ComfyUI-gaussian_preview"
],
"install_type": "git-clone",
"description": "Preview and record 3D Gaussian splatting models with interactive 3D rendering and video export. (Description by CC)"
},
{
"author": "Horizon Team",
"title": "ComfyUI_FluxMod",
@ -25365,6 +25435,16 @@
"install_type": "git-clone",
"description": "ComfyUI custom nodes to reshape/project Conditioning tensors and ensure SDXL conditioning metadata."
},
{
"author": "thezveroboy",
"title": "ComfyUI-LoadRandomText",
"reference": "https://github.com/thezveroboy/ComfyUI-LoadRandomText",
"files": [
"https://github.com/thezveroboy/ComfyUI-LoadRandomText"
],
"install_type": "git-clone",
"description": "Load Random Text is a ComfyUI node pack that instantly picks a text file from a folder and outputs its contents straight into your workflow."
},
{
"author": "tatookan",
"title": "comfyui_ssl_gemini_EXP",
@ -27194,13 +27274,15 @@
},
{
"author": "ArtsticH",
"title": "ComfyUI_EasyKitHT_NodeAlignPro",
"title": "EasyKitHT NodeAlignPro",
"id": "comfyui_easykitht_nodealignpro",
"reference": "https://github.com/ArtsticH/ComfyUI_EasyKitHT_NodeAlignPro",
"files": [
"https://github.com/ArtsticH/ComfyUI_EasyKitHT_NodeAlignPro"
],
"install_type": "git-clone",
"description": "ComfyUI_EasyKitHT_NodeAlignPro is a lightweight ComfyUI node alignment and node coloring tool for refactoring and rewriting the UI based on the open-source projects Comfyui-Align and Comfyui-Nodealigner."
"description": "Professional alignment & real-time node color picker. A must-have plugin for managing node layout and color schemes in ComfyUI. Features a real-time color picker, alignment, 7 preset colors, grayscale/custom modes, and one-click reverse alignment.",
"tags": ["alignment", "color-picker", "node-utility", "ui", "organizer", "node2.0", "frontend"]
},
{
"author": "matorzhin",
@ -27405,6 +27487,16 @@
"install_type": "git-clone",
"description": "A custom node for ComfyUI that allows temporal switching between prompts."
},
{
"author": "BigStationW",
"title": "ComfyUi-TextEncodeQwenImageEditAdvanced",
"reference": "https://github.com/BigStationW/ComfyUi-TextEncodeQwenImageEditAdvanced",
"files": [
"https://github.com/BigStationW/ComfyUi-TextEncodeQwenImageEditAdvanced"
],
"install_type": "git-clone",
"description": "Qwen Image Edit uses a Vision Language Model to analyze input images and automatically enhance prompts with detailed descriptions."
},
{
"author": "matoo",
"title": "Compare Videos",
@ -27895,6 +27987,16 @@
"install_type": "git-clone",
"description": "for use jimeng ai in comfyui"
},
{
"author": "xuhongming251",
"title": "ComfyUI-InfiniteTalk-MultiImage",
"reference": "https://github.com/xuhongming251/ComfyUI-InfiniteTalk-MultiImage",
"files": [
"https://github.com/xuhongming251/ComfyUI-InfiniteTalk-MultiImage"
],
"install_type": "git-clone",
"description": "ComfyUI nodes for multi-image processing and data manipulation in InfiniteTalk workflows. (Description by CC)"
},
{
"author": "Kyron Mahan",
"title": "ComfyUI Smart Scaler",
@ -31317,6 +31419,16 @@
"install_type": "git-clone",
"description": "ComfyUI node that applies vintage/retro effects through JPG compression, color grading, film grain, vignette, and more to emulate more realistic photo aesthetics."
},
{
"author": "brucew4yn3rp",
"title": "ComfyUI_FrontEndCleanup",
"reference": "https://github.com/brucew4yn3rp/ComfyUI_FrontEndCleanup",
"files": [
"https://github.com/brucew4yn3rp/ComfyUI_FrontEndCleanup"
],
"install_type": "git-clone",
"description": "Customizes ComfyUI frontend by relocating action bar and hiding UI elements to reduce visual clutter."
},
{
"author": "cedarconnor",
"title": "ComfyUI LatLong - Equirectangular Image Processing Nodes",
@ -35478,6 +35590,16 @@
"install_type": "git-clone",
"description": "NeurCADRecon: Neural Implicit CAD Reconstruction - Originally from ComfyUI-CADabra"
},
{
"author": "PozzettiAndrea",
"title": "ComfyUI-MIDI3D",
"reference": "https://github.com/PozzettiAndrea/ComfyUI-MIDI3D",
"files": [
"https://github.com/PozzettiAndrea/ComfyUI-MIDI3D"
],
"install_type": "git-clone",
"description": "THIS NODE IS WIP. ComfyUI wrapper for MIDI-3D: Multi-Instance Diffusion for single image to compositional 3D scene generation."
},
{
"author": "rookiestar28",
"title": "Danbooru Tags Upsampler for ComfyUI",
@ -35685,6 +35807,27 @@
"install_type": "git-clone",
"description": "ComfyUI integration for BAAI's Emu3.5 multimodal models for text-to-image generation and multimodal understanding. (Description by CC)"
},
{
"author": "EricRollei",
"title": "Qwen Layers Diffuser Pipeline",
"id": "qwen-layers-diffuser-pipeline",
"reference": "https://github.com/EricRollei/Qwen_Layers_Diffuser_Pipeline_Comfyui",
"files": [
"https://github.com/EricRollei/Qwen_Layers_Diffuser_Pipeline_Comfyui"
],
"install_type": "git-clone",
"description": "Decompose images into semantic RGBA layers using Qwen-Image-Layered model. Features AI-powered layer naming, PSD/TIFF export with proper layer structure, original resolution support, and VRAM management. Includes 18 nodes for layer decomposition, manipulation, and saving."
},
{
"author": "EricRollei",
"title": "comfyui-refocus",
"reference": "https://github.com/EricRollei/comfyui-refocus",
"files": [
"https://github.com/EricRollei/comfyui-refocus"
],
"install_type": "git-clone",
"description": "ComfyUI nodes for Generative Refocusing - Deblurring and Bokeh generation using FLUX LoRA adapters based on Genfocus"
},
{
"author": "nomadoor",
"title": "ComfyUI Temporal Mask Tools",
@ -37111,6 +37254,36 @@
"install_type": "git-clone",
"description": "ComfyUI node: extract fps/frames/duration/width/height from video"
},
{
"author": "princepainter",
"title": "ComfyUI-PainterImageFromBatch",
"reference": "https://github.com/princepainter/ComfyUI-PainterImageFromBatch",
"files": [
"https://github.com/princepainter/ComfyUI-PainterImageFromBatch"
],
"install_type": "git-clone",
"description": "PainterImageFromBatch is an enhanced ComfyUI custom node for extracting contiguous frames from image batches."
},
{
"author": "princepainter",
"title": "ComfyUI-PainterImageLoad",
"reference": "https://github.com/princepainter/ComfyUI-PainterImageLoad",
"files": [
"https://github.com/princepainter/ComfyUI-PainterImageLoad"
],
"install_type": "git-clone",
"description": "ComfyUI node for saving, previewing, and passing images downstream while supporting mask editing and intermediate node execution. (Description by CC)"
},
{
"author": "princepainter",
"title": "ComfyUI-PainterI2Vadvanced",
"reference": "https://github.com/princepainter/ComfyUI-PainterI2Vadvanced",
"files": [
"https://github.com/princepainter/ComfyUI-PainterI2Vadvanced"
],
"install_type": "git-clone",
"description": "Enhanced Wan2.2 image-to-video node with dual sampler workflow, dynamic enhancement, and intelligent color drift correction for video generation. (Description by CC)"
},
{
"author": "rafacost",
"title": "rafacostComfy",
@ -37485,6 +37658,16 @@
"install_type": "git-clone",
"description": "Custom utility nodes for ComfyUI by huyl2 (sort list v2, etc.)"
},
{
"author": "huyl3-cpu",
"title": "comfyui_segment_anything_a100",
"reference": "https://github.com/huyl3-cpu/comfyui_segment_anything_a100",
"files": [
"https://github.com/huyl3-cpu/comfyui_segment_anything_a100"
],
"install_type": "git-clone",
"description": "Heavily optimized A100 GPU fork of ComfyUI Segment Anything using BF16 precision, VRAM Locking, and Zero-Copy GPU processing for massive batch processing."
},
{
"author": "7BEII",
"title": "comfyui-PD_comfy-api-node",
@ -38281,15 +38464,25 @@
"description": "A practical and lightweight collection of custom nodes for ComfyUI, providing utility tools and convenient workflow functions."
},
{
"author": "ShootTheSound",
"title": "Realtime LoRA Trainer",
"id": "comfyui-realtime-lora",
"reference": "https://github.com/ShootTheSound/comfyUI-Realtime-Lora",
"files": [
"https://github.com/ShootTheSound/comfyUI-Realtime-Lora"
],
"install_type": "git-clone",
"description": "Train LoRAs directly inside ComfyUI. Supports SDXL (via sd-scripts), FLUX, Z-Image Turbo, and Wan 2.2 (via AI-Toolkit)."
"author": "ShootTheSound",
"title": "Realtime LoRA Trainer",
"id": "comfyui-realtime-lora",
"reference": "https://github.com/ShootTheSound/comfyUI-Realtime-Lora",
"files": [
"https://github.com/ShootTheSound/comfyUI-Realtime-Lora"
],
"install_type": "git-clone",
"description": "Train LoRAs directly inside ComfyUI. Supports SDXL (via sd-scripts), FLUX, Z-Image Turbo, and Wan 2.2 (via AI-Toolkit)."
},
{
"author": "shootthesound",
"title": "comfyUI-LongLook",
"reference": "https://github.com/shootthesound/comfyUI-LongLook",
"files": [
"https://github.com/shootthesound/comfyUI-LongLook"
],
"install_type": "git-clone",
"description": "FreeLong spectral blending for consistent motion and prompt adherence in Wan 2.2 video generation with chunked generation support."
},
{
"author": "HackAfterDark",
@ -38465,6 +38658,16 @@
"install_type": "git-clone",
"description": "Generate audio-reactive SCAIL pose sequences for character animation without requiring input video tracking. Now supports Multi-Character Choreography. (Description by CC)"
},
{
"author": "ckinpdx",
"title": "ComfyUI-LoadAudioandSplit",
"reference": "https://github.com/ckinpdx/ComfyUI-LoadAudioandSplit",
"files": [
"https://github.com/ckinpdx/ComfyUI-LoadAudioandSplit"
],
"install_type": "git-clone",
"description": "Splits audio into frame-synced chunks for video generation workflows with support for overlapping segments for seamless transitions."
},
{
"author": "jessesep",
"title": "SimpleVariables",
@ -38717,6 +38920,39 @@
"install_type": "git-clone",
"description": "A ComfyUI custom node for downloading and managing LoRA models directly within the UI."
},
{
"author": "huihuihuiz",
"title": "UNet Downloader for ComfyUI",
"id": "unet_downloader",
"reference": "https://github.com/huihuihuiz/unet_downloader",
"files": [
"https://github.com/huihuihuiz/unet_downloader"
],
"install_type": "git-clone",
"description": "A ComfyUI custom node for downloading and managing UNet/Diffusion models (FLUX, SD3, etc.) directly within the UI."
},
{
"author": "huihuihuiz",
"title": "Plugin Downloader for ComfyUI",
"id": "plugin_downloader",
"reference": "https://github.com/huihuihuiz/plugin_downloader",
"files": [
"https://github.com/huihuihuiz/plugin_downloader"
],
"install_type": "git-clone",
"description": "A ComfyUI custom node for downloading and backing up all custom nodes/plugins as ZIP files."
},
{
"author": "huihuihuiz",
"title": "Upscaler Downloader for ComfyUI",
"id": "upscaler_downloader",
"reference": "https://github.com/huihuihuiz/upscaler_downloader",
"files": [
"https://github.com/huihuihuiz/upscaler_downloader"
],
"install_type": "git-clone",
"description": "A ComfyUI custom node for downloading and managing upscaler/super-resolution models directly within the UI."
},
{
"author": "aTanguay",
"title": "ComfyUI_Detonate",
@ -38838,6 +39074,16 @@
"install_type": "git-clone",
"description": "A dedicated suite of custom nodes for batch testing artists, styles, and prompts in ComfyUI, solving the caching problem with anti-cache logic and dynamic filename generation."
},
{
"author": "g7b2",
"title": "ComfyUI-MultiCheckpoint-Tester",
"reference": "https://github.com/g7b2/ComfyUI-MultiCheckpoint-Tester",
"files": [
"https://github.com/g7b2/ComfyUI-MultiCheckpoint-Tester"
],
"install_type": "git-clone",
"description": "A powerful ComfyUI custom node for model comparison testing supporting 1-5 checkpoint models with automatic VRAM management and comparison image generation. (Description by CC)"
},
{
"author": "ubisoft",
"title": "ComfyUI-Chord",
@ -38878,6 +39124,16 @@
"install_type": "git-clone",
"description": "llama-cpp-python wrapper, with support for vision models. It allows the user to generate text responses from prompts using llama.cpp."
},
{
"author": "sebagallo",
"title": "comfyui-sg-openai-client",
"reference": "https://github.com/sebagallo/comfyui-sg-openai-client",
"files": [
"https://github.com/sebagallo/comfyui-sg-openai-client"
],
"install_type": "git-clone",
"description": "ComfyUI custom nodes for OpenAI API integration (local supported) with chat completion and image support."
},
{
"author": "hubo502",
"title": "ComfyUI-Env-Loader",
@ -39098,6 +39354,26 @@
"install_type": "git-clone",
"description": "ComfyUI custom node for managing trigger words with 50+ editable presets optimized for SDXL Illustrious"
},
{
"author": "revisionhiep-create",
"title": "Comfyui-Prompt-Sorter",
"reference": "https://github.com/revisionhiep-create/Comfyui-Prompt-Sorter",
"files": [
"https://github.com/revisionhiep-create/Comfyui-Prompt-Sorter"
],
"install_type": "git-clone",
"description": "Interactive button-based trigger word manager with precision weighting and portable collections."
},
{
"author": "revisionhiep-create",
"title": "comfyui-universal-trigger-toggle",
"reference": "https://github.com/revisionhiep-create/comfyui-universal-trigger-toggle",
"files": [
"https://github.com/revisionhiep-create/comfyui-universal-trigger-toggle"
],
"install_type": "git-clone",
"description": "A standalone custom node for ComfyUI that provides an interactive tag-based interface to enable/disable trigger words from any LoRA loader or gallery node."
},
{
"author": "Faildes",
"title": "ComfyUI-NegativeFold",
@ -39108,6 +39384,16 @@
"install_type": "git-clone",
"description": "Fold negative prompts into positive prompts for use with Turbo models. (Description by CC)"
},
{
"author": "Faildes",
"title": "ComfyUI-TC_ADV_ZPrompt",
"reference": "https://github.com/Faildes/ComfyUI-TC_ADV_ZPrompt",
"files": [
"https://github.com/Faildes/ComfyUI-TC_ADV_ZPrompt"
],
"install_type": "git-clone",
"description": "Extension that allows to use emphasize and AND on Z-Image."
},
{
"author": "isekai-sh",
"title": "isekai-comfy-node",
@ -39420,6 +39706,317 @@
"install_type": "git-clone",
"description": "ComfyUI custom node pack for fal.ai image generation supporting FLUX models with queue polling, safety controls, and native image I/O conversion."
},
{
"author": "ConstantlyGrowup",
"title": "ComfyUI_Qwen_chat_models",
"reference": "https://github.com/ConstantlyGrowup/ComfyUI_Qwen_chat_models",
"files": [
"https://github.com/ConstantlyGrowup/ComfyUI_Qwen_chat_models"
],
"install_type": "git-clone",
"description": "ComfyUI custom nodes for Qwen text and vision-language models. Supports Qwen2.5/Qwen3, 4-/8-bit quantization, deterministic seeding, auto model download, and shared caching to improve workflow performance."
},
{
"author": "Wakapedia",
"title": "WanVideo Wakawave - Advanced LoRA & Prompt Tools",
"reference": "https://github.com/Wakapedia/ComfyUI-WanVideoWakawave",
"files": [
"https://github.com/Wakapedia/ComfyUI-WanVideoWakawave"
],
"install_type": "git-clone",
"description": "Advanced LoRA management and prompt building tools for WanVideo. Features: Wakawave LoRA Loader (unlimited LoRAs, save/load presets, drag-to-reorder, per-LoRA strength control), Wakawave Prompt Builder (dual positive/negative prompts, weight control, segment mode, visual preset browser with import/export)."
},
{
"author": "nawka12",
"title": "ComfyUI-Adept-Sampler",
"reference": "https://github.com/nawka12/ComfyUI-Adept-Sampler",
"files": [
"https://github.com/nawka12/ComfyUI-Adept-Sampler"
],
"install_type": "git-clone",
"description": "Advanced custom samplers and schedulers for ComfyUI, ported from the Stable Diffusion WebUI reForge extension."
},
{
"author": "zhuyanan",
"title": "Comfy-FilmSimulator",
"reference": "https://github.com/zhuyanan/Comfy-FilmSimulator",
"files": [
"https://github.com/zhuyanan/Comfy-FilmSimulator"
],
"install_type": "git-clone",
"description": "ComfyUI node: realistic, adaptive film simulation for photographic and cinematic looks."
},
{
"author": "id7238",
"title": "ComfyUI-PackedPipes",
"reference": "https://github.com/id7238/ComfyUI-PackedPipes",
"files": [
"https://github.com/id7238/ComfyUI-PackedPipes"
],
"install_type": "git-clone",
"description": "Custom nodes that organize ComfyUI workflow connections by combining multiple links into single pipeline links. (Description by CC)"
},
{
"author": "CornmeisterNL",
"title": "CornmeisterNL PowerPack",
"id": "cornmeisternl-powerpack",
"reference": "https://github.com/Cornmeister/ComfyUI-CornmeisterNL-PowerPack",
"files": [
"https://github.com/Cornmeister/ComfyUI-CornmeisterNL-PowerPack"
],
"install_type": "git-clone",
"description": "A power-user focused collection of ComfyUI nodes: LoRA management, prompt building, resolution presets, metadata-safe image saving (CivitAI-ready), and UNet model loading."
},
{
"author": "LuqP2",
"title": "Image MetaHub Save Node",
"reference": "https://github.com/LuqP2/ImageMetaHub-ComfyUI-Save",
"files": [
"https://github.com/LuqP2/ImageMetaHub-ComfyUI-Save"
],
"install_type": "git-clone",
"description": "Official companion node for Image MetaHub. Saves A1111/Civitai-compatible parameters plus extended Image MetaHub metadata (workflow JSON, SHA256 model hashes, VRAM peak/total, GPU device, generation time, and steps/sec) for reproducibility and benchmarking."
},
{
"author": "room3dev",
"title": "ComfyUI-FrameIO",
"id": "comfyui-frameio",
"reference": "https://github.com/room3dev/ComfyUI-FrameIO",
"files": [
"https://github.com/room3dev/ComfyUI-FrameIO"
],
"install_type": "git-clone",
"description": "High-performance frame IO nodes (WEBP)"
},
{
"author": "chrishill197724-gif",
"title": "ComfyUI-Wan22-GGUF-Storyboard",
"reference": "https://github.com/chrishill197724-gif/ComfyUI-Wan22-GGUF-Storyboard",
"files": [
"https://github.com/chrishill197724-gif/ComfyUI-Wan22-GGUF-Storyboard"
],
"install_type": "git-clone",
"description": "An optimized storyboard-based latent generator for Wan 2.2 with low VRAM optimization and storyboard logic for controlled video composition."
},
{
"author": "mrm987",
"title": "ComfyUI-Multi-Prompt-Generator",
"reference": "https://github.com/mrm987/ComfyUI_Multi_Prompt_Generator",
"files": [
"https://github.com/mrm987/ComfyUI_Multi_Prompt_Generator"
],
"install_type": "git-clone",
"description": "Generate multiple images from prompt list with upscaling, LUT, and real-time preview"
},
{
"author": "gushob21",
"title": "comfyui-google-genmedia-custom-nodes",
"id": "Google",
"reference": "https://github.com/GoogleCloudPlatform/comfyui-google-genmedia-custom-nodes",
"files": [
"https://github.com/GoogleCloudPlatform/comfyui-google-genmedia-custom-nodes"
],
"install_type": "git-clone",
"description": "A suite of experimental custom nodes that allows access to Google's 1P models like Veo, Imagen, Nano Banana, Gemini, Virtual-try-on, Lyria"
},
{
"author": "tj5miniop",
"title": "VRAM_Fix_Comfy",
"reference": "https://github.com/tj5miniop/VRAM_Fix_Comfy",
"files": [
"https://github.com/tj5miniop/VRAM_Fix_Comfy"
],
"install_type": "git-clone",
"description": "Potential manual fix as a custom node for ComfyUI for users seeing memory bug issues"
},
{
"author": "JuanBerta",
"title": "comfyui_ollama_vl_prompt",
"reference": "https://github.com/JuanBerta/comfyui_ollama_vl_prompt",
"files": [
"https://github.com/JuanBerta/comfyui_ollama_vl_prompt"
],
"install_type": "git-clone",
"description": "A custom ComfyUI node that uses Ollama vision-language models to generate or refine prompts from multiple input images and text."
},
{
"author": "pentarab",
"title": "ComfyUI-For-ChatterBox",
"reference": "https://github.com/pentarab/ComfyUI-For-ChatterBox",
"files": [
"https://github.com/pentarab/ComfyUI-For-ChatterBox"
],
"install_type": "git-clone",
"description": "ComfyUI custom nodes for Chatterbox TTS with multilingual support (23 languages)"
},
{
"author": "mikemojen",
"title": "ComfyUI-HappNodeSet",
"reference": "https://github.com/mikemojen/ComfyUI-HappNodeSet",
"files": [
"https://github.com/mikemojen/ComfyUI-HappNodeSet"
],
"install_type": "git-clone",
"description": "NODES: AutoCrop, ColorQuantizer, ExtractBlack, ExtractBlackAdvanced, RasterToUniformSVG, ... (10 total)"
},
{
"author": "Akkisdiary",
"title": "ComfyUI-AnyLLM",
"reference": "https://github.com/Akkisdiary/ComfyUI-AnyLLM",
"files": [
"https://github.com/Akkisdiary/ComfyUI-AnyLLM"
],
"install_type": "git-clone",
"description": "A ComfyUI custom node pack for calling LLM providers."
},
{
"author": "polymath-wtf",
"title": "ComfyUI-Polymath-Vibenodes",
"reference": "https://github.com/polymath-wtf/ComfyUI-Polymath-Vibenodes",
"files": [
"https://github.com/polymath-wtf/ComfyUI-Polymath-Vibenodes"
],
"install_type": "git-clone",
"description": "Custom ComfyUI node for auto-generating JSON prompts with dynamic placeholder templating syntax."
},
{
"author": "baijunty",
"title": "comfyui_image_embeddings",
"reference": "https://github.com/baijunty/comfyui_image_embeddings",
"files": [
"https://github.com/baijunty/comfyui_image_embeddings"
],
"install_type": "git-clone",
"description": "use comfyui to image embeddings"
},
{
"author": "anveshane",
"title": "Comfyui_StoryMem",
"reference": "https://github.com/anveshane/Comfyui_StoryMem",
"files": [
"https://github.com/anveshane/Comfyui_StoryMem"
],
"install_type": "git-clone",
"description": "Custom ComfyUI nodes for StoryMem - Multi-shot long video storytelling with memory-conditioned video diffusion models."
},
{
"author": "K3NK3",
"title": "ComfyUI-K3NKImageGrab",
"reference": "https://github.com/K3NK3/ComfyUI-K3NKImageGrab",
"files": [
"https://github.com/K3NK3/ComfyUI-K3NKImageGrab"
],
"install_type": "git-clone",
"description": "Standalone ComfyUI node to grab the last N frames from a directory as a single batch with optional frame stride, designed for video continuation workflows and temporal consistency improvement."
},
{
"author": "ytoaa",
"title": "ComfyUI-LLM-Client",
"reference": "https://github.com/ytoaa/ComfyUI-LLM-Client",
"files": [
"https://github.com/ytoaa/ComfyUI-LLM-Client"
],
"install_type": "git-clone",
"description": "ComfyUI node for LLM client integration. (Description by CC)"
},
{
"author": "I-ShadowStar",
"title": "L2UC",
"reference": "https://github.com/I-ShadowStar/L2UC",
"files": [
"https://github.com/I-ShadowStar/L2UC"
],
"install_type": "git-clone",
"description": "A conditioning node for ablated versions of Lumina2"
},
{
"author": "XuanYu-github",
"title": "comfyui-PlyPreview",
"reference": "https://github.com/XuanYu-github/comfyui-PlyPreview",
"files": [
"https://github.com/XuanYu-github/comfyui-PlyPreview"
],
"install_type": "git-clone",
"description": "Gaussian Splat PLY loading and preview nodes with built-in gsplat.js frontend viewer. (Description by CC)"
},
{
"author": "maxious",
"title": "comfyui-dap",
"reference": "https://github.com/maxious/comfyui-dap",
"files": [
"https://github.com/maxious/comfyui-dap"
],
"install_type": "git-clone",
"description": "A ComfyUI extension for the Insta360 DAP (Depth Any Panorama) model providing high-quality depth estimation optimized for panoramic (equirectangular) images."
},
{
"author": "LaVie024",
"title": "comfyui-lopi999-llm",
"reference": "https://github.com/LaVie024/comfyui-lopi999-llm",
"files": [
"https://github.com/LaVie024/comfyui-lopi999-llm"
],
"install_type": "git-clone",
"description": "LLM nodes for ComfyUI including a Canary-Qwen-2.5B audio transcription node. (Description by CC)"
},
{
"author": "elgalardi",
"title": "comfyui-clip-prompt-splitter",
"reference": "https://github.com/elgalardi/comfyui-clip-prompt-splitter",
"files": [
"https://github.com/elgalardi/comfyui-clip-prompt-splitter"
],
"install_type": "git-clone",
"description": "Custom node for ComfyUI that splits a multiline prompt into 5 independent CLIP conditionings."
},
{
"author": "NexusEast",
"title": "ComfyUI_ContactSheet_Auto",
"reference": "https://github.com/NexusEast/ComfyUI_ContactSheet_Auto",
"files": [
"https://github.com/NexusEast/ComfyUI_ContactSheet_Auto"
],
"install_type": "git-clone",
"description": "A lightweight ComfyUI extension designed to automate the creation of video contact sheets (preview grids)."
},
{
"author": "daveand",
"title": "ComfyUI-daveand-utils",
"reference": "https://github.com/daveand/ComfyUI-daveand-utils",
"files": [
"https://github.com/daveand/ComfyUI-daveand-utils"
],
"install_type": "git-clone",
"description": "Utility nodes including ModelConfigSelector for saving checkpoint configurations and managing manual sampler overrides. (Description by CC)"
},
{
"author": "NewBieAI-Lab",
"title": "ComfyUI-MltoXml",
"reference": "https://github.com/NewBieAI-Lab/ComfyUI-MltoXml",
"files": [
"https://github.com/NewBieAI-Lab/ComfyUI-MltoXml"
],
"install_type": "git-clone",
"description": "Simple node to convert ML or XML format data. (Description by CC)"
},
{
"author": "Hahihula",
"title": "ComfyUI-batching-nodes",
"reference": "https://github.com/Hahihula/ComfyUI-batching-nodes",
"files": [
"https://github.com/Hahihula/ComfyUI-batching-nodes"
],
"install_type": "git-clone",
"description": "A collection of custom nodes for ComfyUI that enable powerful batch processing capabilities for both prompts and images."
},

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -11,7 +11,10 @@ import threading
import re
import shutil
import git
import glob
import json
from datetime import datetime
from contextlib import contextmanager
from server import PromptServer
import manager_core as core
@ -762,6 +765,86 @@ async def fetch_updates(request):
except:
traceback.print_exc()
return web.Response(status=400)
@routes.get("/customnode/get_node_types_in_workflows")
async def get_node_types_in_workflows(request):
try:
# get our username from the request header
user_id = PromptServer.instance.user_manager.get_request_user_id(request)
# get the base workflow directory (TODO: figure out if non-standard directories are possible, and how to find them)
workflow_files_base_path = os.path.abspath(os.path.join(folder_paths.get_user_directory(), user_id, "workflows"))
logging.debug(f"workflows base path: {workflow_files_base_path}")
# workflow directory doesn't actually exist, return 204 (No Content)
if not os.path.isdir(workflow_files_base_path):
logging.debug("workflows base path doesn't exist - nothing to do...")
return web.Response(status=204)
# get all JSON files under the workflow directory
workflow_file_relative_paths: list[str] = glob.glob(pathname="**/*.json", root_dir=workflow_files_base_path, recursive=True)
logging.debug(f"found the following workflows: {workflow_file_relative_paths}")
# set up our list of workflow/node-lists
workflow_node_mappings: list[dict[str, str | list[str]]] = []
# iterate over each found JSON file
for workflow_file_path in workflow_file_relative_paths:
try:
workflow_file_absolute_path = os.path.abspath(os.path.join(workflow_files_base_path, workflow_file_path))
logging.debug(f"starting work on {workflow_file_absolute_path}")
# load the JSON file
workflow_file_data = json.load(open(workflow_file_absolute_path, "r"))
# make sure there's a nodes key (otherwise this might not actually be a workflow file)
if "nodes" not in workflow_file_data:
logging.warning(f"{workflow_file_path} has no 'nodes' key (possibly invalid?) - skipping...")
# skip to next file
continue
# now this looks like a valid file, so let's get to work
new_mapping = {"workflow_file_name": workflow_file_path}
# we can't use an actual set, because you can't use dicts as set members
node_set = []
# iterate over each node in the workflow
for node in workflow_file_data["nodes"]:
if "id" not in node:
logging.warning(f"Found a node with no ID - possibly corrupt/invalid workflow?")
continue
# if there's no type, throw a warning
if "type" not in node:
logging.warning(f"Node type not found in {workflow_file_path} for node ID {node['id']}")
# skip to next node
continue
node_data_to_return = {"type": node["type"]}
if "properties" not in node:
logging.warning(f"Node ${node['id']} has no properties field - can't determine cnr_id")
else:
for property_key in ["cnr_id", "ver"]:
if property_key in node["properties"]:
node_data_to_return[property_key] = node["properties"][property_key]
# add it to the list for this workflow
if not node_data_to_return in node_set:
node_set.append(node_data_to_return)
# annoyingly, Python can't serialize sets to JSON
new_mapping["node_types"] = list(node_set)
workflow_node_mappings.append(new_mapping)
except Exception as e:
logging.warning(f"Couldn't open {workflow_file_path}: {e}")
return web.json_response(workflow_node_mappings, content_type='application/json')
except:
traceback.print_exc()
return web.Response(status=500)
@routes.get("/manager/queue/update_all")

View File

@ -9,6 +9,7 @@ This directory contains the JavaScript frontend implementation for ComfyUI-Manag
- **model-manager.js**: Handles the model management interface for downloading and organizing AI models.
- **components-manager.js**: Manages reusable workflow components system.
- **snapshot.js**: Implements the snapshot system for backing up and restoring installations.
- **node-usage-analyzer.js**: Implements the UI for analyzing node usage in workflows.
## Sharing Components
@ -46,5 +47,6 @@ The frontend follows a modular component-based architecture:
CSS files are included for specific components:
- **custom-nodes-manager.css**: Styling for the node management UI
- **model-manager.css**: Styling for the model management UI
- **node-usage-analyzer.css**: Styling for the node usage analyzer UI
This frontend implementation provides a comprehensive yet user-friendly interface for managing the ComfyUI ecosystem.

View File

@ -18,6 +18,7 @@ import {
} from "./common.js";
import { ComponentBuilderDialog, getPureName, load_components, set_component_policy } from "./components-manager.js";
import { CustomNodesManager } from "./custom-nodes-manager.js";
import { NodeUsageAnalyzer } from "./node-usage-analyzer.js";
import { ModelManager } from "./model-manager.js";
import { SnapshotManager } from "./snapshot.js";
import { buildGuiFrame, createSettingsCombo } from "./comfyui-gui-builder.js";
@ -909,6 +910,17 @@ class ManagerMenuDialog extends ComfyDialog {
CustomNodesManager.instance.show(CustomNodesManager.ShowMode.IN_WORKFLOW);
}
}),
$el("button.cm-button", {
type: "button",
textContent: "Node Usage Analyzer",
onclick:
() => {
if(!NodeUsageAnalyzer.instance) {
NodeUsageAnalyzer.instance = new NodeUsageAnalyzer(app, self);
}
NodeUsageAnalyzer.instance.show(NodeUsageAnalyzer.SortMode.BY_PACKAGE);
}
}),
$el("div", {}, []),
$el("button.p-button.p-component.cm-button", {

View File

@ -122,9 +122,9 @@ export async function customConfirm(message) {
let res = await
window['app'].extensionManager.dialog
.confirm({
title: 'Confirm',
message: message
});
title: 'Confirm',
message: message
});
return res;
}
@ -164,9 +164,9 @@ export async function customPrompt(title, message) {
let res = await
window['app'].extensionManager.dialog
.prompt({
title: title,
message: message
});
title: title,
message: message
});
return res;
}
@ -667,4 +667,449 @@ function initTooltip () {
document.body.addEventListener('mouseleave', mouseleaveHandler, true);
}
export async function uninstallNodes(nodeList, options = {}) {
const {
title = `${nodeList.length} custom nodes`,
onProgress = () => {},
onError = () => {},
onSuccess = () => {},
channel = 'default',
mode = 'default'
} = options;
// Check if queue is busy
let stats = await api.fetchApi('/manager/queue/status');
stats = await stats.json();
if (stats.is_processing) {
customAlert(`[ComfyUI-Manager] There are already tasks in progress. Please try again after it is completed. (${stats.done_count}/${stats.total_count})`);
return { success: false, error: 'Queue is busy' };
}
// Confirmation dialog for uninstall
const confirmed = await customConfirm(`Are you sure uninstall ${title}?`);
if (!confirmed) {
return { success: false, error: 'User cancelled' };
}
let errorMsg = "";
let target_items = [];
await api.fetchApi('/manager/queue/reset');
for (const nodeItem of nodeList) {
target_items.push(nodeItem);
onProgress(`Uninstall ${nodeItem.title || nodeItem.name} ...`);
const data = nodeItem.originalData || nodeItem;
data.channel = channel;
data.mode = mode;
data.ui_id = nodeItem.hash || md5(nodeItem.name || nodeItem.title);
const res = await api.fetchApi(`/manager/queue/uninstall`, {
method: 'POST',
body: JSON.stringify(data)
});
if (res.status != 200) {
errorMsg = `'${nodeItem.title || nodeItem.name}': `;
if (res.status == 403) {
errorMsg += `This action is not allowed with this security level configuration.\n`;
} else if (res.status == 404) {
errorMsg += `With the current security level configuration, only custom nodes from the <B>"default channel"</B> can be uninstalled.\n`;
} else {
errorMsg += await res.text() + '\n';
}
break;
}
}
if (errorMsg) {
onError(errorMsg);
show_message("[Uninstall Errors]\n" + errorMsg);
return { success: false, error: errorMsg, targets: target_items };
} else {
await api.fetchApi('/manager/queue/start');
onSuccess(target_items);
showTerminal();
return { success: true, targets: target_items };
}
}
// ===========================================================================================
// Workflow Utilities Consolidation
export async function getWorkflowNodeTypes() {
try {
const res = await fetchData('/customnode/get_node_types_in_workflows');
if (res.status === 200) {
return { success: true, data: res.data };
} else if (res.status === 204) {
// No workflows found - return empty list
return { success: true, data: [] };
} else {
return { success: false, error: res.error };
}
} catch (error) {
return { success: false, error: error };
}
}
export function findPackageByCnrId(cnrId, nodePackages, installedOnly = true) {
if (!cnrId || !nodePackages) {
return null;
}
// Tier 1: Direct key match
if (nodePackages[cnrId]) {
const pack = nodePackages[cnrId];
if (!installedOnly || pack.state !== "not-installed") {
return { key: cnrId, pack: pack };
}
}
// Tier 2: Case-insensitive match
const cnrIdLower = cnrId.toLowerCase();
for (const packKey of Object.keys(nodePackages)) {
if (packKey.toLowerCase() === cnrIdLower) {
const pack = nodePackages[packKey];
if (!installedOnly || pack.state !== "not-installed") {
return { key: packKey, pack: pack };
}
}
}
// Tier 3: URL/reference contains match
for (const packKey of Object.keys(nodePackages)) {
const pack = nodePackages[packKey];
// Skip non-installed packages if installedOnly is true
if (installedOnly && pack.state === "not-installed") {
continue;
}
// Check if reference URL contains cnr_id
if (pack.reference && pack.reference.includes(cnrId)) {
return { key: packKey, pack: pack };
}
// Check if any file URL contains cnr_id
if (pack.files && Array.isArray(pack.files)) {
for (const fileUrl of pack.files) {
if (fileUrl.includes(cnrId)) {
return { key: packKey, pack: pack };
}
}
}
}
return null;
}
export async function analyzeWorkflowUsage(nodePackages) {
const result = await getWorkflowNodeTypes();
if (!result.success) {
return { success: false, error: result.error };
}
const workflowNodeList = result.data;
const usageMap = new Map();
const workflowDetailsMap = new Map();
if (workflowNodeList && Array.isArray(workflowNodeList)) {
const cnrIdCounts = new Map();
const cnrIdToWorkflows = new Map();
// Process each workflow
workflowNodeList.forEach((workflowObj, workflowIndex) => {
if (workflowObj.node_types && Array.isArray(workflowObj.node_types)) {
const workflowCnrIds = new Set();
// Get workflow filename
const workflowFilename = workflowObj.workflow_file_name ||
workflowObj.filename ||
workflowObj.file ||
workflowObj.name ||
workflowObj.path ||
`Workflow ${workflowIndex + 1}`;
// Count nodes per cnr_id in this workflow
const workflowCnrIdCounts = new Map();
workflowObj.node_types.forEach(nodeTypeObj => {
const cnrId = nodeTypeObj.cnr_id;
if (cnrId && cnrId !== "comfy-core") {
// Track unique cnr_ids per workflow
workflowCnrIds.add(cnrId);
// Count nodes per cnr_id in this specific workflow
const workflowNodeCount = workflowCnrIdCounts.get(cnrId) || 0;
workflowCnrIdCounts.set(cnrId, workflowNodeCount + 1);
}
});
// Record workflow details for each unique cnr_id found in this workflow
workflowCnrIds.forEach(cnrId => {
// Count occurrences of this cnr_id across all workflows
const currentCount = cnrIdCounts.get(cnrId) || 0;
cnrIdCounts.set(cnrId, currentCount + 1);
// Track workflow details
if (!cnrIdToWorkflows.has(cnrId)) {
cnrIdToWorkflows.set(cnrId, []);
}
cnrIdToWorkflows.get(cnrId).push({
filename: workflowFilename,
nodeCount: workflowCnrIdCounts.get(cnrId) || 0
});
});
}
});
// Map cnr_id to installed packages with workflow details
cnrIdCounts.forEach((count, cnrId) => {
const workflowDetails = cnrIdToWorkflows.get(cnrId) || [];
const foundPackage = findPackageByCnrId(cnrId, nodePackages, true);
if (foundPackage) {
usageMap.set(foundPackage.key, count);
workflowDetailsMap.set(foundPackage.key, workflowDetails);
}
});
}
return {
success: true,
usageMap: usageMap,
workflowDetailsMap: workflowDetailsMap
};
}
// Size formatting utilities - consolidated from model-manager.js and node-usage-analyzer.js
export function formatSize(v) {
const base = 1000;
const units = ['', 'K', 'M', 'G', 'T', 'P'];
const space = '';
const postfix = 'B';
if (v <= 0) {
return `0${space}${postfix}`;
}
for (let i = 0, l = units.length; i < l; i++) {
const min = Math.pow(base, i);
const max = Math.pow(base, i + 1);
if (v > min && v <= max) {
const unit = units[i];
if (unit) {
const n = v / min;
const nl = n.toString().split('.')[0].length;
const fl = Math.max(3 - nl, 1);
v = n.toFixed(fl);
}
v = v + space + unit + postfix;
break;
}
}
return v;
}
// for size sort
export function sizeToBytes(v) {
if (typeof v === "number") {
return v;
}
if (typeof v === "string") {
const n = parseFloat(v);
const unit = v.replace(/[0-9.B]+/g, "").trim().toUpperCase();
if (unit === "K") {
return n * 1000;
}
if (unit === "M") {
return n * 1000 * 1000;
}
if (unit === "G") {
return n * 1000 * 1000 * 1000;
}
if (unit === "T") {
return n * 1000 * 1000 * 1000 * 1000;
}
}
return v;
}
// Flyover component - consolidated from custom-nodes-manager.js and node-usage-analyzer.js
export function createFlyover(container, options = {}) {
const {
enableHover = false,
hoverHandler = null,
context = null
} = options;
const $flyover = document.createElement("div");
$flyover.className = "cn-flyover";
$flyover.innerHTML = `<div class="cn-flyover-header">
<div class="cn-flyover-close">${icons.arrowRight}</div>
<div class="cn-flyover-title"></div>
<div class="cn-flyover-close">${icons.close}</div>
</div>
<div class="cn-flyover-body"></div>`
container.appendChild($flyover);
const $flyoverTitle = $flyover.querySelector(".cn-flyover-title");
const $flyoverBody = $flyover.querySelector(".cn-flyover-body");
let width = '50%';
let visible = false;
let timeHide;
const closeHandler = (e) => {
if ($flyover === e.target || $flyover.contains(e.target)) {
return;
}
clearTimeout(timeHide);
timeHide = setTimeout(() => {
flyover.hide();
}, 100);
}
const displayHandler = () => {
if (visible) {
$flyover.classList.remove("cn-slide-in-right");
} else {
$flyover.classList.remove("cn-slide-out-right");
$flyover.style.width = '0px';
$flyover.style.display = "none";
}
}
const flyover = {
show: (titleHtml, bodyHtml) => {
clearTimeout(timeHide);
if (context && context.element) {
context.element.removeEventListener("click", closeHandler);
}
$flyoverTitle.innerHTML = titleHtml;
$flyoverBody.innerHTML = bodyHtml;
$flyover.style.display = "block";
$flyover.style.width = width;
if(!visible) {
$flyover.classList.add("cn-slide-in-right");
}
visible = true;
setTimeout(() => {
if (context && context.element) {
context.element.addEventListener("click", closeHandler);
}
}, 100);
},
hide: (now) => {
visible = false;
if (context && context.element) {
context.element.removeEventListener("click", closeHandler);
}
if(now) {
displayHandler();
return;
}
$flyover.classList.add("cn-slide-out-right");
}
}
$flyover.addEventListener("animationend", (e) => {
displayHandler();
});
// Add hover handlers if enabled
if (enableHover && hoverHandler) {
$flyover.addEventListener("mouseenter", hoverHandler, true);
$flyover.addEventListener("mouseleave", hoverHandler, true);
}
$flyover.addEventListener("click", (e) => {
if(e.target.classList.contains("cn-flyover-close")) {
flyover.hide();
return;
}
// Forward other click events to the provided handler or context
if (context && context.handleFlyoverClick) {
context.handleFlyoverClick(e);
}
});
return flyover;
}
// Shared UI State Methods - consolidated from multiple managers
export function createUIStateManager(element, selectors) {
return {
showSelection: (msg) => {
const el = element.querySelector(selectors.selection);
if (el) el.innerHTML = msg;
},
showError: (err) => {
const el = element.querySelector(selectors.message);
if (el) {
const msg = err ? `<font color="red">${err}</font>` : "";
el.innerHTML = msg;
}
},
showMessage: (msg, color) => {
const el = element.querySelector(selectors.message);
if (el) {
if (color) {
msg = `<font color="${color}">${msg}</font>`;
}
el.innerHTML = msg;
}
},
showStatus: (msg, color) => {
const el = element.querySelector(selectors.status);
if (el) {
if (color) {
msg = `<font color="${color}">${msg}</font>`;
}
el.innerHTML = msg;
}
},
showLoading: (grid) => {
if (grid) {
grid.showLoading();
grid.showMask({
opacity: 0.05
});
}
},
hideLoading: (grid) => {
if (grid) {
grid.hideLoading();
grid.hideMask();
}
},
showRefresh: () => {
const el = element.querySelector(selectors.refresh);
if (el) el.style.display = "block";
},
showStop: () => {
const el = element.querySelector(selectors.stop);
if (el) el.style.display = "block";
},
hideStop: () => {
const el = element.querySelector(selectors.stop);
if (el) el.style.display = "none";
}
};
}
initTooltip();

View File

@ -8,7 +8,7 @@ import {
fetchData, md5, icons, show_message, customConfirm, customAlert, customPrompt,
sanitizeHTML, infoToast, showTerminal, setNeedRestart,
storeColumnWidth, restoreColumnWidth, getTimeAgo, copyText, loadCss,
showPopover, hidePopover, handle403Response
showPopover, hidePopover, getWorkflowNodeTypes, findPackageByCnrId, analyzeWorkflowUsage, createFlyover
} from "./common.js";
// https://cenfun.github.io/turbogrid/api.html
@ -42,6 +42,8 @@ const ShowMode = {
FAVORITES: "Favorites",
ALTERNATIVES: "Alternatives",
IN_WORKFLOW: "In Workflow",
USED_IN_ANY_WORKFLOW: "Used In Any Workflow",
NOT_USED_IN_ANY_WORKFLOW: "Installed and Unused",
};
export class CustomNodesManager {
@ -271,6 +273,14 @@ export class CustomNodesManager {
label: "In Workflow",
value: ShowMode.IN_WORKFLOW,
hasData: false
}, {
label: "Used In Any Workflow",
value: ShowMode.USED_IN_ANY_WORKFLOW,
hasData: false
}, {
label: "Installed and Unused",
value: ShowMode.NOT_USED_IN_ANY_WORKFLOW,
hasData: false
}, {
label: "Missing",
value: ShowMode.MISSING,
@ -521,7 +531,11 @@ export class CustomNodesManager {
const grid = new TG.Grid(container);
this.grid = grid;
this.flyover = this.createFlyover(container);
this.flyover = createFlyover(container, {
enableHover: true,
hoverHandler: this.handleFlyoverHover.bind(this),
context: this
});
let prevViewRowsLength = -1;
grid.bind('onUpdated', (e, d) => {
@ -1063,143 +1077,63 @@ export class CustomNodesManager {
hidePopover();
}
createFlyover(container) {
const $flyover = document.createElement("div");
$flyover.className = "cn-flyover";
$flyover.innerHTML = `<div class="cn-flyover-header">
<div class="cn-flyover-close">${icons.arrowRight}</div>
<div class="cn-flyover-title"></div>
<div class="cn-flyover-close">${icons.close}</div>
</div>
<div class="cn-flyover-body"></div>`
container.appendChild($flyover);
const $flyoverTitle = $flyover.querySelector(".cn-flyover-title");
const $flyoverBody = $flyover.querySelector(".cn-flyover-body");
let width = '50%';
let visible = false;
let timeHide;
const closeHandler = (e) => {
if ($flyover === e.target || $flyover.contains(e.target)) {
return;
}
clearTimeout(timeHide);
timeHide = setTimeout(() => {
flyover.hide();
}, 100);
}
const hoverHandler = (e) => {
if(e.type === "mouseenter") {
if(e.target.classList.contains("cn-nodes-name")) {
this.showNodePreview(e.target);
}
return;
}
this.hideNodePreview();
}
const displayHandler = () => {
if (visible) {
$flyover.classList.remove("cn-slide-in-right");
} else {
$flyover.classList.remove("cn-slide-out-right");
$flyover.style.width = '0px';
$flyover.style.display = "none";
}
}
const flyover = {
show: (titleHtml, bodyHtml) => {
clearTimeout(timeHide);
this.element.removeEventListener("click", closeHandler);
$flyoverTitle.innerHTML = titleHtml;
$flyoverBody.innerHTML = bodyHtml;
$flyover.style.display = "block";
$flyover.style.width = width;
if(!visible) {
$flyover.classList.add("cn-slide-in-right");
}
visible = true;
setTimeout(() => {
this.element.addEventListener("click", closeHandler);
}, 100);
},
hide: (now) => {
visible = false;
this.element.removeEventListener("click", closeHandler);
if(now) {
displayHandler();
return;
}
$flyover.classList.add("cn-slide-out-right");
}
}
$flyover.addEventListener("animationend", (e) => {
displayHandler();
});
$flyover.addEventListener("mouseenter", hoverHandler, true);
$flyover.addEventListener("mouseleave", hoverHandler, true);
$flyover.addEventListener("click", (e) => {
handleFlyoverHover(e) {
if(e.type === "mouseenter") {
if(e.target.classList.contains("cn-nodes-name")) {
const nodeName = e.target.innerText;
const nodeItem = this.nodeMap[nodeName];
if (!nodeItem) {
copyText(nodeName).then((res) => {
if (res) {
e.target.setAttribute("action", "Copied");
e.target.classList.add("action");
setTimeout(() => {
e.target.classList.remove("action");
e.target.removeAttribute("action");
}, 1000);
}
});
return;
}
this.showNodePreview(e.target);
}
return;
}
this.hideNodePreview();
}
const [x, y, w, h] = app.canvas.ds.visible_area;
const dpi = Math.max(window.devicePixelRatio ?? 1, 1);
const node = window.LiteGraph?.createNode(
nodeItem.name,
nodeItem.display_name,
{
pos: [x + (w-300) / dpi / 2, y]
handleFlyoverClick(e) {
if(e.target.classList.contains("cn-nodes-name")) {
const nodeName = e.target.innerText;
const nodeItem = this.nodeMap[nodeName];
if (!nodeItem) {
copyText(nodeName).then((res) => {
if (res) {
e.target.setAttribute("action", "Copied");
e.target.classList.add("action");
setTimeout(() => {
e.target.classList.remove("action");
e.target.removeAttribute("action");
}, 1000);
}
);
if (node) {
app.graph.add(node);
e.target.setAttribute("action", "Added to Workflow");
e.target.classList.add("action");
setTimeout(() => {
e.target.classList.remove("action");
e.target.removeAttribute("action");
}, 1000);
}
});
return;
}
if(e.target.classList.contains("cn-nodes-pack")) {
const hash = e.target.getAttribute("hash");
const rowItem = this.grid.getRowItemBy("hash", hash);
//console.log(rowItem);
this.grid.scrollToRow(rowItem);
this.addHighlight(rowItem);
return;
}
if(e.target.classList.contains("cn-flyover-close")) {
flyover.hide();
return;
}
});
return flyover;
const [x, y, w, h] = app.canvas.ds.visible_area;
const dpi = Math.max(window.devicePixelRatio ?? 1, 1);
const node = window.LiteGraph?.createNode(
nodeItem.name,
nodeItem.display_name,
{
pos: [x + (w-300) / dpi / 2, y]
}
);
if (node) {
app.graph.add(node);
e.target.setAttribute("action", "Added to Workflow");
e.target.classList.add("action");
setTimeout(() => {
e.target.classList.remove("action");
e.target.removeAttribute("action");
}, 1000);
}
return;
}
if(e.target.classList.contains("cn-nodes-pack")) {
const hash = e.target.getAttribute("hash");
const rowItem = this.grid.getRowItemBy("hash", hash);
//console.log(rowItem);
this.grid.scrollToRow(rowItem);
this.addHighlight(rowItem);
return;
}
}
showNodes(d) {
@ -1874,7 +1808,10 @@ export class CustomNodesManager {
for(let k in allUsedNodes) {
var item;
if(allUsedNodes[k].properties.cnr_id) {
item = this.custom_nodes[allUsedNodes[k].properties.cnr_id];
const foundPackage = findPackageByCnrId(allUsedNodes[k].properties.cnr_id, this.custom_nodes, false);
if (foundPackage) {
item = foundPackage.pack;
}
}
else if(allUsedNodes[k].properties.aux_id) {
item = aux_id_to_pack[allUsedNodes[k].properties.aux_id];
@ -1921,6 +1858,48 @@ export class CustomNodesManager {
return hashMap;
}
async getUsedInAnyWorkflow() {
this.showStatus(`Loading workflow usage analysis ...`);
const result = await analyzeWorkflowUsage(this.custom_nodes);
if (!result.success) {
this.showError(`Failed to get workflow data: ${result.error}`);
return {};
}
const hashMap = {};
// Convert usage map keys to hash map
result.usageMap.forEach((count, packageKey) => {
const pack = this.custom_nodes[packageKey];
if (pack && pack.hash) {
hashMap[pack.hash] = true;
}
});
return hashMap;
}
async getNotUsedInAnyWorkflow() {
this.showStatus(`Loading workflow usage analysis ...`);
// Get the used packages first using common utility
const usedHashMap = await this.getUsedInAnyWorkflow();
const notUsedHashMap = {};
// Find all installed packages that are NOT in the used list
for(let k in this.custom_nodes) {
let nodepack = this.custom_nodes[k];
// Only consider installed packages
if (nodepack.state !== "not-installed" && !usedHashMap[nodepack.hash]) {
notUsedHashMap[nodepack.hash] = true;
}
}
return notUsedHashMap;
}
async loadData(show_mode = ShowMode.NORMAL) {
const isElectron = 'electronAPI' in window;
@ -1990,6 +1969,10 @@ export class CustomNodesManager {
hashMap = await this.getFavorites();
} else if(this.show_mode == ShowMode.IN_WORKFLOW) {
hashMap = await this.getNodepackInWorkflow();
} else if(this.show_mode == ShowMode.USED_IN_ANY_WORKFLOW) {
hashMap = await this.getUsedInAnyWorkflow();
} else if(this.show_mode == ShowMode.NOT_USED_IN_ANY_WORKFLOW) {
hashMap = await this.getNotUsedInAnyWorkflow();
}
filterItem.hashMap = hashMap;

View File

@ -3,7 +3,7 @@ import { $el } from "../../scripts/ui.js";
import {
manager_instance, rebootAPI,
fetchData, md5, icons, show_message, customAlert, infoToast, showTerminal,
storeColumnWidth, restoreColumnWidth, loadCss, handle403Response
storeColumnWidth, restoreColumnWidth, loadCss, formatSize, sizeToBytes
} from "./common.js";
import { api } from "../../scripts/api.js";
@ -359,7 +359,7 @@ export class ModelManager {
width: 100,
formatter: (size) => {
if (typeof size === "number") {
return this.formatSize(size);
return formatSize(size);
}
return size;
}
@ -582,7 +582,7 @@ export class ModelManager {
models.forEach((item, i) => {
const { type, base, name, reference, installed } = item;
item.originalData = JSON.parse(JSON.stringify(item));
item.size = this.sizeToBytes(item.size);
item.size = sizeToBytes(item.size);
item.hash = md5(name + reference);
item.id = i + 1;
@ -659,7 +659,6 @@ export class ModelManager {
const { models } = res.data;
this.modelList = this.getModelList(models);
// console.log("models", this.modelList);
this.updateFilter();
@ -671,56 +670,6 @@ export class ModelManager {
// ===========================================================================================
formatSize(v) {
const base = 1000;
const units = ['', 'K', 'M', 'G', 'T', 'P'];
const space = '';
const postfix = 'B';
if (v <= 0) {
return `0${space}${postfix}`;
}
for (let i = 0, l = units.length; i < l; i++) {
const min = Math.pow(base, i);
const max = Math.pow(base, i + 1);
if (v > min && v <= max) {
const unit = units[i];
if (unit) {
const n = v / min;
const nl = n.toString().split('.')[0].length;
const fl = Math.max(3 - nl, 1);
v = n.toFixed(fl);
}
v = v + space + unit + postfix;
break;
}
}
return v;
}
// for size sort
sizeToBytes(v) {
if (typeof v === "number") {
return v;
}
if (typeof v === "string") {
const n = parseFloat(v);
const unit = v.replace(/[0-9.B]+/g, "").trim().toUpperCase();
if (unit === "K") {
return n * 1000;
}
if (unit === "M") {
return n * 1000 * 1000;
}
if (unit === "G") {
return n * 1000 * 1000 * 1000;
}
if (unit === "T") {
return n * 1000 * 1000 * 1000 * 1000;
}
}
return v;
}
showSelection(msg) {
this.element.querySelector(".cmm-manager-selection").innerHTML = msg;
}

699
js/node-usage-analyzer.css Normal file
View File

@ -0,0 +1,699 @@
.nu-manager {
--grid-font: -apple-system, BlinkMacSystemFont, "Segue UI", "Noto Sans", Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji";
z-index: 1099;
width: 80%;
height: 80%;
display: flex;
flex-direction: column;
gap: 10px;
color: var(--fg-color);
font-family: arial, sans-serif;
text-underline-offset: 3px;
outline: none;
}
.nu-manager .nu-flex-auto {
flex: auto;
}
.nu-manager button {
font-size: 16px;
color: var(--input-text);
background-color: var(--comfy-input-bg);
border-radius: 8px;
border-color: var(--border-color);
border-style: solid;
margin: 0;
padding: 4px 8px;
min-width: 100px;
}
.nu-manager button:disabled,
.nu-manager input:disabled,
.nu-manager select:disabled {
color: gray;
}
.nu-manager button:disabled {
background-color: var(--comfy-input-bg);
}
.nu-manager .nu-manager-restart {
display: none;
background-color: #500000;
color: white;
}
.nu-manager .nu-manager-stop {
display: none;
background-color: #500000;
color: white;
}
.nu-manager .nu-manager-back {
align-items: center;
justify-content: center;
}
.arrow-icon {
height: 1em;
width: 1em;
margin-right: 5px;
transform: translateY(2px);
}
.cn-icon {
display: block;
width: 16px;
height: 16px;
}
.cn-icon svg {
display: block;
margin: 0;
pointer-events: none;
}
.nu-manager-header {
display: flex;
flex-wrap: wrap;
gap: 5px;
align-items: center;
padding: 0 5px;
}
.nu-manager-header label {
display: flex;
gap: 5px;
align-items: center;
}
.nu-manager-filter {
height: 28px;
line-height: 28px;
}
.nu-manager-keywords {
height: 28px;
line-height: 28px;
padding: 0 5px 0 26px;
background-size: 16px;
background-position: 5px center;
background-repeat: no-repeat;
background-image: url("data:image/svg+xml;charset=utf8,%3Csvg%20viewBox%3D%220%200%2024%2024%22%20width%3D%22100%25%22%20height%3D%22100%25%22%20pointer-events%3D%22none%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%3E%3Cpath%20fill%3D%22none%22%20stroke%3D%22%23888%22%20stroke-linecap%3D%22round%22%20stroke-linejoin%3D%22round%22%20stroke-width%3D%222%22%20d%3D%22m21%2021-4.486-4.494M19%2010.5a8.5%208.5%200%201%201-17%200%208.5%208.5%200%200%201%2017%200%22%2F%3E%3C%2Fsvg%3E");
}
.nu-manager-status {
padding-left: 10px;
}
.nu-manager-grid {
flex: auto;
border: 1px solid var(--border-color);
overflow: hidden;
position: relative;
}
.nu-manager-selection {
display: flex;
flex-wrap: wrap;
gap: 10px;
align-items: center;
}
.nu-manager-message {
position: relative;
}
.nu-manager-footer {
display: flex;
flex-wrap: wrap;
gap: 10px;
align-items: center;
}
.nu-manager-grid .tg-turbogrid {
font-family: var(--grid-font);
font-size: 15px;
background: var(--bg-color);
}
.nu-manager-grid .tg-turbogrid .tg-highlight::after {
position: absolute;
top: 0;
left: 0;
content: "";
display: block;
width: 100%;
height: 100%;
box-sizing: border-box;
background-color: #80bdff11;
pointer-events: none;
}
.nu-manager-grid .nu-pack-name a {
color: skyblue;
text-decoration: none;
word-break: break-word;
}
.nu-manager-grid .cn-pack-desc a {
color: #5555FF;
font-weight: bold;
text-decoration: none;
}
.nu-manager-grid .tg-cell a:hover {
text-decoration: underline;
}
.nu-manager-grid .cn-pack-version {
line-height: 100%;
display: flex;
flex-direction: column;
justify-content: center;
height: 100%;
gap: 5px;
}
.nu-manager-grid .cn-pack-nodes {
line-height: 100%;
display: flex;
flex-direction: column;
justify-content: center;
gap: 5px;
cursor: pointer;
height: 100%;
}
.nu-manager-grid .cn-pack-nodes:hover {
text-decoration: underline;
}
.nu-manager-grid .cn-pack-conflicts {
color: orange;
}
.cn-popover {
position: fixed;
z-index: 10000;
padding: 20px;
color: #1e1e1e;
filter: drop-shadow(1px 5px 5px rgb(0 0 0 / 30%));
overflow: hidden;
}
.cn-flyover {
position: absolute;
top: 0;
right: 0;
z-index: 1000;
display: none;
width: 50%;
height: 100%;
background-color: var(--comfy-menu-bg);
animation-duration: 0.2s;
animation-fill-mode: both;
flex-direction: column;
}
.cn-flyover::before {
position: absolute;
top: 0;
content: "";
z-index: 10;
display: block;
width: 10px;
height: 100%;
pointer-events: none;
left: -10px;
background-image: linear-gradient(to left, rgb(0 0 0 / 20%), rgb(0 0 0 / 0%));
}
.cn-flyover-header {
height: 45px;
display: flex;
align-items: center;
gap: 5px;
border-bottom: 1px solid var(--border-color);
}
.cn-flyover-close {
display: flex;
align-items: center;
padding: 0 10px;
justify-content: center;
cursor: pointer;
opacity: 0.8;
height: 100%;
}
.cn-flyover-close:hover {
opacity: 1;
}
.cn-flyover-close svg {
display: block;
margin: 0;
pointer-events: none;
width: 20px;
height: 20px;
}
.cn-flyover-title {
display: flex;
align-items: center;
font-weight: bold;
gap: 10px;
flex: auto;
}
.cn-flyover-body {
height: calc(100% - 45px);
overflow-y: auto;
position: relative;
background-color: var(--comfy-menu-secondary-bg);
}
@keyframes cn-slide-in-right {
from {
visibility: visible;
transform: translate3d(100%, 0, 0);
}
to {
transform: translate3d(0, 0, 0);
}
}
.cn-slide-in-right {
animation-name: cn-slide-in-right;
}
@keyframes cn-slide-out-right {
from {
transform: translate3d(0, 0, 0);
}
to {
visibility: hidden;
transform: translate3d(100%, 0, 0);
}
}
.cn-slide-out-right {
animation-name: cn-slide-out-right;
}
.cn-nodes-list {
width: 100%;
}
.cn-nodes-row {
display: flex;
align-items: center;
gap: 10px;
}
.cn-nodes-row:nth-child(odd) {
background-color: rgb(0 0 0 / 5%);
}
.cn-nodes-row:hover {
background-color: rgb(0 0 0 / 10%);
}
.cn-nodes-sn {
text-align: right;
min-width: 35px;
color: var(--drag-text);
flex-shrink: 0;
font-size: 12px;
padding: 8px 5px;
}
.cn-nodes-name {
cursor: pointer;
white-space: nowrap;
flex-shrink: 0;
position: relative;
padding: 8px 5px;
}
.cn-nodes-name::after {
content: attr(action);
position: absolute;
pointer-events: none;
top: 50%;
left: 100%;
transform: translate(5px, -50%);
font-size: 12px;
color: var(--drag-text);
background-color: var(--comfy-input-bg);
border-radius: 10px;
border: 1px solid var(--border-color);
padding: 3px 8px;
display: none;
}
.cn-nodes-name.action::after {
display: block;
}
.cn-nodes-name:hover {
text-decoration: underline;
}
.cn-nodes-conflict .cn-nodes-name,
.cn-nodes-conflict .cn-icon {
color: orange;
}
.cn-conflicts-list {
display: flex;
flex-wrap: wrap;
gap: 5px;
align-items: center;
padding: 5px 0;
}
.cn-conflicts-list b {
font-weight: normal;
color: var(--descrip-text);
}
.cn-nodes-pack {
cursor: pointer;
color: skyblue;
}
.cn-nodes-pack:hover {
text-decoration: underline;
}
.cn-pack-badge {
font-size: 12px;
font-weight: normal;
background-color: var(--comfy-input-bg);
border-radius: 10px;
border: 1px solid var(--border-color);
padding: 3px 8px;
color: var(--error-text);
}
.cn-preview {
min-width: 300px;
max-width: 500px;
min-height: 120px;
overflow: hidden;
font-size: 12px;
pointer-events: none;
padding: 12px;
color: var(--fg-color);
}
.cn-preview-header {
display: flex;
gap: 8px;
align-items: center;
border-bottom: 1px solid var(--comfy-input-bg);
padding: 5px 10px;
}
.cn-preview-dot {
width: 8px;
height: 8px;
border-radius: 50%;
background-color: grey;
position: relative;
filter: drop-shadow(1px 2px 3px rgb(0 0 0 / 30%));
}
.cn-preview-dot.cn-preview-optional::after {
content: "";
position: absolute;
pointer-events: none;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
background-color: var(--comfy-input-bg);
border-radius: 50%;
width: 3px;
height: 3px;
}
.cn-preview-dot.cn-preview-grid {
border-radius: 0;
}
.cn-preview-dot.cn-preview-grid::before {
content: '';
position: absolute;
border-left: 1px solid var(--comfy-input-bg);
border-right: 1px solid var(--comfy-input-bg);
width: 4px;
height: 100%;
left: 2px;
top: 0;
z-index: 1;
}
.cn-preview-dot.cn-preview-grid::after {
content: '';
position: absolute;
border-top: 1px solid var(--comfy-input-bg);
border-bottom: 1px solid var(--comfy-input-bg);
width: 100%;
height: 4px;
left: 0;
top: 2px;
z-index: 1;
}
.cn-preview-name {
flex: auto;
font-size: 14px;
}
.cn-preview-io {
display: flex;
justify-content: space-between;
padding: 10px 10px;
}
.cn-preview-column > div {
display: flex;
gap: 10px;
align-items: center;
height: 18px;
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
}
.cn-preview-input {
justify-content: flex-start;
}
.cn-preview-output {
justify-content: flex-end;
}
.cn-preview-list {
display: flex;
flex-direction: column;
gap: 3px;
padding: 0 10px 10px 10px;
}
.cn-preview-switch {
position: relative;
display: flex;
justify-content: space-between;
align-items: center;
background: var(--bg-color);
border: 2px solid var(--border-color);
border-radius: 10px;
text-wrap: nowrap;
padding: 2px 20px;
gap: 10px;
}
.cn-preview-switch::before,
.cn-preview-switch::after {
position: absolute;
pointer-events: none;
top: 50%;
transform: translate(0, -50%);
color: var(--fg-color);
opacity: 0.8;
}
.cn-preview-switch::before {
content: "◀";
left: 5px;
}
.cn-preview-switch::after {
content: "▶";
right: 5px;
}
.cn-preview-value {
color: var(--descrip-text);
}
.cn-preview-string {
min-height: 30px;
max-height: 300px;
background: var(--bg-color);
color: var(--descrip-text);
border-radius: 3px;
padding: 3px 5px;
overflow-y: auto;
overflow-x: hidden;
}
.cn-preview-description {
margin: 0px 10px 10px 10px;
padding: 6px;
background: var(--border-color);
color: var(--descrip-text);
border-radius: 5px;
font-style: italic;
word-break: break-word;
}
.cn-tag-list {
display: flex;
flex-wrap: wrap;
gap: 5px;
align-items: center;
margin-bottom: 5px;
}
.cn-tag-list > div {
background-color: var(--border-color);
border-radius: 5px;
padding: 0 5px;
}
.cn-install-buttons {
display: flex;
flex-direction: column;
gap: 3px;
padding: 3px;
align-items: center;
justify-content: center;
height: 100%;
}
.cn-selected-buttons {
display: flex;
gap: 5px;
align-items: center;
padding-right: 20px;
}
.nu-manager .cn-btn-enable {
background-color: #333399;
color: white;
}
.nu-manager .cn-btn-disable {
background-color: #442277;
color: white;
}
.nu-manager .cn-btn-update {
background-color: #1155AA;
color: white;
}
.nu-manager .cn-btn-try-update {
background-color: Gray;
color: white;
}
.nu-manager .cn-btn-try-fix {
background-color: #6495ED;
color: white;
}
.nu-manager .cn-btn-import-failed {
background-color: #AA1111;
font-size: 10px;
font-weight: bold;
color: white;
}
.nu-manager .cn-btn-install {
background-color: black;
color: white;
}
.nu-manager .cn-btn-try-install {
background-color: Gray;
color: white;
}
.nu-manager .cn-btn-uninstall {
background-color: #993333;
color: white;
}
.nu-manager .cn-btn-reinstall {
background-color: #993333;
color: white;
}
.nu-manager .cn-btn-switch {
background-color: #448833;
color: white;
}
@keyframes nu-btn-loading-bg {
0% {
left: 0;
}
100% {
left: -105px;
}
}
.nu-manager button.nu-btn-loading {
position: relative;
overflow: hidden;
border-color: rgb(0 119 207 / 80%);
background-color: var(--comfy-input-bg);
}
.nu-manager button.nu-btn-loading::after {
position: absolute;
top: 0;
left: 0;
content: "";
width: 500px;
height: 100%;
background-image: repeating-linear-gradient(
-45deg,
rgb(0 119 207 / 30%),
rgb(0 119 207 / 30%) 10px,
transparent 10px,
transparent 15px
);
animation: nu-btn-loading-bg 2s linear infinite;
}
.nu-manager-light .nu-pack-name a {
color: blue;
}
.nu-manager-light .cm-warn-note {
background-color: #ccc !important;
}
.nu-manager-light .cn-btn-install {
background-color: #333;
}

742
js/node-usage-analyzer.js Normal file
View File

@ -0,0 +1,742 @@
import { app } from "../../scripts/app.js";
import { $el } from "../../scripts/ui.js";
import {
manager_instance,
fetchData, md5, show_message, customAlert, infoToast, showTerminal,
storeColumnWidth, restoreColumnWidth, loadCss, uninstallNodes,
analyzeWorkflowUsage, sizeToBytes, createFlyover, createUIStateManager
} from "./common.js";
import { api } from "../../scripts/api.js";
// https://cenfun.github.io/turbogrid/api.html
import TG from "./turbogrid.esm.js";
loadCss("./node-usage-analyzer.css");
const gridId = "model";
const pageHtml = `
<div class="nu-manager-header">
<div class="nu-manager-status"></div>
<input type="text" class="nu-manager-keywords" placeholder="Filter keywords..." />
<div class="nu-flex-auto"></div>
</div>
<div class="nu-manager-grid"></div>
<div class="nu-manager-selection"></div>
<div class="nu-manager-message"></div>
<div class="nu-manager-footer">
<button class="nu-manager-back">
<svg class="arrow-icon" width="14" height="14" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M2 8H18M2 8L8 2M2 8L8 14" stroke="white" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
</svg>
Back
</button>
<button class="nu-manager-refresh">Refresh</button>
<button class="nu-manager-stop">Stop</button>
<div class="nu-flex-auto"></div>
</div>
`;
export class NodeUsageAnalyzer {
static instance = null;
static SortMode = {
BY_PACKAGE: 'by_package'
};
constructor(app, manager_dialog) {
this.app = app;
this.manager_dialog = manager_dialog;
this.id = "nu-manager";
this.filter = '';
this.type = '';
this.base = '';
this.keywords = '';
this.init();
// Initialize shared UI state manager
this.ui = createUIStateManager(this.element, {
selection: ".nu-manager-selection",
message: ".nu-manager-message",
status: ".nu-manager-status",
refresh: ".nu-manager-refresh",
stop: ".nu-manager-stop"
});
api.addEventListener("cm-queue-status", this.onQueueStatus);
}
init() {
this.element = $el("div", {
parent: document.body,
className: "comfy-modal nu-manager"
});
this.element.innerHTML = pageHtml;
this.bindEvents();
this.initGrid();
}
bindEvents() {
const eventsMap = {
".nu-manager-selection": {
click: (e) => {
const target = e.target;
const mode = target.getAttribute("mode");
if (mode === "install") {
this.installModels(this.selectedModels, target);
} else if (mode === "uninstall") {
this.uninstallModels(this.selectedModels, target);
}
}
},
".nu-manager-refresh": {
click: () => {
app.refreshComboInNodes();
}
},
".nu-manager-stop": {
click: () => {
api.fetchApi('/manager/queue/reset');
infoToast('Cancel', 'Remaining tasks will stop after completing the current task.');
}
},
".nu-manager-back": {
click: (e) => {
this.close()
manager_instance.show();
}
}
};
Object.keys(eventsMap).forEach(selector => {
const target = this.element.querySelector(selector);
if (target) {
const events = eventsMap[selector];
if (events) {
Object.keys(events).forEach(type => {
target.addEventListener(type, events[type]);
});
}
}
});
}
// ===========================================================================================
initGrid() {
const container = this.element.querySelector(".nu-manager-grid");
const grid = new TG.Grid(container);
this.grid = grid;
this.flyover = createFlyover(container, { context: this });
grid.bind('onUpdated', (e, d) => {
this.ui.showStatus(`${grid.viewRows.length.toLocaleString()} installed packages`);
});
grid.bind('onSelectChanged', (e, changes) => {
this.renderSelected();
});
grid.bind("onColumnWidthChanged", (e, columnItem) => {
storeColumnWidth(gridId, columnItem)
});
grid.bind('onClick', (e, d) => {
const { rowItem } = d;
const target = d.e.target;
const mode = target.getAttribute("mode");
if (mode === "install") {
this.installModels([rowItem], target);
return;
}
if (mode === "uninstall") {
this.uninstallModels([rowItem], target);
return;
}
// Handle click on usage count
if (d.columnItem.id === "used_in_count" && rowItem.used_in_count > 0) {
this.showUsageDetails(rowItem);
return;
}
});
grid.setOption({
theme: 'dark',
selectVisible: true,
selectMultiple: true,
selectAllVisible: true,
textSelectable: true,
scrollbarRound: true,
frozenColumn: 1,
rowNotFound: "No Results",
rowHeight: 40,
bindWindowResize: true,
bindContainerResize: true,
cellResizeObserver: (rowItem, columnItem) => {
const autoHeightColumns = ['name', 'description'];
return autoHeightColumns.includes(columnItem.id)
}
});
}
renderGrid() {
// update theme
const colorPalette = this.app.ui.settings.settingsValues['Comfy.ColorPalette'];
Array.from(this.element.classList).forEach(cn => {
if (cn.startsWith("nu-manager-")) {
this.element.classList.remove(cn);
}
});
this.element.classList.add(`nu-manager-${colorPalette}`);
const options = {
theme: colorPalette === "light" ? "" : "dark"
};
const rows = this.modelList || [];
const columns = [{
id: 'title',
name: 'Title',
width: 200,
minWidth: 100,
maxWidth: 500,
classMap: 'nu-pack-name',
formatter: function (name, rowItem, columnItem, cellNode) {
return `<a href=${rowItem.reference} target="_blank"><b>${name}</b></a>`;
}
}, {
id: 'used_in_count',
name: 'Used in',
width: 100,
formatter: function (usedCount, rowItem, columnItem) {
if (!usedCount || usedCount === 0) {
return '0';
}
const plural = usedCount > 1 ? 's' : '';
return `<div class="cn-pack-nodes" style="cursor: pointer;">${usedCount} workflow${plural}</div>`;
}
}, {
id: 'action',
name: 'Action',
width: 160,
minWidth: 140,
maxWidth: 200,
sortable: false,
align: 'center',
formatter: function (action, rowItem, columnItem) {
// Only show uninstall button for installed packages
if (rowItem.originalData && rowItem.originalData.state && rowItem.originalData.state !== "not-installed") {
return `<div class="cn-install-buttons"><button class="nu-btn-uninstall" mode="uninstall">Uninstall</button></div>`;
}
return '';
}
}];
restoreColumnWidth(gridId, columns);
this.grid.setData({
options,
rows,
columns
});
this.grid.render();
}
updateGrid() {
if (this.grid) {
this.grid.update();
}
}
showUsageDetails(rowItem) {
const workflowList = rowItem.workflowDetails;
if (!workflowList || workflowList.length === 0) {
return;
}
let titleHtml = `<div class="cn-nodes-pack">${rowItem.title}</div>`;
const list = [];
list.push(`<div class="cn-nodes-list">`);
workflowList.forEach((workflow, i) => {
list.push(`<div class="cn-nodes-row">`);
list.push(`<div class="cn-nodes-sn">${i + 1}</div>`);
list.push(`<div class="cn-nodes-name">${workflow.filename}</div>`);
list.push(`<div class="cn-nodes-details">${workflow.nodeCount} node${workflow.nodeCount > 1 ? 's' : ''}</div>`);
list.push(`</div>`);
});
list.push("</div>");
const bodyHtml = list.join("");
this.flyover.show(titleHtml, bodyHtml);
}
renderSelected() {
const selectedList = this.grid.getSelectedRows();
if (!selectedList.length) {
this.ui.showSelection("");
return;
}
const installedSelected = selectedList.filter(item =>
item.originalData && item.originalData.state && item.originalData.state !== "not-installed"
);
if (installedSelected.length === 0) {
this.ui.showSelection(`<span>Selected <b>${selectedList.length}</b> packages (none can be uninstalled)</span>`);
return;
}
this.selectedModels = installedSelected;
this.ui.showSelection(`
<div class="nu-selected-buttons">
<span>Selected <b>${installedSelected.length}</b> installed packages</span>
<button class="nu-btn-uninstall" mode="uninstall">Uninstall Selected</button>
</div>
`);
}
// ===========================================================================================
async installModels(list, btn) {
let stats = await api.fetchApi('/manager/queue/status');
stats = await stats.json();
if (stats.is_processing) {
customAlert(`[ComfyUI-Manager] There are already tasks in progress. Please try again after it is completed. (${stats.done_count}/${stats.total_count})`);
return;
}
btn.classList.add("nu-btn-loading");
this.ui.showError("");
let needRefresh = false;
let errorMsg = "";
await api.fetchApi('/manager/queue/reset');
let target_items = [];
for (const item of list) {
this.grid.scrollRowIntoView(item);
target_items.push(item);
this.ui.showStatus(`Install ${item.name} ...`);
const data = item.originalData;
data.ui_id = item.hash;
const res = await api.fetchApi(`/manager/queue/install_model`, {
method: 'POST',
body: JSON.stringify(data)
});
if (res.status != 200) {
errorMsg = `'${item.name}': `;
if (res.status == 403) {
errorMsg += `This action is not allowed with this security level configuration.\n`;
} else {
errorMsg += await res.text() + '\n';
}
break;
}
}
this.install_context = { btn: btn, targets: target_items };
if (errorMsg) {
this.ui.showError(errorMsg);
show_message("[Installation Errors]\n" + errorMsg);
// reset
for (let k in target_items) {
const item = target_items[k];
this.grid.updateCell(item, "installed");
}
}
else {
await api.fetchApi('/manager/queue/start');
this.ui.showStop();
showTerminal();
}
}
async uninstallModels(list, btn) {
btn.classList.add("nu-btn-loading");
this.ui.showError("");
const result = await uninstallNodes(list, {
title: list.length === 1 ? list[0].title || list[0].name : `${list.length} custom nodes`,
channel: 'default',
mode: 'default',
onProgress: (msg) => {
this.showStatus(msg);
},
onError: (errorMsg) => {
this.showError(errorMsg);
},
onSuccess: (targets) => {
this.showStatus(`Uninstalled ${targets.length} custom node(s) successfully`);
this.showMessage(`To apply the uninstalled custom nodes, please restart ComfyUI and refresh browser.`, "red");
// Update the grid to reflect changes
for (let item of targets) {
if (item.originalData) {
item.originalData.state = "not-installed";
}
this.grid.updateRow(item);
}
}
});
if (result.success) {
this.showStop();
}
btn.classList.remove("nu-btn-loading");
}
async onQueueStatus(event) {
let self = NodeUsageAnalyzer.instance;
if (event.detail.status == 'in_progress' && (event.detail.ui_target == 'model_manager' || event.detail.ui_target == 'nodepack_manager')) {
const hash = event.detail.target;
const item = self.grid.getRowItemBy("hash", hash);
if (item) {
item.refresh = true;
self.grid.setRowSelected(item, false);
item.selectable = false;
self.grid.updateRow(item);
}
}
else if (event.detail.status == 'done') {
self.hideStop();
self.onQueueCompleted(event.detail);
}
}
async onQueueCompleted(info) {
let result = info.model_result || info.nodepack_result;
if (!result || result.length == 0) {
return;
}
let self = NodeUsageAnalyzer.instance;
if (!self.install_context) {
return;
}
let btn = self.install_context.btn;
self.hideLoading();
btn.classList.remove("nu-btn-loading");
let errorMsg = "";
for (let hash in result) {
let v = result[hash];
if (v != 'success' && v != 'skip')
errorMsg += v + '\n';
}
for (let k in self.install_context.targets) {
let item = self.install_context.targets[k];
if (info.model_result) {
self.grid.updateCell(item, "installed");
} else if (info.nodepack_result) {
// Handle uninstall completion
if (item.originalData) {
item.originalData.state = "not-installed";
}
self.grid.updateRow(item);
}
}
if (errorMsg) {
self.showError(errorMsg);
show_message("Operation Error:\n" + errorMsg);
} else {
if (info.model_result) {
self.showStatus(`Install ${Object.keys(result).length} models successfully`);
self.showRefresh();
self.showMessage(`To apply the installed model, please click the 'Refresh' button.`, "red");
} else if (info.nodepack_result) {
self.showStatus(`Uninstall ${Object.keys(result).length} custom node(s) successfully`);
self.showMessage(`To apply the uninstalled custom nodes, please restart ComfyUI and refresh browser.`, "red");
}
}
infoToast('Tasks done', `[ComfyUI-Manager] All tasks in the queue have been completed.\n${info.done_count}/${info.total_count}`);
self.install_context = undefined;
}
getModelList(models) {
const typeMap = new Map();
const baseMap = new Map();
models.forEach((item, i) => {
const { type, base, name, reference, installed } = item;
// CRITICAL FIX: Do NOT overwrite originalData - it contains the needed state field!
item.size = sizeToBytes(item.size);
item.hash = md5(name + reference);
if (installed === "True") {
item.selectable = false;
}
typeMap.set(type, type);
baseMap.set(base, base);
});
const typeList = [];
typeMap.forEach(type => {
typeList.push({
label: type,
value: type
});
});
typeList.sort((a, b) => {
const au = a.label.toUpperCase();
const bu = b.label.toUpperCase();
if (au !== bu) {
return au > bu ? 1 : -1;
}
return 0;
});
this.typeList = [{
label: "All",
value: ""
}].concat(typeList);
const baseList = [];
baseMap.forEach(base => {
baseList.push({
label: base,
value: base
});
});
baseList.sort((a, b) => {
const au = a.label.toUpperCase();
const bu = b.label.toUpperCase();
if (au !== bu) {
return au > bu ? 1 : -1;
}
return 0;
});
this.baseList = [{
label: "All",
value: ""
}].concat(baseList);
return models;
}
// ===========================================================================================
async loadData() {
this.showLoading();
this.showStatus(`Analyzing node usage ...`);
const mode = manager_instance.datasrc_combo.value;
const nodeListRes = await fetchData(`/customnode/getlist?mode=${mode}&skip_update=true`);
if (nodeListRes.error) {
this.showError("Failed to get custom node list.");
this.hideLoading();
return;
}
const { channel, node_packs } = nodeListRes.data;
delete node_packs['comfyui-manager'];
this.installed_custom_node_packs = node_packs;
// Use the consolidated workflow analysis utility
const result = await analyzeWorkflowUsage(node_packs);
if (!result.success) {
if (result.error.toString().includes('204')) {
this.showMessage("No workflows were found for analysis.");
} else {
this.showError(result.error);
this.hideLoading();
return;
}
}
// Transform node_packs into models format - ONLY INSTALLED PACKAGES
const models = [];
Object.keys(node_packs).forEach((packKey, index) => {
const pack = node_packs[packKey];
// Only include installed packages (filter out "not-installed" packages)
if (pack.state === "not-installed") {
return; // Skip non-installed packages
}
const usedCount = result.usageMap?.get(packKey) || 0;
const workflowDetails = result.workflowDetailsMap?.get(packKey) || [];
models.push({
title: pack.title || packKey,
reference: pack.reference || pack.files?.[0] || '#',
used_in_count: usedCount,
workflowDetails: workflowDetails,
name: packKey,
originalData: pack
});
});
// Sort by usage count (descending) then by title
models.sort((a, b) => {
if (b.used_in_count !== a.used_in_count) {
return b.used_in_count - a.used_in_count;
}
return a.title.localeCompare(b.title);
});
this.modelList = this.getModelList(models);
this.renderGrid();
this.hideLoading();
}
// ===========================================================================================
showSelection(msg) {
this.element.querySelector(".nu-manager-selection").innerHTML = msg;
}
showError(err) {
this.showMessage(err, "red");
}
showMessage(msg, color) {
if (color) {
msg = `<font color="${color}">${msg}</font>`;
}
this.element.querySelector(".nu-manager-message").innerHTML = msg;
}
showStatus(msg, color) {
if (color) {
msg = `<font color="${color}">${msg}</font>`;
}
this.element.querySelector(".nu-manager-status").innerHTML = msg;
}
showLoading() {
// this.setDisabled(true);
if (this.grid) {
this.grid.showLoading();
this.grid.showMask({
opacity: 0.05
});
}
}
hideLoading() {
// this.setDisabled(false);
if (this.grid) {
this.grid.hideLoading();
this.grid.hideMask();
}
}
setDisabled(disabled) {
const $close = this.element.querySelector(".nu-manager-close");
const $refresh = this.element.querySelector(".nu-manager-refresh");
const $stop = this.element.querySelector(".nu-manager-stop");
const list = [
".nu-manager-header input",
".nu-manager-header select",
".nu-manager-footer button",
".nu-manager-selection button"
].map(s => {
return Array.from(this.element.querySelectorAll(s));
})
.flat()
.filter(it => {
return it !== $close && it !== $refresh && it !== $stop;
});
list.forEach($elem => {
if (disabled) {
$elem.setAttribute("disabled", "disabled");
} else {
$elem.removeAttribute("disabled");
}
});
Array.from(this.element.querySelectorAll(".nu-btn-loading")).forEach($elem => {
$elem.classList.remove("nu-btn-loading");
});
}
showRefresh() {
this.element.querySelector(".nu-manager-refresh").style.display = "block";
}
showStop() {
this.element.querySelector(".nu-manager-stop").style.display = "block";
}
hideStop() {
this.element.querySelector(".nu-manager-stop").style.display = "none";
}
setKeywords(keywords = "") {
this.keywords = keywords;
this.element.querySelector(".nu-manager-keywords").value = keywords;
}
show(sortMode) {
this.element.style.display = "flex";
this.setKeywords("");
this.showSelection("");
this.showMessage("");
this.loadData();
}
close() {
this.element.style.display = "none";
}
}

View File

@ -1,5 +1,105 @@
{
"custom_nodes": [
{
"author": "supaidauen",
"title": "ComfyUI-supaidauen [WIP]",
"reference": "https://github.com/supaidauen/ComfyUI-supaidauen",
"files": [
"https://github.com/supaidauen/ComfyUI-supaidauen"
],
"install_type": "git-clone",
"description": "Collection of custom ComfyUI nodes including VRAM management, image processing, latent manipulation, character I/O, and advanced sampling utilities. (Description by CC)\nNOTE: The files in the repo are not organized."
},
{
"author": "huhu-tiger",
"title": "ComfyUI-RemoteResource",
"reference": "https://github.com/huhu-tiger/ComfyUI-RemoteResource",
"files": [
"https://github.com/huhu-tiger/ComfyUI-RemoteResource"
],
"install_type": "git-clone",
"description": "ComfyUI node for loading images from remote sources. (Description by CC)"
},
{
"author": "nomadop",
"title": "ComfyUI-Video-Matting [NAME CONFLICT]",
"reference": "https://github.com/nomadop/ComfyUI-Video-Matting",
"files": [
"https://github.com/nomadop/ComfyUI-Video-Matting"
],
"install_type": "git-clone",
"description": "Modular video matting nodes supporting multiple models (RVM, MODNet, U2Net, RMBG-2.0) and video object segmentation with Cutie and edge refinement via ViTMatte."
},
{
"author": "agavesunset",
"title": "Comfyui_SiliconFlow_AgaveSunset",
"reference": "https://github.com/agavesunset/Comfyui_SiliconFlow_AgaveSunset",
"files": [
"https://github.com/agavesunset/Comfyui_SiliconFlow_AgaveSunset"
],
"install_type": "git-clone",
"description": "NODES: SiliconFlowLoader_AS, SiliconFlowSampler_AS"
},
{
"author": "IIEleven11",
"title": "[WIP] ComfyUI-Dataset_Maker",
"reference": "https://github.com/IIEleven11/ComfyUI-Dataset_Maker",
"files": [
"https://github.com/IIEleven11/ComfyUI-Dataset_Maker"
],
"install_type": "git-clone",
"description": "Custom node pack that automates dataset creation by generating images for concept lists, each with a specific LoRA. (Description by CC)\nNOTE: The files in the repo are not organized."
},
{
"author": "bhaveek424",
"title": "ComfyUI-HMNodes",
"reference": "https://github.com/bhaveek424/ComfyUI-HMNodes",
"files": [
"https://github.com/bhaveek424/ComfyUI-HMNodes"
],
"install_type": "git-clone",
"description": "ComfyUI nodes for audio processing, image enhancement, and AI-powered prompting including FFT analysis, automatic white balance, lens effects, and realism optimization. (Description by CC)"
},
{
"author": "raohammad",
"title": "ComfyUI-VTUtilNodes [WIP]",
"reference": "https://github.com/raohammad/ComfyUI-VTUtilNodes",
"files": [
"https://github.com/raohammad/ComfyUI-VTUtilNodes"
],
"install_type": "git-clone",
"description": "A collection of utility custom nodes for ComfyUI.\nNOTE: The files in the repo are not organized."
},
{
"author": "huyl3-cpu",
"title": "ComfyUI_A100_Ultimate_Optimizer",
"reference": "https://github.com/huyl3-cpu/ComfyUI_A100_Ultimate_Optimizer",
"files": [
"https://github.com/huyl3-cpu/ComfyUI_A100_Ultimate_Optimizer"
],
"install_type": "git-clone",
"description": "A100 GPU batch processing and optimization node for ComfyUI. (Description by CC)"
},
{
"author": "yamanacn",
"title": "ComfyUI-ImageMask-Random-Sync-Picker",
"reference": "https://github.com/yamanacn/ComfyUI-ImageMask-Random-Sync-Picker",
"files": [
"https://github.com/yamanacn/ComfyUI-ImageMask-Random-Sync-Picker"
],
"install_type": "git-clone",
"description": "Node for randomly selecting and synchronizing image masks with selectable options. (Description by CC)"
},
{
"author": "tdrminglin",
"title": "ComfyUI_SceneSplitter",
"reference": "https://github.com/tdrminglin/ComfyUI_SceneSplitter",
"files": [
"https://github.com/tdrminglin/ComfyUI_SceneSplitter"
],
"install_type": "git-clone",
"description": "Scene detection and splitting nodes for ComfyUI enabling frame-level scene detection and start frame tracking. (Description by CC)"
},
{
"author": "starsFriday",
"title": "ComfyUI-KLingAI-OmniVideo [WIP]",
@ -190,16 +290,6 @@
"install_type": "git-clone",
"description": "Comfy UI nodes for IMtalker to run native weights.)\nNOTE: The files in the repo are not organized."
},
{
"author": "yutrodimitri-ship-it",
"title": "ComfyUI-YUTRO-CastingStudio-v2 [WIP]",
"reference": "https://github.com/yutrodimitri-ship-it/ComfyUI-YUTRO-CastingStudio-v2",
"files": [
"https://github.com/yutrodimitri-ship-it/ComfyUI-YUTRO-CastingStudio-v2"
],
"install_type": "git-clone",
"description": "A professional modular suite of nodes for ComfyUI designed for virtual casting agencies, professional photographers, and content creators to generate high-quality model portfolios efficiently. (Description by CC)\nNOTE: The files in the repo are not organized."
},
{
"author": "yuyu0218yu",
"title": "comfyui-NXCM-tool [UNSAFE]",
@ -4967,16 +5057,6 @@
"install_type": "git-clone",
"description": "Generate random prompts easily for FMJ.\nNOTE: The files in the repo are not organized."
},
{
"author": "amamisonlyuser",
"title": "MixvtonComfyui [WIP]",
"reference": "https://github.com/amamisonlyuser/MixvtonComfyui",
"files": [
"https://github.com/amamisonlyuser/MixvtonComfyui"
],
"install_type": "git-clone",
"description": "NODES: CXH_Leffa_Viton_Load, CXH_Leffa_Viton_Run\nNOTE: The files in the repo are not organized."
},
{
"author": "pictorialink",
"title": "comfyui-static-resource[UNSAFE]",

View File

@ -889,7 +889,7 @@
"TS_Qwen3_VL",
"TS_QwenCanvas",
"TS_QwenSafeResize",
"TS_Qwen_3VL_FP8",
"TS_Qwen_3VL_V2",
"TS_VideoDepthNode",
"TS_Video_Upscale_With_Model",
"TS_WAN_SafeResize"
@ -1672,8 +1672,10 @@
"Donut Simple Calibration",
"DonutApplyLoRAStack",
"DonutCacheDebug",
"DonutCheckpointSave",
"DonutClipEncode",
"DonutDetailerZIT",
"DonutFaceDetailer",
"DonutFillerClip",
"DonutFillerModel",
"DonutHotReload",
@ -1683,6 +1685,7 @@
"DonutLoRAStack",
"DonutLoRAStackCivitAI",
"DonutManualCleanup",
"DonutModelSave",
"DonutMultiModelSampler",
"DonutOpenCivitAI",
"DonutSDXLTeaCache",
@ -1696,6 +1699,7 @@
"LoraBlockInfo //Inspire",
"LoraLoaderBlockWeight //Inspire",
"MakeLBW //Inspire",
"ModelMergeZIT",
"SaveLBW //Inspire",
"XY Input: Lora Block Weight //Inspire"
],
@ -2228,6 +2232,18 @@
"title_aux": "Comfyui_XF_Custom_Actual-Node"
}
],
"https://github.com/IIEleven11/ComfyUI-Dataset_Maker": [
[
"ConceptList",
"DatasetGenerator",
"DatasetLoraLoader",
"DatasetPromptBuilder",
"LoraList"
],
{
"title_aux": "[WIP] ComfyUI-Dataset_Maker"
}
],
"https://github.com/IO-AtelierTech/comfyui-genai-connectors": [
[
"BooleanInput_fal",
@ -2272,7 +2288,8 @@
"IGT_ImageResizer",
"IGT_ImageTilesCalc",
"IGT_IntMinMax",
"IGT_SimpleTilesCalc"
"IGT_SimpleTilesCalc",
"IGT_TelegramSender"
],
{
"title_aux": "ComfyUI-igTools"
@ -2733,6 +2750,7 @@
"LunaCheckpointLoader",
"LunaCheckpointTunnel",
"LunaChessRefiner",
"LunaChessTileTest",
"LunaCivitaiBatchScraper",
"LunaCivitaiScraper",
"LunaConfigGateway",
@ -2751,6 +2769,7 @@
"LunaKSampler",
"LunaKSamplerAdvanced",
"LunaKSamplerHeadless",
"LunaKSamplerScaffold",
"LunaLoRARandomizer",
"LunaLoRAStacker",
"LunaLoRATriggerInjector",
@ -2759,6 +2778,7 @@
"LunaModelRouter",
"LunaMultiSaver",
"LunaNF4Loader",
"LunaNativeCanvasDownscale",
"LunaOptimizedWeightsManager",
"LunaPipeExpander",
"LunaPromptCraft",
@ -2773,6 +2793,7 @@
"LunaSuperUpscaler",
"LunaSuperUpscalerSimple",
"LunaUNetTunnel",
"LunaUSDUClone",
"LunaVLMPromptGenerator",
"LunaVisionNode",
"LunaWildcardBuilder",
@ -3519,16 +3540,15 @@
"image_iterator",
"img2url_v2_Node",
"img_understanding_Node",
"kie_base64_upload_node",
"kie_nano_get_node",
"kie_nano_post_node",
"klingai_video_Node",
"liblib_auto_video_node",
"nano_banana_node",
"path_join_Node",
"save_img_NODE",
"save_img_v2_NODE",
"set_api_Node",
"suchuang_get_node",
"suchuang_nano_post_node",
"sora2",
"sora2_suchuang_node",
"text_replace_node"
],
{
@ -3680,13 +3700,19 @@
[
"BFParameters",
"BFParametersSimple",
"CheckpointComboParameter",
"DetailerSchedulerComboParameter",
"IntegerPicker",
"KsamplerSamplersComboParameter",
"KsamplerSchedulersComboParameter",
"OuputDirByModelName",
"RandomImageSizeAdvanced",
"RandomImageSizeAdvancedYAML",
"RandomInteger",
"UnetComboParameter",
"UniqueStringList",
"UniqueStringListAdvanced"
"UniqueStringListAdvanced",
"VAEComboParameter"
],
{
"title_aux": "comfyui-random-image-size"
@ -3959,20 +3985,17 @@
"https://github.com/PozzettiAndrea/ComfyUI-SAM3DObjects": [
[
"LoadSAM3DModel",
"SAM3DExportMesh",
"SAM3DExportPLY",
"SAM3DExportPLYBatch",
"SAM3DExtractMesh",
"SAM3DGaussianDecode",
"SAM3DGenerateSLAT",
"SAM3DMeshDecode",
"SAM3DRenderSingle",
"SAM3DSLATGen",
"SAM3DSparseGen",
"SAM3DSceneGenerate",
"SAM3DTextureBake",
"SAM3DVisualizer",
"SAM3D_DepthEstimate",
"SAM3D_PoseOptimization",
"SAM3D_PreviewPointCloud",
"SAM3D_ScenePoseOptimize",
"SAM3D_UnloadModel"
],
{
@ -5631,6 +5654,15 @@
"title_aux": "ComfyUI_LoRA_Tracker"
}
],
"https://github.com/agavesunset/Comfyui_SiliconFlow_AgaveSunset": [
[
"SiliconFlowLoader_AS",
"SiliconFlowSampler_AS"
],
{
"title_aux": "Comfyui_SiliconFlow_AgaveSunset"
}
],
"https://github.com/ahkimkoo/ComfyUI-OSS-Upload": [
[
"OSSAudioUploader",
@ -5836,15 +5868,6 @@
"title_aux": "Dream Painter [WIP]"
}
],
"https://github.com/amamisonlyuser/MixvtonComfyui": [
[
"CXH_Leffa_Viton_Load",
"CXH_Leffa_Viton_Run"
],
{
"title_aux": "MixvtonComfyui [WIP]"
}
],
"https://github.com/ammahmoudi/ComfyUI-Legendary-Nodes": [
[
"Legendary Dataset Saver",
@ -6173,6 +6196,21 @@
"title_aux": "ComfyUI_BeySoft"
}
],
"https://github.com/bhaveek424/ComfyUI-HMNodes": [
[
"Analogizer",
"FFTSurgeon",
"HearmemanAI_Prompter",
"Realism_AdaptiveGrain",
"Realism_AutoWB",
"Realism_LensEffects",
"Realism_MicroContrast",
"Realism_SpectrumMatch"
],
{
"title_aux": "ComfyUI-HMNodes"
}
],
"https://github.com/bheins/ComfyUI-glb-to-stl": [
[
"GLBToSTLNode"
@ -6938,6 +6976,7 @@
"KlingImageToVideoWithAudio",
"KlingLipSyncAudioToVideoNode",
"KlingLipSyncTextToVideoNode",
"KlingMotionControl",
"KlingOmniProEditVideoNode",
"KlingOmniProFirstLastFrameNode",
"KlingOmniProImageNode",
@ -7007,6 +7046,7 @@
"LumaVideoNode",
"Mahiro",
"MakeTrainingDataset",
"ManualSigmas",
"MaskComposite",
"MaskPreview",
"MaskToImage",
@ -8424,6 +8464,7 @@
"XIS_SetColor",
"XIS_ShapeAndText",
"XIS_ShapeData",
"XIS_StringListMerger",
"XIS_StringSwitch",
"XIS_UnpackImages"
],
@ -8433,8 +8474,11 @@
],
"https://github.com/grokuku/ComfyUI-Holaf": [
[
"HolafBundleCreator",
"HolafBundleExtractor",
"HolafBypasser",
"HolafGroupBypasser",
"HolafImageAdjustment",
"HolafImageBatchSlice",
"HolafImageComparer",
"HolafInstagramResize",
@ -8444,14 +8488,15 @@
"HolafLutSaver",
"HolafMaskToBoolean",
"HolafOverlayNode",
"HolafRatioCalculator",
"HolafRemote",
"HolafResolutionPreset",
"HolafSaveImage",
"HolafSaveVideo",
"HolafShortcut",
"HolafShortcutUser",
"HolafTextBox",
"HolafTiledKSampler",
"HolafToText",
"HolafVideoPreview",
"UpscaleImageHolaf"
],
@ -8860,6 +8905,7 @@
"Alta:Add(Math)",
"Alta:AddInt(Math)",
"Alta:BuildFilePath",
"Alta:ComboWrapper(Logic)",
"Alta:CompareFolders(File)",
"Alta:DeleteAudioFromMemory",
"Alta:DeleteFile(Util)",
@ -8919,6 +8965,14 @@
"title_aux": "comfyui_alta_nodes"
}
],
"https://github.com/huyl3-cpu/ComfyUI_A100_Ultimate_Optimizer": [
[
"A100MonsterBatch"
],
{
"title_aux": "ComfyUI_A100_Ultimate_Optimizer"
}
],
"https://github.com/hy134300/comfyui-hb-node": [
[
"generate story",
@ -9309,6 +9363,7 @@
"https://github.com/jorin91/ComfyUI-JSG-Utils": [
[
"JSGAddMetadata",
"JSGCaptionBuilder",
"JSGDeleteFilePassAny",
"JSGDeleteFilePassImage",
"JSGDeleteFilePassString",
@ -11006,13 +11061,12 @@
],
"https://github.com/nobinBB/comfyui-samenodes": [
[
"A1111PromptSplitter",
"BatchImageProcessor",
"CivitaiBulkDownloader",
"ExtractPromptFromImage",
"FloatToString",
"FloatToStringWithPrefix",
"IsComfyQueueEmpty",
"LoRASyntaxExtractor",
"LoraWildcardGenerator",
"RepeatTextLines"
],
@ -11052,7 +11106,12 @@
],
"https://github.com/nschpy/ComfyUI_MovisAdapter": [
[
"Example"
"MPA Brightness Effect",
"MPA Combine Videos",
"MPA Contrast Effect",
"MPA Speed Effect",
"MPA Text Overlay",
"MPA Video Transition"
],
{
"title_aux": "ComfyUI_MovisAdapter [UNSAFE]"
@ -11575,6 +11634,21 @@
"title_aux": "ComfyUI-HDRConversion [WIP]"
}
],
"https://github.com/raohammad/ComfyUI-VTUtilNodes": [
[
"JSONKeyExtractor",
"JSONListIterator",
"JSONQueue",
"JSONQueueOutput",
"JSONQueueSignal",
"SignalCounter",
"SimpleCounter",
"TextToJSON"
],
{
"title_aux": "ComfyUI-VTUtilNodes [WIP]"
}
],
"https://github.com/realm-weaver/ComfyUI-tile-seamstress-360": [
[
"RW_EquirectangularMask",
@ -11802,7 +11876,6 @@
],
"https://github.com/rookiestar28/ComfyUI_Security_Audit": [
[
"ComfyUI_Node_Audit",
"ComfyUI_Security_Audit"
],
{
@ -11937,6 +12010,8 @@
"FinalFrameSelector",
"NthLastFrameSelector",
"PreviewImageWithCounter",
"RemoveFirstAndLastFrame",
"RemoveFirstFrame",
"VideoMerge"
],
{
@ -12686,6 +12761,15 @@
"title_aux": "ComfyUI_Save_Flux_Image"
}
],
"https://github.com/tdrminglin/ComfyUI_SceneSplitter": [
[
"SceneDetectSplitter",
"SceneStartFramesNode"
],
{
"title_aux": "ComfyUI_SceneSplitter"
}
],
"https://github.com/techidsk/comfyui_molook_nodes": [
[
"ImageOutpaintPadding(Molook)",
@ -13595,6 +13679,14 @@
"title_aux": "ComfyUI-Direct3DS2 [WIP]"
}
],
"https://github.com/yamanacn/ComfyUI-ImageMask-Random-Sync-Picker": [
[
"ImageMaskRandomSelector"
],
{
"title_aux": "ComfyUI-ImageMask-Random-Sync-Picker"
}
],
"https://github.com/yamanacn/ComfyUI-QwenVL3-image": [
[
"QwenVL3_image",
@ -13763,14 +13855,6 @@
"title_aux": "ComfyUI-Dropbox-API [WIP]"
}
],
"https://github.com/yutrodimitri-ship-it/ComfyUI-YUTRO-CastingStudio-v2": [
[
"YUTROWardrobePreset"
],
{
"title_aux": "ComfyUI-YUTRO-CastingStudio-v2 [WIP]"
}
],
"https://github.com/yuvraj108c/ComfyUI-HYPIR": [
[
"HYPIRProcess",
@ -13928,6 +14012,7 @@
"TxtCounterNodeZV",
"UniversalBBOXToMaskZV",
"Veo31Image2VideoSubmitZV",
"VideoGeneratorFFmpegZV",
"VideoSceneDetectorZV",
"VideoSpeedZV",
"doubaoI2INodeZV",

File diff suppressed because it is too large Load Diff

View File

@ -1,5 +1,35 @@
{
"custom_nodes": [
{
"author": "scott-createplay",
"title": "ComfyUI_frontend_tools [REMOVED]",
"reference": "https://github.com/scott-createplay/ComfyUI_frontend_tools",
"files": [
"https://github.com/scott-createplay/ComfyUI_frontend_tools"
],
"install_type": "git-clone",
"description": "A comprehensive utility suite for ComfyUI that helps maintain clean, organized workflows with node cleaner, layout tools, HUD projection, and wireless connection management.\nNOTE: The files in the repo are not organized."
},
{
"author": "yutrodimitri-ship-it",
"title": "ComfyUI-YUTRO-CastingStudio-v2 [REMOVED]",
"reference": "https://github.com/yutrodimitri-ship-it/ComfyUI-YUTRO-CastingStudio-v2",
"files": [
"https://github.com/yutrodimitri-ship-it/ComfyUI-YUTRO-CastingStudio-v2"
],
"install_type": "git-clone",
"description": "A professional modular suite of nodes for ComfyUI designed for virtual casting agencies, professional photographers, and content creators to generate high-quality model portfolios efficiently. (Description by CC)\nNOTE: The files in the repo are not organized."
},
{
"author": "amamisonlyuser",
"title": "MixvtonComfyui [REMOVED]",
"reference": "https://github.com/amamisonlyuser/MixvtonComfyui",
"files": [
"https://github.com/amamisonlyuser/MixvtonComfyui"
],
"install_type": "git-clone",
"description": "NODES: CXH_Leffa_Viton_Load, CXH_Leffa_Viton_Run\nNOTE: The files in the repo are not organized."
},
{
"author": "AhBumm",
"title": "ComfyUI_MangaLineExtraction [REMOVED]",

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -250,6 +250,30 @@ paths:
type: object
additionalProperties:
$ref: '#/components/schemas/NodePackageMetadata'
/customnode/get_node_types_in_workflows:
get:
summary: List node types used by all user workflows
description: Scan through all workflows in the Comfy user directory, and return a list of all node types used in each one.
responses:
'200':
description: Successful operation
content:
application/json:
schema:
type: array
items:
type: object
properties:
workflow_file_name:
type: string
node_types:
type: array
items:
type: string
'500':
description: Error occurred
/customnode/alternatives:
get: