Compare commits

..

No commits in common. "main" and "3.38.2" have entirely different histories.
main ... 3.38.2

23 changed files with 16447 additions and 25625 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
version_code = [3, 39]
version_code = [3, 38, 2]
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)
return "fail", None
versions, current_tag, latest_tag = get_comfyui_versions(repo)
if latest_tag is None:
versions, current_tag, _ = get_comfyui_versions(repo)
if len(versions) == 0 or (len(versions) == 1 and versions[0] == 'nightly'):
logging.info("[ComfyUI-Manager] Unable to update to the stable ComfyUI version.")
return "fail", None
if versions[0] == 'nightly':
latest_tag = versions[1]
else:
latest_tag = versions[0]
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:
if current_tag == latest_tag:
return "skip", None
else:
logging.info(f"[ComfyUI-Manager] Updating ComfyUI: {current_tag} -> {latest_tag}")
repo.git.checkout(tag_ref.name)
repo.git.checkout(latest_tag)
execute_install_script("ComfyUI", repo_path, instant_execution=False, no_deps=False)
return 'updated', latest_tag
except:
@ -3370,80 +3370,36 @@ async def restore_snapshot(snapshot_path, git_helper_extras=None):
def get_comfyui_versions(repo=None):
repo = repo or git.Repo(comfy_path)
if repo is None:
repo = git.Repo(comfy_path)
remote_name = None
try:
remote_name = get_remote_name(repo)
repo.remotes[remote_name].fetch()
remote = get_remote_name(repo)
repo.remotes[remote].fetch()
except:
logging.error("[ComfyUI-Manager] Failed to fetch ComfyUI")
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
versions = [x.name for x in repo.tags if x.name.startswith('v')]
def normalize_describe(tag_name):
if not tag_name:
return None
base = tag_name.split('-', 1)[0]
return base if parse_semver(base) else None
# nearest tag
versions = sorted(versions, key=lambda v: repo.git.log('-1', '--format=%ct', v), reverse=True)
versions = versions[:4]
# 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]
current_tag = repo.git.describe('--tags')
latest_tag = semver_tags[0] if semver_tags else None
if current_tag not in versions:
versions = sorted(versions + [current_tag], key=lambda v: repo.git.log('-1', '--format=%ct', v), reverse=True)
versions = versions[:4]
try:
described = repo.git.describe('--tags')
except Exception:
described = ''
main_branch = repo.heads.master
latest_commit = main_branch.commit
latest_tag = repo.git.describe('--tags', latest_commit.hexsha)
try:
exact_tag = repo.git.describe('--tags', '--exact-match')
except Exception:
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'
if latest_tag != versions[0]:
versions.insert(0, '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]
versions[0] = 'nightly'
current_tag = 'nightly'
return versions, current_tag, latest_tag

View File

@ -38,25 +38,6 @@ SECURITY_MESSAGE_NORMAL_MINUS_MODEL = "ERROR: Downloading models that are not in
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):
stream.reconfigure(encoding=locale.getpreferredencoding(), errors='replace')
for msg in stream:
@ -201,19 +182,10 @@ def set_preview_method(method):
core.get_config()['preview_method'] = method
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:
if args.preview_method == latent_preview.LatentPreviewMethod.NoPreviews:
set_preview_method(core.get_config()['preview_method'])
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):
@ -1510,26 +1482,14 @@ async def install_model(request):
@routes.get("/manager/preview_method")
async def preview_method(request):
# Setting change request
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'])
core.write_config()
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)
return web.Response(status=200)
@routes.get("/manager/db_mode")
async def db_mode(request):

View File

@ -1,227 +0,0 @@
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,7 +20,6 @@ import { ComponentBuilderDialog, getPureName, load_components, set_component_pol
import { CustomNodesManager } from "./custom-nodes-manager.js";
import { ModelManager } from "./model-manager.js";
import { SnapshotManager } from "./snapshot.js";
import { buildGuiFrame, createSettingsCombo } from "./comfyui-gui-builder.js";
let manager_version = await getVersion();
@ -45,16 +44,12 @@ docStyle.innerHTML = `
#cm-manager-dialog {
width: 1000px;
height: auto;
height: 455px;
box-sizing: content-box;
z-index: 1000;
overflow-y: auto;
}
#cm-manager-dialog br {
margin-bottom: 1em;
}
.cb-widget {
width: 400px;
height: 25px;
@ -85,7 +80,6 @@ docStyle.innerHTML = `
}
.cm-menu-container {
padding : calc(var(--spacing)*2);
column-gap: 20px;
display: flex;
flex-wrap: wrap;
@ -146,8 +140,8 @@ docStyle.innerHTML = `
}
.cm-notice-board {
width: auto;
height: 280px;
width: 290px;
height: 230px;
overflow: auto;
color: var(--input-text);
border: 1px solid var(--descrip-text);
@ -244,50 +238,68 @@ var is_updating = false;
// copied style from https://github.com/pythongosssss/ComfyUI-Custom-Scripts
const style = `
#workflowgallery-button {
height: 50px;
width: 310px;
height: 27px;
padding: 0px !important;
position: relative;
overflow: hidden;
font-size: 17px !important;
}
#cm-nodeinfo-button {
width: 310px;
height: 27px;
padding: 0px !important;
position: relative;
overflow: hidden;
font-size: 17px !important;
}
#cm-manual-button {
width: 310px;
height: 27px;
position: relative;
overflow: hidden;
}
.cm-button {
width: auto;
width: 310px;
height: 30px;
position: relative;
overflow: hidden;
background-color: var(--comfy-menu-secondary-bg);
border-color: var(--border-color);
color: var(--input-text);
}
.cm-button:hover {
filter: brightness(125%);
font-size: 17px !important;
}
.cm-button-red {
width: 310px;
height: 30px;
position: relative;
overflow: hidden;
font-size: 17px !important;
background-color: #500000 !important;
border-color: #88181b !important;
color: white !important;
}
.cm-button-red:hover {
background-color: #88181b !important;
}
.cm-button-orange {
width: 310px;
height: 30px;
position: relative;
overflow: hidden;
font-size: 17px !important;
font-weight: bold;
background-color: orange !important;
color: black !important;
}
.cm-experimental-button {
width: 100%;
width: 290px;
height: 30px;
position: relative;
overflow: hidden;
font-size: 17px !important;
}
.cm-experimental {
width: 310px;
border: 1px solid #555;
border-radius: 5px;
padding: 10px;
@ -314,14 +326,8 @@ const style = `
.cm-menu-combo {
cursor: pointer;
padding: 0.5em 0.5em;
border: 1px solid var(--border-color);
border-radius: 6px;
background: var(--comfy-menu-secondary-bg);
}
.cm-menu-combo:hover {
filter: brightness(125%);
width: 310px;
box-sizing: border-box;
}
.cm-small-button {
@ -825,7 +831,7 @@ class ManagerMenuDialog extends ComfyDialog {
const isElectron = 'electronAPI' in window;
update_comfyui_button =
$el("button.p-button.p-component.cm-button", {
$el("button.cm-button", {
type: "button",
textContent: "Update ComfyUI",
style: {
@ -836,7 +842,7 @@ class ManagerMenuDialog extends ComfyDialog {
});
switch_comfyui_button =
$el("button.p-button.p-component.cm-button", {
$el("button.cm-button", {
type: "button",
textContent: "Switch ComfyUI",
style: {
@ -847,7 +853,7 @@ class ManagerMenuDialog extends ComfyDialog {
});
restart_stop_button =
$el("button.p-button.p-component.cm-button-red", {
$el("button.cm-button-red", {
type: "button",
textContent: "Restart",
onclick: () => restartOrStop()
@ -855,7 +861,7 @@ class ManagerMenuDialog extends ComfyDialog {
if(isElectron) {
update_all_button =
$el("button.p-button.p-component.cm-button", {
$el("button.cm-button", {
type: "button",
textContent: "Update All Custom Nodes",
onclick:
@ -864,7 +870,7 @@ class ManagerMenuDialog extends ComfyDialog {
}
else {
update_all_button =
$el("button.p-button.p-component.cm-button", {
$el("button.cm-button", {
type: "button",
textContent: "Update All",
onclick:
@ -874,7 +880,7 @@ class ManagerMenuDialog extends ComfyDialog {
const res =
[
$el("button.p-button.p-component.cm-button", {
$el("button.cm-button", {
type: "button",
textContent: "Custom Nodes Manager",
onclick:
@ -886,7 +892,7 @@ class ManagerMenuDialog extends ComfyDialog {
}
}),
$el("button.p-button.p-component.cm-button", {
$el("button.cm-button", {
type: "button",
textContent: "Install Missing Custom Nodes",
onclick:
@ -898,7 +904,7 @@ class ManagerMenuDialog extends ComfyDialog {
}
}),
$el("button.p-button.p-component.cm-button", {
$el("button.cm-button", {
type: "button",
textContent: "Custom Nodes In Workflow",
onclick:
@ -910,8 +916,8 @@ class ManagerMenuDialog extends ComfyDialog {
}
}),
$el("div", {}, []),
$el("button.p-button.p-component.cm-button", {
$el("br", {}, []),
$el("button.cm-button", {
type: "button",
textContent: "Model Manager",
onclick:
@ -923,7 +929,7 @@ class ManagerMenuDialog extends ComfyDialog {
}
}),
$el("button.p-button.p-component.cm-button", {
$el("button.cm-button", {
type: "button",
textContent: "Install via Git URL",
onclick: async () => {
@ -935,13 +941,13 @@ class ManagerMenuDialog extends ComfyDialog {
}
}),
$el("div", {}, []),
$el("br", {}, []),
update_all_button,
update_comfyui_button,
switch_comfyui_button,
// fetch_updates_button,
$el("div", {}, []),
$el("br", {}, []),
restart_stop_button,
];
@ -954,13 +960,12 @@ class ManagerMenuDialog extends ComfyDialog {
let self = this;
// db mode
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.className = "cm-menu-combo p-select p-component p-inputwrapper p-inputwrapper-filled ";
this.datasrc_combo.appendChild($el('option', { value: 'cache', text: 'Channel (1day cache)' }, []));
this.datasrc_combo.appendChild($el('option', { value: 'local', text: 'Local' }, []));
this.datasrc_combo.appendChild($el('option', { value: 'remote', text: 'Channel (remote)' }, []));
this.datasrc_combo.className = "cm-menu-combo";
this.datasrc_combo.appendChild($el('option', { value: 'cache', text: 'DB: Channel (1day cache)' }, []));
this.datasrc_combo.appendChild($el('option', { value: 'local', text: 'DB: Local' }, []));
this.datasrc_combo.appendChild($el('option', { value: 'remote', text: 'DB: Channel (remote)' }, []));
api.fetchApi('/manager/db_mode')
.then(response => response.text())
@ -970,110 +975,27 @@ class ManagerMenuDialog extends ComfyDialog {
api.fetchApi(`/manager/db_mode?value=${event.target.value}`);
});
const dbRetrievalSetttingItem = createSettingsCombo("DB", this.datasrc_combo);
// preview method
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.className = "cm-menu-combo p-select p-component p-inputwrapper p-inputwrapper-filled";
preview_combo.className = "cm-menu-combo";
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')
.then(response => response.text())
.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';
});
.then(data => { preview_combo.value = data; });
preview_combo.addEventListener('change', function (event) {
// 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);
});
api.fetchApi(`/manager/preview_method?value=${event.target.value}`);
});
const previewSetttingItem = createSettingsCombo("Preview method", preview_combo);
// channel
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.className = "cm-menu-combo p-select p-component p-inputwrapper p-inputwrapper-filled";
channel_combo.className = "cm-menu-combo";
api.fetchApi('/manager/channel_url_list')
.then(response => response.json())
.then(async data => {
@ -1082,7 +1004,7 @@ class ManagerMenuDialog extends ComfyDialog {
for (let i in urls) {
if (urls[i] != '') {
let name_url = urls[i].split('::');
channel_combo.appendChild($el('option', { value: name_url[0], text: `${name_url[0]}` }, []));
channel_combo.appendChild($el('option', { value: name_url[0], text: `Channel: ${name_url[0]}` }, []));
}
}
@ -1097,13 +1019,11 @@ class ManagerMenuDialog extends ComfyDialog {
}
});
const channelSetttingItem = createSettingsCombo("Channel", channel_combo);
// share
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.className = "cm-menu-combo p-select p-component p-inputwrapper p-inputwrapper-filled";
share_combo.className = "cm-menu-combo";
const share_options = [
['none', 'None'],
['openart', 'OpenArt AI'],
@ -1114,7 +1034,7 @@ class ManagerMenuDialog extends ComfyDialog {
['all', 'All'],
];
for (const option of share_options) {
share_combo.appendChild($el('option', { value: option[0], text: `${option[1]}` }, []));
share_combo.appendChild($el('option', { value: option[0], text: `Share: ${option[1]}` }, []));
}
api.fetchApi('/manager/share_option')
@ -1136,14 +1056,12 @@ class ManagerMenuDialog extends ComfyDialog {
}
});
const shareSetttingItem = createSettingsCombo("Share", share_combo);
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.className = "cm-menu-combo p-select p-component p-inputwrapper p-inputwrapper-filled";
component_policy_combo.appendChild($el('option', { value: 'workflow', text: 'Use workflow version' }, []));
component_policy_combo.appendChild($el('option', { value: 'higher', text: 'Use higher version' }, []));
component_policy_combo.appendChild($el('option', { value: 'mine', text: 'Use my version' }, []));
component_policy_combo.className = "cm-menu-combo";
component_policy_combo.appendChild($el('option', { value: 'workflow', text: 'Component: Use workflow version' }, []));
component_policy_combo.appendChild($el('option', { value: 'higher', text: 'Component: Use higher version' }, []));
component_policy_combo.appendChild($el('option', { value: 'mine', text: 'Component: Use my version' }, []));
api.fetchApi('/manager/policy/component')
.then(response => response.text())
.then(data => {
@ -1156,14 +1074,15 @@ class ManagerMenuDialog extends ComfyDialog {
set_component_policy(event.target.value);
});
const componentSetttingItem = createSettingsCombo("Component", component_policy_combo);
update_policy_combo = document.createElement("select");
if(isElectron)
update_policy_combo.style.display = 'none';
update_policy_combo.setAttribute("title", "Sets the policy to be applied when performing an update.");
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: 'ComfyUI Stable Version' }, []));
update_policy_combo.appendChild($el('option', { value: 'nightly-comfyui', text: 'ComfyUI Nightly Version' }, []));
update_policy_combo.className = "cm-menu-combo";
update_policy_combo.appendChild($el('option', { value: 'stable-comfyui', text: 'Update: ComfyUI Stable Version' }, []));
update_policy_combo.appendChild($el('option', { value: 'nightly-comfyui', text: 'Update: ComfyUI Nightly Version' }, []));
api.fetchApi('/manager/policy/update')
.then(response => response.text())
.then(data => {
@ -1174,22 +1093,20 @@ class ManagerMenuDialog extends ComfyDialog {
api.fetchApi(`/manager/policy/update?value=${event.target.value}`);
});
const updateSetttingItem = createSettingsCombo("Update", update_policy_combo);
if(isElectron)
updateSetttingItem.style.display = 'none';
return [
dbRetrievalSetttingItem,
channelSetttingItem,
previewSetttingItem,
shareSetttingItem,
componentSetttingItem,
updateSetttingItem,
//[TODO] replace mt-2 with wrapper div with flex column gap
$el("filedset.cm-experimental.mt-auto", {}, [
$el("br", {}, []),
this.datasrc_combo,
channel_combo,
preview_combo,
share_combo,
component_policy_combo,
update_policy_combo,
$el("br", {}, []),
$el("br", {}, []),
$el("filedset.cm-experimental", {}, [
$el("legend.cm-experimental-legend", {}, ["EXPERIMENTAL"]),
$el("button.p-button.p-component.cm-button.cm-experimental-button", {
$el("button.cm-experimental-button", {
type: "button",
textContent: "Snapshot Manager",
onclick:
@ -1199,7 +1116,7 @@ class ManagerMenuDialog extends ComfyDialog {
SnapshotManager.instance.show();
}
}),
$el("button.p-button.p-component.cm-button.cm-experimental-button.mt-2", {
$el("button.cm-experimental-button", {
type: "button",
textContent: "Install PIP packages",
onclick:
@ -1217,7 +1134,7 @@ class ManagerMenuDialog extends ComfyDialog {
createControlsRight() {
const elts = [
$el("button.p-button.p-component.cm-button", {
$el("button.cm-button", {
id: 'cm-manual-button',
type: "button",
textContent: "Community Manual",
@ -1268,11 +1185,11 @@ class ManagerMenuDialog extends ComfyDialog {
})
]),
$el("button.p-button.p-component.cm-button", {
$el("button", {
id: 'workflowgallery-button',
type: "button",
style: {
// ...(localStorage.getItem("wg_last_visited") ? {height: '50px'} : {})
...(localStorage.getItem("wg_last_visited") ? {height: '50px'} : {})
},
onclick: (e) => {
const last_visited_site = localStorage.getItem("wg_last_visited")
@ -1295,7 +1212,7 @@ class ManagerMenuDialog extends ComfyDialog {
}, [
$el("p", {
id: 'workflowgallery-button-last-visited-label',
textContent: `(${localStorage.getItem("wg_last_visited") ? localStorage.getItem("wg_last_visited").split('/')[2] : 'none selected'})`,
textContent: `(${localStorage.getItem("wg_last_visited") ? localStorage.getItem("wg_last_visited").split('/')[2] : ''})`,
style: {
'text-align': 'center',
'color': 'var(--input-text)',
@ -1311,12 +1228,13 @@ class ManagerMenuDialog extends ComfyDialog {
})
]),
$el("button.p-button.p-component.cm-button", {
$el("button.cm-button", {
id: 'cm-nodeinfo-button',
type: "button",
textContent: "Nodes Info",
onclick: () => { window.open("https://ltdrdata.github.io/", "comfyui-node-info"); }
}),
$el("br", {}, []),
];
var textarea = document.createElement("div");
@ -1331,23 +1249,31 @@ class ManagerMenuDialog extends ComfyDialog {
constructor() {
super();
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 close_button = $el("button", { id: "cm-close-button", type: "button", textContent: "Close", onclick: () => this.close() });
const frame = buildGuiFrame(
'cm-manager-dialog', // dialog id
`ComfyUI Manager ${manager_version}`, // dialog title
"i.mdi.mdi-puzzle", // dialog icon class to show before title
content, // dialog content element
this
); // send this so we can attach close functions
const content =
$el("div.comfy-modal-content",
[
$el("tr.cm-title", {}, [
$el("font", {size:6, color:"white"}, [`ComfyUI Manager ${manager_version}`])]
),
$el("br", {}, []),
$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()])
]),
this.element = frame;
$el("br", {}, []),
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() {
@ -1355,7 +1281,7 @@ class ManagerMenuDialog extends ComfyDialog {
}
show() {
this.element.style.display = "flex";
this.element.style.display = "block";
}
toggleVisibility() {

View File

@ -1,9 +1,8 @@
.cn-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;
width: 80%;
height: 80%;
display: flex;
flex-direction: column;
gap: 10px;
@ -11,7 +10,6 @@
font-family: arial, sans-serif;
text-underline-offset: 3px;
outline: none;
margin: calc(var(--spacing)*2);
}
.cn-manager .cn-flex-auto {
@ -19,21 +17,17 @@
}
.cn-manager button {
width: auto;
position: relative;
overflow: hidden;
font-size: 16px;
color: var(--input-text);
background-color: var(--comfy-input-bg);
border-radius: 8px;
border-color: var(--border-color);
border-style: solid;
margin: 0;
padding: 4px 8px;
min-width: 100px;
}
.cn-manager button:hover {
filter: brightness(125%);
}
.cn-manager button:disabled,
.cn-manager input:disabled,
.cn-manager select:disabled {
@ -46,13 +40,8 @@
.cn-manager .cn-manager-restart {
display: none;
background-color: #500000 !important;
border-color: #88181b !important;
color: white !important;
}
.cn-manager .cn-manager-restart:hover {
background-color: #88181b !important;
background-color: #500000;
color: white;
}
.cn-manager .cn-manager-stop {
@ -90,6 +79,7 @@
flex-wrap: wrap;
gap: 5px;
align-items: center;
padding: 0 5px;
}
.cn-manager-header label {
@ -101,32 +91,16 @@
.cn-manager-filter {
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 {
height: 28px;
line-height: 28px;
padding: 0 5px 0 26px;
background: var(--comfy-input-bg);
background-size: 16px;
background-position: 5px center;
background-repeat: no-repeat;
background-image: url("data:image/svg+xml;charset=utf8,%3Csvg%20viewBox%3D%220%200%2024%2024%22%20width%3D%22100%25%22%20height%3D%22100%25%22%20pointer-events%3D%22none%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%3E%3Cpath%20fill%3D%22none%22%20stroke%3D%22%23888%22%20stroke-linecap%3D%22round%22%20stroke-linejoin%3D%22round%22%20stroke-width%3D%222%22%20d%3D%22m21%2021-4.486-4.494M19%2010.5a8.5%208.5%200%201%201-17%200%208.5%208.5%200%200%201%2017%200%22%2F%3E%3C%2Fsvg%3E");
border: 1px solid var(--border-color);
border-radius: 6px;
outline-color: transparent;
}
.cn-manager-status {
@ -614,10 +588,6 @@
height: 100%;
}
.cn-install-buttons button {
padding: 4px 8px;
}
.cn-selected-buttons {
display: flex;
gap: 5px;

View File

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

View File

@ -1,15 +1,13 @@
.cmm-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;
width: 80%;
height: 80%;
display: flex;
flex-direction: column;
gap: 10px;
color: var(--fg-color);
font-family: arial, sans-serif;
margin: calc(var(--spacing)*2);
}
.cmm-manager .cmm-flex-auto {
@ -20,15 +18,14 @@
font-size: 16px;
color: var(--input-text);
background-color: var(--comfy-input-bg);
border-radius: 8px;
border-color: var(--border-color);
border-style: solid;
margin: 0;
padding: 4px 8px;
min-width: 100px;
}
.cmm-manager button:hover {
filter: brightness(125%);
}
.cmm-manager button:disabled,
.cmm-manager input:disabled,
.cmm-manager select:disabled {
@ -41,7 +38,7 @@
.cmm-manager .cmm-manager-refresh {
display: none;
background-color: #000080 !important;
background-color: #000080;
color: white;
}
@ -56,6 +53,7 @@
flex-wrap: wrap;
gap: 5px;
align-items: center;
padding: 0 5px;
}
.cmm-manager-header label {
@ -69,34 +67,16 @@
.cmm-manager-filter {
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 {
height: 28px;
line-height: 28px;
padding: 0 5px 0 26px;
background: var(--comfy-input-bg);
background-size: 16px;
background-position: 5px center;
background-repeat: no-repeat;
background-image: url("data:image/svg+xml;charset=utf8,%3Csvg%20viewBox%3D%220%200%2024%2024%22%20width%3D%22100%25%22%20height%3D%22100%25%22%20pointer-events%3D%22none%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%3E%3Cpath%20fill%3D%22none%22%20stroke%3D%22%23888%22%20stroke-linecap%3D%22round%22%20stroke-linejoin%3D%22round%22%20stroke-width%3D%222%22%20d%3D%22m21%2021-4.486-4.494M19%2010.5a8.5%208.5%200%201%201-17%200%208.5%208.5%200%200%201%2017%200%22%2F%3E%3C%2Fsvg%3E");
border: 1px solid var(--border-color);
border-radius: 6px;
outline-color: transparent;
}
.cmm-manager-status {
@ -168,10 +148,6 @@
color: white;
}
.cmm-btn-install {
padding: 4px 8px;
}
.cmm-btn-download {
width: 18px;
height: 18px;

View File

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

View File

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

View File

@ -1,665 +1,5 @@
{
"custom_nodes": [
{
"author": "scott-createplay",
"title": "ComfyUI_frontend_tools [WIP]",
"reference": "https://github.com/scott-createplay/ComfyUI_frontend_tools",
"files": [
"https://github.com/scott-createplay/ComfyUI_frontend_tools"
],
"install_type": "git-clone",
"description": "A comprehensive utility suite for ComfyUI that helps maintain clean, organized workflows with node cleaner, layout tools, HUD projection, and wireless connection management.\nNOTE: The files in the repo are not organized."
},
{
"author": "raohammad",
"title": "ComfyUI-VTUtilNodes [WIP]",
"reference": "https://github.com/raohammad/ComfyUI-VTUtilNodes",
"files": [
"https://github.com/raohammad/ComfyUI-VTUtilNodes"
],
"install_type": "git-clone",
"description": "A collection of utility custom nodes for ComfyUI.\nNOTE: The files in the repo are not organized."
},
{
"author": "huyl3-cpu",
"title": "ComfyUI_A100_Ultimate_Optimizer",
"reference": "https://github.com/huyl3-cpu/ComfyUI_A100_Ultimate_Optimizer",
"files": [
"https://github.com/huyl3-cpu/ComfyUI_A100_Ultimate_Optimizer"
],
"install_type": "git-clone",
"description": "A100 GPU batch processing and optimization node for ComfyUI. (Description by CC)"
},
{
"author": "yamanacn",
"title": "ComfyUI-ImageMask-Random-Sync-Picker",
"reference": "https://github.com/yamanacn/ComfyUI-ImageMask-Random-Sync-Picker",
"files": [
"https://github.com/yamanacn/ComfyUI-ImageMask-Random-Sync-Picker"
],
"install_type": "git-clone",
"description": "Node for randomly selecting and synchronizing image masks with selectable options. (Description by CC)"
},
{
"author": "tdrminglin",
"title": "ComfyUI_SceneSplitter",
"reference": "https://github.com/tdrminglin/ComfyUI_SceneSplitter",
"files": [
"https://github.com/tdrminglin/ComfyUI_SceneSplitter"
],
"install_type": "git-clone",
"description": "Scene detection and splitting nodes for ComfyUI enabling frame-level scene detection and start frame tracking. (Description by CC)"
},
{
"author": "starsFriday",
"title": "ComfyUI-KLingAI-OmniVideo [WIP]",
"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": "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",
"title": "comfyui-charlierz",
@ -2014,6 +1354,16 @@
"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."
},
{
"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",
"title": "ComfyUI Feedback Sampler [WIP]",
@ -2434,6 +1784,16 @@
"install_type": "git-clone",
"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",
"title": "ks_nodes",
@ -5007,6 +4367,16 @@
"install_type": "git-clone",
"description": "Generate random prompts easily for FMJ.\nNOTE: The files in the repo are not organized."
},
{
"author": "amamisonlyuser",
"title": "MixvtonComfyui [WIP]",
"reference": "https://github.com/amamisonlyuser/MixvtonComfyui",
"files": [
"https://github.com/amamisonlyuser/MixvtonComfyui"
],
"install_type": "git-clone",
"description": "NODES: CXH_Leffa_Viton_Load, CXH_Leffa_Viton_Run\nNOTE: The files in the repo are not organized."
},
{
"author": "pictorialink",
"title": "comfyui-static-resource[UNSAFE]",
@ -5628,6 +4998,16 @@
"description": "NODES: Properties, Apply SVG to Image",
"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",
"title": "Comfyui-StringUtils",
@ -5678,6 +5058,16 @@
"install_type": "git-clone",
"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",
"title": "ComfyUI_ReduxEmbedToolkit",
@ -8970,6 +8360,16 @@
"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.]"
},
{
"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",
"title": "ComfyUI-TextOverlay",
@ -9021,6 +8421,16 @@
"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]"
},
{
"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",
"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,15 +1,5 @@
{
"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",
"title": "ComfyUI Sync Lipsync Node",

View File

@ -1,218 +1,5 @@
{
"custom_nodes": [
{
"author": "yutrodimitri-ship-it",
"title": "ComfyUI-YUTRO-CastingStudio-v2 [REMOVED]",
"reference": "https://github.com/yutrodimitri-ship-it/ComfyUI-YUTRO-CastingStudio-v2",
"files": [
"https://github.com/yutrodimitri-ship-it/ComfyUI-YUTRO-CastingStudio-v2"
],
"install_type": "git-clone",
"description": "A professional modular suite of nodes for ComfyUI designed for virtual casting agencies, professional photographers, and content creators to generate high-quality model portfolios efficiently. (Description by CC)\nNOTE: The files in the repo are not organized."
},
{
"author": "amamisonlyuser",
"title": "MixvtonComfyui [REMOVED]",
"reference": "https://github.com/amamisonlyuser/MixvtonComfyui",
"files": [
"https://github.com/amamisonlyuser/MixvtonComfyui"
],
"install_type": "git-clone",
"description": "NODES: CXH_Leffa_Viton_Load, CXH_Leffa_Viton_Run\nNOTE: The files in the repo are not organized."
},
{
"author": "AhBumm",
"title": "ComfyUI_MangaLineExtraction [REMOVED]",
"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",
"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]
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."
version = "3.39"
version = "3.38.2"
license = { file = "LICENSE.txt" }
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
# Scanner version for cache invalidation
SCANNER_VERSION = "2.0.12" # Add dict comprehension + export list detection
SCANNER_VERSION = "2.0.11" # Multi-layer detection: class existence + display names
# Cache for extract_nodes and extract_nodes_enhanced results
_extract_nodes_cache: Dict[str, Set[str]] = {}
@ -552,22 +552,12 @@ def extract_nodes_enhanced(
if exists:
phase5_nodes.add(node_name)
# 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)
# Union all results (FIX: Scanner 2.0.9 bug + Scanner 2.0.10 bug)
# 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.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
all_nodes = phase1_nodes | phase2_nodes | phase3_nodes | phase4_nodes | phase5_nodes
# Phase 8: Empty dict detector (logging only, doesn't add nodes)
# Phase 6: Empty dict detector (logging only, doesn't add nodes)
if not all_nodes:
_fallback_empty_dict_detector(code_text, file_path, verbose)
@ -626,7 +616,7 @@ def _fallback_classname_resolver(code_text: str, file_path: Optional[Path]) -> S
def _fallback_item_assignment(code_text: str) -> Set[str]:
"""
Detect item assignment pattern.
Pattern:
NODE_CLASS_MAPPINGS = {}
NODE_CLASS_MAPPINGS["MyNode"] = MyNode
@ -637,9 +627,9 @@ def _fallback_item_assignment(code_text: str) -> Set[str]:
parsed = ast.parse(code_text)
except:
return set()
nodes = set()
for node in ast.walk(parsed):
if isinstance(node, ast.Assign):
for target in node.targets:
@ -650,156 +640,10 @@ def _fallback_item_assignment(code_text: str) -> Set[str]:
if isinstance(target.slice, ast.Constant):
if isinstance(target.slice.value, str):
nodes.add(target.slice.value)
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:
"""
Extract repository name from file path.