Compare commits

...

67 Commits
3.38.2 ... main

Author SHA1 Message Date
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
Dr.Lt.Data
2478d20e76 update DB
Some checks failed
Python Linting / Run Ruff (push) Has been cancelled
2025-12-24 12:50:12 +09:00
Dr.Lt.Data
cc3428eb3b update DB 2025-12-24 02:11:26 +09:00
Dr.Lt.Data
6001bd4940 update DB
Some checks are pending
Python Linting / Run Ruff (push) Waiting to run
2025-12-24 01:47:04 +09:00
BlackVortexAI
f8709f4091
Update custom-node-list.json fro BV-NodePack (#2412)
* Update custom-node-list.json

* Update custom-node-list.json. Add BV-NodePack
2025-12-24 01:46:09 +09:00
Dr.Lt.Data
3cff881b5b update DB 2025-12-24 01:43:49 +09:00
AugustusLXIII
b79e997a14
Added "Custom Resolution I2V" node for WAN to the list. (#2416) 2025-12-24 01:42:50 +09:00
Dr.Lt.Data
ed2c34143c update DB 2025-12-24 01:42:23 +09:00
Asidert
639b17ef6b
Add "ComfyUI_Base64Images" to custom-node-list.json (#2418) 2025-12-24 01:41:25 +09:00
Dr.Lt.Data
7834411ef3 update DB 2025-12-24 01:33:46 +09:00
HB2k
d8ea83a44c
Add ComfyUI-FlashVSR_Stable node to custom-node-list (#2421)
* Add ComfyUI-FlashVSR_Stable node to custom-node-list

Add ComfyUI-FlashVSR_Stable

* Update custom-node-list.json

---------

Co-authored-by: Dr.Lt.Data <128333288+ltdrdata@users.noreply.github.com>
2025-12-24 01:33:00 +09:00
Dr.Lt.Data
6b9818b748 update DB 2025-12-24 01:27:43 +09:00
Rakka Rage
b4d5b228ae
Add ComfyRage text nodes. (#2408) 2025-12-24 01:24:31 +09:00
Dr.Lt.Data
29b4824ee2 update DB
Some checks are pending
Python Linting / Run Ruff (push) Waiting to run
2025-12-23 12:35:16 +09:00
Dr.Lt.Data
e3a8b669b2 improved: scanner.py - more pattern 2025-12-23 12:34:55 +09:00
Dr.Lt.Data
80e5c8a987 update DB
Some checks are pending
Python Linting / Run Ruff (push) Waiting to run
2025-12-22 18:52:24 +09:00
Dr.Lt.Data
e0e4886e63 update DB
Some checks are pending
Python Linting / Run Ruff (push) Waiting to run
2025-12-22 12:29:41 +09:00
Dr.Lt.Data
c0947f4192 update DB 2025-12-22 12:15:03 +09:00
pixelpainter
7706b047ce
Update custom-node-list.json (#2414) 2025-12-22 12:08:57 +09:00
akawana
a44c6ff27c
Update Utils Extra to AK Pack with new details (#2413)
Updated the title and reference for a utility tool, and enhanced the description and tags.
2025-12-22 12:07:17 +09:00
Dr.Lt.Data
f4fdd51ce9 feat(preview): disable Manager preview method when ComfyUI native feature is available
Some checks failed
Python Linting / Run Ruff (push) Has been cancelled
- Add detection for ComfyUI PR #11261 (per-queue preview override)
- Return DISABLED status when native feature is detected
- Improve UI loading state and prevent flash of enabled state
- Add accessibility attributes and visual feedback for disabled state
- Show user notification when feature transitions to native
- Version bump to 3.39
2025-12-19 23:05:52 +09:00
David
ae6c7dd673
Changed Main Dialog to match aesthetics and close button location as Original ComfyUI Interface (#2349)
Some checks are pending
Python Linting / Run Ruff (push) Waiting to run
* Started changing UI to match the rest of ComfyUI

Completed Main Container

* - Added layout formatting to components of the Manager dialog box
- Pulled name from select and put it into a label (eg "DB: Channel" now has a label of DB and a dropdown with channel, etc)
- Fixed incorrect z-index

* Removed this.close() I added before finding z-index issue.

* Matched buttons and drop downs to match style of ComfyUI interface while keeping the colours the same as OG ComfyUI Manager

* - Took gui building out and put into its own .js
- Applied theme to Nodes Manager
- Made theme respect user theme colors

* - Themed model manager and snapshot manager
- fixed incorrect id in gui builder

* Fix syntax error in color property

---------

Co-authored-by: Dr.Lt.Data <128333288+ltdrdata@users.noreply.github.com>
2025-12-19 12:34:20 +09:00
Dr.Lt.Data
0cbc773126 update DB
Some checks are pending
Python Linting / Run Ruff (push) Waiting to run
2025-12-19 11:38:53 +09:00
Dr.Lt.Data
45bd3473fa update DB 2025-12-19 06:46:21 +09:00
shootthesound
02175844da
Updated my entry to include my new Block Editing Nodes (#2405)
* Add Realtime LoRA Trainer node to custom-node-list

Added a new node for Realtime LoRA Trainer with details.

* Enhance description for ComfyUI Loras training

Updated the description to include Block Edit and Save Loras functionality and Musubi Tuner support.
2025-12-19 06:43:52 +09:00
Dr.Lt.Data
fd60f7ee70 update DB
Some checks failed
Python Linting / Run Ruff (push) Has been cancelled
2025-12-17 13:03:57 +09:00
Dr.Lt.Data
9eb4c3ab23 update DB
Some checks are pending
Python Linting / Run Ruff (push) Waiting to run
2025-12-17 07:40:52 +09:00
Dr.Lt.Data
72d1aa7d97 update DB 2025-12-17 06:58:00 +09:00
Dr.Lt.Data
57628ead80 update DB 2025-12-17 06:04:44 +09:00
revisiontony
9733c2328b
Update custom-node-list.json (#2398) 2025-12-17 06:03:56 +09:00
Dr.Lt.Data
70663cecc3 update DB 2025-12-17 06:02:43 +09:00
tppp2806
7c77942a92
Add ComfyUI-YoloTrack node to custom-node-list.json (#2400)
* Add ComfyUI-YoloTrack node to custom-node-list.json

Added a new node for ComfyUI-YoloTrack with details.

* Update custom-node-list.json

---------

Co-authored-by: Dr.Lt.Data <128333288+ltdrdata@users.noreply.github.com>
2025-12-17 06:01:42 +09:00
Dr.Lt.Data
04cf18e149 update DB 2025-12-17 06:00:16 +09:00
akawana
1825edda7e
Add AK XZ Axis node information to custom-node-list (#2399) 2025-12-17 05:58:31 +09:00
Dr.Lt.Data
045f91c411 update DB
Some checks are pending
Python Linting / Run Ruff (push) Waiting to run
2025-12-16 12:53:56 +09:00
Dr.Lt.Data
96d24f548c update DB 2025-12-16 12:51:59 +09:00
Dr.Lt.Data
c7f03ad64e update DB
Some checks are pending
Python Linting / Run Ruff (push) Waiting to run
2025-12-15 22:28:27 +09:00
Dr.Lt.Data
1232989d7d update DB 2025-12-15 22:04:50 +09:00
Dr.Lt.Data
8f66a7997f update DB
Some checks are pending
Python Linting / Run Ruff (push) Waiting to run
2025-12-15 12:57:52 +09:00
Dr.Lt.Data
f32dd80c24 update DB
Some checks are pending
Python Linting / Run Ruff (push) Waiting to run
2025-12-15 03:16:01 +09:00
Gero Doll
a06ba343de
Add ComfyUI-PromptGenerator to custom node list (#2391)
Some checks are pending
Python Linting / Run Ruff (push) Waiting to run
Generate Stable Diffusion prompts using Qwen3-8B via Ollama with 7 style presets (cinematic, anime, photorealistic, fantasy, abstract, cyberpunk, sci-fi)
2025-12-15 00:36:47 +09:00
Dr.Lt.Data
bba55d4d5a update DB
Some checks failed
Python Linting / Run Ruff (push) Has been cancelled
2025-12-12 23:08:38 +09:00
Dr.Lt.Data
87111bd889 update DB 2025-12-12 22:09:35 +09:00
SKFRMSEHF
3661ffd3ab
Update custom-node-list.json (#2388) 2025-12-12 21:41:13 +09:00
Dr.Lt.Data
d8f111a5e3 bump version 2025-12-12 18:16:51 +09:00
Benjamin Lu
ae5565ce68
ComfyUI version listing + nightly current fix (#2334)
* Improve comfyui version listing

* Fix ComfyUI semver selection and stable update

* Fix nightly current detection on default branch

* Fix: use tag_ref.name explicitly and cache get_remote_name result

- Use tag_ref.name instead of tag_ref object for checkout
- Cache get_remote_name() result to avoid duplicate calls

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

---------

Co-authored-by: Dr.Lt.Data <dr.lt.data@gmail.com>
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-12 18:15:12 +09:00
Dr.Lt.Data
e4c370a7d9 update DB
Some checks are pending
Python Linting / Run Ruff (push) Waiting to run
2025-12-12 12:29:09 +09:00
23 changed files with 25023 additions and 16501 deletions

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

File diff suppressed because it is too large Load Diff

View File

@ -44,7 +44,7 @@ import manager_migration
from node_package import InstalledNodePackage from node_package import InstalledNodePackage
version_code = [3, 38, 2] version_code = [3, 39]
version_str = f"V{version_code[0]}.{version_code[1]}" + (f'.{version_code[2]}' if len(version_code) > 2 else '') version_str = f"V{version_code[0]}.{version_code[1]}" + (f'.{version_code[2]}' if len(version_code) > 2 else '')
@ -2531,22 +2531,22 @@ def update_to_stable_comfyui(repo_path):
logging.error('\t'+branch.name) logging.error('\t'+branch.name)
return "fail", None return "fail", None
versions, current_tag, _ = get_comfyui_versions(repo) versions, current_tag, latest_tag = get_comfyui_versions(repo)
if len(versions) == 0 or (len(versions) == 1 and versions[0] == 'nightly'): if latest_tag is None:
logging.info("[ComfyUI-Manager] Unable to update to the stable ComfyUI version.") logging.info("[ComfyUI-Manager] Unable to update to the stable ComfyUI version.")
return "fail", None return "fail", None
if versions[0] == 'nightly':
latest_tag = versions[1]
else:
latest_tag = versions[0]
if current_tag == latest_tag: tag_ref = next((t for t in repo.tags if t.name == latest_tag), None)
if tag_ref is None:
logging.info(f"[ComfyUI-Manager] Unable to locate tag '{latest_tag}' in repository.")
return "fail", None
if repo.head.commit == tag_ref.commit:
return "skip", None return "skip", None
else: else:
logging.info(f"[ComfyUI-Manager] Updating ComfyUI: {current_tag} -> {latest_tag}") logging.info(f"[ComfyUI-Manager] Updating ComfyUI: {current_tag} -> {latest_tag}")
repo.git.checkout(latest_tag) repo.git.checkout(tag_ref.name)
execute_install_script("ComfyUI", repo_path, instant_execution=False, no_deps=False) execute_install_script("ComfyUI", repo_path, instant_execution=False, no_deps=False)
return 'updated', latest_tag return 'updated', latest_tag
except: except:
@ -3370,36 +3370,80 @@ async def restore_snapshot(snapshot_path, git_helper_extras=None):
def get_comfyui_versions(repo=None): def get_comfyui_versions(repo=None):
if repo is None: repo = repo or git.Repo(comfy_path)
repo = git.Repo(comfy_path)
remote_name = None
try: try:
remote = get_remote_name(repo) remote_name = get_remote_name(repo)
repo.remotes[remote].fetch() repo.remotes[remote_name].fetch()
except: except:
logging.error("[ComfyUI-Manager] Failed to fetch ComfyUI") logging.error("[ComfyUI-Manager] Failed to fetch ComfyUI")
versions = [x.name for x in repo.tags if x.name.startswith('v')] def parse_semver(tag_name):
match = re.match(r'^v(\d+)\.(\d+)\.(\d+)$', tag_name)
return tuple(int(x) for x in match.groups()) if match else None
# nearest tag def normalize_describe(tag_name):
versions = sorted(versions, key=lambda v: repo.git.log('-1', '--format=%ct', v), reverse=True) if not tag_name:
versions = versions[:4] return None
base = tag_name.split('-', 1)[0]
return base if parse_semver(base) else None
current_tag = repo.git.describe('--tags') # Collect semver tags and sort descending (highest first)
semver_tags = []
for tag in repo.tags:
semver = parse_semver(tag.name)
if semver:
semver_tags.append((semver, tag.name))
semver_tags.sort(key=lambda x: x[0], reverse=True)
semver_tags = [name for _, name in semver_tags]
if current_tag not in versions: latest_tag = semver_tags[0] if semver_tags else None
versions = sorted(versions + [current_tag], key=lambda v: repo.git.log('-1', '--format=%ct', v), reverse=True)
versions = versions[:4]
main_branch = repo.heads.master try:
latest_commit = main_branch.commit described = repo.git.describe('--tags')
latest_tag = repo.git.describe('--tags', latest_commit.hexsha) except Exception:
described = ''
if latest_tag != versions[0]: try:
versions.insert(0, 'nightly') exact_tag = repo.git.describe('--tags', '--exact-match')
else: except Exception:
versions[0] = 'nightly' exact_tag = ''
head_is_default = False
if remote_name:
try:
default_head_ref = repo.refs[f'{remote_name}/HEAD']
default_commit = default_head_ref.reference.commit
head_is_default = repo.head.commit == default_commit
except Exception:
head_is_default = False
nearest_semver = normalize_describe(described)
exact_semver = exact_tag if parse_semver(exact_tag) else None
if head_is_default and not exact_tag:
current_tag = 'nightly' current_tag = 'nightly'
else:
current_tag = exact_tag or described or 'nightly'
# Prepare semver list for display: top 4 plus the current/nearest semver if missing
display_semver_tags = semver_tags[:4]
if exact_semver and exact_semver not in display_semver_tags:
display_semver_tags.append(exact_semver)
elif nearest_semver and nearest_semver not in display_semver_tags:
display_semver_tags.append(nearest_semver)
versions = ['nightly']
if current_tag and not exact_semver and current_tag not in versions and current_tag not in display_semver_tags:
versions.append(current_tag)
for tag in display_semver_tags:
if tag not in versions:
versions.append(tag)
versions = versions[:6]
return versions, current_tag, latest_tag return versions, current_tag, latest_tag

View File

@ -38,6 +38,25 @@ SECURITY_MESSAGE_NORMAL_MINUS_MODEL = "ERROR: Downloading models that are not in
routes = PromptServer.instance.routes routes = PromptServer.instance.routes
def has_per_queue_preview():
"""
Check if ComfyUI PR #11261 (per-queue live preview override) is merged
Returns:
bool: True if ComfyUI has per-queue preview feature
"""
try:
import latent_preview
return hasattr(latent_preview, 'set_preview_method')
except ImportError:
return False
# Detect ComfyUI per-queue preview override feature (PR #11261)
COMFYUI_HAS_PER_QUEUE_PREVIEW = has_per_queue_preview()
def handle_stream(stream, prefix): def handle_stream(stream, prefix):
stream.reconfigure(encoding=locale.getpreferredencoding(), errors='replace') stream.reconfigure(encoding=locale.getpreferredencoding(), errors='replace')
for msg in stream: for msg in stream:
@ -182,10 +201,19 @@ def set_preview_method(method):
core.get_config()['preview_method'] = method core.get_config()['preview_method'] = method
if args.preview_method == latent_preview.LatentPreviewMethod.NoPreviews: if COMFYUI_HAS_PER_QUEUE_PREVIEW:
logging.info(
"[ComfyUI-Manager] ComfyUI per-queue preview override detected (PR #11261). "
"Manager's preview method feature is disabled. "
"Use ComfyUI's --preview-method CLI option or 'Settings > Execution > Live preview method'."
)
elif args.preview_method == latent_preview.LatentPreviewMethod.NoPreviews:
set_preview_method(core.get_config()['preview_method']) set_preview_method(core.get_config()['preview_method'])
else: else:
logging.warning("[ComfyUI-Manager] Since --preview-method is set, ComfyUI-Manager's preview method feature will be ignored.") logging.warning(
"[ComfyUI-Manager] Since --preview-method is set, "
"ComfyUI-Manager's preview method feature will be ignored."
)
def set_component_policy(mode): def set_component_policy(mode):
@ -1482,13 +1510,25 @@ async def install_model(request):
@routes.get("/manager/preview_method") @routes.get("/manager/preview_method")
async def preview_method(request): async def preview_method(request):
# Setting change request
if "value" in request.rel_url.query: if "value" in request.rel_url.query:
# Reject setting change if per-queue preview feature is available
if COMFYUI_HAS_PER_QUEUE_PREVIEW:
return web.Response(text="DISABLED", status=403)
# Process normally if not available
set_preview_method(request.rel_url.query['value']) set_preview_method(request.rel_url.query['value'])
core.write_config() core.write_config()
else: return web.Response(status=200)
return web.Response(text=core.manager_funcs.get_current_preview_method(), status=200)
return web.Response(status=200) # Status query request
else:
# Return DISABLED if per-queue preview feature is available
if COMFYUI_HAS_PER_QUEUE_PREVIEW:
return web.Response(text="DISABLED", status=200)
# Return current value if not available
return web.Response(text=core.manager_funcs.get_current_preview_method(), status=200)
@routes.get("/manager/db_mode") @routes.get("/manager/db_mode")

227
js/comfyui-gui-builder.js Normal file
View File

@ -0,0 +1,227 @@
import { $el } from "../../scripts/ui.js";
function normalizeContent(content) {
const tmp = document.createElement('div');
if (typeof content === 'string') {
tmp.innerHTML = content;
return Array.from(tmp.childNodes);
}
if (content instanceof Node) {
return content;
}
return content;
}
export function createSettingsCombo(label, content) {
const settingItem = $el("div.setting-item", {}, [
$el("div.flex.flex-row.items-center.gap-2",[
$el("div.form-label.flex.grow.items-center", [
$el("span.text-muted", { textContent: label },)
]),
$el("div.form-input.flex.justify-end",
[content]
)
]
)
]);
return settingItem;
}
export function buildGuiFrame(dialogId, title, iconClass, content, owner) {
const dialog_mask = $el("div.p-dialog-mask.p-overlay-mask.p-overlay-mask-enter", {
parent: document.body,
style: {
position: "fixed",
height: "100%",
width: "100%",
left: "0px",
top: "0px",
display: "flex",
justifyContent: "center",
alignItems: "center",
pointerEvents: "auto",
zIndex: "1000"
},
onclick: (e) => {
if (e.target === dialog_mask) {
owner.close();
}
}
// data-pc-section="mask"
});
const header_actions = $el("div.p-dialog-header-actions", {
// [TODO]
// data-pc-section="headeractions"
}
);
const close_button = $el("button.p-button.p-component.p-button-icon-only.p-button-secondary.p-button-rounded.p-button-text.p-dialog-close-button", {
parent: header_actions,
type: "button",
ariaLabel: "Close",
onclick: () => owner.close(),
// "data-pc-name": "pcclosebutton",
// "data-p-disabled": "false",
// "data-p-severity": "secondary",
// "data-pc-group-section": "headericon",
// "data-pc-extend": "button",
// "data-pc-section": "root",
// [FIXME] Not sure how to do most of the SVG using $el
innerHTML: '<svg width="14" height="14" viewBox="0 0 14 14" fill="none" xmlns="http://www.w3.org/2000/svg" class="p-icon p-button-icon" aria-hidden="true"><path d="M8.01186 7.00933L12.27 2.75116C12.341 2.68501 12.398 2.60524 12.4375 2.51661C12.4769 2.42798 12.4982 2.3323 12.4999 2.23529C12.5016 2.13827 12.4838 2.0419 12.4474 1.95194C12.4111 1.86197 12.357 1.78024 12.2884 1.71163C12.2198 1.64302 12.138 1.58893 12.0481 1.55259C11.9581 1.51625 11.8617 1.4984 11.7647 1.50011C11.6677 1.50182 11.572 1.52306 11.4834 1.56255C11.3948 1.60204 11.315 1.65898 11.2488 1.72997L6.99067 5.98814L2.7325 1.72997C2.59553 1.60234 2.41437 1.53286 2.22718 1.53616C2.03999 1.53946 1.8614 1.61529 1.72901 1.74767C1.59663 1.88006 1.5208 2.05865 1.5175 2.24584C1.5142 2.43303 1.58368 2.61419 1.71131 2.75116L5.96948 7.00933L1.71131 11.2675C1.576 11.403 1.5 11.5866 1.5 11.7781C1.5 11.9696 1.576 12.1532 1.71131 12.2887C1.84679 12.424 2.03043 12.5 2.2219 12.5C2.41338 12.5 2.59702 12.424 2.7325 12.2887L6.99067 8.03052L11.2488 12.2887C11.3843 12.424 11.568 12.5 11.7594 12.5C11.9509 12.5 12.1346 12.424 12.27 12.2887C12.4053 12.1532 12.4813 11.9696 12.4813 11.7781C12.4813 11.5866 12.4053 11.403 12.27 11.2675L8.01186 7.00933Z" fill="currentColor"></path></svg><span class="p-button-label" data-pc-section="label">&nbsp;</span><!---->'
}
);
const dialog_header = $el("div.p-dialog-header",
[
$el("div", [
$el("div",
{
id: "frame-title-container",
},
[
$el("h2.px-4", [
$el(iconClass, {
style: {
"font-size": "1.25rem",
"margin-right": ".5rem"
}
}),
$el("span", { textContent: title })
])
]
)
]),
header_actions
]
);
const contentFrame = $el("div.p-dialog-content", {}, normalizeContent(content));
const manager_dialog = $el("div.p-dialog.p-component.global-dialog", {
id: dialogId,
parent: dialog_mask,
style: {
'display': 'flex',
'flex-direction': 'column',
'pointer-events': 'auto',
'margin': '0px',
},
role: 'dialog',
ariaModal: 'true',
// [TODO]
// ariaLabbelledby: 'cm-title',
// maximized: 'false',
// data-pc-name: 'dialog',
// data-pc-section: 'root',
// data-pd-focustrap: 'true'
},
[ dialog_header, contentFrame ]
);
const hidden_accessible = $el("span.p-hidden-accessible.p-hidden-focusable", {
parent: manager_dialog,
tabindex: "0",
role: "presentation",
ariaHidden: "true",
"data-p-hidden-accessible": "true",
"data-p-hidden-focusable": "true",
"data-pc-section": "firstfocusableelement"
});
return dialog_mask;
}
export function buildGuiFrameCustomHeader(dialogId, customHeader, content, owner) {
const dialog_mask = $el("div.p-dialog-mask.p-overlay-mask.p-overlay-mask-enter", {
parent: document.body,
style: {
position: "fixed",
height: "100%",
width: "100%",
left: "0px",
top: "0px",
display: "flex",
justifyContent: "center",
alignItems: "center",
pointerEvents: "auto",
zIndex: "1000"
},
onclick: (e) => {
if (e.target === dialog_mask) {
owner.close();
}
}
// data-pc-section="mask"
});
const header_actions = $el("div.p-dialog-header-actions", {
// [TODO]
// data-pc-section="headeractions"
}
);
const close_button = $el("button.p-button.p-component.p-button-icon-only.p-button-secondary.p-button-rounded.p-button-text.p-dialog-close-button", {
parent: header_actions,
type: "button",
ariaLabel: "Close",
onclick: () => owner.close(),
// "data-pc-name": "pcclosebutton",
// "data-p-disabled": "false",
// "data-p-severity": "secondary",
// "data-pc-group-section": "headericon",
// "data-pc-extend": "button",
// "data-pc-section": "root",
// [FIXME] Not sure how to do most of the SVG using $el
innerHTML: '<svg width="14" height="14" viewBox="0 0 14 14" fill="none" xmlns="http://www.w3.org/2000/svg" class="p-icon p-button-icon" aria-hidden="true"><path d="M8.01186 7.00933L12.27 2.75116C12.341 2.68501 12.398 2.60524 12.4375 2.51661C12.4769 2.42798 12.4982 2.3323 12.4999 2.23529C12.5016 2.13827 12.4838 2.0419 12.4474 1.95194C12.4111 1.86197 12.357 1.78024 12.2884 1.71163C12.2198 1.64302 12.138 1.58893 12.0481 1.55259C11.9581 1.51625 11.8617 1.4984 11.7647 1.50011C11.6677 1.50182 11.572 1.52306 11.4834 1.56255C11.3948 1.60204 11.315 1.65898 11.2488 1.72997L6.99067 5.98814L2.7325 1.72997C2.59553 1.60234 2.41437 1.53286 2.22718 1.53616C2.03999 1.53946 1.8614 1.61529 1.72901 1.74767C1.59663 1.88006 1.5208 2.05865 1.5175 2.24584C1.5142 2.43303 1.58368 2.61419 1.71131 2.75116L5.96948 7.00933L1.71131 11.2675C1.576 11.403 1.5 11.5866 1.5 11.7781C1.5 11.9696 1.576 12.1532 1.71131 12.2887C1.84679 12.424 2.03043 12.5 2.2219 12.5C2.41338 12.5 2.59702 12.424 2.7325 12.2887L6.99067 8.03052L11.2488 12.2887C11.3843 12.424 11.568 12.5 11.7594 12.5C11.9509 12.5 12.1346 12.424 12.27 12.2887C12.4053 12.1532 12.4813 11.9696 12.4813 11.7781C12.4813 11.5866 12.4053 11.403 12.27 11.2675L8.01186 7.00933Z" fill="currentColor"></path></svg><span class="p-button-label" data-pc-section="label">&nbsp;</span><!---->'
}
);
const _customHeader = normalizeContent(customHeader);
const dialog_header = $el("div.p-dialog-header",
[
$el("div", [
$el("div",
{
id: "frame-title-container",
},
Array.isArray(_customHeader) ? _customHeader : [_customHeader]
)
]),
header_actions
]
);
const contentFrame = $el("div.p-dialog-content", {}, normalizeContent(content));
const manager_dialog = $el("div.p-dialog.p-component.global-dialog", {
id: dialogId,
parent: dialog_mask,
style: {
'display': 'flex',
'flex-direction': 'column',
'pointer-events': 'auto',
'margin': '0px',
},
role: 'dialog',
ariaModal: 'true',
// [TODO]
// ariaLabbelledby: 'cm-title',
// maximized: 'false',
// data-pc-name: 'dialog',
// data-pc-section: 'root',
// data-pd-focustrap: 'true'
},
[ dialog_header, contentFrame ]
);
const hidden_accessible = $el("span.p-hidden-accessible.p-hidden-focusable", {
parent: manager_dialog,
tabindex: "0",
role: "presentation",
ariaHidden: "true",
"data-p-hidden-accessible": "true",
"data-p-hidden-focusable": "true",
"data-pc-section": "firstfocusableelement"
});
return dialog_mask;
}

View File

@ -20,6 +20,7 @@ import { ComponentBuilderDialog, getPureName, load_components, set_component_pol
import { CustomNodesManager } from "./custom-nodes-manager.js"; import { CustomNodesManager } from "./custom-nodes-manager.js";
import { ModelManager } from "./model-manager.js"; import { ModelManager } from "./model-manager.js";
import { SnapshotManager } from "./snapshot.js"; import { SnapshotManager } from "./snapshot.js";
import { buildGuiFrame, createSettingsCombo } from "./comfyui-gui-builder.js";
let manager_version = await getVersion(); let manager_version = await getVersion();
@ -44,12 +45,16 @@ docStyle.innerHTML = `
#cm-manager-dialog { #cm-manager-dialog {
width: 1000px; width: 1000px;
height: 455px; height: auto;
box-sizing: content-box; box-sizing: content-box;
z-index: 1000; z-index: 1000;
overflow-y: auto; overflow-y: auto;
} }
#cm-manager-dialog br {
margin-bottom: 1em;
}
.cb-widget { .cb-widget {
width: 400px; width: 400px;
height: 25px; height: 25px;
@ -80,6 +85,7 @@ docStyle.innerHTML = `
} }
.cm-menu-container { .cm-menu-container {
padding : calc(var(--spacing)*2);
column-gap: 20px; column-gap: 20px;
display: flex; display: flex;
flex-wrap: wrap; flex-wrap: wrap;
@ -140,8 +146,8 @@ docStyle.innerHTML = `
} }
.cm-notice-board { .cm-notice-board {
width: 290px; width: auto;
height: 230px; height: 280px;
overflow: auto; overflow: auto;
color: var(--input-text); color: var(--input-text);
border: 1px solid var(--descrip-text); border: 1px solid var(--descrip-text);
@ -238,68 +244,50 @@ var is_updating = false;
// copied style from https://github.com/pythongosssss/ComfyUI-Custom-Scripts // copied style from https://github.com/pythongosssss/ComfyUI-Custom-Scripts
const style = ` const style = `
#workflowgallery-button { #workflowgallery-button {
width: 310px; height: 50px;
height: 27px;
padding: 0px !important; padding: 0px !important;
position: relative;
overflow: hidden;
font-size: 17px !important;
} }
#cm-nodeinfo-button { #cm-nodeinfo-button {
width: 310px;
height: 27px;
padding: 0px !important;
position: relative;
overflow: hidden;
font-size: 17px !important;
} }
#cm-manual-button { #cm-manual-button {
width: 310px;
height: 27px;
position: relative;
overflow: hidden;
} }
.cm-button { .cm-button {
width: 310px; width: auto;
height: 30px;
position: relative; position: relative;
overflow: hidden; overflow: hidden;
font-size: 17px !important; background-color: var(--comfy-menu-secondary-bg);
border-color: var(--border-color);
color: var(--input-text);
}
.cm-button:hover {
filter: brightness(125%);
} }
.cm-button-red { .cm-button-red {
width: 310px;
height: 30px;
position: relative;
overflow: hidden;
font-size: 17px !important;
background-color: #500000 !important; background-color: #500000 !important;
border-color: #88181b !important;
color: white !important; color: white !important;
} }
.cm-button-red:hover {
background-color: #88181b !important;
}
.cm-button-orange { .cm-button-orange {
width: 310px;
height: 30px;
position: relative;
overflow: hidden;
font-size: 17px !important;
font-weight: bold; font-weight: bold;
background-color: orange !important; background-color: orange !important;
color: black !important; color: black !important;
} }
.cm-experimental-button { .cm-experimental-button {
width: 290px; width: 100%;
height: 30px;
position: relative;
overflow: hidden;
font-size: 17px !important;
} }
.cm-experimental { .cm-experimental {
width: 310px;
border: 1px solid #555; border: 1px solid #555;
border-radius: 5px; border-radius: 5px;
padding: 10px; padding: 10px;
@ -326,8 +314,14 @@ const style = `
.cm-menu-combo { .cm-menu-combo {
cursor: pointer; cursor: pointer;
width: 310px; padding: 0.5em 0.5em;
box-sizing: border-box; border: 1px solid var(--border-color);
border-radius: 6px;
background: var(--comfy-menu-secondary-bg);
}
.cm-menu-combo:hover {
filter: brightness(125%);
} }
.cm-small-button { .cm-small-button {
@ -831,7 +825,7 @@ class ManagerMenuDialog extends ComfyDialog {
const isElectron = 'electronAPI' in window; const isElectron = 'electronAPI' in window;
update_comfyui_button = update_comfyui_button =
$el("button.cm-button", { $el("button.p-button.p-component.cm-button", {
type: "button", type: "button",
textContent: "Update ComfyUI", textContent: "Update ComfyUI",
style: { style: {
@ -842,7 +836,7 @@ class ManagerMenuDialog extends ComfyDialog {
}); });
switch_comfyui_button = switch_comfyui_button =
$el("button.cm-button", { $el("button.p-button.p-component.cm-button", {
type: "button", type: "button",
textContent: "Switch ComfyUI", textContent: "Switch ComfyUI",
style: { style: {
@ -853,7 +847,7 @@ class ManagerMenuDialog extends ComfyDialog {
}); });
restart_stop_button = restart_stop_button =
$el("button.cm-button-red", { $el("button.p-button.p-component.cm-button-red", {
type: "button", type: "button",
textContent: "Restart", textContent: "Restart",
onclick: () => restartOrStop() onclick: () => restartOrStop()
@ -861,7 +855,7 @@ class ManagerMenuDialog extends ComfyDialog {
if(isElectron) { if(isElectron) {
update_all_button = update_all_button =
$el("button.cm-button", { $el("button.p-button.p-component.cm-button", {
type: "button", type: "button",
textContent: "Update All Custom Nodes", textContent: "Update All Custom Nodes",
onclick: onclick:
@ -870,7 +864,7 @@ class ManagerMenuDialog extends ComfyDialog {
} }
else { else {
update_all_button = update_all_button =
$el("button.cm-button", { $el("button.p-button.p-component.cm-button", {
type: "button", type: "button",
textContent: "Update All", textContent: "Update All",
onclick: onclick:
@ -880,7 +874,7 @@ class ManagerMenuDialog extends ComfyDialog {
const res = const res =
[ [
$el("button.cm-button", { $el("button.p-button.p-component.cm-button", {
type: "button", type: "button",
textContent: "Custom Nodes Manager", textContent: "Custom Nodes Manager",
onclick: onclick:
@ -892,7 +886,7 @@ class ManagerMenuDialog extends ComfyDialog {
} }
}), }),
$el("button.cm-button", { $el("button.p-button.p-component.cm-button", {
type: "button", type: "button",
textContent: "Install Missing Custom Nodes", textContent: "Install Missing Custom Nodes",
onclick: onclick:
@ -904,7 +898,7 @@ class ManagerMenuDialog extends ComfyDialog {
} }
}), }),
$el("button.cm-button", { $el("button.p-button.p-component.cm-button", {
type: "button", type: "button",
textContent: "Custom Nodes In Workflow", textContent: "Custom Nodes In Workflow",
onclick: onclick:
@ -916,8 +910,8 @@ class ManagerMenuDialog extends ComfyDialog {
} }
}), }),
$el("br", {}, []), $el("div", {}, []),
$el("button.cm-button", { $el("button.p-button.p-component.cm-button", {
type: "button", type: "button",
textContent: "Model Manager", textContent: "Model Manager",
onclick: onclick:
@ -929,7 +923,7 @@ class ManagerMenuDialog extends ComfyDialog {
} }
}), }),
$el("button.cm-button", { $el("button.p-button.p-component.cm-button", {
type: "button", type: "button",
textContent: "Install via Git URL", textContent: "Install via Git URL",
onclick: async () => { onclick: async () => {
@ -941,13 +935,13 @@ class ManagerMenuDialog extends ComfyDialog {
} }
}), }),
$el("br", {}, []), $el("div", {}, []),
update_all_button, update_all_button,
update_comfyui_button, update_comfyui_button,
switch_comfyui_button, switch_comfyui_button,
// fetch_updates_button, // fetch_updates_button,
$el("br", {}, []), $el("div", {}, []),
restart_stop_button, restart_stop_button,
]; ];
@ -960,12 +954,13 @@ class ManagerMenuDialog extends ComfyDialog {
let self = this; let self = this;
// db mode // db mode
this.datasrc_combo = document.createElement("select"); this.datasrc_combo = document.createElement("select");
this.datasrc_combo.setAttribute("title", "Configure where to retrieve node/model information. If set to 'local,' the channel is ignored, and if set to 'channel (remote),' it fetches the latest information each time the list is opened."); this.datasrc_combo.setAttribute("title", "Configure where to retrieve node/model information. If set to 'local,' the channel is ignored, and if set to 'channel (remote),' it fetches the latest information each time the list is opened.");
this.datasrc_combo.className = "cm-menu-combo"; this.datasrc_combo.className = "cm-menu-combo p-select p-component p-inputwrapper p-inputwrapper-filled ";
this.datasrc_combo.appendChild($el('option', { value: 'cache', text: 'DB: Channel (1day cache)' }, [])); this.datasrc_combo.appendChild($el('option', { value: 'cache', text: 'Channel (1day cache)' }, []));
this.datasrc_combo.appendChild($el('option', { value: 'local', text: 'DB: Local' }, [])); this.datasrc_combo.appendChild($el('option', { value: 'local', text: 'Local' }, []));
this.datasrc_combo.appendChild($el('option', { value: 'remote', text: 'DB: Channel (remote)' }, [])); this.datasrc_combo.appendChild($el('option', { value: 'remote', text: 'Channel (remote)' }, []));
api.fetchApi('/manager/db_mode') api.fetchApi('/manager/db_mode')
.then(response => response.text()) .then(response => response.text())
@ -975,27 +970,110 @@ class ManagerMenuDialog extends ComfyDialog {
api.fetchApi(`/manager/db_mode?value=${event.target.value}`); api.fetchApi(`/manager/db_mode?value=${event.target.value}`);
}); });
const dbRetrievalSetttingItem = createSettingsCombo("DB", this.datasrc_combo);
// preview method // preview method
let preview_combo = document.createElement("select"); let preview_combo = document.createElement("select");
preview_combo.setAttribute("title", "Configure how latent variables will be decoded during preview in the sampling process."); preview_combo.setAttribute("title", "Configure how latent variables will be decoded during preview in the sampling process.");
preview_combo.className = "cm-menu-combo"; preview_combo.className = "cm-menu-combo p-select p-component p-inputwrapper p-inputwrapper-filled";
preview_combo.appendChild($el('option', { value: 'auto', text: 'Preview method: Auto' }, []));
preview_combo.appendChild($el('option', { value: 'taesd', text: 'Preview method: TAESD (slow)' }, []));
preview_combo.appendChild($el('option', { value: 'latent2rgb', text: 'Preview method: Latent2RGB (fast)' }, []));
preview_combo.appendChild($el('option', { value: 'none', text: 'Preview method: None (very fast)' }, []));
// Loading state to prevent flash of enabled state
preview_combo.appendChild($el('option', { value: '', text: 'Loading...', disabled: true }, []));
preview_combo.appendChild($el('option', { value: 'auto', text: 'Auto' }, []));
preview_combo.appendChild($el('option', { value: 'taesd', text: 'TAESD (slow)' }, []));
preview_combo.appendChild($el('option', { value: 'latent2rgb', text: 'Latent2RGB (fast)' }, []));
preview_combo.appendChild($el('option', { value: 'none', text: 'None (very fast)' }, []));
// Start disabled to prevent flash
preview_combo.disabled = true;
preview_combo.value = '';
// Fetch current state
api.fetchApi('/manager/preview_method') api.fetchApi('/manager/preview_method')
.then(response => response.text()) .then(response => response.text())
.then(data => { preview_combo.value = data; }); .then(data => {
// Remove loading option
preview_combo.querySelector('option[value=""]')?.remove();
if (data === "DISABLED") {
// ComfyUI per-queue preview feature is active
preview_combo.disabled = true;
preview_combo.value = 'auto';
// Accessibility attributes
preview_combo.setAttribute("aria-disabled", "true");
preview_combo.setAttribute("aria-label",
"Preview method setting (disabled - managed by ComfyUI). " +
"Use Settings > Execution > Live preview method instead."
);
// Tooltip for mouse users
preview_combo.setAttribute("title",
"This feature is now provided natively by ComfyUI. " +
"Please use 'Settings > Execution > Live preview method' instead."
);
// Visual feedback
preview_combo.style.opacity = '0.6';
preview_combo.style.cursor = 'not-allowed';
} else {
// Manager feature is active
preview_combo.disabled = false;
preview_combo.value = data;
// Accessibility for enabled state
preview_combo.setAttribute("aria-label",
"Preview method setting. Select how latent variables are decoded during preview."
);
}
})
.catch(error => {
console.error('[ComfyUI-Manager] Failed to fetch preview method status:', error);
// Error recovery: fallback to enabled
preview_combo.querySelector('option[value=""]')?.remove();
preview_combo.disabled = false;
preview_combo.value = 'auto';
});
preview_combo.addEventListener('change', function (event) { preview_combo.addEventListener('change', function (event) {
api.fetchApi(`/manager/preview_method?value=${event.target.value}`); // Ignore if disabled
if (preview_combo.disabled) {
event.preventDefault();
return;
}
// Normal operation
api.fetchApi(`/manager/preview_method?value=${event.target.value}`)
.then(response => {
if (response.status === 403) {
// Feature transitioned to native
alert(
'This feature is now provided natively by ComfyUI.\n' +
'Please use \'Settings > Execution > Live preview method\' instead.'
);
preview_combo.disabled = true;
preview_combo.style.opacity = '0.6';
preview_combo.style.cursor = 'not-allowed';
// Update aria attributes
preview_combo.setAttribute("aria-disabled", "true");
preview_combo.setAttribute("aria-label",
"Preview method setting (disabled - managed by ComfyUI). " +
"Use Settings > Execution > Live preview method instead."
);
}
})
.catch(error => {
console.error('[ComfyUI-Manager] Preview method update failed:', error);
});
}); });
const previewSetttingItem = createSettingsCombo("Preview method", preview_combo);
// channel // channel
let channel_combo = document.createElement("select"); let channel_combo = document.createElement("select");
channel_combo.setAttribute("title", "Configure the channel for retrieving data from the Custom Node list (including missing nodes) or the Model list."); channel_combo.setAttribute("title", "Configure the channel for retrieving data from the Custom Node list (including missing nodes) or the Model list.");
channel_combo.className = "cm-menu-combo"; channel_combo.className = "cm-menu-combo p-select p-component p-inputwrapper p-inputwrapper-filled";
api.fetchApi('/manager/channel_url_list') api.fetchApi('/manager/channel_url_list')
.then(response => response.json()) .then(response => response.json())
.then(async data => { .then(async data => {
@ -1004,7 +1082,7 @@ class ManagerMenuDialog extends ComfyDialog {
for (let i in urls) { for (let i in urls) {
if (urls[i] != '') { if (urls[i] != '') {
let name_url = urls[i].split('::'); let name_url = urls[i].split('::');
channel_combo.appendChild($el('option', { value: name_url[0], text: `Channel: ${name_url[0]}` }, [])); channel_combo.appendChild($el('option', { value: name_url[0], text: `${name_url[0]}` }, []));
} }
} }
@ -1019,11 +1097,13 @@ class ManagerMenuDialog extends ComfyDialog {
} }
}); });
const channelSetttingItem = createSettingsCombo("Channel", channel_combo);
// share // share
let share_combo = document.createElement("select"); let share_combo = document.createElement("select");
share_combo.setAttribute("title", "Hide the share button in the main menu or set the default action upon clicking it. Additionally, configure the default share site when sharing via the context menu's share button."); share_combo.setAttribute("title", "Hide the share button in the main menu or set the default action upon clicking it. Additionally, configure the default share site when sharing via the context menu's share button.");
share_combo.className = "cm-menu-combo"; share_combo.className = "cm-menu-combo p-select p-component p-inputwrapper p-inputwrapper-filled";
const share_options = [ const share_options = [
['none', 'None'], ['none', 'None'],
['openart', 'OpenArt AI'], ['openart', 'OpenArt AI'],
@ -1034,7 +1114,7 @@ class ManagerMenuDialog extends ComfyDialog {
['all', 'All'], ['all', 'All'],
]; ];
for (const option of share_options) { for (const option of share_options) {
share_combo.appendChild($el('option', { value: option[0], text: `Share: ${option[1]}` }, [])); share_combo.appendChild($el('option', { value: option[0], text: `${option[1]}` }, []));
} }
api.fetchApi('/manager/share_option') api.fetchApi('/manager/share_option')
@ -1056,12 +1136,14 @@ class ManagerMenuDialog extends ComfyDialog {
} }
}); });
const shareSetttingItem = createSettingsCombo("Share", share_combo);
let component_policy_combo = document.createElement("select"); let component_policy_combo = document.createElement("select");
component_policy_combo.setAttribute("title", "When loading the workflow, configure which version of the component to use."); component_policy_combo.setAttribute("title", "When loading the workflow, configure which version of the component to use.");
component_policy_combo.className = "cm-menu-combo"; component_policy_combo.className = "cm-menu-combo p-select p-component p-inputwrapper p-inputwrapper-filled";
component_policy_combo.appendChild($el('option', { value: 'workflow', text: 'Component: Use workflow version' }, [])); component_policy_combo.appendChild($el('option', { value: 'workflow', text: 'Use workflow version' }, []));
component_policy_combo.appendChild($el('option', { value: 'higher', text: 'Component: Use higher version' }, [])); component_policy_combo.appendChild($el('option', { value: 'higher', text: 'Use higher version' }, []));
component_policy_combo.appendChild($el('option', { value: 'mine', text: 'Component: Use my version' }, [])); component_policy_combo.appendChild($el('option', { value: 'mine', text: 'Use my version' }, []));
api.fetchApi('/manager/policy/component') api.fetchApi('/manager/policy/component')
.then(response => response.text()) .then(response => response.text())
.then(data => { .then(data => {
@ -1074,15 +1156,14 @@ class ManagerMenuDialog extends ComfyDialog {
set_component_policy(event.target.value); set_component_policy(event.target.value);
}); });
update_policy_combo = document.createElement("select"); const componentSetttingItem = createSettingsCombo("Component", component_policy_combo);
if(isElectron) update_policy_combo = document.createElement("select");
update_policy_combo.style.display = 'none';
update_policy_combo.setAttribute("title", "Sets the policy to be applied when performing an update."); update_policy_combo.setAttribute("title", "Sets the policy to be applied when performing an update.");
update_policy_combo.className = "cm-menu-combo"; update_policy_combo.className = "cm-menu-combo p-select p-component p-inputwrapper p-inputwrapper-filled";
update_policy_combo.appendChild($el('option', { value: 'stable-comfyui', text: 'Update: ComfyUI Stable Version' }, [])); update_policy_combo.appendChild($el('option', { value: 'stable-comfyui', text: 'ComfyUI Stable Version' }, []));
update_policy_combo.appendChild($el('option', { value: 'nightly-comfyui', text: 'Update: ComfyUI Nightly Version' }, [])); update_policy_combo.appendChild($el('option', { value: 'nightly-comfyui', text: 'ComfyUI Nightly Version' }, []));
api.fetchApi('/manager/policy/update') api.fetchApi('/manager/policy/update')
.then(response => response.text()) .then(response => response.text())
.then(data => { .then(data => {
@ -1093,20 +1174,22 @@ class ManagerMenuDialog extends ComfyDialog {
api.fetchApi(`/manager/policy/update?value=${event.target.value}`); api.fetchApi(`/manager/policy/update?value=${event.target.value}`);
}); });
return [ const updateSetttingItem = createSettingsCombo("Update", update_policy_combo);
$el("br", {}, []),
this.datasrc_combo, if(isElectron)
channel_combo, updateSetttingItem.style.display = 'none';
preview_combo,
share_combo,
component_policy_combo,
update_policy_combo,
$el("br", {}, []),
$el("br", {}, []), return [
$el("filedset.cm-experimental", {}, [ dbRetrievalSetttingItem,
channelSetttingItem,
previewSetttingItem,
shareSetttingItem,
componentSetttingItem,
updateSetttingItem,
//[TODO] replace mt-2 with wrapper div with flex column gap
$el("filedset.cm-experimental.mt-auto", {}, [
$el("legend.cm-experimental-legend", {}, ["EXPERIMENTAL"]), $el("legend.cm-experimental-legend", {}, ["EXPERIMENTAL"]),
$el("button.cm-experimental-button", { $el("button.p-button.p-component.cm-button.cm-experimental-button", {
type: "button", type: "button",
textContent: "Snapshot Manager", textContent: "Snapshot Manager",
onclick: onclick:
@ -1116,7 +1199,7 @@ class ManagerMenuDialog extends ComfyDialog {
SnapshotManager.instance.show(); SnapshotManager.instance.show();
} }
}), }),
$el("button.cm-experimental-button", { $el("button.p-button.p-component.cm-button.cm-experimental-button.mt-2", {
type: "button", type: "button",
textContent: "Install PIP packages", textContent: "Install PIP packages",
onclick: onclick:
@ -1134,7 +1217,7 @@ class ManagerMenuDialog extends ComfyDialog {
createControlsRight() { createControlsRight() {
const elts = [ const elts = [
$el("button.cm-button", { $el("button.p-button.p-component.cm-button", {
id: 'cm-manual-button', id: 'cm-manual-button',
type: "button", type: "button",
textContent: "Community Manual", textContent: "Community Manual",
@ -1185,11 +1268,11 @@ class ManagerMenuDialog extends ComfyDialog {
}) })
]), ]),
$el("button", { $el("button.p-button.p-component.cm-button", {
id: 'workflowgallery-button', id: 'workflowgallery-button',
type: "button", type: "button",
style: { style: {
...(localStorage.getItem("wg_last_visited") ? {height: '50px'} : {}) // ...(localStorage.getItem("wg_last_visited") ? {height: '50px'} : {})
}, },
onclick: (e) => { onclick: (e) => {
const last_visited_site = localStorage.getItem("wg_last_visited") const last_visited_site = localStorage.getItem("wg_last_visited")
@ -1212,7 +1295,7 @@ class ManagerMenuDialog extends ComfyDialog {
}, [ }, [
$el("p", { $el("p", {
id: 'workflowgallery-button-last-visited-label', id: 'workflowgallery-button-last-visited-label',
textContent: `(${localStorage.getItem("wg_last_visited") ? localStorage.getItem("wg_last_visited").split('/')[2] : ''})`, textContent: `(${localStorage.getItem("wg_last_visited") ? localStorage.getItem("wg_last_visited").split('/')[2] : 'none selected'})`,
style: { style: {
'text-align': 'center', 'text-align': 'center',
'color': 'var(--input-text)', 'color': 'var(--input-text)',
@ -1228,13 +1311,12 @@ class ManagerMenuDialog extends ComfyDialog {
}) })
]), ]),
$el("button.cm-button", { $el("button.p-button.p-component.cm-button", {
id: 'cm-nodeinfo-button', id: 'cm-nodeinfo-button',
type: "button", type: "button",
textContent: "Nodes Info", textContent: "Nodes Info",
onclick: () => { window.open("https://ltdrdata.github.io/", "comfyui-node-info"); } onclick: () => { window.open("https://ltdrdata.github.io/", "comfyui-node-info"); }
}), }),
$el("br", {}, []),
]; ];
var textarea = document.createElement("div"); var textarea = document.createElement("div");
@ -1249,31 +1331,23 @@ class ManagerMenuDialog extends ComfyDialog {
constructor() { constructor() {
super(); super();
const close_button = $el("button", { id: "cm-close-button", type: "button", textContent: "Close", onclick: () => this.close() }); const content = $el("div.cm-menu-container",
[
$el("div.cm-menu-column.gap-2", [...this.createControlsLeft()]),
$el("div.cm-menu-column.gap-2", [...this.createControlsMid()]),
$el("div.cm-menu-column.gap-2", [...this.createControlsRight()])
]
);
const content = const frame = buildGuiFrame(
$el("div.comfy-modal-content", 'cm-manager-dialog', // dialog id
[ `ComfyUI Manager ${manager_version}`, // dialog title
$el("tr.cm-title", {}, [ "i.mdi.mdi-puzzle", // dialog icon class to show before title
$el("font", {size:6, color:"white"}, [`ComfyUI Manager ${manager_version}`])] content, // dialog content element
), this
$el("br", {}, []), ); // send this so we can attach close functions
$el("div.cm-menu-container",
[
$el("div.cm-menu-column", [...this.createControlsLeft()]),
$el("div.cm-menu-column", [...this.createControlsMid()]),
$el("div.cm-menu-column", [...this.createControlsRight()])
]),
$el("br", {}, []), this.element = frame;
close_button,
]
);
content.style.width = '100%';
content.style.height = '100%';
this.element = $el("div.comfy-modal", { id:'cm-manager-dialog', parent: document.body }, [ content ]);
} }
get isVisible() { get isVisible() {
@ -1281,7 +1355,7 @@ class ManagerMenuDialog extends ComfyDialog {
} }
show() { show() {
this.element.style.display = "block"; this.element.style.display = "flex";
} }
toggleVisibility() { toggleVisibility() {

View File

@ -1,8 +1,9 @@
.cn-manager { .cn-manager {
--grid-font: -apple-system, BlinkMacSystemFont, "Segoe UI", "Noto Sans", Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji"; --grid-font: -apple-system, BlinkMacSystemFont, "Segoe UI", "Noto Sans", Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji";
z-index: 1099; z-index: 1099;
width: 80%; width: 80vw;
height: 80%; height: 75vh;
min-height: 30em;
display: flex; display: flex;
flex-direction: column; flex-direction: column;
gap: 10px; gap: 10px;
@ -10,6 +11,7 @@
font-family: arial, sans-serif; font-family: arial, sans-serif;
text-underline-offset: 3px; text-underline-offset: 3px;
outline: none; outline: none;
margin: calc(var(--spacing)*2);
} }
.cn-manager .cn-flex-auto { .cn-manager .cn-flex-auto {
@ -17,17 +19,21 @@
} }
.cn-manager button { .cn-manager button {
width: auto;
position: relative;
overflow: hidden;
font-size: 16px; font-size: 16px;
color: var(--input-text); color: var(--input-text);
background-color: var(--comfy-input-bg); background-color: var(--comfy-input-bg);
border-radius: 8px;
border-color: var(--border-color); border-color: var(--border-color);
border-style: solid;
margin: 0; margin: 0;
padding: 4px 8px;
min-width: 100px; min-width: 100px;
} }
.cn-manager button:hover {
filter: brightness(125%);
}
.cn-manager button:disabled, .cn-manager button:disabled,
.cn-manager input:disabled, .cn-manager input:disabled,
.cn-manager select:disabled { .cn-manager select:disabled {
@ -40,8 +46,13 @@
.cn-manager .cn-manager-restart { .cn-manager .cn-manager-restart {
display: none; display: none;
background-color: #500000; background-color: #500000 !important;
color: white; border-color: #88181b !important;
color: white !important;
}
.cn-manager .cn-manager-restart:hover {
background-color: #88181b !important;
} }
.cn-manager .cn-manager-stop { .cn-manager .cn-manager-stop {
@ -79,7 +90,6 @@
flex-wrap: wrap; flex-wrap: wrap;
gap: 5px; gap: 5px;
align-items: center; align-items: center;
padding: 0 5px;
} }
.cn-manager-header label { .cn-manager-header label {
@ -91,16 +101,32 @@
.cn-manager-filter { .cn-manager-filter {
height: 28px; height: 28px;
line-height: 28px; line-height: 28px;
cursor: pointer;
padding: 0.5em 0.5em;
border: 1px solid var(--border-color);
border-radius: 6px;
background: var(--comfy-input-bg);
}
.cn-manager-filter:hover {
filter: brightness(125%);
} }
.cn-manager-keywords { .cn-manager-keywords {
height: 28px; height: 28px;
line-height: 28px; line-height: 28px;
padding: 0 5px 0 26px; padding: 0 5px 0 26px;
background: var(--comfy-input-bg);
background-size: 16px; background-size: 16px;
background-position: 5px center; background-position: 5px center;
background-repeat: no-repeat; 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"); 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");
border: 1px solid var(--border-color);
border-radius: 6px;
outline-color: transparent;
} }
.cn-manager-status { .cn-manager-status {
@ -588,6 +614,10 @@
height: 100%; height: 100%;
} }
.cn-install-buttons button {
padding: 4px 8px;
}
.cn-selected-buttons { .cn-selected-buttons {
display: flex; display: flex;
gap: 5px; gap: 5px;

View File

@ -1,6 +1,7 @@
import { app } from "../../scripts/app.js"; import { app } from "../../scripts/app.js";
import { ComfyDialog, $el } from "../../scripts/ui.js"; import { ComfyDialog, $el } from "../../scripts/ui.js";
import { api } from "../../scripts/api.js"; import { api } from "../../scripts/api.js";
import { buildGuiFrameCustomHeader, createSettingsCombo } from "./comfyui-gui-builder.js";
import { import {
manager_instance, rebootAPI, install_via_git_url, manager_instance, rebootAPI, install_via_git_url,
@ -18,32 +19,19 @@ loadCss("./custom-nodes-manager.css");
const gridId = "node"; const gridId = "node";
const pageHtml = ` const pageHtml = `
<div class="cn-manager-header"> <div class="cn-manager cn-manager-dark">
<label>Filter <div class="cn-manager-grid"></div>
<select class="cn-manager-filter"></select> <div class="cn-manager-selection"></div>
</label> <div class="cn-manager-message"></div>
<input class="cn-manager-keywords" type="search" placeholder="Search" /> <div class="cn-manager-footer">
<div class="cn-manager-status"></div> <button class="cn-manager-restart p-button p-component">Restart</button>
<div class="cn-flex-auto"></div> <button class="cn-manager-stop p-button p-component">Stop</button>
<div class="cn-manager-channel"></div> <div class="cn-flex-auto"></div>
</div> <button class="cn-manager-used-in-workflow p-button p-component">Used In Workflow</button>
<div class="cn-manager-grid"></div> <button class="cn-manager-check-update p-button p-component">Check Update</button>
<div class="cn-manager-selection"></div> <button class="cn-manager-check-missing p-button p-component">Check Missing</button>
<div class="cn-manager-message"></div> <button class="cn-manager-install-url p-button p-component">Install via Git URL</button>
<div class="cn-manager-footer"> </div>
<button class="cn-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="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
</svg>
Back
</button>
<button class="cn-manager-restart">Restart</button>
<button class="cn-manager-stop">Stop</button>
<div class="cn-flex-auto"></div>
<button class="cn-manager-used-in-workflow">Used In Workflow</button>
<button class="cn-manager-check-update">Check Update</button>
<button class="cn-manager-check-missing">Check Missing</button>
<button class="cn-manager-install-url">Install via Git URL</button>
</div> </div>
`; `;
@ -89,11 +77,26 @@ export class CustomNodesManager {
} }
init() { init() {
this.element = $el("div", { const header = $el("div.cn-manager-header.px-2", {}, [
parent: document.body, // $el("label", {}, [
className: "comfy-modal cn-manager" // $el("span", { textContent: "Filter" }),
}); // $el("select.cn-manager-filter")
this.element.innerHTML = pageHtml; // ]),
createSettingsCombo("Filter", $el("select.cn-manager-filter")),
$el("input.cn-manager-keywords.p-inputtext.p-component", { type: "search", placeholder: "Search" }),
$el("div.cn-manager-status"),
$el("div.cn-flex-auto"),
$el("div.cn-manager-channel")
]);
const frame = buildGuiFrameCustomHeader(
'cn-manager-dialog', // dialog id
header, // custom header element
pageHtml, // dialog content element
this
); // send this so we can attach close functions
this.element = frame;
this.element.setAttribute("tabindex", 0); this.element.setAttribute("tabindex", 0);
this.element.focus(); this.element.focus();
@ -372,7 +375,7 @@ export class CustomNodesManager {
return list.map(id => { return list.map(id => {
const bt = buttons[id]; const bt = buttons[id];
return `<button class="cn-btn-${id}" group="${action}" mode="${bt.mode}">${bt.label}</button>`; return `<button class="cn-btn-${id} p-button p-component" group="${action}" mode="${bt.mode}">${bt.label}</button>`;
}).join(""); }).join("");
} }
@ -655,7 +658,6 @@ export class CustomNodesManager {
} }
renderGrid() { renderGrid() {
// update theme // update theme
const globalStyle = window.getComputedStyle(document.body); const globalStyle = window.getComputedStyle(document.body);
this.colorVars = { this.colorVars = {

View File

@ -1,13 +1,15 @@
.cmm-manager { .cmm-manager {
--grid-font: -apple-system, BlinkMacSystemFont, "Segoe UI", "Noto Sans", Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji"; --grid-font: -apple-system, BlinkMacSystemFont, "Segoe UI", "Noto Sans", Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji";
z-index: 1099; z-index: 1099;
width: 80%; width: 80vw;
height: 80%; height: 75vh;
min-height: 30em;
display: flex; display: flex;
flex-direction: column; flex-direction: column;
gap: 10px; gap: 10px;
color: var(--fg-color); color: var(--fg-color);
font-family: arial, sans-serif; font-family: arial, sans-serif;
margin: calc(var(--spacing)*2);
} }
.cmm-manager .cmm-flex-auto { .cmm-manager .cmm-flex-auto {
@ -18,14 +20,15 @@
font-size: 16px; font-size: 16px;
color: var(--input-text); color: var(--input-text);
background-color: var(--comfy-input-bg); background-color: var(--comfy-input-bg);
border-radius: 8px;
border-color: var(--border-color); border-color: var(--border-color);
border-style: solid;
margin: 0; margin: 0;
padding: 4px 8px;
min-width: 100px; min-width: 100px;
} }
.cmm-manager button:hover {
filter: brightness(125%);
}
.cmm-manager button:disabled, .cmm-manager button:disabled,
.cmm-manager input:disabled, .cmm-manager input:disabled,
.cmm-manager select:disabled { .cmm-manager select:disabled {
@ -38,7 +41,7 @@
.cmm-manager .cmm-manager-refresh { .cmm-manager .cmm-manager-refresh {
display: none; display: none;
background-color: #000080; background-color: #000080 !important;
color: white; color: white;
} }
@ -53,7 +56,6 @@
flex-wrap: wrap; flex-wrap: wrap;
gap: 5px; gap: 5px;
align-items: center; align-items: center;
padding: 0 5px;
} }
.cmm-manager-header label { .cmm-manager-header label {
@ -67,16 +69,34 @@
.cmm-manager-filter { .cmm-manager-filter {
height: 28px; height: 28px;
line-height: 28px; line-height: 28px;
cursor: pointer;
padding: 0.5em 0.5em;
border: 1px solid var(--border-color);
border-radius: 6px;
background: var(--comfy-input-bg);
}
.cmm-manager-type:hover,
.cmm-manager-base:hover,
.cmm-manager-filter:hover {
filter: brightness(125%);
} }
.cmm-manager-keywords { .cmm-manager-keywords {
height: 28px; height: 28px;
line-height: 28px; line-height: 28px;
padding: 0 5px 0 26px; padding: 0 5px 0 26px;
background: var(--comfy-input-bg);
background-size: 16px; background-size: 16px;
background-position: 5px center; background-position: 5px center;
background-repeat: no-repeat; 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"); 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");
border: 1px solid var(--border-color);
border-radius: 6px;
outline-color: transparent;
} }
.cmm-manager-status { .cmm-manager-status {
@ -148,6 +168,10 @@
color: white; color: white;
} }
.cmm-btn-install {
padding: 4px 8px;
}
.cmm-btn-download { .cmm-btn-download {
width: 18px; width: 18px;
height: 18px; height: 18px;

View File

@ -9,39 +9,22 @@ import { api } from "../../scripts/api.js";
// https://cenfun.github.io/turbogrid/api.html // https://cenfun.github.io/turbogrid/api.html
import TG from "./turbogrid.esm.js"; import TG from "./turbogrid.esm.js";
import { buildGuiFrameCustomHeader, createSettingsCombo } from "./comfyui-gui-builder.js";
loadCss("./model-manager.css"); loadCss("./model-manager.css");
const gridId = "model"; const gridId = "model";
const pageHtml = ` const pageHtml = `
<div class="cmm-manager-header"> <div class="cmm-manager cmm-manager-dark">
<label>Filter <div class="cmm-manager-grid"></div>
<select class="cmm-manager-filter"></select> <div class="cmm-manager-selection"></div>
</label> <div class="cmm-manager-message"></div>
<label>Type <div class="cmm-manager-footer">
<select class="cmm-manager-type"></select> <button class="cmm-manager-refresh p-button p-component">Refresh</button>
</label> <button class="cmm-manager-stop p-button p-component">Stop</button>
<label>Base <div class="cmm-flex-auto"></div>
<select class="cmm-manager-base"></select> </div>
</label>
<input class="cmm-manager-keywords" type="search" placeholder="Search" />
<div class="cmm-manager-status"></div>
<div class="cmm-flex-auto"></div>
</div>
<div class="cmm-manager-grid"></div>
<div class="cmm-manager-selection"></div>
<div class="cmm-manager-message"></div>
<div class="cmm-manager-footer">
<button class="cmm-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="cmm-manager-refresh">Refresh</button>
<button class="cmm-manager-stop">Stop</button>
<div class="cmm-flex-auto"></div>
</div> </div>
`; `;
@ -64,11 +47,23 @@ export class ModelManager {
} }
init() { init() {
this.element = $el("div", { const header = $el("div.cmm-manager-header", {}, [
parent: document.body, createSettingsCombo("Filter", $el("select.cmm-manager-filter")),
className: "comfy-modal cmm-manager" createSettingsCombo("Type", $el("select.cmm-manager-type")),
}); createSettingsCombo("Base", $el("select.cmm-manager-base")),
this.element.innerHTML = pageHtml; $el("input.cmm-manager-keywords.p-inputtext.p-component", { type: "search", placeholder: "Search" }),
$el("div.cmm-manager-status"),
$el("div.cmm-flex-auto")
]);
const frame = buildGuiFrameCustomHeader(
'cmm-manager-dialog', // dialog id
header, // custom header element
pageHtml, // dialog content element
this
); // send this so we can attach close functions
this.element = frame;
this.initFilter(); this.initFilter();
this.bindEvents(); this.bindEvents();
this.initGrid(); this.initGrid();
@ -347,7 +342,7 @@ export class ModelManager {
if (installed === "True") { if (installed === "True") {
return `<div class="cmm-icon-passed">${icons.passed}</div>`; return `<div class="cmm-icon-passed">${icons.passed}</div>`;
} }
return `<button class="cmm-btn-install" mode="install">Install</button>`; return `<button class="cmm-btn-install p-button p-component" mode="install">Install</button>`;
} }
}, { }, {
id: 'url', id: 'url',
@ -420,7 +415,7 @@ export class ModelManager {
} }
this.selectedModels = selectedList; this.selectedModels = selectedList;
this.showSelection(`<span>Selected <b>${selectedList.length}</b> models <button class="cmm-btn-install" mode="install">Install</button>`); this.showSelection(`<span>Selected <b>${selectedList.length}</b> models <button class="cmm-btn-install p-button p-component" mode="install">Install</button>`);
} }
focusInstall(item) { focusInstall(item) {

65
js/snapshot.css Normal file
View File

@ -0,0 +1,65 @@
.snapshot-manager {
--grid-font: -apple-system, BlinkMacSystemFont, "Segoe UI", "Noto Sans", Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji";
z-index: 1099;
width: 80vw;
height: 75vh;
min-height: 30em;
display: flex;
flex-direction: column;
gap: 10px;
color: var(--fg-color);
font-family: arial, sans-serif;
text-underline-offset: 3px;
outline: none;
margin: calc(var(--spacing)*2);
}
.snapshot-manager button {
width: auto;
position: relative;
overflow: hidden;
font-size: 16px;
color: var(--input-text);
background-color: var(--comfy-input-bg);
border-color: var(--border-color);
margin: 0;
min-width: 100px;
padding: 4px 8px;
}
.snapshot-manager .snapshot-restore-btn {
background-color: #00158f !important;
border-color: #2025b9 !important;
color: white !important;
}
.snapshot-manager .snapshot-remove-btn {
background-color: #970000 !important;
border-color: #be2127 !important;
color: white !important;
}
.snapshot-manager button:hover {
filter: brightness(125%);
}
.snapshot-manager .data-btns {
display: flex;
flex-direction: column;
gap: calc(var(--spacing)*2);
padding: calc(var(--spacing)*2);
align-items: center;
justify-content: center;
height: 100%;
}
.snapshot-footer {
display: flex;
flex-wrap: wrap;
gap: 10px;
align-items: center;
}
.snapshot-manager .cn-flex-auto {
flex: auto;
}

View File

@ -1,8 +1,10 @@
import { app } from "../../scripts/app.js"; import { app } from "../../scripts/app.js";
import { api } from "../../scripts/api.js" import { api } from "../../scripts/api.js"
import { ComfyDialog, $el } from "../../scripts/ui.js"; import { ComfyDialog, $el } from "../../scripts/ui.js";
import { manager_instance, rebootAPI, show_message, handle403Response } from "./common.js"; import { manager_instance, rebootAPI, show_message, handle403Response, loadCss } from "./common.js";
import { buildGuiFrame } from "./comfyui-gui-builder.js";
loadCss("./snapshot.css");
async function restore_snapshot(target) { async function restore_snapshot(target) {
if(SnapshotManager.instance) { if(SnapshotManager.instance) {
@ -27,7 +29,7 @@ async function restore_snapshot(target) {
} }
finally { finally {
await SnapshotManager.instance.invalidateControl(); await SnapshotManager.instance.invalidateControl();
SnapshotManager.instance.updateMessage("<BR>To apply the snapshot, please <button id='cm-reboot-button2' class='cm-small-button'>RESTART</button> ComfyUI. And refresh browser.", 'cm-reboot-button2'); SnapshotManager.instance.updateMessage("<BR>To apply the snapshot, please <button id='cm-reboot-button2' class='p-button p-component'>RESTART</button> ComfyUI. And refresh browser.", 'cm-reboot-button2');
} }
} }
} }
@ -88,6 +90,8 @@ export class SnapshotManager extends ComfyDialog {
message_box = null; message_box = null;
data = null; data = null;
content = $el("div.snapshot-manager");
clear() { clear() {
this.restore_buttons = []; this.restore_buttons = [];
this.message_box = null; this.message_box = null;
@ -96,9 +100,18 @@ export class SnapshotManager extends ComfyDialog {
constructor(app, manager_dialog) { constructor(app, manager_dialog) {
super(); super();
this.manager_dialog = manager_dialog; // this.manager_dialog = manager_dialog;
this.search_keyword = ''; this.search_keyword = '';
this.element = $el("div.comfy-modal", { parent: document.body }, []);
const frame = buildGuiFrame(
'snapshot-manager-dialog', // dialog id
'Snapshot Manager', // title
'i.mdi.mdi-puzzle', // icon class
this.content, // dialog content element
this
); // send this so we can attach close functions
this.element = frame;
} }
async remove_item() { async remove_item() {
@ -109,7 +122,7 @@ export class SnapshotManager extends ComfyDialog {
createControls() { createControls() {
return [ return [
$el("button.cm-small-button", { $el("button.p-button.p-component", {
type: "button", type: "button",
textContent: "Close", textContent: "Close",
onclick: () => { this.close(); } onclick: () => { this.close(); }
@ -132,8 +145,8 @@ export class SnapshotManager extends ComfyDialog {
this.clear(); this.clear();
this.data = (await getSnapshotList()).items; this.data = (await getSnapshotList()).items;
while (this.element.children.length) { while (this.content.children.length) {
this.element.removeChild(this.element.children[0]); this.content.removeChild(this.content.children[0]);
} }
await this.createGrid(); await this.createGrid();
@ -204,20 +217,21 @@ export class SnapshotManager extends ComfyDialog {
data2.innerHTML = `&nbsp;${data}`; data2.innerHTML = `&nbsp;${data}`;
var data_button = document.createElement('td'); var data_button = document.createElement('td');
data_button.style.textAlign = "center"; data_button.style.textAlign = "center";
data_button.className = "data-btns";
var restoreBtn = document.createElement('button'); var restoreBtn = document.createElement('button');
restoreBtn.className = "snapshot-restore-btn p-button p-component";
restoreBtn.innerHTML = 'Restore'; restoreBtn.innerHTML = 'Restore';
restoreBtn.style.width = "100px"; restoreBtn.style.width = "100px";
restoreBtn.style.backgroundColor = 'blue';
restoreBtn.addEventListener('click', function() { restoreBtn.addEventListener('click', function() {
restore_snapshot(data); restore_snapshot(data);
}); });
var removeBtn = document.createElement('button'); var removeBtn = document.createElement('button');
removeBtn.className = "snapshot-remove-btn p-button p-component";
removeBtn.innerHTML = 'Remove'; removeBtn.innerHTML = 'Remove';
removeBtn.style.width = "100px"; removeBtn.style.width = "100px";
removeBtn.style.backgroundColor = 'red';
removeBtn.addEventListener('click', function() { removeBtn.addEventListener('click', function() {
remove_snapshot(data); remove_snapshot(data);
@ -241,13 +255,14 @@ export class SnapshotManager extends ComfyDialog {
let self = this; let self = this;
const panel = document.createElement('div'); const panel = document.createElement('div');
panel.style.width = "100%"; panel.style.width = "100%";
panel.style.height = "100%";
panel.appendChild(grid); panel.appendChild(grid);
function handleResize() { function handleResize() {
const parentHeight = self.element.clientHeight; const parentHeight = self.element.clientHeight;
const gridHeight = parentHeight - 200; const gridHeight = parentHeight - 200;
grid.style.height = gridHeight + "px"; // grid.style.height = gridHeight + "px";
} }
window.addEventListener("resize", handleResize); window.addEventListener("resize", handleResize);
@ -256,25 +271,17 @@ export class SnapshotManager extends ComfyDialog {
grid.style.width = "100%"; grid.style.width = "100%";
grid.style.height = "100%"; grid.style.height = "100%";
grid.style.overflowY = "scroll"; grid.style.overflowY = "scroll";
this.element.style.height = "85%";
this.element.style.width = "80%"; this.content.appendChild(panel);
this.element.appendChild(panel);
handleResize(); handleResize();
} }
async createBottomControls() { async createBottomControls() {
var close_button = document.createElement("button");
close_button.className = "cm-small-button";
close_button.innerHTML = "Close";
close_button.onclick = () => { this.close(); }
close_button.style.display = "inline-block";
var save_button = document.createElement("button"); var save_button = document.createElement("button");
save_button.className = "cm-small-button"; save_button.className = "p-button p-component";
save_button.innerHTML = "Save snapshot"; save_button.innerHTML = "Save snapshot";
save_button.onclick = () => { save_current_snapshot(); } save_button.onclick = () => { save_current_snapshot(); }
save_button.style.display = "inline-block";
save_button.style.horizontalAlign = "right"; save_button.style.horizontalAlign = "right";
save_button.style.width = "170px"; save_button.style.width = "170px";
@ -282,15 +289,19 @@ export class SnapshotManager extends ComfyDialog {
this.message_box.style.height = '60px'; this.message_box.style.height = '60px';
this.message_box.style.verticalAlign = 'middle'; this.message_box.style.verticalAlign = 'middle';
this.element.appendChild(this.message_box); const footer = $el("div.snapshot-footer");
this.element.appendChild(close_button); const spacer = $el("div.cn-flex-auto");
this.element.appendChild(save_button); footer.appendChild(spacer);
footer.appendChild(save_button);
this.content.appendChild(this.message_box);
this.content.appendChild(footer);
} }
async show() { async show() {
try { try {
this.invalidateControl(); this.invalidateControl();
this.element.style.display = "block"; this.element.style.display = "flex";
this.element.style.zIndex = 1099; this.element.style.zIndex = 1099;
} }
catch(exception) { catch(exception) {

View File

@ -1,5 +1,635 @@
{ {
"custom_nodes": [ "custom_nodes": [
{
"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]",
"reference": "https://github.com/starsFriday/ComfyUI-KLingAI-OmniVideo",
"files": [
"https://github.com/starsFriday/ComfyUI-KLingAI-OmniVideo"
],
"install_type": "git-clone",
"description": "Five API nodes for KLingAI's OmniVideo (O1) usage\nNOTE: The files in the repo are not organized."
},
{
"author": "Hifunyo",
"title": "comfyui_google_ai",
"reference": "https://github.com/Hifunyo/comfyui_google_ai",
"files": [
"https://github.com/Hifunyo/comfyui_google_ai"
],
"install_type": "git-clone",
"description": "ComfyUI integration node for Google AI image generation capabilities. (Description by CC)"
},
{
"author": "nschpy",
"title": "ComfyUI_MovisAdapter [UNSAFE]",
"reference": "https://github.com/nschpy/ComfyUI_MovisAdapter",
"files": [
"https://github.com/nschpy/ComfyUI_MovisAdapter"
],
"install_type": "git-clone",
"description": "A collection of custom nodes for ComfyUI[w/This nodepack contains a node that has a vulnerability allowing write to arbitrary file paths.]"
},
{
"author": "devzeroLL",
"title": "comfyui-lxj-Node",
"reference": "https://github.com/devzeroLL/comfyui-lxj-Node",
"files": [
"https://github.com/devzeroLL/comfyui-lxj-Node"
],
"install_type": "git-clone",
"description": "NODES: lxj_ImageBatch14, lxj_TextBatch14"
},
{
"author": "hgh086",
"title": "Comfyui-HghImage",
"reference": "https://github.com/hgh086/Comfyui-HghImage",
"files": [
"https://github.com/hgh086/Comfyui-HghImage"
],
"install_type": "git-clone",
"description": "ComfyUI custom node for image comparison functionality. (Description by CC)"
},
{
"author": "Ginolazy",
"title": "ComfyUI-FluxKontextImageCompensate [WIP]",
"reference": "https://github.com/Ginolazy/ComfyUI-FluxKontextImageCompensate",
"files": [
"https://github.com/Ginolazy/ComfyUI-FluxKontextImageCompensate"
],
"install_type": "git-clone",
"description": "A focused ComfyUI plugin to handle the vertical stretching issue introduced by the Flux Kontext model.\nNOTE: The files in the repo are not organized."
},
{
"author": "IO-AtelierTech",
"title": "comfyui-genai-connectors [WIP]",
"reference": "https://github.com/IO-AtelierTech/comfyui-genai-connectors",
"files": [
"https://github.com/IO-AtelierTech/comfyui-genai-connectors"
],
"install_type": "git-clone",
"description": "The ComfyUI-fal-Connector is a tool designed to provide an integration between ComfyUI and fal. This extension allows users to execute their ComfyUI workflows directly on [a/fal.ai](https://fal.ai/). This enables users to leverage the computational power and resources provided by fal.ai for running their ComfyUI workflows.\nNOTE: The files in the repo are not organized."
},
{
"author": "PozzettiAndrea",
"title": "ComfyUI-MVDUST3R [UNSAFE]",
"reference": "https://github.com/PozzettiAndrea/ComfyUI-MVDUST3R",
"files": [
"https://github.com/PozzettiAndrea/ComfyUI-MVDUST3R"
],
"install_type": "git-clone",
"description": "ComfyUI nodes for MVDUST3R multi-view 3D reconstruction[w/This nodepack contains a node that has a vulnerability allowing write to arbitrary file paths.]"
},
{
"author": "ProjectAtlantis-dev",
"title": "comfyui-atlantis-json [UNSAFE]",
"reference": "https://github.com/ProjectAtlantis-dev/comfyui-atlantis-json",
"files": [
"https://github.com/ProjectAtlantis-dev/comfyui-atlantis-json"
],
"install_type": "git-clone",
"description": "Custom ComfyUI nodes for JSON processing and transcription workflows, including text-to-JSON conversion, SRT subtitle parsing, and file saving. (Description by CC)[w/This nodepack contains a node that has a vulnerability allowing write to arbitrary file paths.]"
},
{
"author": "ShammiG",
"title": "ComfyUI_Text_Tools_SG [UNSAFE]",
"reference": "https://github.com/ShammiG/ComfyUI_Text_Tools_SG",
"files": [
"https://github.com/ShammiG/ComfyUI_Text_Tools_SG"
],
"install_type": "git-clone",
"description": "Text Editor node with Markdown editing plus quick shortcuts, Text Viewer node, with extra features plus Text Merge, Text Save and Load Text from anywhere nodes.[w/This nodepack contains a node that has a vulnerability allowing write to arbitrary file paths.]"
},
{
"author": "Smyshnikof",
"title": "ComfyUI-PresetDownloadManager [UNSAFE]",
"reference": "https://github.com/Smyshnikof/ComfyUI-PresetDownloadManager",
"files": [
"https://github.com/Smyshnikof/ComfyUI-PresetDownloadManager"
],
"install_type": "git-clone",
"description": "A custom ComfyUI node for managing and downloading models from HuggingFace with preset support[w/This nodepack contains a node that has a vulnerability allowing write to arbitrary file paths.]"
},
{
"author": "gulajawalegit",
"title": "ComfyUI-Telegram-Sender [UNSAFE]",
"reference": "https://github.com/gulajawalegit/ComfyUI-Telegram-Sender",
"files": [
"https://github.com/gulajawalegit/ComfyUI-Telegram-Sender"
],
"install_type": "git-clone",
"description": "ComfyUI custom node for sending generated videos to Telegram, enabling direct output sharing to messaging platforms. (Description by CC)[w/This nodepack contains a node that has a vulnerability allowing write to arbitrary file paths.]"
},
{
"author": "Laolilzp",
"title": "Laoli3D [UNSAFE]",
"reference": "https://github.com/Laolilzp/Laoli3D",
"files": [
"https://github.com/Laolilzp/Laoli3D"
],
"install_type": "git-clone",
"description": "ComfyUI 3D pose editor enabling visual character manipulation and ControlNet image generation for precise AI figure control without prompt description. (Description by CC)[w/This nodepack contains a node that has a vulnerability allowing write to arbitrary file paths.]"
},
{
"author": "Goldlionren",
"title": "ComfyUI_Bridge_fabric [UNSAFE]",
"reference": "https://github.com/Goldlionren/ComfyUI_Bridge_fabric",
"files": [
"https://github.com/Goldlionren/ComfyUI_Bridge_fabric"
],
"install_type": "git-clone",
"description": "AI Compute Fabric bridge for ComfyUI enabling distributed CLIP, VAE, and ControlNet inference across local and remote machines. (Description by CC)[w/This nodepack contains a node that has a vulnerability allowing write to arbitrary file paths.]"
},
{
"author": "CypherNaught-0x",
"title": "ComfyUI-StarVector [WIP]",
"reference": "https://github.com/CypherNaught-0x/ComfyUI-StarVector",
"files": [
"https://github.com/CypherNaught-0x/ComfyUI-StarVector"
],
"install_type": "git-clone",
"description": "ComfyUI custom nodes for SVG generation using StarVector models\nNOTE: The files in the repo are not organized."
},
{
"author": "Clivey1234",
"title": "ComfyUI_FBX_Import [UNSAFE]",
"reference": "https://github.com/Clivey1234/ComfyUI_FBX_Import",
"files": [
"https://github.com/Clivey1234/ComfyUI_FBX_Import"
],
"install_type": "git-clone",
"description": "Convert FBX animations into ControlNet OpenPose images for driving AI video generation with motion from any animation source. (Description by CC)[w/This nodepack has a vulnerability that allows arbitrary code execution remotely.]"
},
{
"author": "TobiasGlaubach",
"title": "ComfyUI-TG_PyCode [UNSAFE]",
"reference": "https://github.com/TobiasGlaubach/ComfyUI-TG_PyCode",
"files": [
"https://github.com/TobiasGlaubach/ComfyUI-TG_PyCode"
],
"install_type": "git-clone",
"description": "ComfyUI node library with an editor and nodes to run Python code within ComfyUI workflows.[w/This nodepack has a vulnerability that allows arbitrary code execution remotely.]"
},
{
"author": "jchiotaka",
"title": "ComfyUI-ClarityAI-Upscaler",
"reference": "https://github.com/jchiotaka/ComfyUI-ClarityAI-Upscaler",
"files": [
"https://github.com/jchiotaka/ComfyUI-ClarityAI-Upscaler"
],
"install_type": "git-clone",
"description": "ComfyUI upscaler nodes including ClarityCreativeUpscaler, ClarityCrystalUpscaler, and ClarityFluxUpscaler. (Description by CC)"
},
{
"author": "tpc2233",
"title": "ComfyUI-TP-IMtalker [WIP]",
"reference": "https://github.com/tpc2233/ComfyUI-TP-IMtalker",
"files": [
"https://github.com/tpc2233/ComfyUI-TP-IMtalker"
],
"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]",
"reference": "https://github.com/yuyu0218yu/comfyui-NXCM-tool",
"files": [
"https://github.com/yuyu0218yu/comfyui-NXCM-tool"
],
"install_type": "git-clone",
"description": "Next-generation creative media encryption toolkit for ComfyUI providing image and video encryption with AES-256-CTR + HMAC-SHA256, metadata preservation, and batch processing support. (Description by CC) [w/hardcoded encryption key]"
},
{
"author": "SergeyKarleev",
"title": "[WIP] comfyui-textutils",
"reference": "https://github.com/SergeyKarleev/comfyui-textutils",
"files": [
"https://github.com/SergeyKarleev/comfyui-textutils"
],
"install_type": "git-clone",
"description": "Small utility nodes for ComfyUI text workflows.\nNOTE: The files in the repo are not organized."
},
{
"author": "love530love",
"title": "[WIP] ComfyUI-TorchMonitor",
"reference": "https://github.com/love530love/ComfyUI-TorchMonitor",
"files": [
"https://github.com/love530love/ComfyUI-TorchMonitor"
],
"install_type": "git-clone",
"description": "Fixed-position real-time monitor for ComfyUI displaying CPU, RAM, VRAM, and GPU temperature metrics with zero configuration and single-file installation.\nNOTE: The files in the repo are not organized."
},
{
"author": "Enferlain",
"title": "ComfyUI-SamplerCustom-3Decimals",
"reference": "https://github.com/Enferlain/ComfyUI-SamplerCustom-3Decimals",
"files": [
"https://github.com/Enferlain/ComfyUI-SamplerCustom-3Decimals"
],
"install_type": "git-clone",
"description": "ComfyUI sampler node with custom 3 decimals precision for sampling parameters. (Description by CC)"
},
{
"author": "lfelipegg",
"title": "[WIP] lfgg_custom_nodes_comfyui",
"reference": "https://github.com/lfelipegg/lfgg_custom_nodes_comfyui",
"files": [
"https://github.com/lfelipegg/lfgg_custom_nodes_comfyui"
],
"install_type": "git-clone",
"description": "LFGG custom nodes for ComfyUI providing resolution-first utilities, latent sizing by aspect ratio, and divisibility-aware image processing.\nNOTE: The files in the repo are not organized."
},
{
"author": "saltchicken",
"title": "ComfyUI-Selector",
"reference": "https://github.com/saltchicken/ComfyUI-Selector",
"files": [
"https://github.com/saltchicken/ComfyUI-Selector"
],
"install_type": "git-clone",
"description": "ComfyUI node providing simple selector functionality. (Description by CC)"
},
{
"author": "saltchicken",
"title": "ComfyUI-Video-Utils",
"reference": "https://github.com/saltchicken/ComfyUI-Video-Utils",
"files": [
"https://github.com/saltchicken/ComfyUI-Video-Utils"
],
"install_type": "git-clone",
"description": "ComfyUI nodes for video processing including FinalFrameSelector and VideoMerge functionality. (Description by CC)"
},
{
"author": "EricRorich",
"title": "[WIP] ComfyUI-MegaTran-cutom-node",
"reference": "https://github.com/EricRorich/ComfyUI-MegaTran-cutom-node",
"files": [
"https://github.com/EricRorich/ComfyUI-MegaTran-cutom-node"
],
"install_type": "git-clone",
"description": "ComfyUI custom node applying image transformations (scaling, rotation, translation) around a pivot point with optional canvas expansion and pivot visualization. (Description by CC)\nNOTE: The files in the repo are not organized."
},
{
"author": "OhSeongHyeon",
"title": "comfyui-random-image-size",
"reference": "https://github.com/OhSeongHyeon/comfyui-random-image-size",
"files": [
"https://github.com/OhSeongHyeon/comfyui-random-image-size"
],
"install_type": "git-clone",
"description": "ComfyUI Random Image Size"
},
{
"author": "Enferlain",
"title": "ComfyUI-Model-Comparison [WIP]",
"reference": "https://github.com/Enferlain/ComfyUI-Model-Comparison",
"files": [
"https://github.com/Enferlain/ComfyUI-Model-Comparison"
],
"install_type": "git-clone",
"description": "ComfyUI node for comparing multiple models side-by-side. (Description by CC)\nNOTE: The files in the repo are not organized."
},
{
"author": "AMTPorn",
"title": "comfyui_amt",
"reference": "https://github.com/AMTPorn/comfyui_amt",
"files": [
"https://github.com/AMTPorn/comfyui_amt"
],
"install_type": "git-clone",
"description": "NODES: AMTStringDeduplication"
},
{
"author": "gajjar4",
"title": "ComfyUI-Qwen-Image-i2L [UNSAFE]",
"reference": "https://github.com/gajjar4/ComfyUI-Qwen-Image-i2L",
"files": [
"https://github.com/gajjar4/ComfyUI-Qwen-Image-i2L"
],
"install_type": "git-clone",
"description": "A fully optimized ComfyUI custom node for Qwen-Image-i2L (Image-to-LoRA) that extracts style, composition, or details from images and saves them as lightweight LoRA files with intelligent VRAM optimization. (Description by CC)[w/This nodepack contains a node that has a vulnerability allowing write to arbitrary file paths.]"
},
{
"author": "edvardtoth",
"title": "ComfyUI-ETNodes",
"reference": "https://github.com/edvardtoth/ComfyUI-ETNodes",
"files": [
"https://github.com/edvardtoth/ComfyUI-ETNodes"
],
"install_type": "git-clone",
"description": "NODES: ETNodes-Color-Selector, ETNodes-Gemini-API-Image, ETNodes-Gemini-API-Text, ETNodes-List-Items, ETNodes-List-Selector, ETNodes-Text-Previe"
},
{
"author": "jtydhr88",
"title": "ComfyUI-PolotnoCanvasEditor [UNSAFE]",
"reference": "https://github.com/jtydhr88/ComfyUI-PolotnoCanvasEditor",
"files": [
"https://github.com/jtydhr88/ComfyUI-PolotnoCanvasEditor"
],
"install_type": "git-clone",
"description": "Integrates Polotno Canvas Editor into ComfyUI for advanced image editing and design.[w/This nodepack contains a path traversal vulnerability.]"
},
{
"author": "Taremin",
"title": "comfyui-remove-print",
"reference": "https://github.com/Taremin/comfyui-remove-print",
"files": [
"https://github.com/Taremin/comfyui-remove-print"
],
"install_type": "git-clone",
"description": "ComfyUI extension for removing or suppressing print statements in node output. (Description by CC)"
},
{
"author": "JiangAogo",
"title": "ComfyUI-Gemini-API [WIP]",
"reference": "https://github.com/JiangAogo/ComfyUI-Gemini-API",
"files": [
"https://github.com/JiangAogo/ComfyUI-Gemini-API"
],
"install_type": "git-clone",
"description": "ComfyUI custom nodes for Google Gemini API integration, supporting both text generation (LLM) and image generation.\nNOTE: The files in the repo are not organized."
},
{
"author": "AprEcho",
"title": "ComfyUI-RandomSeed",
"reference": "https://github.com/AprEcho/ComfyUI-RandomSeed",
"files": [
"https://github.com/AprEcho/ComfyUI-RandomSeed"
],
"install_type": "git-clone",
"description": "Generates random seed values for ComfyUI workflows. (Description by CC)"
},
{
"author": "Suzu008",
"title": "ComfyUI-ImageCritic",
"reference": "https://github.com/Suzu008/ComfyUI-ImageCritic",
"files": [
"https://github.com/Suzu008/ComfyUI-ImageCritic"
],
"install_type": "git-clone",
"description": "ComfyUI custom node for image analysis and evaluation. (Description by CC)"
},
{
"author": "Spicely",
"title": "[WIP] ComfyUI-Luma",
"reference": "https://github.com/Spicely/ComfyUI-Luma",
"files": [
"https://github.com/Spicely/ComfyUI-Luma"
],
"install_type": "git-clone",
"description": "ComfyUI utility providing video and audio processing capabilities including text watermarking, audio-video separation, and audio-to-subtitle conversion. (Description by CC)\nNOTE: The files in the repo are not organized."
},
{
"author": "SSYCloud",
"title": "comfyui-ssy-syncapi [WIP]",
"reference": "https://github.com/SSYCloud/comfyui-ssy-syncapi",
"files": [
"https://github.com/SSYCloud/comfyui-ssy-syncapi"
],
"install_type": "git-clone",
"description": "Powerful ComfyUI custom node collection providing 4 dedicated nodes to access SSY Cloud synchronous image generation and processing models including Google Gemini, ByteDance Doubao, OpenAI, and image enhancement APIs. (Description by CC)\nNOTE: The files in the repo are not organized."
},
{
"author": "LMietkiewicz",
"title": "HiggsfieldAPI_Node",
"reference": "https://github.com/LMietkiewicz/HiggsfieldAPI_Node",
"files": [
"https://github.com/LMietkiewicz/HiggsfieldAPI_Node"
],
"install_type": "git-clone",
"description": "ComfyUI custom node for Higgsfield API integration. (Description by CC)"
},
{
"author": "starsFriday",
"title": "ComfyUI-LongCat-Image [WIP]",
"reference": "https://github.com/starsFriday/ComfyUI-LongCat-Image",
"files": [
"https://github.com/starsFriday/ComfyUI-LongCat-Image"
],
"install_type": "git-clone",
"description": "ComfyUI wrapper nodes for the LongCat-Image text-to-image and image-editing pipelines with Python 3.12 support.\nNOTE: The files in the repo are not organized."
},
{
"author": "logicalor",
"title": "comfyui_mv_adapter [WIP]",
"reference": "https://github.com/logicalor/comfyui_mv_adapter",
"files": [
"https://github.com/logicalor/comfyui_mv_adapter"
],
"install_type": "git-clone",
"description": "MV-Adapter nodes for ComfyUI - Multi-view image generation\nNOTE: The files in the repo are not organized."
},
{
"author": "rafstahelin",
"title": "ComfyUI_KieNanoBananaPro",
"reference": "https://github.com/rafstahelin/ComfyUI_KieNanoBananaPro",
"files": [
"https://github.com/rafstahelin/ComfyUI_KieNanoBananaPro"
],
"install_type": "git-clone",
"description": "ComfyUI nodes for processing and filtering using KieNanoBananaPro algorithm. (Description by CC)"
},
{
"author": "jinchanz",
"title": "ComfyUI-Midjourney",
"reference": "https://github.com/jinchanz/ComfyUI-Midjourney",
"files": [
"https://github.com/jinchanz/ComfyUI-Midjourney"
],
"install_type": "git-clone",
"description": "ComfyUI integration for Midjourney API with nodes for submitting requests, polling results, and extracting JSON data. (Description by CC)"
},
{
"author": "saltchicken",
"title": "ComfyUI-Local-Loader",
"reference": "https://github.com/saltchicken/ComfyUI-Local-Loader",
"files": [
"https://github.com/saltchicken/ComfyUI-Local-Loader"
],
"install_type": "git-clone",
"description": "ComfyUI custom nodes for loading images from specified directories and file paths. (Description by CC)"
},
{
"author": "satyasairazole",
"title": "ComfyUI-Turbandetection [WIP]",
"reference": "https://github.com/satyasairazole/ComfyUI-Turbandetection",
"files": [
"https://github.com/satyasairazole/ComfyUI-Turbandetection"
],
"install_type": "git-clone",
"description": "ComfyUI node for detecting turbans in images using computer vision. (Description by CC)\nNOTE: The files in the repo are not organized."
},
{
"author": "dougbtv",
"title": "comfyui-vllm-omni",
"reference": "https://github.com/dougbtv/comfyui-vllm-omni",
"files": [
"https://github.com/dougbtv/comfyui-vllm-omni"
],
"install_type": "git-clone",
"description": "ComfyUI custom node for vLLM-Omni text-to-image generation"
},
{
"author": "u5dev",
"title": "ComfyUI_u5_EasyScripter [UNSAFE]",
"reference": "https://github.com/u5dev/ComfyUI_u5_EasyScripter",
"files": [
"https://github.com/u5dev/ComfyUI_u5_EasyScripter"
],
"install_type": "git-clone",
"description": "EASY VBA-style script for ComfyUI. Anything you want be in 1 node. Conditional branching, iteration, prompt generation, parameter adjustment, memory release, file input/output, Using HTTP RestAPI ... with 100+ built-in functions![w/This nodepack has RCE, SSRF, and arbitrary file read vulnerabilities.]"
},
{
"author": "stalkervr",
"title": "ComfyUI-StalkerVr",
"reference": "https://github.com/stalkervr/ComfyUI-StalkerVr",
"files": [
"https://github.com/stalkervr/ComfyUI-StalkerVr"
],
"install_type": "git-clone",
"description": "ComfyUI custom nodes for image processing, aspect ratio fixing, batch cropping, grid manipulation, JSON handling, and value extraction. (Description by CC)"
},
{
"author": "baoanhng",
"title": "ComfyUI-utils",
"reference": "https://github.com/baoanhng/ComfyUI-utils",
"files": [
"https://github.com/baoanhng/ComfyUI-utils"
],
"install_type": "git-clone",
"description": "Provides text utility nodes (TextJoiner, TextSplitter) for ComfyUI workflows. (Description by CC)"
},
{
"author": "xuchenxu168",
"title": "[WIP] comfyui_meituan_image",
"reference": "https://github.com/xuchenxu168/comfyui_meituan_image",
"files": [
"https://github.com/xuchenxu168/comfyui_meituan_image"
],
"install_type": "git-clone",
"description": "Generate high-quality images from text prompts with excellent Chinese text renderingEdit images using natural language instructions..\nNOTE: The files in the repo are not organized."
},
{
"author": "saltchicken",
"title": "ComfyUI-Identity-Mixer",
"reference": "https://github.com/saltchicken/ComfyUI-Identity-Mixer",
"files": [
"https://github.com/saltchicken/ComfyUI-Identity-Mixer"
],
"install_type": "git-clone",
"description": "Mixes multiple identity LoRAs with normalized strength."
},
{
"author": "saltchicken",
"title": "ComfyUI-Prompter",
"reference": "https://github.com/saltchicken/ComfyUI-Prompter",
"files": [
"https://github.com/saltchicken/ComfyUI-Prompter"
],
"install_type": "git-clone",
"description": "ComfyUI custom node providing customizable prompt generation capabilities. (Description by CC)"
},
{
"author": "JBKing514",
"title": "[WIP] map_comfyui",
"reference": "https://github.com/JBKing514/map_comfyui",
"files": [
"https://github.com/JBKing514/map_comfyui"
],
"install_type": "git-clone",
"description": "A custom node implementing the Manifold Alignment Protocol (MAP) within ComfyUI, transforming diffusion sampling into a measurable and visualizable geometric process. (Description by CC)\nNOTE: The files in the repo are not organized."
},
{
"author": "Nynxz",
"title": "ComfyUI_DiffsynthPause",
"reference": "https://github.com/Nynxz/ComfyUI_DiffsynthPause",
"files": [
"https://github.com/Nynxz/ComfyUI_DiffsynthPause"
],
"install_type": "git-clone",
"description": "ComfyUI custom node for controlling Diffsynth checkpoint pausing behavior during image generation workflows. (Description by CC)"
},
{
"author": "binarystatic",
"title": "ComfyUI-BinarystaticMasterSeed",
"reference": "https://github.com/binarystatic/ComfyUI-BinarystaticMasterSeed",
"files": [
"https://github.com/binarystatic/ComfyUI-BinarystaticMasterSeed"
],
"install_type": "git-clone",
"description": "BinarystaticMasterSeed node for ComfyUI. (Description by CC)"
},
{
"author": "Aruntd008",
"title": "[WIP] ComfyUI_SeamlessPattern",
"reference": "https://github.com/Aruntd008/ComfyUI_SeamlessPattern",
"files": [
"https://github.com/Aruntd008/ComfyUI_SeamlessPattern"
],
"install_type": "git-clone",
"description": "SeamlessPatternNode for ComfyUI. (Description by CC)\nNOTE: The files in the repo are not organized."
},
{
"author": "SilentLuxRay",
"title": "[WIP] ComfyUI-Furrey-Super-Prompt",
"reference": "https://github.com/SilentLuxRay/ComfyUI-Furrey-Super-Prompt",
"files": [
"https://github.com/SilentLuxRay/ComfyUI-Furrey-Super-Prompt"
],
"install_type": "git-clone",
"description": "A personalized all-in-one node for ComfyUI that simplifies prompt management and LoRA handling with automatic translation to English. (Description by CC)\nNOTE: The files in the repo are not organized."
},
{
"author": "Rayen21",
"title": "[WIP] ComfyUI-PromptLinePlus",
"reference": "https://github.com/Rayen21/ComfyUI-PromptLinePlus",
"files": [
"https://github.com/Rayen21/ComfyUI-PromptLinePlus"
],
"install_type": "git-clone",
"description": "ComfyUI custom node that splits multi-line prompts by line, enabling batch image generation with each line triggering one execution and supporting custom prompt boxes. (Description by CC)\nNOTE: The files in the repo are not organized."
},
{
"author": "rookiestar28",
"title": "ComfyUI_Security_Audit",
"reference": "https://github.com/rookiestar28/ComfyUI_Security_Audit",
"files": [
"https://github.com/rookiestar28/ComfyUI_Security_Audit"
],
"install_type": "git-clone",
"description": "A lightweight, dual-layer security extension for ComfyUI using AST-based static analysis and runtime monitoring to detect malicious code in custom nodes."
},
{
"author": "c1660181647-hash",
"title": "ComfyUI-MM-Visual-Encryption",
"reference": "https://github.com/c1660181647-hash/ComfyUI-MM-Visual-Encryption",
"files": [
"https://github.com/c1660181647-hash/ComfyUI-MM-Visual-Encryption"
],
"install_type": "git-clone",
"description": "A visual noise encryption custom node for ComfyUI, supporting Image and Video privacy protection."
},
{ {
"author": "charlierz", "author": "charlierz",
"title": "comfyui-charlierz", "title": "comfyui-charlierz",
@ -1354,16 +1984,6 @@
"install_type": "git-clone", "install_type": "git-clone",
"description": "A powerful ComfyUI extension that helps you focus on selected nodes by highlighting their connections while dimming or hiding unrelated links\nNOTE: The files in the repo are not organized." "description": "A powerful ComfyUI extension that helps you focus on selected nodes by highlighting their connections while dimming or hiding unrelated links\nNOTE: The files in the repo are not organized."
}, },
{
"author": "pizurny",
"title": "ComfyUI-Just-DWPose [WIP]",
"reference": "https://github.com/pizurny/ComfyUI-Just-DWPose",
"files": [
"https://github.com/pizurny/ComfyUI-Just-DWPose"
],
"install_type": "git-clone",
"description": "An advanced DWPose annotator for ComfyUI with TorchScript and ONNX backends, featuring comprehensive pose detection, bone validation, temporal smoothing, and custom visualization tools."
},
{ {
"author": "pizurny", "author": "pizurny",
"title": "ComfyUI Feedback Sampler [WIP]", "title": "ComfyUI Feedback Sampler [WIP]",
@ -1784,16 +2404,6 @@
"install_type": "git-clone", "install_type": "git-clone",
"description": "NODES: 'Pluto : Auto Crop Faces', 'Pluto : Composite Image', 'Pluto : Float Math', 'Pluto : GetSizeFromImage', 'Pluto : Text Append', ..." "description": "NODES: 'Pluto : Auto Crop Faces', 'Pluto : Composite Image', 'Pluto : Float Math', 'Pluto : GetSizeFromImage', 'Pluto : Text Append', ..."
}, },
{
"author": "lfelipegg",
"title": "lfgg_custom_nodes [WIP]",
"reference": "https://github.com/lfelipegg/lfgg_custom_nodes",
"files": [
"https://github.com/lfelipegg/lfgg_custom_nodes"
],
"install_type": "git-clone",
"description": "NODES: ModelMergeCombos\nNOTE: The files in the repo are not organized."
},
{ {
"author": "lu64k", "author": "lu64k",
"title": "ks_nodes", "title": "ks_nodes",
@ -4367,16 +4977,6 @@
"install_type": "git-clone", "install_type": "git-clone",
"description": "Generate random prompts easily for FMJ.\nNOTE: The files in the repo are not organized." "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", "author": "pictorialink",
"title": "comfyui-static-resource[UNSAFE]", "title": "comfyui-static-resource[UNSAFE]",
@ -4998,16 +5598,6 @@
"description": "NODES: Properties, Apply SVG to Image", "description": "NODES: Properties, Apply SVG to Image",
"install_type": "git-clone" "install_type": "git-clone"
}, },
{
"author": "AhBumm",
"title": "ComfyUI_MangaLineExtraction",
"reference": "https://github.com/AhBumm/ComfyUI_MangaLineExtraction-hf",
"files": [
"https://github.com/AhBumm/ComfyUI_MangaLineExtraction-hf"
],
"description": "p1atdev/MangaLineExtraction-hf as a node in comfyui",
"install_type": "git-clone"
},
{ {
"author": "Kur0butiMegane", "author": "Kur0butiMegane",
"title": "Comfyui-StringUtils", "title": "Comfyui-StringUtils",
@ -5058,16 +5648,6 @@
"install_type": "git-clone", "install_type": "git-clone",
"description": "ComfyUI implementation of the partfield nvidea segmentation models\nNOTE: The files in the repo are not organized." "description": "ComfyUI implementation of the partfield nvidea segmentation models\nNOTE: The files in the repo are not organized."
}, },
{
"author": "shinich39",
"title": "comfyui-nothing-happened",
"reference": "httphttps://github.com/shinich39/comfyui-nothing-happened",
"files": [
"https://github.com/shinich39/comfyui-nothing-happened"
],
"description": "Save image and keep metadata.",
"install_type": "git-clone"
},
{ {
"author": "silveroxides", "author": "silveroxides",
"title": "ComfyUI_ReduxEmbedToolkit", "title": "ComfyUI_ReduxEmbedToolkit",
@ -8360,16 +8940,6 @@
"install_type": "git-clone", "install_type": "git-clone",
"description": "Gets a random file from a directory. Returns the filpath as a STRING. [w/This node allows access to arbitrary files through the workflow, which could pose a security threat.]" "description": "Gets a random file from a directory. Returns the filpath as a STRING. [w/This node allows access to arbitrary files through the workflow, which could pose a security threat.]"
}, },
{
"author": "neeltheninja",
"title": "ComfyUI-ControlNeXt [WIP]",
"reference": "https://github.com/neverbiasu/ComfyUI-ControlNeXt",
"files": [
"https://github.com/neverbiasu/ComfyUI-ControlNeXt"
],
"install_type": "git-clone",
"description": "In progress🚧"
},
{ {
"author": "neeltheninja", "author": "neeltheninja",
"title": "ComfyUI-TextOverlay", "title": "ComfyUI-TextOverlay",
@ -8421,16 +8991,6 @@
"install_type": "git-clone", "install_type": "git-clone",
"description": "A powerful debugging tool designed to provide in-depth analysis of your environment and dependencies by exposing API endpoints. This tool allows you to inspect environment variables, pip packages, python info and dependency trees, making it easier to diagnose and resolve issues in your ComfyUI setup.[w/This tool may expose sensitive system information if used on a public server]" "description": "A powerful debugging tool designed to provide in-depth analysis of your environment and dependencies by exposing API endpoints. This tool allows you to inspect environment variables, pip packages, python info and dependency trees, making it easier to diagnose and resolve issues in your ComfyUI setup.[w/This tool may expose sensitive system information if used on a public server]"
}, },
{
"author": "Futureversecom",
"title": "ComfyUI-JEN",
"reference": "https://github.com/futureversecom/ComfyUI-JEN",
"files": [
"https://github.com/futureversecom/ComfyUI-JEN"
],
"install_type": "git-clone",
"description": "Comfy UI custom nodes for JEN music generation powered by Futureverse"
},
{ {
"author": "denislov", "author": "denislov",
"title": "Comfyui_AutoSurvey", "title": "Comfyui_AutoSurvey",

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -1,5 +1,15 @@
{ {
"custom_nodes": [ "custom_nodes": [
{
"author": "Fossiel",
"title": "ComfyUI-MultiGPU-Patched",
"reference": "https://github.com/Fossiel/ComfyUI-MultiGPU-Patched",
"files": [
"https://github.com/Fossiel/ComfyUI-MultiGPU-Patched"
],
"install_type": "git-clone",
"description": "Patched fork of ComfyUI-MultiGPU providing universal .safetensors and GGUF multi-GPU distribution with DisTorch 2.0 engine, model-driven allocation options (bytes/ratio modes), WanVideoWrapper integration, and up to 10% faster GGUF inference. (Description by CC)"
},
{ {
"author": "synchronicity-labs", "author": "synchronicity-labs",
"title": "ComfyUI Sync Lipsync Node", "title": "ComfyUI Sync Lipsync Node",

View File

@ -1,5 +1,208 @@
{ {
"custom_nodes": [ "custom_nodes": [
{
"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]",
"reference": "https://github.com/AhBumm/ComfyUI_MangaLineExtraction-hf",
"files": [
"https://github.com/AhBumm/ComfyUI_MangaLineExtraction-hf"
],
"description": "p1atdev/MangaLineExtraction-hf as a node in comfyui",
"install_type": "git-clone"
},
{
"author": "danieljanata",
"title": "ComfyUI-QwenVL-Override [REMOVED]",
"reference": "https://github.com/danieljanata/ComfyUI-QwenVL-Override",
"files": [
"https://github.com/danieljanata/ComfyUI-QwenVL-Override"
],
"install_type": "git-clone",
"description": "Adds two nodes that reuse upstream ComfyUI-QwenVL presets but add a runtime override that can be wired/unwired without getting stuck."
},
{
"author": "Futureversecom",
"title": "ComfyUI-JEN [REMOVED]",
"reference": "https://github.com/futureversecom/ComfyUI-JEN",
"files": [
"https://github.com/futureversecom/ComfyUI-JEN"
],
"install_type": "git-clone",
"description": "Comfy UI custom nodes for JEN music generation powered by Futureverse"
},
{
"author": "TheBill2001",
"title": "comfyui-upscale-by-model [REMOVED]",
"reference": "https://github.com/TheBill2001/comfyui-upscale-by-model",
"files": [
"https://github.com/TheBill2001/comfyui-upscale-by-model"
],
"install_type": "git-clone",
"description": "This custom node allow upscaling an image by a factor using a model."
},
{
"author": "XYMikky12138",
"title": "ComfyUI-NanoBanana-inpaint [REMOVED]",
"reference": "https://github.com/XYMikky12138/ComfyUI-NanoBanana-inpaint",
"files": [
"https://github.com/XYMikky12138/ComfyUI-NanoBanana-inpaint"
],
"install_type": "git-clone",
"description": "ComfyUI nodes for API-based inpainting (Gemini, Imagen) with aspect ratio constraints, smart cropping, resize fitting, intelligent paste-back with transparency support. (Description by CC)"
},
{
"author": "Blonicx",
"title": "ComfyUI-Rework-X [REMOVED]",
"id": "rework-x",
"reference": "https://github.com/Blonicx/ComfyUI-X-Rework",
"files": [
"https://github.com/Blonicx/ComfyUI-X-Rework"
],
"install_type": "git-clone",
"description": "This is a plugin for ComfyUI that adds new Util Nodes and Nodes for easier image creation and sharing."
},
{
"author": "scott-createplay",
"title": "ComfyUI_video_essentials [REMOVED]",
"reference": "https://github.com/scott-createplay/ComfyUI_video_essentials",
"files": [
"https://github.com/scott-createplay/ComfyUI_video_essentials"
],
"install_type": "git-clone",
"description": "Essential video processing nodes for ComfyUI"
},
{
"author": "thnikk",
"title": "comfyui-thnikk-utils [REMOVED]",
"reference": "https://github.com/thnikk/comfyui-thnikk-utils",
"files": [
"https://github.com/thnikk/comfyui-thnikk-utils"
],
"install_type": "git-clone",
"description": "Nodes to clean up your workflow."
},
{
"author": "Tr1dae",
"title": "LoRA Matcher Nodes for ComfyUI [REMOVED]",
"reference": "https://github.com/Tr1dae/ComfyUI-LoraPromptMatcher",
"files": [
"https://github.com/Tr1dae/ComfyUI-LoraPromptMatcher"
],
"install_type": "git-clone",
"description": "This custom node provides two different approaches to automatically match text prompts with LoRA models using their descriptions."
},
{
"author": "jkhayiying",
"title": "ImageLoadFromLocalOrUrl Node for ComfyUI [REMOVED]",
"id": "JkhaImageLoaderPathOrUrl",
"reference": "https://gitee.com/yyh915/jkha-load-img",
"files": [
"https://gitee.com/yyh915/jkha-load-img"
],
"install_type": "git-clone",
"description": "This is a node to load an image from local path or url."
},
{
"author": "pizurny",
"title": "ComfyUI-Just-DWPose [REMOVED]",
"reference": "https://github.com/pizurny/ComfyUI-Just-DWPose",
"files": [
"https://github.com/pizurny/ComfyUI-Just-DWPose"
],
"install_type": "git-clone",
"description": "An advanced DWPose annotator for ComfyUI with TorchScript and ONNX backends, featuring comprehensive pose detection, bone validation, temporal smoothing, and custom visualization tools."
},
{
"author": "lfelipegg",
"title": "lfgg_custom_nodes [REMOVED]",
"reference": "https://github.com/lfelipegg/lfgg_custom_nodes",
"files": [
"https://github.com/lfelipegg/lfgg_custom_nodes"
],
"install_type": "git-clone",
"description": "NODES: ModelMergeCombos\nNOTE: The files in the repo are not organized."
},
{
"author": "AndSni",
"title": "Comfy-FL-Nodes [REMOVED]",
"reference": "https://github.com/AndSni/Comfy-FL-Nodes",
"files": [
"https://github.com/AndSni/Comfy-FL-Nodes"
],
"install_type": "git-clone",
"description": "Generates human characters for commerce applications in ComfyUI. (Description by CC)"
},
{
"author": "neeltheninja",
"title": "ComfyUI-ControlNeXt [REMOVED]",
"reference": "https://github.com/neverbiasu/ComfyUI-ControlNeXt",
"files": [
"https://github.com/neverbiasu/ComfyUI-ControlNeXt"
],
"install_type": "git-clone",
"description": "In progress🚧"
},
{
"author": "TheArtOfficial",
"title": "ComfyUI-Nitra [REMOVED]",
"reference": "https://github.com/TheArtOfficial/ComfyUI-Nitra",
"files": [
"https://github.com/TheArtOfficial/ComfyUI-Nitra"
],
"install_type": "git-clone",
"description": "Nitra custom node for ComfyUI"
},
{
"author": "AngelCookies",
"title": "ComfyUI-Seed-Tracker [REMOVED]",
"reference": "https://github.com/AngelCookies/ComfyUI-Seed-Tracker",
"files": [
"https://github.com/AngelCookies/ComfyUI-Seed-Tracker"
],
"install_type": "git-clone",
"description": "A ComfyUI extension that tracks random seeds throughout your image generation workflows"
},
{
"author": "shinich39",
"title": "comfyui-nothing-happened [REMOVED]",
"reference": "httphttps://github.com/shinich39/comfyui-nothing-happened",
"files": [
"https://github.com/shinich39/comfyui-nothing-happened"
],
"description": "Save image and keep metadata.",
"install_type": "git-clone"
},
{
"author": "ashtar1984",
"title": "comfyui-switch-bypass-mute-by-group [REMOVED]",
"reference": "https://github.com/ashtar1984/comfyui-switch-bypass-mute-by-group",
"files": [
"https://github.com/ashtar1984/comfyui-switch-bypass-mute-by-group"
],
"install_type": "git-clone",
"description": "ComfyUI custom node for group-based node switching, bypassing, and muting control. (Description by CC)"
},
{
"author": "wallen0322",
"title": "ComfyUI-TTM-WAN22 [REMOVED]",
"reference": "https://github.com/wallen0322/ComfyUI-TTM-WAN22",
"files": [
"https://github.com/wallen0322/ComfyUI-TTM-WAN22"
],
"install_type": "git-clone",
"description": "TTM (Time-to-Move) node for ComfyUI enabling motion-controlled video generation with Wan2.2 models using dual-clock denoising for independent background and object animation control."
},
{ {
"author": "cdanielp", "author": "cdanielp",
"title": "COMFYUI_PROMPTMODELS [REMOVED]", "title": "COMFYUI_PROMPTMODELS [REMOVED]",

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -1,7 +1,7 @@
[project] [project]
name = "comfyui-manager" name = "comfyui-manager"
description = "ComfyUI-Manager provides features to install and manage custom nodes for ComfyUI, as well as various functionalities to assist with ComfyUI." description = "ComfyUI-Manager provides features to install and manage custom nodes for ComfyUI, as well as various functionalities to assist with ComfyUI."
version = "3.38.2" version = "3.39"
license = { file = "LICENSE.txt" } license = { file = "LICENSE.txt" }
dependencies = ["GitPython", "PyGithub", "matrix-nio", "transformers", "huggingface-hub>0.20", "typer", "rich", "typing-extensions", "toml", "uv", "chardet"] dependencies = ["GitPython", "PyGithub", "matrix-nio", "transformers", "huggingface-hub>0.20", "typer", "rich", "typing-extensions", "toml", "uv", "chardet"]

View File

@ -20,7 +20,7 @@ from pathlib import Path
from typing import Set, Dict, Optional from typing import Set, Dict, Optional
# Scanner version for cache invalidation # Scanner version for cache invalidation
SCANNER_VERSION = "2.0.11" # Multi-layer detection: class existence + display names SCANNER_VERSION = "2.0.12" # Add dict comprehension + export list detection
# Cache for extract_nodes and extract_nodes_enhanced results # Cache for extract_nodes and extract_nodes_enhanced results
_extract_nodes_cache: Dict[str, Set[str]] = {} _extract_nodes_cache: Dict[str, Set[str]] = {}
@ -552,12 +552,22 @@ def extract_nodes_enhanced(
if exists: if exists:
phase5_nodes.add(node_name) phase5_nodes.add(node_name)
# Union all results (FIX: Scanner 2.0.9 bug + Scanner 2.0.10 bug) # Phase 6: Dict comprehension pattern (NEW in 2.0.12)
# Detects: NODE_CLASS_MAPPINGS = {cls.__name__: cls for cls in to_export}
# Example: TobiasGlaubach/ComfyUI-TG_PyCode
phase6_nodes = _fallback_dict_comprehension(code_text, file_path)
# Phase 7: Import-based class names for dict comprehension (NEW in 2.0.12)
# Detects imported classes that are added to export lists
phase7_nodes = _fallback_import_class_names(code_text, file_path)
# Union all results (FIX: Scanner 2.0.9 bug + Scanner 2.0.10 bug + Scanner 2.0.12 dict comp)
# 2.0.9: Used early return which missed Phase 3 nodes # 2.0.9: Used early return which missed Phase 3 nodes
# 2.0.10: Only checked registrations, missed classes referenced in display names # 2.0.10: Only checked registrations, missed classes referenced in display names
all_nodes = phase1_nodes | phase2_nodes | phase3_nodes | phase4_nodes | phase5_nodes # 2.0.12: Added dict comprehension and import-based class detection
all_nodes = phase1_nodes | phase2_nodes | phase3_nodes | phase4_nodes | phase5_nodes | phase6_nodes | phase7_nodes
# Phase 6: Empty dict detector (logging only, doesn't add nodes) # Phase 8: Empty dict detector (logging only, doesn't add nodes)
if not all_nodes: if not all_nodes:
_fallback_empty_dict_detector(code_text, file_path, verbose) _fallback_empty_dict_detector(code_text, file_path, verbose)
@ -616,7 +626,7 @@ def _fallback_classname_resolver(code_text: str, file_path: Optional[Path]) -> S
def _fallback_item_assignment(code_text: str) -> Set[str]: def _fallback_item_assignment(code_text: str) -> Set[str]:
""" """
Detect item assignment pattern. Detect item assignment pattern.
Pattern: Pattern:
NODE_CLASS_MAPPINGS = {} NODE_CLASS_MAPPINGS = {}
NODE_CLASS_MAPPINGS["MyNode"] = MyNode NODE_CLASS_MAPPINGS["MyNode"] = MyNode
@ -627,9 +637,9 @@ def _fallback_item_assignment(code_text: str) -> Set[str]:
parsed = ast.parse(code_text) parsed = ast.parse(code_text)
except: except:
return set() return set()
nodes = set() nodes = set()
for node in ast.walk(parsed): for node in ast.walk(parsed):
if isinstance(node, ast.Assign): if isinstance(node, ast.Assign):
for target in node.targets: for target in node.targets:
@ -640,10 +650,156 @@ def _fallback_item_assignment(code_text: str) -> Set[str]:
if isinstance(target.slice, ast.Constant): if isinstance(target.slice, ast.Constant):
if isinstance(target.slice.value, str): if isinstance(target.slice.value, str):
nodes.add(target.slice.value) nodes.add(target.slice.value)
return nodes return nodes
def _fallback_dict_comprehension(code_text: str, file_path: Optional[Path] = None) -> Set[str]:
"""
Detect dict comprehension pattern with __name__ attribute access.
Pattern:
NODE_CLASS_MAPPINGS = {cls.__name__: cls for cls in to_export}
NODE_CLASS_MAPPINGS = {c.__name__: c for c in [ClassA, ClassB]}
This function detects dict comprehension assignments to NODE_CLASS_MAPPINGS
and extracts class names from the iterable (list literal or variable reference).
Returns:
Set of class names extracted from the dict comprehension
"""
try:
with warnings.catch_warnings():
warnings.filterwarnings('ignore', category=SyntaxWarning)
parsed = ast.parse(code_text)
except:
return set()
nodes = set()
export_lists = {} # Track list variables and their contents
# First pass: collect list assignments (to_export = [...], exports = [...])
for node in ast.walk(parsed):
if isinstance(node, ast.Assign):
for target in node.targets:
if isinstance(target, ast.Name):
var_name = target.id
# Check for list literal
if isinstance(node.value, ast.List):
class_names = set()
for elt in node.value.elts:
if isinstance(elt, ast.Name):
class_names.add(elt.id)
export_lists[var_name] = class_names
# Handle augmented assignment: to_export += [...]
elif isinstance(node, ast.AugAssign):
if isinstance(node.target, ast.Name) and isinstance(node.op, ast.Add):
var_name = node.target.id
if isinstance(node.value, ast.List):
class_names = set()
for elt in node.value.elts:
if isinstance(elt, ast.Name):
class_names.add(elt.id)
if var_name in export_lists:
export_lists[var_name].update(class_names)
else:
export_lists[var_name] = class_names
# Second pass: find NODE_CLASS_MAPPINGS dict comprehension
for node in ast.walk(parsed):
if isinstance(node, ast.Assign):
for target in node.targets:
if isinstance(target, ast.Name) and target.id in ['NODE_CLASS_MAPPINGS', 'NODE_CONFIG']:
# Check for dict comprehension
if isinstance(node.value, ast.DictComp):
dictcomp = node.value
# Check if key is cls.__name__ pattern
key = dictcomp.key
if isinstance(key, ast.Attribute) and key.attr == '__name__':
# Get the iterable from the first generator
for generator in dictcomp.generators:
iter_node = generator.iter
# Case 1: Inline list [ClassA, ClassB, ...]
if isinstance(iter_node, ast.List):
for elt in iter_node.elts:
if isinstance(elt, ast.Name):
nodes.add(elt.id)
# Case 2: Variable reference (to_export, exports, etc.)
elif isinstance(iter_node, ast.Name):
var_name = iter_node.id
if var_name in export_lists:
nodes.update(export_lists[var_name])
return nodes
def _fallback_import_class_names(code_text: str, file_path: Optional[Path] = None) -> Set[str]:
"""
Extract class names from imports that are added to export lists.
Pattern:
from .module import ClassA, ClassB
to_export = [ClassA, ClassB]
NODE_CLASS_MAPPINGS = {cls.__name__: cls for cls in to_export}
This is a complementary fallback that works with _fallback_dict_comprehension
to resolve import-based node registrations.
Returns:
Set of imported class names that appear in export-like contexts
"""
try:
with warnings.catch_warnings():
warnings.filterwarnings('ignore', category=SyntaxWarning)
parsed = ast.parse(code_text)
except:
return set()
# Collect imported names
imported_names = set()
for node in ast.walk(parsed):
if isinstance(node, ast.ImportFrom):
for alias in node.names:
name = alias.asname if alias.asname else alias.name
imported_names.add(name)
# Check if these names appear in list assignments that feed into NODE_CLASS_MAPPINGS
export_candidates = set()
has_dict_comp_mapping = False
for node in ast.walk(parsed):
# Check for dict comprehension NODE_CLASS_MAPPINGS
if isinstance(node, ast.Assign):
for target in node.targets:
if isinstance(target, ast.Name) and target.id == 'NODE_CLASS_MAPPINGS':
if isinstance(node.value, ast.DictComp):
has_dict_comp_mapping = True
# Collect list contents
if isinstance(node, ast.Assign):
if isinstance(node.value, ast.List):
for elt in node.value.elts:
if isinstance(elt, ast.Name) and elt.id in imported_names:
export_candidates.add(elt.id)
# Handle augmented assignment
elif isinstance(node, ast.AugAssign):
if isinstance(node.value, ast.List):
for elt in node.value.elts:
if isinstance(elt, ast.Name) and elt.id in imported_names:
export_candidates.add(elt.id)
# Only return if there's a dict comprehension mapping
if has_dict_comp_mapping:
return export_candidates
return set()
def _extract_repo_name(file_path: Path) -> str: def _extract_repo_name(file_path: Path) -> str:
""" """
Extract repository name from file path. Extract repository name from file path.