mirror of
https://github.com/Comfy-Org/ComfyUI-Manager.git
synced 2025-12-29 00:00:49 +08:00
Compare commits
68 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
8662f6e527 | ||
|
|
3103fc9864 | ||
|
|
637678db20 | ||
|
|
e97407a286 | ||
|
|
e494abb779 | ||
|
|
44093a42fa | ||
|
|
8e1481ae78 | ||
|
|
9c59e7498f | ||
|
|
0a202dd506 | ||
|
|
7eb4a3f961 | ||
|
|
1ce5603379 | ||
|
|
97b86b02ad | ||
|
|
f2da1635f2 | ||
|
|
f0ed5c3433 | ||
|
|
aca5925e57 | ||
|
|
b8d78174a5 | ||
|
|
edf2a43122 | ||
|
|
21de993546 | ||
|
|
49bc24b66e | ||
|
|
771d627c5a | ||
|
|
98967de31b | ||
|
|
c87c07dbd5 | ||
|
|
2478d20e76 | ||
|
|
cc3428eb3b | ||
|
|
6001bd4940 | ||
|
|
f8709f4091 | ||
|
|
3cff881b5b | ||
|
|
b79e997a14 | ||
|
|
ed2c34143c | ||
|
|
639b17ef6b | ||
|
|
7834411ef3 | ||
|
|
d8ea83a44c | ||
|
|
6b9818b748 | ||
|
|
b4d5b228ae | ||
|
|
29b4824ee2 | ||
|
|
e3a8b669b2 | ||
|
|
80e5c8a987 | ||
|
|
e0e4886e63 | ||
|
|
c0947f4192 | ||
|
|
7706b047ce | ||
|
|
a44c6ff27c | ||
|
|
f4fdd51ce9 | ||
|
|
ae6c7dd673 | ||
|
|
0cbc773126 | ||
|
|
45bd3473fa | ||
|
|
02175844da | ||
|
|
fd60f7ee70 | ||
|
|
9eb4c3ab23 | ||
|
|
72d1aa7d97 | ||
|
|
57628ead80 | ||
|
|
9733c2328b | ||
|
|
70663cecc3 | ||
|
|
7c77942a92 | ||
|
|
04cf18e149 | ||
|
|
1825edda7e | ||
|
|
045f91c411 | ||
|
|
96d24f548c | ||
|
|
c7f03ad64e | ||
|
|
1232989d7d | ||
|
|
8f66a7997f | ||
|
|
f32dd80c24 | ||
|
|
a06ba343de | ||
|
|
bba55d4d5a | ||
|
|
87111bd889 | ||
|
|
3661ffd3ab | ||
|
|
d8f111a5e3 | ||
|
|
ae5565ce68 | ||
|
|
e4c370a7d9 |
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
18238
github-stats-cache.json
18238
github-stats-cache.json
File diff suppressed because it is too large
Load Diff
10726
github-stats.json
10726
github-stats.json
File diff suppressed because it is too large
Load Diff
@ -44,7 +44,7 @@ import manager_migration
|
|||||||
from node_package import InstalledNodePackage
|
from node_package import InstalledNodePackage
|
||||||
|
|
||||||
|
|
||||||
version_code = [3, 38, 2]
|
version_code = [3, 39]
|
||||||
version_str = f"V{version_code[0]}.{version_code[1]}" + (f'.{version_code[2]}' if len(version_code) > 2 else '')
|
version_str = f"V{version_code[0]}.{version_code[1]}" + (f'.{version_code[2]}' if len(version_code) > 2 else '')
|
||||||
|
|
||||||
|
|
||||||
@ -2531,22 +2531,22 @@ def update_to_stable_comfyui(repo_path):
|
|||||||
logging.error('\t'+branch.name)
|
logging.error('\t'+branch.name)
|
||||||
return "fail", None
|
return "fail", None
|
||||||
|
|
||||||
versions, current_tag, _ = get_comfyui_versions(repo)
|
versions, current_tag, latest_tag = get_comfyui_versions(repo)
|
||||||
|
|
||||||
if len(versions) == 0 or (len(versions) == 1 and versions[0] == 'nightly'):
|
if latest_tag is None:
|
||||||
logging.info("[ComfyUI-Manager] Unable to update to the stable ComfyUI version.")
|
logging.info("[ComfyUI-Manager] Unable to update to the stable ComfyUI version.")
|
||||||
return "fail", None
|
return "fail", None
|
||||||
|
|
||||||
if versions[0] == 'nightly':
|
tag_ref = next((t for t in repo.tags if t.name == latest_tag), None)
|
||||||
latest_tag = versions[1]
|
if tag_ref is None:
|
||||||
else:
|
logging.info(f"[ComfyUI-Manager] Unable to locate tag '{latest_tag}' in repository.")
|
||||||
latest_tag = versions[0]
|
return "fail", None
|
||||||
|
|
||||||
if current_tag == latest_tag:
|
if repo.head.commit == tag_ref.commit:
|
||||||
return "skip", None
|
return "skip", None
|
||||||
else:
|
else:
|
||||||
logging.info(f"[ComfyUI-Manager] Updating ComfyUI: {current_tag} -> {latest_tag}")
|
logging.info(f"[ComfyUI-Manager] Updating ComfyUI: {current_tag} -> {latest_tag}")
|
||||||
repo.git.checkout(latest_tag)
|
repo.git.checkout(tag_ref.name)
|
||||||
execute_install_script("ComfyUI", repo_path, instant_execution=False, no_deps=False)
|
execute_install_script("ComfyUI", repo_path, instant_execution=False, no_deps=False)
|
||||||
return 'updated', latest_tag
|
return 'updated', latest_tag
|
||||||
except:
|
except:
|
||||||
@ -3370,36 +3370,80 @@ async def restore_snapshot(snapshot_path, git_helper_extras=None):
|
|||||||
|
|
||||||
|
|
||||||
def get_comfyui_versions(repo=None):
|
def get_comfyui_versions(repo=None):
|
||||||
if repo is None:
|
repo = repo or git.Repo(comfy_path)
|
||||||
repo = git.Repo(comfy_path)
|
|
||||||
|
|
||||||
|
remote_name = None
|
||||||
try:
|
try:
|
||||||
remote = get_remote_name(repo)
|
remote_name = get_remote_name(repo)
|
||||||
repo.remotes[remote].fetch()
|
repo.remotes[remote_name].fetch()
|
||||||
except:
|
except:
|
||||||
logging.error("[ComfyUI-Manager] Failed to fetch ComfyUI")
|
logging.error("[ComfyUI-Manager] Failed to fetch ComfyUI")
|
||||||
|
|
||||||
versions = [x.name for x in repo.tags if x.name.startswith('v')]
|
def parse_semver(tag_name):
|
||||||
|
match = re.match(r'^v(\d+)\.(\d+)\.(\d+)$', tag_name)
|
||||||
|
return tuple(int(x) for x in match.groups()) if match else None
|
||||||
|
|
||||||
# nearest tag
|
def normalize_describe(tag_name):
|
||||||
versions = sorted(versions, key=lambda v: repo.git.log('-1', '--format=%ct', v), reverse=True)
|
if not tag_name:
|
||||||
versions = versions[:4]
|
return None
|
||||||
|
base = tag_name.split('-', 1)[0]
|
||||||
|
return base if parse_semver(base) else None
|
||||||
|
|
||||||
current_tag = repo.git.describe('--tags')
|
# Collect semver tags and sort descending (highest first)
|
||||||
|
semver_tags = []
|
||||||
|
for tag in repo.tags:
|
||||||
|
semver = parse_semver(tag.name)
|
||||||
|
if semver:
|
||||||
|
semver_tags.append((semver, tag.name))
|
||||||
|
semver_tags.sort(key=lambda x: x[0], reverse=True)
|
||||||
|
semver_tags = [name for _, name in semver_tags]
|
||||||
|
|
||||||
if current_tag not in versions:
|
latest_tag = semver_tags[0] if semver_tags else None
|
||||||
versions = sorted(versions + [current_tag], key=lambda v: repo.git.log('-1', '--format=%ct', v), reverse=True)
|
|
||||||
versions = versions[:4]
|
|
||||||
|
|
||||||
main_branch = repo.heads.master
|
try:
|
||||||
latest_commit = main_branch.commit
|
described = repo.git.describe('--tags')
|
||||||
latest_tag = repo.git.describe('--tags', latest_commit.hexsha)
|
except Exception:
|
||||||
|
described = ''
|
||||||
|
|
||||||
if latest_tag != versions[0]:
|
try:
|
||||||
versions.insert(0, 'nightly')
|
exact_tag = repo.git.describe('--tags', '--exact-match')
|
||||||
else:
|
except Exception:
|
||||||
versions[0] = 'nightly'
|
exact_tag = ''
|
||||||
|
|
||||||
|
head_is_default = False
|
||||||
|
if remote_name:
|
||||||
|
try:
|
||||||
|
default_head_ref = repo.refs[f'{remote_name}/HEAD']
|
||||||
|
default_commit = default_head_ref.reference.commit
|
||||||
|
head_is_default = repo.head.commit == default_commit
|
||||||
|
except Exception:
|
||||||
|
head_is_default = False
|
||||||
|
|
||||||
|
nearest_semver = normalize_describe(described)
|
||||||
|
exact_semver = exact_tag if parse_semver(exact_tag) else None
|
||||||
|
|
||||||
|
if head_is_default and not exact_tag:
|
||||||
current_tag = 'nightly'
|
current_tag = 'nightly'
|
||||||
|
else:
|
||||||
|
current_tag = exact_tag or described or 'nightly'
|
||||||
|
|
||||||
|
# Prepare semver list for display: top 4 plus the current/nearest semver if missing
|
||||||
|
display_semver_tags = semver_tags[:4]
|
||||||
|
if exact_semver and exact_semver not in display_semver_tags:
|
||||||
|
display_semver_tags.append(exact_semver)
|
||||||
|
elif nearest_semver and nearest_semver not in display_semver_tags:
|
||||||
|
display_semver_tags.append(nearest_semver)
|
||||||
|
|
||||||
|
versions = ['nightly']
|
||||||
|
|
||||||
|
if current_tag and not exact_semver and current_tag not in versions and current_tag not in display_semver_tags:
|
||||||
|
versions.append(current_tag)
|
||||||
|
|
||||||
|
for tag in display_semver_tags:
|
||||||
|
if tag not in versions:
|
||||||
|
versions.append(tag)
|
||||||
|
|
||||||
|
versions = versions[:6]
|
||||||
|
|
||||||
return versions, current_tag, latest_tag
|
return versions, current_tag, latest_tag
|
||||||
|
|
||||||
|
|||||||
@ -38,6 +38,25 @@ SECURITY_MESSAGE_NORMAL_MINUS_MODEL = "ERROR: Downloading models that are not in
|
|||||||
|
|
||||||
routes = PromptServer.instance.routes
|
routes = PromptServer.instance.routes
|
||||||
|
|
||||||
|
|
||||||
|
def has_per_queue_preview():
|
||||||
|
"""
|
||||||
|
Check if ComfyUI PR #11261 (per-queue live preview override) is merged
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
bool: True if ComfyUI has per-queue preview feature
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
import latent_preview
|
||||||
|
return hasattr(latent_preview, 'set_preview_method')
|
||||||
|
except ImportError:
|
||||||
|
return False
|
||||||
|
|
||||||
|
|
||||||
|
# Detect ComfyUI per-queue preview override feature (PR #11261)
|
||||||
|
COMFYUI_HAS_PER_QUEUE_PREVIEW = has_per_queue_preview()
|
||||||
|
|
||||||
|
|
||||||
def handle_stream(stream, prefix):
|
def handle_stream(stream, prefix):
|
||||||
stream.reconfigure(encoding=locale.getpreferredencoding(), errors='replace')
|
stream.reconfigure(encoding=locale.getpreferredencoding(), errors='replace')
|
||||||
for msg in stream:
|
for msg in stream:
|
||||||
@ -182,10 +201,19 @@ def set_preview_method(method):
|
|||||||
core.get_config()['preview_method'] = method
|
core.get_config()['preview_method'] = method
|
||||||
|
|
||||||
|
|
||||||
if args.preview_method == latent_preview.LatentPreviewMethod.NoPreviews:
|
if COMFYUI_HAS_PER_QUEUE_PREVIEW:
|
||||||
|
logging.info(
|
||||||
|
"[ComfyUI-Manager] ComfyUI per-queue preview override detected (PR #11261). "
|
||||||
|
"Manager's preview method feature is disabled. "
|
||||||
|
"Use ComfyUI's --preview-method CLI option or 'Settings > Execution > Live preview method'."
|
||||||
|
)
|
||||||
|
elif args.preview_method == latent_preview.LatentPreviewMethod.NoPreviews:
|
||||||
set_preview_method(core.get_config()['preview_method'])
|
set_preview_method(core.get_config()['preview_method'])
|
||||||
else:
|
else:
|
||||||
logging.warning("[ComfyUI-Manager] Since --preview-method is set, ComfyUI-Manager's preview method feature will be ignored.")
|
logging.warning(
|
||||||
|
"[ComfyUI-Manager] Since --preview-method is set, "
|
||||||
|
"ComfyUI-Manager's preview method feature will be ignored."
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
def set_component_policy(mode):
|
def set_component_policy(mode):
|
||||||
@ -1482,14 +1510,26 @@ async def install_model(request):
|
|||||||
|
|
||||||
@routes.get("/manager/preview_method")
|
@routes.get("/manager/preview_method")
|
||||||
async def preview_method(request):
|
async def preview_method(request):
|
||||||
|
# Setting change request
|
||||||
if "value" in request.rel_url.query:
|
if "value" in request.rel_url.query:
|
||||||
|
# Reject setting change if per-queue preview feature is available
|
||||||
|
if COMFYUI_HAS_PER_QUEUE_PREVIEW:
|
||||||
|
return web.Response(text="DISABLED", status=403)
|
||||||
|
|
||||||
|
# Process normally if not available
|
||||||
set_preview_method(request.rel_url.query['value'])
|
set_preview_method(request.rel_url.query['value'])
|
||||||
core.write_config()
|
core.write_config()
|
||||||
else:
|
|
||||||
return web.Response(text=core.manager_funcs.get_current_preview_method(), status=200)
|
|
||||||
|
|
||||||
return web.Response(status=200)
|
return web.Response(status=200)
|
||||||
|
|
||||||
|
# Status query request
|
||||||
|
else:
|
||||||
|
# Return DISABLED if per-queue preview feature is available
|
||||||
|
if COMFYUI_HAS_PER_QUEUE_PREVIEW:
|
||||||
|
return web.Response(text="DISABLED", status=200)
|
||||||
|
|
||||||
|
# Return current value if not available
|
||||||
|
return web.Response(text=core.manager_funcs.get_current_preview_method(), status=200)
|
||||||
|
|
||||||
|
|
||||||
@routes.get("/manager/db_mode")
|
@routes.get("/manager/db_mode")
|
||||||
async def db_mode(request):
|
async def db_mode(request):
|
||||||
|
|||||||
227
js/comfyui-gui-builder.js
Normal file
227
js/comfyui-gui-builder.js
Normal file
@ -0,0 +1,227 @@
|
|||||||
|
import { $el } from "../../scripts/ui.js";
|
||||||
|
|
||||||
|
function normalizeContent(content) {
|
||||||
|
const tmp = document.createElement('div');
|
||||||
|
if (typeof content === 'string') {
|
||||||
|
tmp.innerHTML = content;
|
||||||
|
return Array.from(tmp.childNodes);
|
||||||
|
}
|
||||||
|
if (content instanceof Node) {
|
||||||
|
return content;
|
||||||
|
}
|
||||||
|
return content;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function createSettingsCombo(label, content) {
|
||||||
|
const settingItem = $el("div.setting-item", {}, [
|
||||||
|
$el("div.flex.flex-row.items-center.gap-2",[
|
||||||
|
$el("div.form-label.flex.grow.items-center", [
|
||||||
|
$el("span.text-muted", { textContent: label },)
|
||||||
|
]),
|
||||||
|
$el("div.form-input.flex.justify-end",
|
||||||
|
[content]
|
||||||
|
)
|
||||||
|
]
|
||||||
|
)
|
||||||
|
]);
|
||||||
|
return settingItem;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function buildGuiFrame(dialogId, title, iconClass, content, owner) {
|
||||||
|
const dialog_mask = $el("div.p-dialog-mask.p-overlay-mask.p-overlay-mask-enter", {
|
||||||
|
parent: document.body,
|
||||||
|
style: {
|
||||||
|
position: "fixed",
|
||||||
|
height: "100%",
|
||||||
|
width: "100%",
|
||||||
|
left: "0px",
|
||||||
|
top: "0px",
|
||||||
|
display: "flex",
|
||||||
|
justifyContent: "center",
|
||||||
|
alignItems: "center",
|
||||||
|
pointerEvents: "auto",
|
||||||
|
zIndex: "1000"
|
||||||
|
},
|
||||||
|
onclick: (e) => {
|
||||||
|
if (e.target === dialog_mask) {
|
||||||
|
owner.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// data-pc-section="mask"
|
||||||
|
});
|
||||||
|
|
||||||
|
const header_actions = $el("div.p-dialog-header-actions", {
|
||||||
|
// [TODO]
|
||||||
|
// data-pc-section="headeractions"
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
const close_button = $el("button.p-button.p-component.p-button-icon-only.p-button-secondary.p-button-rounded.p-button-text.p-dialog-close-button", {
|
||||||
|
parent: header_actions,
|
||||||
|
type: "button",
|
||||||
|
ariaLabel: "Close",
|
||||||
|
onclick: () => owner.close(),
|
||||||
|
// "data-pc-name": "pcclosebutton",
|
||||||
|
// "data-p-disabled": "false",
|
||||||
|
// "data-p-severity": "secondary",
|
||||||
|
// "data-pc-group-section": "headericon",
|
||||||
|
// "data-pc-extend": "button",
|
||||||
|
// "data-pc-section": "root",
|
||||||
|
// [FIXME] Not sure how to do most of the SVG using $el
|
||||||
|
innerHTML: '<svg width="14" height="14" viewBox="0 0 14 14" fill="none" xmlns="http://www.w3.org/2000/svg" class="p-icon p-button-icon" aria-hidden="true"><path d="M8.01186 7.00933L12.27 2.75116C12.341 2.68501 12.398 2.60524 12.4375 2.51661C12.4769 2.42798 12.4982 2.3323 12.4999 2.23529C12.5016 2.13827 12.4838 2.0419 12.4474 1.95194C12.4111 1.86197 12.357 1.78024 12.2884 1.71163C12.2198 1.64302 12.138 1.58893 12.0481 1.55259C11.9581 1.51625 11.8617 1.4984 11.7647 1.50011C11.6677 1.50182 11.572 1.52306 11.4834 1.56255C11.3948 1.60204 11.315 1.65898 11.2488 1.72997L6.99067 5.98814L2.7325 1.72997C2.59553 1.60234 2.41437 1.53286 2.22718 1.53616C2.03999 1.53946 1.8614 1.61529 1.72901 1.74767C1.59663 1.88006 1.5208 2.05865 1.5175 2.24584C1.5142 2.43303 1.58368 2.61419 1.71131 2.75116L5.96948 7.00933L1.71131 11.2675C1.576 11.403 1.5 11.5866 1.5 11.7781C1.5 11.9696 1.576 12.1532 1.71131 12.2887C1.84679 12.424 2.03043 12.5 2.2219 12.5C2.41338 12.5 2.59702 12.424 2.7325 12.2887L6.99067 8.03052L11.2488 12.2887C11.3843 12.424 11.568 12.5 11.7594 12.5C11.9509 12.5 12.1346 12.424 12.27 12.2887C12.4053 12.1532 12.4813 11.9696 12.4813 11.7781C12.4813 11.5866 12.4053 11.403 12.27 11.2675L8.01186 7.00933Z" fill="currentColor"></path></svg><span class="p-button-label" data-pc-section="label"> </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"> </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;
|
||||||
|
}
|
||||||
@ -20,6 +20,7 @@ import { ComponentBuilderDialog, getPureName, load_components, set_component_pol
|
|||||||
import { CustomNodesManager } from "./custom-nodes-manager.js";
|
import { CustomNodesManager } from "./custom-nodes-manager.js";
|
||||||
import { ModelManager } from "./model-manager.js";
|
import { ModelManager } from "./model-manager.js";
|
||||||
import { SnapshotManager } from "./snapshot.js";
|
import { SnapshotManager } from "./snapshot.js";
|
||||||
|
import { buildGuiFrame, createSettingsCombo } from "./comfyui-gui-builder.js";
|
||||||
|
|
||||||
let manager_version = await getVersion();
|
let manager_version = await getVersion();
|
||||||
|
|
||||||
@ -44,12 +45,16 @@ docStyle.innerHTML = `
|
|||||||
|
|
||||||
#cm-manager-dialog {
|
#cm-manager-dialog {
|
||||||
width: 1000px;
|
width: 1000px;
|
||||||
height: 455px;
|
height: auto;
|
||||||
box-sizing: content-box;
|
box-sizing: content-box;
|
||||||
z-index: 1000;
|
z-index: 1000;
|
||||||
overflow-y: auto;
|
overflow-y: auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#cm-manager-dialog br {
|
||||||
|
margin-bottom: 1em;
|
||||||
|
}
|
||||||
|
|
||||||
.cb-widget {
|
.cb-widget {
|
||||||
width: 400px;
|
width: 400px;
|
||||||
height: 25px;
|
height: 25px;
|
||||||
@ -80,6 +85,7 @@ docStyle.innerHTML = `
|
|||||||
}
|
}
|
||||||
|
|
||||||
.cm-menu-container {
|
.cm-menu-container {
|
||||||
|
padding : calc(var(--spacing)*2);
|
||||||
column-gap: 20px;
|
column-gap: 20px;
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-wrap: wrap;
|
flex-wrap: wrap;
|
||||||
@ -140,8 +146,8 @@ docStyle.innerHTML = `
|
|||||||
}
|
}
|
||||||
|
|
||||||
.cm-notice-board {
|
.cm-notice-board {
|
||||||
width: 290px;
|
width: auto;
|
||||||
height: 230px;
|
height: 280px;
|
||||||
overflow: auto;
|
overflow: auto;
|
||||||
color: var(--input-text);
|
color: var(--input-text);
|
||||||
border: 1px solid var(--descrip-text);
|
border: 1px solid var(--descrip-text);
|
||||||
@ -238,68 +244,50 @@ var is_updating = false;
|
|||||||
// copied style from https://github.com/pythongosssss/ComfyUI-Custom-Scripts
|
// copied style from https://github.com/pythongosssss/ComfyUI-Custom-Scripts
|
||||||
const style = `
|
const style = `
|
||||||
#workflowgallery-button {
|
#workflowgallery-button {
|
||||||
width: 310px;
|
height: 50px;
|
||||||
height: 27px;
|
|
||||||
padding: 0px !important;
|
padding: 0px !important;
|
||||||
position: relative;
|
|
||||||
overflow: hidden;
|
|
||||||
font-size: 17px !important;
|
|
||||||
}
|
}
|
||||||
#cm-nodeinfo-button {
|
#cm-nodeinfo-button {
|
||||||
width: 310px;
|
|
||||||
height: 27px;
|
|
||||||
padding: 0px !important;
|
|
||||||
position: relative;
|
|
||||||
overflow: hidden;
|
|
||||||
font-size: 17px !important;
|
|
||||||
}
|
}
|
||||||
#cm-manual-button {
|
#cm-manual-button {
|
||||||
width: 310px;
|
|
||||||
height: 27px;
|
|
||||||
position: relative;
|
|
||||||
overflow: hidden;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.cm-button {
|
.cm-button {
|
||||||
width: 310px;
|
width: auto;
|
||||||
height: 30px;
|
|
||||||
position: relative;
|
position: relative;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
font-size: 17px !important;
|
background-color: var(--comfy-menu-secondary-bg);
|
||||||
|
border-color: var(--border-color);
|
||||||
|
color: var(--input-text);
|
||||||
|
}
|
||||||
|
|
||||||
|
.cm-button:hover {
|
||||||
|
filter: brightness(125%);
|
||||||
}
|
}
|
||||||
|
|
||||||
.cm-button-red {
|
.cm-button-red {
|
||||||
width: 310px;
|
|
||||||
height: 30px;
|
|
||||||
position: relative;
|
|
||||||
overflow: hidden;
|
|
||||||
font-size: 17px !important;
|
|
||||||
background-color: #500000 !important;
|
background-color: #500000 !important;
|
||||||
|
border-color: #88181b !important;
|
||||||
color: white !important;
|
color: white !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.cm-button-red:hover {
|
||||||
|
background-color: #88181b !important;
|
||||||
|
}
|
||||||
|
|
||||||
.cm-button-orange {
|
.cm-button-orange {
|
||||||
width: 310px;
|
|
||||||
height: 30px;
|
|
||||||
position: relative;
|
|
||||||
overflow: hidden;
|
|
||||||
font-size: 17px !important;
|
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
background-color: orange !important;
|
background-color: orange !important;
|
||||||
color: black !important;
|
color: black !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.cm-experimental-button {
|
.cm-experimental-button {
|
||||||
width: 290px;
|
width: 100%;
|
||||||
height: 30px;
|
|
||||||
position: relative;
|
|
||||||
overflow: hidden;
|
|
||||||
font-size: 17px !important;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.cm-experimental {
|
.cm-experimental {
|
||||||
width: 310px;
|
|
||||||
border: 1px solid #555;
|
border: 1px solid #555;
|
||||||
border-radius: 5px;
|
border-radius: 5px;
|
||||||
padding: 10px;
|
padding: 10px;
|
||||||
@ -326,8 +314,14 @@ const style = `
|
|||||||
|
|
||||||
.cm-menu-combo {
|
.cm-menu-combo {
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
width: 310px;
|
padding: 0.5em 0.5em;
|
||||||
box-sizing: border-box;
|
border: 1px solid var(--border-color);
|
||||||
|
border-radius: 6px;
|
||||||
|
background: var(--comfy-menu-secondary-bg);
|
||||||
|
}
|
||||||
|
|
||||||
|
.cm-menu-combo:hover {
|
||||||
|
filter: brightness(125%);
|
||||||
}
|
}
|
||||||
|
|
||||||
.cm-small-button {
|
.cm-small-button {
|
||||||
@ -831,7 +825,7 @@ class ManagerMenuDialog extends ComfyDialog {
|
|||||||
const isElectron = 'electronAPI' in window;
|
const isElectron = 'electronAPI' in window;
|
||||||
|
|
||||||
update_comfyui_button =
|
update_comfyui_button =
|
||||||
$el("button.cm-button", {
|
$el("button.p-button.p-component.cm-button", {
|
||||||
type: "button",
|
type: "button",
|
||||||
textContent: "Update ComfyUI",
|
textContent: "Update ComfyUI",
|
||||||
style: {
|
style: {
|
||||||
@ -842,7 +836,7 @@ class ManagerMenuDialog extends ComfyDialog {
|
|||||||
});
|
});
|
||||||
|
|
||||||
switch_comfyui_button =
|
switch_comfyui_button =
|
||||||
$el("button.cm-button", {
|
$el("button.p-button.p-component.cm-button", {
|
||||||
type: "button",
|
type: "button",
|
||||||
textContent: "Switch ComfyUI",
|
textContent: "Switch ComfyUI",
|
||||||
style: {
|
style: {
|
||||||
@ -853,7 +847,7 @@ class ManagerMenuDialog extends ComfyDialog {
|
|||||||
});
|
});
|
||||||
|
|
||||||
restart_stop_button =
|
restart_stop_button =
|
||||||
$el("button.cm-button-red", {
|
$el("button.p-button.p-component.cm-button-red", {
|
||||||
type: "button",
|
type: "button",
|
||||||
textContent: "Restart",
|
textContent: "Restart",
|
||||||
onclick: () => restartOrStop()
|
onclick: () => restartOrStop()
|
||||||
@ -861,7 +855,7 @@ class ManagerMenuDialog extends ComfyDialog {
|
|||||||
|
|
||||||
if(isElectron) {
|
if(isElectron) {
|
||||||
update_all_button =
|
update_all_button =
|
||||||
$el("button.cm-button", {
|
$el("button.p-button.p-component.cm-button", {
|
||||||
type: "button",
|
type: "button",
|
||||||
textContent: "Update All Custom Nodes",
|
textContent: "Update All Custom Nodes",
|
||||||
onclick:
|
onclick:
|
||||||
@ -870,7 +864,7 @@ class ManagerMenuDialog extends ComfyDialog {
|
|||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
update_all_button =
|
update_all_button =
|
||||||
$el("button.cm-button", {
|
$el("button.p-button.p-component.cm-button", {
|
||||||
type: "button",
|
type: "button",
|
||||||
textContent: "Update All",
|
textContent: "Update All",
|
||||||
onclick:
|
onclick:
|
||||||
@ -880,7 +874,7 @@ class ManagerMenuDialog extends ComfyDialog {
|
|||||||
|
|
||||||
const res =
|
const res =
|
||||||
[
|
[
|
||||||
$el("button.cm-button", {
|
$el("button.p-button.p-component.cm-button", {
|
||||||
type: "button",
|
type: "button",
|
||||||
textContent: "Custom Nodes Manager",
|
textContent: "Custom Nodes Manager",
|
||||||
onclick:
|
onclick:
|
||||||
@ -892,7 +886,7 @@ class ManagerMenuDialog extends ComfyDialog {
|
|||||||
}
|
}
|
||||||
}),
|
}),
|
||||||
|
|
||||||
$el("button.cm-button", {
|
$el("button.p-button.p-component.cm-button", {
|
||||||
type: "button",
|
type: "button",
|
||||||
textContent: "Install Missing Custom Nodes",
|
textContent: "Install Missing Custom Nodes",
|
||||||
onclick:
|
onclick:
|
||||||
@ -904,7 +898,7 @@ class ManagerMenuDialog extends ComfyDialog {
|
|||||||
}
|
}
|
||||||
}),
|
}),
|
||||||
|
|
||||||
$el("button.cm-button", {
|
$el("button.p-button.p-component.cm-button", {
|
||||||
type: "button",
|
type: "button",
|
||||||
textContent: "Custom Nodes In Workflow",
|
textContent: "Custom Nodes In Workflow",
|
||||||
onclick:
|
onclick:
|
||||||
@ -916,8 +910,8 @@ class ManagerMenuDialog extends ComfyDialog {
|
|||||||
}
|
}
|
||||||
}),
|
}),
|
||||||
|
|
||||||
$el("br", {}, []),
|
$el("div", {}, []),
|
||||||
$el("button.cm-button", {
|
$el("button.p-button.p-component.cm-button", {
|
||||||
type: "button",
|
type: "button",
|
||||||
textContent: "Model Manager",
|
textContent: "Model Manager",
|
||||||
onclick:
|
onclick:
|
||||||
@ -929,7 +923,7 @@ class ManagerMenuDialog extends ComfyDialog {
|
|||||||
}
|
}
|
||||||
}),
|
}),
|
||||||
|
|
||||||
$el("button.cm-button", {
|
$el("button.p-button.p-component.cm-button", {
|
||||||
type: "button",
|
type: "button",
|
||||||
textContent: "Install via Git URL",
|
textContent: "Install via Git URL",
|
||||||
onclick: async () => {
|
onclick: async () => {
|
||||||
@ -941,13 +935,13 @@ class ManagerMenuDialog extends ComfyDialog {
|
|||||||
}
|
}
|
||||||
}),
|
}),
|
||||||
|
|
||||||
$el("br", {}, []),
|
$el("div", {}, []),
|
||||||
update_all_button,
|
update_all_button,
|
||||||
update_comfyui_button,
|
update_comfyui_button,
|
||||||
switch_comfyui_button,
|
switch_comfyui_button,
|
||||||
// fetch_updates_button,
|
// fetch_updates_button,
|
||||||
|
|
||||||
$el("br", {}, []),
|
$el("div", {}, []),
|
||||||
restart_stop_button,
|
restart_stop_button,
|
||||||
];
|
];
|
||||||
|
|
||||||
@ -960,12 +954,13 @@ class ManagerMenuDialog extends ComfyDialog {
|
|||||||
let self = this;
|
let self = this;
|
||||||
|
|
||||||
// db mode
|
// db mode
|
||||||
|
|
||||||
this.datasrc_combo = document.createElement("select");
|
this.datasrc_combo = document.createElement("select");
|
||||||
this.datasrc_combo.setAttribute("title", "Configure where to retrieve node/model information. If set to 'local,' the channel is ignored, and if set to 'channel (remote),' it fetches the latest information each time the list is opened.");
|
this.datasrc_combo.setAttribute("title", "Configure where to retrieve node/model information. If set to 'local,' the channel is ignored, and if set to 'channel (remote),' it fetches the latest information each time the list is opened.");
|
||||||
this.datasrc_combo.className = "cm-menu-combo";
|
this.datasrc_combo.className = "cm-menu-combo p-select p-component p-inputwrapper p-inputwrapper-filled ";
|
||||||
this.datasrc_combo.appendChild($el('option', { value: 'cache', text: 'DB: Channel (1day cache)' }, []));
|
this.datasrc_combo.appendChild($el('option', { value: 'cache', text: 'Channel (1day cache)' }, []));
|
||||||
this.datasrc_combo.appendChild($el('option', { value: 'local', text: 'DB: Local' }, []));
|
this.datasrc_combo.appendChild($el('option', { value: 'local', text: 'Local' }, []));
|
||||||
this.datasrc_combo.appendChild($el('option', { value: 'remote', text: 'DB: Channel (remote)' }, []));
|
this.datasrc_combo.appendChild($el('option', { value: 'remote', text: 'Channel (remote)' }, []));
|
||||||
|
|
||||||
api.fetchApi('/manager/db_mode')
|
api.fetchApi('/manager/db_mode')
|
||||||
.then(response => response.text())
|
.then(response => response.text())
|
||||||
@ -975,27 +970,110 @@ class ManagerMenuDialog extends ComfyDialog {
|
|||||||
api.fetchApi(`/manager/db_mode?value=${event.target.value}`);
|
api.fetchApi(`/manager/db_mode?value=${event.target.value}`);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const dbRetrievalSetttingItem = createSettingsCombo("DB", this.datasrc_combo);
|
||||||
|
|
||||||
// preview method
|
// preview method
|
||||||
let preview_combo = document.createElement("select");
|
let preview_combo = document.createElement("select");
|
||||||
preview_combo.setAttribute("title", "Configure how latent variables will be decoded during preview in the sampling process.");
|
preview_combo.setAttribute("title", "Configure how latent variables will be decoded during preview in the sampling process.");
|
||||||
preview_combo.className = "cm-menu-combo";
|
preview_combo.className = "cm-menu-combo p-select p-component p-inputwrapper p-inputwrapper-filled";
|
||||||
preview_combo.appendChild($el('option', { value: 'auto', text: 'Preview method: Auto' }, []));
|
|
||||||
preview_combo.appendChild($el('option', { value: 'taesd', text: 'Preview method: TAESD (slow)' }, []));
|
|
||||||
preview_combo.appendChild($el('option', { value: 'latent2rgb', text: 'Preview method: Latent2RGB (fast)' }, []));
|
|
||||||
preview_combo.appendChild($el('option', { value: 'none', text: 'Preview method: None (very fast)' }, []));
|
|
||||||
|
|
||||||
|
// Loading state to prevent flash of enabled state
|
||||||
|
preview_combo.appendChild($el('option', { value: '', text: 'Loading...', disabled: true }, []));
|
||||||
|
preview_combo.appendChild($el('option', { value: 'auto', text: 'Auto' }, []));
|
||||||
|
preview_combo.appendChild($el('option', { value: 'taesd', text: 'TAESD (slow)' }, []));
|
||||||
|
preview_combo.appendChild($el('option', { value: 'latent2rgb', text: 'Latent2RGB (fast)' }, []));
|
||||||
|
preview_combo.appendChild($el('option', { value: 'none', text: 'None (very fast)' }, []));
|
||||||
|
|
||||||
|
// Start disabled to prevent flash
|
||||||
|
preview_combo.disabled = true;
|
||||||
|
preview_combo.value = '';
|
||||||
|
|
||||||
|
// Fetch current state
|
||||||
api.fetchApi('/manager/preview_method')
|
api.fetchApi('/manager/preview_method')
|
||||||
.then(response => response.text())
|
.then(response => response.text())
|
||||||
.then(data => { preview_combo.value = data; });
|
.then(data => {
|
||||||
|
// Remove loading option
|
||||||
|
preview_combo.querySelector('option[value=""]')?.remove();
|
||||||
|
|
||||||
|
if (data === "DISABLED") {
|
||||||
|
// ComfyUI per-queue preview feature is active
|
||||||
|
preview_combo.disabled = true;
|
||||||
|
preview_combo.value = 'auto';
|
||||||
|
|
||||||
|
// Accessibility attributes
|
||||||
|
preview_combo.setAttribute("aria-disabled", "true");
|
||||||
|
preview_combo.setAttribute("aria-label",
|
||||||
|
"Preview method setting (disabled - managed by ComfyUI). " +
|
||||||
|
"Use Settings > Execution > Live preview method instead."
|
||||||
|
);
|
||||||
|
|
||||||
|
// Tooltip for mouse users
|
||||||
|
preview_combo.setAttribute("title",
|
||||||
|
"This feature is now provided natively by ComfyUI. " +
|
||||||
|
"Please use 'Settings > Execution > Live preview method' instead."
|
||||||
|
);
|
||||||
|
|
||||||
|
// Visual feedback
|
||||||
|
preview_combo.style.opacity = '0.6';
|
||||||
|
preview_combo.style.cursor = 'not-allowed';
|
||||||
|
} else {
|
||||||
|
// Manager feature is active
|
||||||
|
preview_combo.disabled = false;
|
||||||
|
preview_combo.value = data;
|
||||||
|
|
||||||
|
// Accessibility for enabled state
|
||||||
|
preview_combo.setAttribute("aria-label",
|
||||||
|
"Preview method setting. Select how latent variables are decoded during preview."
|
||||||
|
);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch(error => {
|
||||||
|
console.error('[ComfyUI-Manager] Failed to fetch preview method status:', error);
|
||||||
|
// Error recovery: fallback to enabled
|
||||||
|
preview_combo.querySelector('option[value=""]')?.remove();
|
||||||
|
preview_combo.disabled = false;
|
||||||
|
preview_combo.value = 'auto';
|
||||||
|
});
|
||||||
|
|
||||||
preview_combo.addEventListener('change', function (event) {
|
preview_combo.addEventListener('change', function (event) {
|
||||||
api.fetchApi(`/manager/preview_method?value=${event.target.value}`);
|
// Ignore if disabled
|
||||||
|
if (preview_combo.disabled) {
|
||||||
|
event.preventDefault();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Normal operation
|
||||||
|
api.fetchApi(`/manager/preview_method?value=${event.target.value}`)
|
||||||
|
.then(response => {
|
||||||
|
if (response.status === 403) {
|
||||||
|
// Feature transitioned to native
|
||||||
|
alert(
|
||||||
|
'This feature is now provided natively by ComfyUI.\n' +
|
||||||
|
'Please use \'Settings > Execution > Live preview method\' instead.'
|
||||||
|
);
|
||||||
|
preview_combo.disabled = true;
|
||||||
|
preview_combo.style.opacity = '0.6';
|
||||||
|
preview_combo.style.cursor = 'not-allowed';
|
||||||
|
|
||||||
|
// Update aria attributes
|
||||||
|
preview_combo.setAttribute("aria-disabled", "true");
|
||||||
|
preview_combo.setAttribute("aria-label",
|
||||||
|
"Preview method setting (disabled - managed by ComfyUI). " +
|
||||||
|
"Use Settings > Execution > Live preview method instead."
|
||||||
|
);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch(error => {
|
||||||
|
console.error('[ComfyUI-Manager] Preview method update failed:', error);
|
||||||
});
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
const previewSetttingItem = createSettingsCombo("Preview method", preview_combo);
|
||||||
|
|
||||||
// channel
|
// channel
|
||||||
let channel_combo = document.createElement("select");
|
let channel_combo = document.createElement("select");
|
||||||
channel_combo.setAttribute("title", "Configure the channel for retrieving data from the Custom Node list (including missing nodes) or the Model list.");
|
channel_combo.setAttribute("title", "Configure the channel for retrieving data from the Custom Node list (including missing nodes) or the Model list.");
|
||||||
channel_combo.className = "cm-menu-combo";
|
channel_combo.className = "cm-menu-combo p-select p-component p-inputwrapper p-inputwrapper-filled";
|
||||||
api.fetchApi('/manager/channel_url_list')
|
api.fetchApi('/manager/channel_url_list')
|
||||||
.then(response => response.json())
|
.then(response => response.json())
|
||||||
.then(async data => {
|
.then(async data => {
|
||||||
@ -1004,7 +1082,7 @@ class ManagerMenuDialog extends ComfyDialog {
|
|||||||
for (let i in urls) {
|
for (let i in urls) {
|
||||||
if (urls[i] != '') {
|
if (urls[i] != '') {
|
||||||
let name_url = urls[i].split('::');
|
let name_url = urls[i].split('::');
|
||||||
channel_combo.appendChild($el('option', { value: name_url[0], text: `Channel: ${name_url[0]}` }, []));
|
channel_combo.appendChild($el('option', { value: name_url[0], text: `${name_url[0]}` }, []));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1019,11 +1097,13 @@ class ManagerMenuDialog extends ComfyDialog {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const channelSetttingItem = createSettingsCombo("Channel", channel_combo);
|
||||||
|
|
||||||
|
|
||||||
// share
|
// share
|
||||||
let share_combo = document.createElement("select");
|
let share_combo = document.createElement("select");
|
||||||
share_combo.setAttribute("title", "Hide the share button in the main menu or set the default action upon clicking it. Additionally, configure the default share site when sharing via the context menu's share button.");
|
share_combo.setAttribute("title", "Hide the share button in the main menu or set the default action upon clicking it. Additionally, configure the default share site when sharing via the context menu's share button.");
|
||||||
share_combo.className = "cm-menu-combo";
|
share_combo.className = "cm-menu-combo p-select p-component p-inputwrapper p-inputwrapper-filled";
|
||||||
const share_options = [
|
const share_options = [
|
||||||
['none', 'None'],
|
['none', 'None'],
|
||||||
['openart', 'OpenArt AI'],
|
['openart', 'OpenArt AI'],
|
||||||
@ -1034,7 +1114,7 @@ class ManagerMenuDialog extends ComfyDialog {
|
|||||||
['all', 'All'],
|
['all', 'All'],
|
||||||
];
|
];
|
||||||
for (const option of share_options) {
|
for (const option of share_options) {
|
||||||
share_combo.appendChild($el('option', { value: option[0], text: `Share: ${option[1]}` }, []));
|
share_combo.appendChild($el('option', { value: option[0], text: `${option[1]}` }, []));
|
||||||
}
|
}
|
||||||
|
|
||||||
api.fetchApi('/manager/share_option')
|
api.fetchApi('/manager/share_option')
|
||||||
@ -1056,12 +1136,14 @@ class ManagerMenuDialog extends ComfyDialog {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const shareSetttingItem = createSettingsCombo("Share", share_combo);
|
||||||
|
|
||||||
let component_policy_combo = document.createElement("select");
|
let component_policy_combo = document.createElement("select");
|
||||||
component_policy_combo.setAttribute("title", "When loading the workflow, configure which version of the component to use.");
|
component_policy_combo.setAttribute("title", "When loading the workflow, configure which version of the component to use.");
|
||||||
component_policy_combo.className = "cm-menu-combo";
|
component_policy_combo.className = "cm-menu-combo p-select p-component p-inputwrapper p-inputwrapper-filled";
|
||||||
component_policy_combo.appendChild($el('option', { value: 'workflow', text: 'Component: Use workflow version' }, []));
|
component_policy_combo.appendChild($el('option', { value: 'workflow', text: 'Use workflow version' }, []));
|
||||||
component_policy_combo.appendChild($el('option', { value: 'higher', text: 'Component: Use higher version' }, []));
|
component_policy_combo.appendChild($el('option', { value: 'higher', text: 'Use higher version' }, []));
|
||||||
component_policy_combo.appendChild($el('option', { value: 'mine', text: 'Component: Use my version' }, []));
|
component_policy_combo.appendChild($el('option', { value: 'mine', text: 'Use my version' }, []));
|
||||||
api.fetchApi('/manager/policy/component')
|
api.fetchApi('/manager/policy/component')
|
||||||
.then(response => response.text())
|
.then(response => response.text())
|
||||||
.then(data => {
|
.then(data => {
|
||||||
@ -1074,15 +1156,14 @@ class ManagerMenuDialog extends ComfyDialog {
|
|||||||
set_component_policy(event.target.value);
|
set_component_policy(event.target.value);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const componentSetttingItem = createSettingsCombo("Component", component_policy_combo);
|
||||||
|
|
||||||
update_policy_combo = document.createElement("select");
|
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.setAttribute("title", "Sets the policy to be applied when performing an update.");
|
||||||
update_policy_combo.className = "cm-menu-combo";
|
update_policy_combo.className = "cm-menu-combo p-select p-component p-inputwrapper p-inputwrapper-filled";
|
||||||
update_policy_combo.appendChild($el('option', { value: 'stable-comfyui', text: 'Update: ComfyUI Stable Version' }, []));
|
update_policy_combo.appendChild($el('option', { value: 'stable-comfyui', text: 'ComfyUI Stable Version' }, []));
|
||||||
update_policy_combo.appendChild($el('option', { value: 'nightly-comfyui', text: 'Update: ComfyUI Nightly Version' }, []));
|
update_policy_combo.appendChild($el('option', { value: 'nightly-comfyui', text: 'ComfyUI Nightly Version' }, []));
|
||||||
api.fetchApi('/manager/policy/update')
|
api.fetchApi('/manager/policy/update')
|
||||||
.then(response => response.text())
|
.then(response => response.text())
|
||||||
.then(data => {
|
.then(data => {
|
||||||
@ -1093,20 +1174,22 @@ class ManagerMenuDialog extends ComfyDialog {
|
|||||||
api.fetchApi(`/manager/policy/update?value=${event.target.value}`);
|
api.fetchApi(`/manager/policy/update?value=${event.target.value}`);
|
||||||
});
|
});
|
||||||
|
|
||||||
return [
|
const updateSetttingItem = createSettingsCombo("Update", update_policy_combo);
|
||||||
$el("br", {}, []),
|
|
||||||
this.datasrc_combo,
|
|
||||||
channel_combo,
|
|
||||||
preview_combo,
|
|
||||||
share_combo,
|
|
||||||
component_policy_combo,
|
|
||||||
update_policy_combo,
|
|
||||||
$el("br", {}, []),
|
|
||||||
|
|
||||||
$el("br", {}, []),
|
if(isElectron)
|
||||||
$el("filedset.cm-experimental", {}, [
|
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("legend.cm-experimental-legend", {}, ["EXPERIMENTAL"]),
|
$el("legend.cm-experimental-legend", {}, ["EXPERIMENTAL"]),
|
||||||
$el("button.cm-experimental-button", {
|
$el("button.p-button.p-component.cm-button.cm-experimental-button", {
|
||||||
type: "button",
|
type: "button",
|
||||||
textContent: "Snapshot Manager",
|
textContent: "Snapshot Manager",
|
||||||
onclick:
|
onclick:
|
||||||
@ -1116,7 +1199,7 @@ class ManagerMenuDialog extends ComfyDialog {
|
|||||||
SnapshotManager.instance.show();
|
SnapshotManager.instance.show();
|
||||||
}
|
}
|
||||||
}),
|
}),
|
||||||
$el("button.cm-experimental-button", {
|
$el("button.p-button.p-component.cm-button.cm-experimental-button.mt-2", {
|
||||||
type: "button",
|
type: "button",
|
||||||
textContent: "Install PIP packages",
|
textContent: "Install PIP packages",
|
||||||
onclick:
|
onclick:
|
||||||
@ -1134,7 +1217,7 @@ class ManagerMenuDialog extends ComfyDialog {
|
|||||||
|
|
||||||
createControlsRight() {
|
createControlsRight() {
|
||||||
const elts = [
|
const elts = [
|
||||||
$el("button.cm-button", {
|
$el("button.p-button.p-component.cm-button", {
|
||||||
id: 'cm-manual-button',
|
id: 'cm-manual-button',
|
||||||
type: "button",
|
type: "button",
|
||||||
textContent: "Community Manual",
|
textContent: "Community Manual",
|
||||||
@ -1185,11 +1268,11 @@ class ManagerMenuDialog extends ComfyDialog {
|
|||||||
})
|
})
|
||||||
]),
|
]),
|
||||||
|
|
||||||
$el("button", {
|
$el("button.p-button.p-component.cm-button", {
|
||||||
id: 'workflowgallery-button',
|
id: 'workflowgallery-button',
|
||||||
type: "button",
|
type: "button",
|
||||||
style: {
|
style: {
|
||||||
...(localStorage.getItem("wg_last_visited") ? {height: '50px'} : {})
|
// ...(localStorage.getItem("wg_last_visited") ? {height: '50px'} : {})
|
||||||
},
|
},
|
||||||
onclick: (e) => {
|
onclick: (e) => {
|
||||||
const last_visited_site = localStorage.getItem("wg_last_visited")
|
const last_visited_site = localStorage.getItem("wg_last_visited")
|
||||||
@ -1212,7 +1295,7 @@ class ManagerMenuDialog extends ComfyDialog {
|
|||||||
}, [
|
}, [
|
||||||
$el("p", {
|
$el("p", {
|
||||||
id: 'workflowgallery-button-last-visited-label',
|
id: 'workflowgallery-button-last-visited-label',
|
||||||
textContent: `(${localStorage.getItem("wg_last_visited") ? localStorage.getItem("wg_last_visited").split('/')[2] : ''})`,
|
textContent: `(${localStorage.getItem("wg_last_visited") ? localStorage.getItem("wg_last_visited").split('/')[2] : 'none selected'})`,
|
||||||
style: {
|
style: {
|
||||||
'text-align': 'center',
|
'text-align': 'center',
|
||||||
'color': 'var(--input-text)',
|
'color': 'var(--input-text)',
|
||||||
@ -1228,13 +1311,12 @@ class ManagerMenuDialog extends ComfyDialog {
|
|||||||
})
|
})
|
||||||
]),
|
]),
|
||||||
|
|
||||||
$el("button.cm-button", {
|
$el("button.p-button.p-component.cm-button", {
|
||||||
id: 'cm-nodeinfo-button',
|
id: 'cm-nodeinfo-button',
|
||||||
type: "button",
|
type: "button",
|
||||||
textContent: "Nodes Info",
|
textContent: "Nodes Info",
|
||||||
onclick: () => { window.open("https://ltdrdata.github.io/", "comfyui-node-info"); }
|
onclick: () => { window.open("https://ltdrdata.github.io/", "comfyui-node-info"); }
|
||||||
}),
|
}),
|
||||||
$el("br", {}, []),
|
|
||||||
];
|
];
|
||||||
|
|
||||||
var textarea = document.createElement("div");
|
var textarea = document.createElement("div");
|
||||||
@ -1249,31 +1331,23 @@ class ManagerMenuDialog extends ComfyDialog {
|
|||||||
constructor() {
|
constructor() {
|
||||||
super();
|
super();
|
||||||
|
|
||||||
const close_button = $el("button", { id: "cm-close-button", type: "button", textContent: "Close", onclick: () => this.close() });
|
const content = $el("div.cm-menu-container",
|
||||||
|
|
||||||
const content =
|
|
||||||
$el("div.comfy-modal-content",
|
|
||||||
[
|
[
|
||||||
$el("tr.cm-title", {}, [
|
$el("div.cm-menu-column.gap-2", [...this.createControlsLeft()]),
|
||||||
$el("font", {size:6, color:"white"}, [`ComfyUI Manager ${manager_version}`])]
|
$el("div.cm-menu-column.gap-2", [...this.createControlsMid()]),
|
||||||
),
|
$el("div.cm-menu-column.gap-2", [...this.createControlsRight()])
|
||||||
$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()])
|
|
||||||
]),
|
|
||||||
|
|
||||||
$el("br", {}, []),
|
|
||||||
close_button,
|
|
||||||
]
|
]
|
||||||
);
|
);
|
||||||
|
|
||||||
content.style.width = '100%';
|
const frame = buildGuiFrame(
|
||||||
content.style.height = '100%';
|
'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
|
||||||
|
|
||||||
this.element = $el("div.comfy-modal", { id:'cm-manager-dialog', parent: document.body }, [ content ]);
|
this.element = frame;
|
||||||
}
|
}
|
||||||
|
|
||||||
get isVisible() {
|
get isVisible() {
|
||||||
@ -1281,7 +1355,7 @@ class ManagerMenuDialog extends ComfyDialog {
|
|||||||
}
|
}
|
||||||
|
|
||||||
show() {
|
show() {
|
||||||
this.element.style.display = "block";
|
this.element.style.display = "flex";
|
||||||
}
|
}
|
||||||
|
|
||||||
toggleVisibility() {
|
toggleVisibility() {
|
||||||
|
|||||||
@ -1,8 +1,9 @@
|
|||||||
.cn-manager {
|
.cn-manager {
|
||||||
--grid-font: -apple-system, BlinkMacSystemFont, "Segoe UI", "Noto Sans", Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji";
|
--grid-font: -apple-system, BlinkMacSystemFont, "Segoe UI", "Noto Sans", Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji";
|
||||||
z-index: 1099;
|
z-index: 1099;
|
||||||
width: 80%;
|
width: 80vw;
|
||||||
height: 80%;
|
height: 75vh;
|
||||||
|
min-height: 30em;
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
gap: 10px;
|
gap: 10px;
|
||||||
@ -10,6 +11,7 @@
|
|||||||
font-family: arial, sans-serif;
|
font-family: arial, sans-serif;
|
||||||
text-underline-offset: 3px;
|
text-underline-offset: 3px;
|
||||||
outline: none;
|
outline: none;
|
||||||
|
margin: calc(var(--spacing)*2);
|
||||||
}
|
}
|
||||||
|
|
||||||
.cn-manager .cn-flex-auto {
|
.cn-manager .cn-flex-auto {
|
||||||
@ -17,17 +19,21 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.cn-manager button {
|
.cn-manager button {
|
||||||
|
width: auto;
|
||||||
|
position: relative;
|
||||||
|
overflow: hidden;
|
||||||
font-size: 16px;
|
font-size: 16px;
|
||||||
color: var(--input-text);
|
color: var(--input-text);
|
||||||
background-color: var(--comfy-input-bg);
|
background-color: var(--comfy-input-bg);
|
||||||
border-radius: 8px;
|
|
||||||
border-color: var(--border-color);
|
border-color: var(--border-color);
|
||||||
border-style: solid;
|
|
||||||
margin: 0;
|
margin: 0;
|
||||||
padding: 4px 8px;
|
|
||||||
min-width: 100px;
|
min-width: 100px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.cn-manager button:hover {
|
||||||
|
filter: brightness(125%);
|
||||||
|
}
|
||||||
|
|
||||||
.cn-manager button:disabled,
|
.cn-manager button:disabled,
|
||||||
.cn-manager input:disabled,
|
.cn-manager input:disabled,
|
||||||
.cn-manager select:disabled {
|
.cn-manager select:disabled {
|
||||||
@ -40,8 +46,13 @@
|
|||||||
|
|
||||||
.cn-manager .cn-manager-restart {
|
.cn-manager .cn-manager-restart {
|
||||||
display: none;
|
display: none;
|
||||||
background-color: #500000;
|
background-color: #500000 !important;
|
||||||
color: white;
|
border-color: #88181b !important;
|
||||||
|
color: white !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cn-manager .cn-manager-restart:hover {
|
||||||
|
background-color: #88181b !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.cn-manager .cn-manager-stop {
|
.cn-manager .cn-manager-stop {
|
||||||
@ -79,7 +90,6 @@
|
|||||||
flex-wrap: wrap;
|
flex-wrap: wrap;
|
||||||
gap: 5px;
|
gap: 5px;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
padding: 0 5px;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.cn-manager-header label {
|
.cn-manager-header label {
|
||||||
@ -91,16 +101,32 @@
|
|||||||
.cn-manager-filter {
|
.cn-manager-filter {
|
||||||
height: 28px;
|
height: 28px;
|
||||||
line-height: 28px;
|
line-height: 28px;
|
||||||
|
|
||||||
|
cursor: pointer;
|
||||||
|
padding: 0.5em 0.5em;
|
||||||
|
border: 1px solid var(--border-color);
|
||||||
|
border-radius: 6px;
|
||||||
|
background: var(--comfy-input-bg);
|
||||||
|
}
|
||||||
|
|
||||||
|
.cn-manager-filter:hover {
|
||||||
|
filter: brightness(125%);
|
||||||
}
|
}
|
||||||
|
|
||||||
.cn-manager-keywords {
|
.cn-manager-keywords {
|
||||||
height: 28px;
|
height: 28px;
|
||||||
line-height: 28px;
|
line-height: 28px;
|
||||||
padding: 0 5px 0 26px;
|
padding: 0 5px 0 26px;
|
||||||
|
background: var(--comfy-input-bg);
|
||||||
background-size: 16px;
|
background-size: 16px;
|
||||||
background-position: 5px center;
|
background-position: 5px center;
|
||||||
background-repeat: no-repeat;
|
background-repeat: no-repeat;
|
||||||
background-image: url("data:image/svg+xml;charset=utf8,%3Csvg%20viewBox%3D%220%200%2024%2024%22%20width%3D%22100%25%22%20height%3D%22100%25%22%20pointer-events%3D%22none%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%3E%3Cpath%20fill%3D%22none%22%20stroke%3D%22%23888%22%20stroke-linecap%3D%22round%22%20stroke-linejoin%3D%22round%22%20stroke-width%3D%222%22%20d%3D%22m21%2021-4.486-4.494M19%2010.5a8.5%208.5%200%201%201-17%200%208.5%208.5%200%200%201%2017%200%22%2F%3E%3C%2Fsvg%3E");
|
background-image: url("data:image/svg+xml;charset=utf8,%3Csvg%20viewBox%3D%220%200%2024%2024%22%20width%3D%22100%25%22%20height%3D%22100%25%22%20pointer-events%3D%22none%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%3E%3Cpath%20fill%3D%22none%22%20stroke%3D%22%23888%22%20stroke-linecap%3D%22round%22%20stroke-linejoin%3D%22round%22%20stroke-width%3D%222%22%20d%3D%22m21%2021-4.486-4.494M19%2010.5a8.5%208.5%200%201%201-17%200%208.5%208.5%200%200%201%2017%200%22%2F%3E%3C%2Fsvg%3E");
|
||||||
|
|
||||||
|
border: 1px solid var(--border-color);
|
||||||
|
border-radius: 6px;
|
||||||
|
|
||||||
|
outline-color: transparent;
|
||||||
}
|
}
|
||||||
|
|
||||||
.cn-manager-status {
|
.cn-manager-status {
|
||||||
@ -588,6 +614,10 @@
|
|||||||
height: 100%;
|
height: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.cn-install-buttons button {
|
||||||
|
padding: 4px 8px;
|
||||||
|
}
|
||||||
|
|
||||||
.cn-selected-buttons {
|
.cn-selected-buttons {
|
||||||
display: flex;
|
display: flex;
|
||||||
gap: 5px;
|
gap: 5px;
|
||||||
|
|||||||
@ -1,6 +1,7 @@
|
|||||||
import { app } from "../../scripts/app.js";
|
import { app } from "../../scripts/app.js";
|
||||||
import { ComfyDialog, $el } from "../../scripts/ui.js";
|
import { ComfyDialog, $el } from "../../scripts/ui.js";
|
||||||
import { api } from "../../scripts/api.js";
|
import { api } from "../../scripts/api.js";
|
||||||
|
import { buildGuiFrameCustomHeader, createSettingsCombo } from "./comfyui-gui-builder.js";
|
||||||
|
|
||||||
import {
|
import {
|
||||||
manager_instance, rebootAPI, install_via_git_url,
|
manager_instance, rebootAPI, install_via_git_url,
|
||||||
@ -18,32 +19,19 @@ loadCss("./custom-nodes-manager.css");
|
|||||||
const gridId = "node";
|
const gridId = "node";
|
||||||
|
|
||||||
const pageHtml = `
|
const pageHtml = `
|
||||||
<div class="cn-manager-header">
|
<div class="cn-manager cn-manager-dark">
|
||||||
<label>Filter
|
|
||||||
<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-grid"></div>
|
||||||
<div class="cn-manager-selection"></div>
|
<div class="cn-manager-selection"></div>
|
||||||
<div class="cn-manager-message"></div>
|
<div class="cn-manager-message"></div>
|
||||||
<div class="cn-manager-footer">
|
<div class="cn-manager-footer">
|
||||||
<button class="cn-manager-back">
|
<button class="cn-manager-restart p-button p-component">Restart</button>
|
||||||
<svg class="arrow-icon" width="14" height="14" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
|
<button class="cn-manager-stop p-button p-component">Stop</button>
|
||||||
<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>
|
<div class="cn-flex-auto"></div>
|
||||||
<button class="cn-manager-used-in-workflow">Used In Workflow</button>
|
<button class="cn-manager-used-in-workflow p-button p-component">Used In Workflow</button>
|
||||||
<button class="cn-manager-check-update">Check Update</button>
|
<button class="cn-manager-check-update p-button p-component">Check Update</button>
|
||||||
<button class="cn-manager-check-missing">Check Missing</button>
|
<button class="cn-manager-check-missing p-button p-component">Check Missing</button>
|
||||||
<button class="cn-manager-install-url">Install via Git URL</button>
|
<button class="cn-manager-install-url p-button p-component">Install via Git URL</button>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
`;
|
`;
|
||||||
|
|
||||||
@ -89,11 +77,26 @@ export class CustomNodesManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
init() {
|
init() {
|
||||||
this.element = $el("div", {
|
const header = $el("div.cn-manager-header.px-2", {}, [
|
||||||
parent: document.body,
|
// $el("label", {}, [
|
||||||
className: "comfy-modal cn-manager"
|
// $el("span", { textContent: "Filter" }),
|
||||||
});
|
// $el("select.cn-manager-filter")
|
||||||
this.element.innerHTML = pageHtml;
|
// ]),
|
||||||
|
createSettingsCombo("Filter", $el("select.cn-manager-filter")),
|
||||||
|
$el("input.cn-manager-keywords.p-inputtext.p-component", { type: "search", placeholder: "Search" }),
|
||||||
|
$el("div.cn-manager-status"),
|
||||||
|
$el("div.cn-flex-auto"),
|
||||||
|
$el("div.cn-manager-channel")
|
||||||
|
]);
|
||||||
|
|
||||||
|
const frame = buildGuiFrameCustomHeader(
|
||||||
|
'cn-manager-dialog', // dialog id
|
||||||
|
header, // custom header element
|
||||||
|
pageHtml, // dialog content element
|
||||||
|
this
|
||||||
|
); // send this so we can attach close functions
|
||||||
|
|
||||||
|
this.element = frame;
|
||||||
this.element.setAttribute("tabindex", 0);
|
this.element.setAttribute("tabindex", 0);
|
||||||
this.element.focus();
|
this.element.focus();
|
||||||
|
|
||||||
@ -372,7 +375,7 @@ export class CustomNodesManager {
|
|||||||
|
|
||||||
return list.map(id => {
|
return list.map(id => {
|
||||||
const bt = buttons[id];
|
const bt = buttons[id];
|
||||||
return `<button class="cn-btn-${id}" group="${action}" mode="${bt.mode}">${bt.label}</button>`;
|
return `<button class="cn-btn-${id} p-button p-component" group="${action}" mode="${bt.mode}">${bt.label}</button>`;
|
||||||
}).join("");
|
}).join("");
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -655,7 +658,6 @@ export class CustomNodesManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
renderGrid() {
|
renderGrid() {
|
||||||
|
|
||||||
// update theme
|
// update theme
|
||||||
const globalStyle = window.getComputedStyle(document.body);
|
const globalStyle = window.getComputedStyle(document.body);
|
||||||
this.colorVars = {
|
this.colorVars = {
|
||||||
|
|||||||
@ -1,13 +1,15 @@
|
|||||||
.cmm-manager {
|
.cmm-manager {
|
||||||
--grid-font: -apple-system, BlinkMacSystemFont, "Segoe UI", "Noto Sans", Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji";
|
--grid-font: -apple-system, BlinkMacSystemFont, "Segoe UI", "Noto Sans", Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji";
|
||||||
z-index: 1099;
|
z-index: 1099;
|
||||||
width: 80%;
|
width: 80vw;
|
||||||
height: 80%;
|
height: 75vh;
|
||||||
|
min-height: 30em;
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
gap: 10px;
|
gap: 10px;
|
||||||
color: var(--fg-color);
|
color: var(--fg-color);
|
||||||
font-family: arial, sans-serif;
|
font-family: arial, sans-serif;
|
||||||
|
margin: calc(var(--spacing)*2);
|
||||||
}
|
}
|
||||||
|
|
||||||
.cmm-manager .cmm-flex-auto {
|
.cmm-manager .cmm-flex-auto {
|
||||||
@ -18,14 +20,15 @@
|
|||||||
font-size: 16px;
|
font-size: 16px;
|
||||||
color: var(--input-text);
|
color: var(--input-text);
|
||||||
background-color: var(--comfy-input-bg);
|
background-color: var(--comfy-input-bg);
|
||||||
border-radius: 8px;
|
|
||||||
border-color: var(--border-color);
|
border-color: var(--border-color);
|
||||||
border-style: solid;
|
|
||||||
margin: 0;
|
margin: 0;
|
||||||
padding: 4px 8px;
|
|
||||||
min-width: 100px;
|
min-width: 100px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.cmm-manager button:hover {
|
||||||
|
filter: brightness(125%);
|
||||||
|
}
|
||||||
|
|
||||||
.cmm-manager button:disabled,
|
.cmm-manager button:disabled,
|
||||||
.cmm-manager input:disabled,
|
.cmm-manager input:disabled,
|
||||||
.cmm-manager select:disabled {
|
.cmm-manager select:disabled {
|
||||||
@ -38,7 +41,7 @@
|
|||||||
|
|
||||||
.cmm-manager .cmm-manager-refresh {
|
.cmm-manager .cmm-manager-refresh {
|
||||||
display: none;
|
display: none;
|
||||||
background-color: #000080;
|
background-color: #000080 !important;
|
||||||
color: white;
|
color: white;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -53,7 +56,6 @@
|
|||||||
flex-wrap: wrap;
|
flex-wrap: wrap;
|
||||||
gap: 5px;
|
gap: 5px;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
padding: 0 5px;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.cmm-manager-header label {
|
.cmm-manager-header label {
|
||||||
@ -67,16 +69,34 @@
|
|||||||
.cmm-manager-filter {
|
.cmm-manager-filter {
|
||||||
height: 28px;
|
height: 28px;
|
||||||
line-height: 28px;
|
line-height: 28px;
|
||||||
|
|
||||||
|
cursor: pointer;
|
||||||
|
padding: 0.5em 0.5em;
|
||||||
|
border: 1px solid var(--border-color);
|
||||||
|
border-radius: 6px;
|
||||||
|
background: var(--comfy-input-bg);
|
||||||
|
}
|
||||||
|
|
||||||
|
.cmm-manager-type:hover,
|
||||||
|
.cmm-manager-base:hover,
|
||||||
|
.cmm-manager-filter:hover {
|
||||||
|
filter: brightness(125%);
|
||||||
}
|
}
|
||||||
|
|
||||||
.cmm-manager-keywords {
|
.cmm-manager-keywords {
|
||||||
height: 28px;
|
height: 28px;
|
||||||
line-height: 28px;
|
line-height: 28px;
|
||||||
padding: 0 5px 0 26px;
|
padding: 0 5px 0 26px;
|
||||||
|
background: var(--comfy-input-bg);
|
||||||
background-size: 16px;
|
background-size: 16px;
|
||||||
background-position: 5px center;
|
background-position: 5px center;
|
||||||
background-repeat: no-repeat;
|
background-repeat: no-repeat;
|
||||||
background-image: url("data:image/svg+xml;charset=utf8,%3Csvg%20viewBox%3D%220%200%2024%2024%22%20width%3D%22100%25%22%20height%3D%22100%25%22%20pointer-events%3D%22none%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%3E%3Cpath%20fill%3D%22none%22%20stroke%3D%22%23888%22%20stroke-linecap%3D%22round%22%20stroke-linejoin%3D%22round%22%20stroke-width%3D%222%22%20d%3D%22m21%2021-4.486-4.494M19%2010.5a8.5%208.5%200%201%201-17%200%208.5%208.5%200%200%201%2017%200%22%2F%3E%3C%2Fsvg%3E");
|
background-image: url("data:image/svg+xml;charset=utf8,%3Csvg%20viewBox%3D%220%200%2024%2024%22%20width%3D%22100%25%22%20height%3D%22100%25%22%20pointer-events%3D%22none%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%3E%3Cpath%20fill%3D%22none%22%20stroke%3D%22%23888%22%20stroke-linecap%3D%22round%22%20stroke-linejoin%3D%22round%22%20stroke-width%3D%222%22%20d%3D%22m21%2021-4.486-4.494M19%2010.5a8.5%208.5%200%201%201-17%200%208.5%208.5%200%200%201%2017%200%22%2F%3E%3C%2Fsvg%3E");
|
||||||
|
|
||||||
|
border: 1px solid var(--border-color);
|
||||||
|
border-radius: 6px;
|
||||||
|
|
||||||
|
outline-color: transparent;
|
||||||
}
|
}
|
||||||
|
|
||||||
.cmm-manager-status {
|
.cmm-manager-status {
|
||||||
@ -148,6 +168,10 @@
|
|||||||
color: white;
|
color: white;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.cmm-btn-install {
|
||||||
|
padding: 4px 8px;
|
||||||
|
}
|
||||||
|
|
||||||
.cmm-btn-download {
|
.cmm-btn-download {
|
||||||
width: 18px;
|
width: 18px;
|
||||||
height: 18px;
|
height: 18px;
|
||||||
|
|||||||
@ -9,40 +9,23 @@ import { api } from "../../scripts/api.js";
|
|||||||
|
|
||||||
// https://cenfun.github.io/turbogrid/api.html
|
// https://cenfun.github.io/turbogrid/api.html
|
||||||
import TG from "./turbogrid.esm.js";
|
import TG from "./turbogrid.esm.js";
|
||||||
|
import { buildGuiFrameCustomHeader, createSettingsCombo } from "./comfyui-gui-builder.js";
|
||||||
|
|
||||||
loadCss("./model-manager.css");
|
loadCss("./model-manager.css");
|
||||||
|
|
||||||
const gridId = "model";
|
const gridId = "model";
|
||||||
|
|
||||||
const pageHtml = `
|
const pageHtml = `
|
||||||
<div class="cmm-manager-header">
|
<div class="cmm-manager cmm-manager-dark">
|
||||||
<label>Filter
|
|
||||||
<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-grid"></div>
|
||||||
<div class="cmm-manager-selection"></div>
|
<div class="cmm-manager-selection"></div>
|
||||||
<div class="cmm-manager-message"></div>
|
<div class="cmm-manager-message"></div>
|
||||||
<div class="cmm-manager-footer">
|
<div class="cmm-manager-footer">
|
||||||
<button class="cmm-manager-back">
|
<button class="cmm-manager-refresh p-button p-component">Refresh</button>
|
||||||
<svg class="arrow-icon" width="14" height="14" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
|
<button class="cmm-manager-stop p-button p-component">Stop</button>
|
||||||
<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 class="cmm-flex-auto"></div>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
`;
|
`;
|
||||||
|
|
||||||
export class ModelManager {
|
export class ModelManager {
|
||||||
@ -64,11 +47,23 @@ export class ModelManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
init() {
|
init() {
|
||||||
this.element = $el("div", {
|
const header = $el("div.cmm-manager-header", {}, [
|
||||||
parent: document.body,
|
createSettingsCombo("Filter", $el("select.cmm-manager-filter")),
|
||||||
className: "comfy-modal cmm-manager"
|
createSettingsCombo("Type", $el("select.cmm-manager-type")),
|
||||||
});
|
createSettingsCombo("Base", $el("select.cmm-manager-base")),
|
||||||
this.element.innerHTML = pageHtml;
|
$el("input.cmm-manager-keywords.p-inputtext.p-component", { type: "search", placeholder: "Search" }),
|
||||||
|
$el("div.cmm-manager-status"),
|
||||||
|
$el("div.cmm-flex-auto")
|
||||||
|
]);
|
||||||
|
|
||||||
|
const frame = buildGuiFrameCustomHeader(
|
||||||
|
'cmm-manager-dialog', // dialog id
|
||||||
|
header, // custom header element
|
||||||
|
pageHtml, // dialog content element
|
||||||
|
this
|
||||||
|
); // send this so we can attach close functions
|
||||||
|
|
||||||
|
this.element = frame;
|
||||||
this.initFilter();
|
this.initFilter();
|
||||||
this.bindEvents();
|
this.bindEvents();
|
||||||
this.initGrid();
|
this.initGrid();
|
||||||
@ -347,7 +342,7 @@ export class ModelManager {
|
|||||||
if (installed === "True") {
|
if (installed === "True") {
|
||||||
return `<div class="cmm-icon-passed">${icons.passed}</div>`;
|
return `<div class="cmm-icon-passed">${icons.passed}</div>`;
|
||||||
}
|
}
|
||||||
return `<button class="cmm-btn-install" mode="install">Install</button>`;
|
return `<button class="cmm-btn-install p-button p-component" mode="install">Install</button>`;
|
||||||
}
|
}
|
||||||
}, {
|
}, {
|
||||||
id: 'url',
|
id: 'url',
|
||||||
@ -420,7 +415,7 @@ export class ModelManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
this.selectedModels = selectedList;
|
this.selectedModels = selectedList;
|
||||||
this.showSelection(`<span>Selected <b>${selectedList.length}</b> models <button class="cmm-btn-install" mode="install">Install</button>`);
|
this.showSelection(`<span>Selected <b>${selectedList.length}</b> models <button class="cmm-btn-install p-button p-component" mode="install">Install</button>`);
|
||||||
}
|
}
|
||||||
|
|
||||||
focusInstall(item) {
|
focusInstall(item) {
|
||||||
|
|||||||
65
js/snapshot.css
Normal file
65
js/snapshot.css
Normal file
@ -0,0 +1,65 @@
|
|||||||
|
.snapshot-manager {
|
||||||
|
--grid-font: -apple-system, BlinkMacSystemFont, "Segoe UI", "Noto Sans", Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji";
|
||||||
|
z-index: 1099;
|
||||||
|
width: 80vw;
|
||||||
|
height: 75vh;
|
||||||
|
min-height: 30em;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 10px;
|
||||||
|
color: var(--fg-color);
|
||||||
|
font-family: arial, sans-serif;
|
||||||
|
text-underline-offset: 3px;
|
||||||
|
outline: none;
|
||||||
|
margin: calc(var(--spacing)*2);
|
||||||
|
}
|
||||||
|
|
||||||
|
.snapshot-manager button {
|
||||||
|
width: auto;
|
||||||
|
position: relative;
|
||||||
|
overflow: hidden;
|
||||||
|
font-size: 16px;
|
||||||
|
color: var(--input-text);
|
||||||
|
background-color: var(--comfy-input-bg);
|
||||||
|
border-color: var(--border-color);
|
||||||
|
margin: 0;
|
||||||
|
min-width: 100px;
|
||||||
|
padding: 4px 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.snapshot-manager .snapshot-restore-btn {
|
||||||
|
background-color: #00158f !important;
|
||||||
|
border-color: #2025b9 !important;
|
||||||
|
color: white !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.snapshot-manager .snapshot-remove-btn {
|
||||||
|
background-color: #970000 !important;
|
||||||
|
border-color: #be2127 !important;
|
||||||
|
color: white !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.snapshot-manager button:hover {
|
||||||
|
filter: brightness(125%);
|
||||||
|
}
|
||||||
|
|
||||||
|
.snapshot-manager .data-btns {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: calc(var(--spacing)*2);
|
||||||
|
padding: calc(var(--spacing)*2);
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.snapshot-footer {
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
gap: 10px;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.snapshot-manager .cn-flex-auto {
|
||||||
|
flex: auto;
|
||||||
|
}
|
||||||
@ -1,8 +1,10 @@
|
|||||||
import { app } from "../../scripts/app.js";
|
import { app } from "../../scripts/app.js";
|
||||||
import { api } from "../../scripts/api.js"
|
import { api } from "../../scripts/api.js"
|
||||||
import { ComfyDialog, $el } from "../../scripts/ui.js";
|
import { ComfyDialog, $el } from "../../scripts/ui.js";
|
||||||
import { manager_instance, rebootAPI, show_message, handle403Response } from "./common.js";
|
import { manager_instance, rebootAPI, show_message, handle403Response, loadCss } from "./common.js";
|
||||||
|
import { buildGuiFrame } from "./comfyui-gui-builder.js";
|
||||||
|
|
||||||
|
loadCss("./snapshot.css");
|
||||||
|
|
||||||
async function restore_snapshot(target) {
|
async function restore_snapshot(target) {
|
||||||
if(SnapshotManager.instance) {
|
if(SnapshotManager.instance) {
|
||||||
@ -27,7 +29,7 @@ async function restore_snapshot(target) {
|
|||||||
}
|
}
|
||||||
finally {
|
finally {
|
||||||
await SnapshotManager.instance.invalidateControl();
|
await SnapshotManager.instance.invalidateControl();
|
||||||
SnapshotManager.instance.updateMessage("<BR>To apply the snapshot, please <button id='cm-reboot-button2' class='cm-small-button'>RESTART</button> ComfyUI. And refresh browser.", 'cm-reboot-button2');
|
SnapshotManager.instance.updateMessage("<BR>To apply the snapshot, please <button id='cm-reboot-button2' class='p-button p-component'>RESTART</button> ComfyUI. And refresh browser.", 'cm-reboot-button2');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -88,6 +90,8 @@ export class SnapshotManager extends ComfyDialog {
|
|||||||
message_box = null;
|
message_box = null;
|
||||||
data = null;
|
data = null;
|
||||||
|
|
||||||
|
content = $el("div.snapshot-manager");
|
||||||
|
|
||||||
clear() {
|
clear() {
|
||||||
this.restore_buttons = [];
|
this.restore_buttons = [];
|
||||||
this.message_box = null;
|
this.message_box = null;
|
||||||
@ -96,9 +100,18 @@ export class SnapshotManager extends ComfyDialog {
|
|||||||
|
|
||||||
constructor(app, manager_dialog) {
|
constructor(app, manager_dialog) {
|
||||||
super();
|
super();
|
||||||
this.manager_dialog = manager_dialog;
|
// this.manager_dialog = manager_dialog;
|
||||||
this.search_keyword = '';
|
this.search_keyword = '';
|
||||||
this.element = $el("div.comfy-modal", { parent: document.body }, []);
|
|
||||||
|
const frame = buildGuiFrame(
|
||||||
|
'snapshot-manager-dialog', // dialog id
|
||||||
|
'Snapshot Manager', // title
|
||||||
|
'i.mdi.mdi-puzzle', // icon class
|
||||||
|
this.content, // dialog content element
|
||||||
|
this
|
||||||
|
); // send this so we can attach close functions
|
||||||
|
|
||||||
|
this.element = frame;
|
||||||
}
|
}
|
||||||
|
|
||||||
async remove_item() {
|
async remove_item() {
|
||||||
@ -109,7 +122,7 @@ export class SnapshotManager extends ComfyDialog {
|
|||||||
|
|
||||||
createControls() {
|
createControls() {
|
||||||
return [
|
return [
|
||||||
$el("button.cm-small-button", {
|
$el("button.p-button.p-component", {
|
||||||
type: "button",
|
type: "button",
|
||||||
textContent: "Close",
|
textContent: "Close",
|
||||||
onclick: () => { this.close(); }
|
onclick: () => { this.close(); }
|
||||||
@ -132,8 +145,8 @@ export class SnapshotManager extends ComfyDialog {
|
|||||||
this.clear();
|
this.clear();
|
||||||
this.data = (await getSnapshotList()).items;
|
this.data = (await getSnapshotList()).items;
|
||||||
|
|
||||||
while (this.element.children.length) {
|
while (this.content.children.length) {
|
||||||
this.element.removeChild(this.element.children[0]);
|
this.content.removeChild(this.content.children[0]);
|
||||||
}
|
}
|
||||||
|
|
||||||
await this.createGrid();
|
await this.createGrid();
|
||||||
@ -204,20 +217,21 @@ export class SnapshotManager extends ComfyDialog {
|
|||||||
data2.innerHTML = ` ${data}`;
|
data2.innerHTML = ` ${data}`;
|
||||||
var data_button = document.createElement('td');
|
var data_button = document.createElement('td');
|
||||||
data_button.style.textAlign = "center";
|
data_button.style.textAlign = "center";
|
||||||
|
data_button.className = "data-btns";
|
||||||
|
|
||||||
var restoreBtn = document.createElement('button');
|
var restoreBtn = document.createElement('button');
|
||||||
|
restoreBtn.className = "snapshot-restore-btn p-button p-component";
|
||||||
restoreBtn.innerHTML = 'Restore';
|
restoreBtn.innerHTML = 'Restore';
|
||||||
restoreBtn.style.width = "100px";
|
restoreBtn.style.width = "100px";
|
||||||
restoreBtn.style.backgroundColor = 'blue';
|
|
||||||
|
|
||||||
restoreBtn.addEventListener('click', function() {
|
restoreBtn.addEventListener('click', function() {
|
||||||
restore_snapshot(data);
|
restore_snapshot(data);
|
||||||
});
|
});
|
||||||
|
|
||||||
var removeBtn = document.createElement('button');
|
var removeBtn = document.createElement('button');
|
||||||
|
removeBtn.className = "snapshot-remove-btn p-button p-component";
|
||||||
removeBtn.innerHTML = 'Remove';
|
removeBtn.innerHTML = 'Remove';
|
||||||
removeBtn.style.width = "100px";
|
removeBtn.style.width = "100px";
|
||||||
removeBtn.style.backgroundColor = 'red';
|
|
||||||
|
|
||||||
removeBtn.addEventListener('click', function() {
|
removeBtn.addEventListener('click', function() {
|
||||||
remove_snapshot(data);
|
remove_snapshot(data);
|
||||||
@ -241,13 +255,14 @@ export class SnapshotManager extends ComfyDialog {
|
|||||||
let self = this;
|
let self = this;
|
||||||
const panel = document.createElement('div');
|
const panel = document.createElement('div');
|
||||||
panel.style.width = "100%";
|
panel.style.width = "100%";
|
||||||
|
panel.style.height = "100%";
|
||||||
panel.appendChild(grid);
|
panel.appendChild(grid);
|
||||||
|
|
||||||
function handleResize() {
|
function handleResize() {
|
||||||
const parentHeight = self.element.clientHeight;
|
const parentHeight = self.element.clientHeight;
|
||||||
const gridHeight = parentHeight - 200;
|
const gridHeight = parentHeight - 200;
|
||||||
|
|
||||||
grid.style.height = gridHeight + "px";
|
// grid.style.height = gridHeight + "px";
|
||||||
}
|
}
|
||||||
window.addEventListener("resize", handleResize);
|
window.addEventListener("resize", handleResize);
|
||||||
|
|
||||||
@ -256,25 +271,17 @@ export class SnapshotManager extends ComfyDialog {
|
|||||||
grid.style.width = "100%";
|
grid.style.width = "100%";
|
||||||
grid.style.height = "100%";
|
grid.style.height = "100%";
|
||||||
grid.style.overflowY = "scroll";
|
grid.style.overflowY = "scroll";
|
||||||
this.element.style.height = "85%";
|
|
||||||
this.element.style.width = "80%";
|
this.content.appendChild(panel);
|
||||||
this.element.appendChild(panel);
|
|
||||||
|
|
||||||
handleResize();
|
handleResize();
|
||||||
}
|
}
|
||||||
|
|
||||||
async createBottomControls() {
|
async createBottomControls() {
|
||||||
var close_button = document.createElement("button");
|
|
||||||
close_button.className = "cm-small-button";
|
|
||||||
close_button.innerHTML = "Close";
|
|
||||||
close_button.onclick = () => { this.close(); }
|
|
||||||
close_button.style.display = "inline-block";
|
|
||||||
|
|
||||||
var save_button = document.createElement("button");
|
var save_button = document.createElement("button");
|
||||||
save_button.className = "cm-small-button";
|
save_button.className = "p-button p-component";
|
||||||
save_button.innerHTML = "Save snapshot";
|
save_button.innerHTML = "Save snapshot";
|
||||||
save_button.onclick = () => { save_current_snapshot(); }
|
save_button.onclick = () => { save_current_snapshot(); }
|
||||||
save_button.style.display = "inline-block";
|
|
||||||
save_button.style.horizontalAlign = "right";
|
save_button.style.horizontalAlign = "right";
|
||||||
save_button.style.width = "170px";
|
save_button.style.width = "170px";
|
||||||
|
|
||||||
@ -282,15 +289,19 @@ export class SnapshotManager extends ComfyDialog {
|
|||||||
this.message_box.style.height = '60px';
|
this.message_box.style.height = '60px';
|
||||||
this.message_box.style.verticalAlign = 'middle';
|
this.message_box.style.verticalAlign = 'middle';
|
||||||
|
|
||||||
this.element.appendChild(this.message_box);
|
const footer = $el("div.snapshot-footer");
|
||||||
this.element.appendChild(close_button);
|
const spacer = $el("div.cn-flex-auto");
|
||||||
this.element.appendChild(save_button);
|
footer.appendChild(spacer);
|
||||||
|
footer.appendChild(save_button);
|
||||||
|
|
||||||
|
this.content.appendChild(this.message_box);
|
||||||
|
this.content.appendChild(footer);
|
||||||
}
|
}
|
||||||
|
|
||||||
async show() {
|
async show() {
|
||||||
try {
|
try {
|
||||||
this.invalidateControl();
|
this.invalidateControl();
|
||||||
this.element.style.display = "block";
|
this.element.style.display = "flex";
|
||||||
this.element.style.zIndex = 1099;
|
this.element.style.zIndex = 1099;
|
||||||
}
|
}
|
||||||
catch(exception) {
|
catch(exception) {
|
||||||
|
|||||||
@ -1,5 +1,665 @@
|
|||||||
{
|
{
|
||||||
"custom_nodes": [
|
"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 rendering,Edit images using natural language instructions..\nNOTE: The files in the repo are not organized."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"author": "saltchicken",
|
||||||
|
"title": "ComfyUI-Identity-Mixer",
|
||||||
|
"reference": "https://github.com/saltchicken/ComfyUI-Identity-Mixer",
|
||||||
|
"files": [
|
||||||
|
"https://github.com/saltchicken/ComfyUI-Identity-Mixer"
|
||||||
|
],
|
||||||
|
"install_type": "git-clone",
|
||||||
|
"description": "Mixes multiple identity LoRAs with normalized strength."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"author": "saltchicken",
|
||||||
|
"title": "ComfyUI-Prompter",
|
||||||
|
"reference": "https://github.com/saltchicken/ComfyUI-Prompter",
|
||||||
|
"files": [
|
||||||
|
"https://github.com/saltchicken/ComfyUI-Prompter"
|
||||||
|
],
|
||||||
|
"install_type": "git-clone",
|
||||||
|
"description": "ComfyUI custom node providing customizable prompt generation capabilities. (Description by CC)"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"author": "JBKing514",
|
||||||
|
"title": "[WIP] map_comfyui",
|
||||||
|
"reference": "https://github.com/JBKing514/map_comfyui",
|
||||||
|
"files": [
|
||||||
|
"https://github.com/JBKing514/map_comfyui"
|
||||||
|
],
|
||||||
|
"install_type": "git-clone",
|
||||||
|
"description": "A custom node implementing the Manifold Alignment Protocol (MAP) within ComfyUI, transforming diffusion sampling into a measurable and visualizable geometric process. (Description by CC)\nNOTE: The files in the repo are not organized."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"author": "Nynxz",
|
||||||
|
"title": "ComfyUI_DiffsynthPause",
|
||||||
|
"reference": "https://github.com/Nynxz/ComfyUI_DiffsynthPause",
|
||||||
|
"files": [
|
||||||
|
"https://github.com/Nynxz/ComfyUI_DiffsynthPause"
|
||||||
|
],
|
||||||
|
"install_type": "git-clone",
|
||||||
|
"description": "ComfyUI custom node for controlling Diffsynth checkpoint pausing behavior during image generation workflows. (Description by CC)"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"author": "binarystatic",
|
||||||
|
"title": "ComfyUI-BinarystaticMasterSeed",
|
||||||
|
"reference": "https://github.com/binarystatic/ComfyUI-BinarystaticMasterSeed",
|
||||||
|
"files": [
|
||||||
|
"https://github.com/binarystatic/ComfyUI-BinarystaticMasterSeed"
|
||||||
|
],
|
||||||
|
"install_type": "git-clone",
|
||||||
|
"description": "BinarystaticMasterSeed node for ComfyUI. (Description by CC)"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"author": "Aruntd008",
|
||||||
|
"title": "[WIP] ComfyUI_SeamlessPattern",
|
||||||
|
"reference": "https://github.com/Aruntd008/ComfyUI_SeamlessPattern",
|
||||||
|
"files": [
|
||||||
|
"https://github.com/Aruntd008/ComfyUI_SeamlessPattern"
|
||||||
|
],
|
||||||
|
"install_type": "git-clone",
|
||||||
|
"description": "SeamlessPatternNode for ComfyUI. (Description by CC)\nNOTE: The files in the repo are not organized."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"author": "SilentLuxRay",
|
||||||
|
"title": "[WIP] ComfyUI-Furrey-Super-Prompt",
|
||||||
|
"reference": "https://github.com/SilentLuxRay/ComfyUI-Furrey-Super-Prompt",
|
||||||
|
"files": [
|
||||||
|
"https://github.com/SilentLuxRay/ComfyUI-Furrey-Super-Prompt"
|
||||||
|
],
|
||||||
|
"install_type": "git-clone",
|
||||||
|
"description": "A personalized all-in-one node for ComfyUI that simplifies prompt management and LoRA handling with automatic translation to English. (Description by CC)\nNOTE: The files in the repo are not organized."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"author": "Rayen21",
|
||||||
|
"title": "[WIP] ComfyUI-PromptLinePlus",
|
||||||
|
"reference": "https://github.com/Rayen21/ComfyUI-PromptLinePlus",
|
||||||
|
"files": [
|
||||||
|
"https://github.com/Rayen21/ComfyUI-PromptLinePlus"
|
||||||
|
],
|
||||||
|
"install_type": "git-clone",
|
||||||
|
"description": "ComfyUI custom node that splits multi-line prompts by line, enabling batch image generation with each line triggering one execution and supporting custom prompt boxes. (Description by CC)\nNOTE: The files in the repo are not organized."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"author": "rookiestar28",
|
||||||
|
"title": "ComfyUI_Security_Audit",
|
||||||
|
"reference": "https://github.com/rookiestar28/ComfyUI_Security_Audit",
|
||||||
|
"files": [
|
||||||
|
"https://github.com/rookiestar28/ComfyUI_Security_Audit"
|
||||||
|
],
|
||||||
|
"install_type": "git-clone",
|
||||||
|
"description": "A lightweight, dual-layer security extension for ComfyUI using AST-based static analysis and runtime monitoring to detect malicious code in custom nodes."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"author": "c1660181647-hash",
|
||||||
|
"title": "ComfyUI-MM-Visual-Encryption",
|
||||||
|
"reference": "https://github.com/c1660181647-hash/ComfyUI-MM-Visual-Encryption",
|
||||||
|
"files": [
|
||||||
|
"https://github.com/c1660181647-hash/ComfyUI-MM-Visual-Encryption"
|
||||||
|
],
|
||||||
|
"install_type": "git-clone",
|
||||||
|
"description": "A visual noise encryption custom node for ComfyUI, supporting Image and Video privacy protection."
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"author": "charlierz",
|
"author": "charlierz",
|
||||||
"title": "comfyui-charlierz",
|
"title": "comfyui-charlierz",
|
||||||
@ -1354,16 +2014,6 @@
|
|||||||
"install_type": "git-clone",
|
"install_type": "git-clone",
|
||||||
"description": "A powerful ComfyUI extension that helps you focus on selected nodes by highlighting their connections while dimming or hiding unrelated links\nNOTE: The files in the repo are not organized."
|
"description": "A powerful ComfyUI extension that helps you focus on selected nodes by highlighting their connections while dimming or hiding unrelated links\nNOTE: The files in the repo are not organized."
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"author": "pizurny",
|
|
||||||
"title": "ComfyUI-Just-DWPose [WIP]",
|
|
||||||
"reference": "https://github.com/pizurny/ComfyUI-Just-DWPose",
|
|
||||||
"files": [
|
|
||||||
"https://github.com/pizurny/ComfyUI-Just-DWPose"
|
|
||||||
],
|
|
||||||
"install_type": "git-clone",
|
|
||||||
"description": "An advanced DWPose annotator for ComfyUI with TorchScript and ONNX backends, featuring comprehensive pose detection, bone validation, temporal smoothing, and custom visualization tools."
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"author": "pizurny",
|
"author": "pizurny",
|
||||||
"title": "ComfyUI Feedback Sampler [WIP]",
|
"title": "ComfyUI Feedback Sampler [WIP]",
|
||||||
@ -1784,16 +2434,6 @@
|
|||||||
"install_type": "git-clone",
|
"install_type": "git-clone",
|
||||||
"description": "NODES: 'Pluto : Auto Crop Faces', 'Pluto : Composite Image', 'Pluto : Float Math', 'Pluto : GetSizeFromImage', 'Pluto : Text Append', ..."
|
"description": "NODES: 'Pluto : Auto Crop Faces', 'Pluto : Composite Image', 'Pluto : Float Math', 'Pluto : GetSizeFromImage', 'Pluto : Text Append', ..."
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"author": "lfelipegg",
|
|
||||||
"title": "lfgg_custom_nodes [WIP]",
|
|
||||||
"reference": "https://github.com/lfelipegg/lfgg_custom_nodes",
|
|
||||||
"files": [
|
|
||||||
"https://github.com/lfelipegg/lfgg_custom_nodes"
|
|
||||||
],
|
|
||||||
"install_type": "git-clone",
|
|
||||||
"description": "NODES: ModelMergeCombos\nNOTE: The files in the repo are not organized."
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"author": "lu64k",
|
"author": "lu64k",
|
||||||
"title": "ks_nodes",
|
"title": "ks_nodes",
|
||||||
@ -4367,16 +5007,6 @@
|
|||||||
"install_type": "git-clone",
|
"install_type": "git-clone",
|
||||||
"description": "Generate random prompts easily for FMJ.\nNOTE: The files in the repo are not organized."
|
"description": "Generate random prompts easily for FMJ.\nNOTE: The files in the repo are not organized."
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"author": "amamisonlyuser",
|
|
||||||
"title": "MixvtonComfyui [WIP]",
|
|
||||||
"reference": "https://github.com/amamisonlyuser/MixvtonComfyui",
|
|
||||||
"files": [
|
|
||||||
"https://github.com/amamisonlyuser/MixvtonComfyui"
|
|
||||||
],
|
|
||||||
"install_type": "git-clone",
|
|
||||||
"description": "NODES: CXH_Leffa_Viton_Load, CXH_Leffa_Viton_Run\nNOTE: The files in the repo are not organized."
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"author": "pictorialink",
|
"author": "pictorialink",
|
||||||
"title": "comfyui-static-resource[UNSAFE]",
|
"title": "comfyui-static-resource[UNSAFE]",
|
||||||
@ -4998,16 +5628,6 @@
|
|||||||
"description": "NODES: Properties, Apply SVG to Image",
|
"description": "NODES: Properties, Apply SVG to Image",
|
||||||
"install_type": "git-clone"
|
"install_type": "git-clone"
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"author": "AhBumm",
|
|
||||||
"title": "ComfyUI_MangaLineExtraction",
|
|
||||||
"reference": "https://github.com/AhBumm/ComfyUI_MangaLineExtraction-hf",
|
|
||||||
"files": [
|
|
||||||
"https://github.com/AhBumm/ComfyUI_MangaLineExtraction-hf"
|
|
||||||
],
|
|
||||||
"description": "p1atdev/MangaLineExtraction-hf as a node in comfyui",
|
|
||||||
"install_type": "git-clone"
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"author": "Kur0butiMegane",
|
"author": "Kur0butiMegane",
|
||||||
"title": "Comfyui-StringUtils",
|
"title": "Comfyui-StringUtils",
|
||||||
@ -5058,16 +5678,6 @@
|
|||||||
"install_type": "git-clone",
|
"install_type": "git-clone",
|
||||||
"description": "ComfyUI implementation of the partfield nvidea segmentation models\nNOTE: The files in the repo are not organized."
|
"description": "ComfyUI implementation of the partfield nvidea segmentation models\nNOTE: The files in the repo are not organized."
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"author": "shinich39",
|
|
||||||
"title": "comfyui-nothing-happened",
|
|
||||||
"reference": "httphttps://github.com/shinich39/comfyui-nothing-happened",
|
|
||||||
"files": [
|
|
||||||
"https://github.com/shinich39/comfyui-nothing-happened"
|
|
||||||
],
|
|
||||||
"description": "Save image and keep metadata.",
|
|
||||||
"install_type": "git-clone"
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"author": "silveroxides",
|
"author": "silveroxides",
|
||||||
"title": "ComfyUI_ReduxEmbedToolkit",
|
"title": "ComfyUI_ReduxEmbedToolkit",
|
||||||
@ -8360,16 +8970,6 @@
|
|||||||
"install_type": "git-clone",
|
"install_type": "git-clone",
|
||||||
"description": "Gets a random file from a directory. Returns the filpath as a STRING. [w/This node allows access to arbitrary files through the workflow, which could pose a security threat.]"
|
"description": "Gets a random file from a directory. Returns the filpath as a STRING. [w/This node allows access to arbitrary files through the workflow, which could pose a security threat.]"
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"author": "neeltheninja",
|
|
||||||
"title": "ComfyUI-ControlNeXt [WIP]",
|
|
||||||
"reference": "https://github.com/neverbiasu/ComfyUI-ControlNeXt",
|
|
||||||
"files": [
|
|
||||||
"https://github.com/neverbiasu/ComfyUI-ControlNeXt"
|
|
||||||
],
|
|
||||||
"install_type": "git-clone",
|
|
||||||
"description": "In progress🚧"
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"author": "neeltheninja",
|
"author": "neeltheninja",
|
||||||
"title": "ComfyUI-TextOverlay",
|
"title": "ComfyUI-TextOverlay",
|
||||||
@ -8421,16 +9021,6 @@
|
|||||||
"install_type": "git-clone",
|
"install_type": "git-clone",
|
||||||
"description": "A powerful debugging tool designed to provide in-depth analysis of your environment and dependencies by exposing API endpoints. This tool allows you to inspect environment variables, pip packages, python info and dependency trees, making it easier to diagnose and resolve issues in your ComfyUI setup.[w/This tool may expose sensitive system information if used on a public server]"
|
"description": "A powerful debugging tool designed to provide in-depth analysis of your environment and dependencies by exposing API endpoints. This tool allows you to inspect environment variables, pip packages, python info and dependency trees, making it easier to diagnose and resolve issues in your ComfyUI setup.[w/This tool may expose sensitive system information if used on a public server]"
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"author": "Futureversecom",
|
|
||||||
"title": "ComfyUI-JEN",
|
|
||||||
"reference": "https://github.com/futureversecom/ComfyUI-JEN",
|
|
||||||
"files": [
|
|
||||||
"https://github.com/futureversecom/ComfyUI-JEN"
|
|
||||||
],
|
|
||||||
"install_type": "git-clone",
|
|
||||||
"description": "Comfy UI custom nodes for JEN music generation powered by Futureverse"
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"author": "denislov",
|
"author": "denislov",
|
||||||
"title": "Comfyui_AutoSurvey",
|
"title": "Comfyui_AutoSurvey",
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@ -1,5 +1,15 @@
|
|||||||
{
|
{
|
||||||
"custom_nodes": [
|
"custom_nodes": [
|
||||||
|
{
|
||||||
|
"author": "Fossiel",
|
||||||
|
"title": "ComfyUI-MultiGPU-Patched",
|
||||||
|
"reference": "https://github.com/Fossiel/ComfyUI-MultiGPU-Patched",
|
||||||
|
"files": [
|
||||||
|
"https://github.com/Fossiel/ComfyUI-MultiGPU-Patched"
|
||||||
|
],
|
||||||
|
"install_type": "git-clone",
|
||||||
|
"description": "Patched fork of ComfyUI-MultiGPU providing universal .safetensors and GGUF multi-GPU distribution with DisTorch 2.0 engine, model-driven allocation options (bytes/ratio modes), WanVideoWrapper integration, and up to 10% faster GGUF inference. (Description by CC)"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"author": "synchronicity-labs",
|
"author": "synchronicity-labs",
|
||||||
"title": "ComfyUI Sync Lipsync Node",
|
"title": "ComfyUI Sync Lipsync Node",
|
||||||
|
|||||||
@ -1,5 +1,218 @@
|
|||||||
{
|
{
|
||||||
"custom_nodes": [
|
"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",
|
"author": "cdanielp",
|
||||||
"title": "COMFYUI_PROMPTMODELS [REMOVED]",
|
"title": "COMFYUI_PROMPTMODELS [REMOVED]",
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@ -1,7 +1,7 @@
|
|||||||
[project]
|
[project]
|
||||||
name = "comfyui-manager"
|
name = "comfyui-manager"
|
||||||
description = "ComfyUI-Manager provides features to install and manage custom nodes for ComfyUI, as well as various functionalities to assist with ComfyUI."
|
description = "ComfyUI-Manager provides features to install and manage custom nodes for ComfyUI, as well as various functionalities to assist with ComfyUI."
|
||||||
version = "3.38.2"
|
version = "3.39"
|
||||||
license = { file = "LICENSE.txt" }
|
license = { file = "LICENSE.txt" }
|
||||||
dependencies = ["GitPython", "PyGithub", "matrix-nio", "transformers", "huggingface-hub>0.20", "typer", "rich", "typing-extensions", "toml", "uv", "chardet"]
|
dependencies = ["GitPython", "PyGithub", "matrix-nio", "transformers", "huggingface-hub>0.20", "typer", "rich", "typing-extensions", "toml", "uv", "chardet"]
|
||||||
|
|
||||||
|
|||||||
164
scanner.py
164
scanner.py
@ -20,7 +20,7 @@ from pathlib import Path
|
|||||||
from typing import Set, Dict, Optional
|
from typing import Set, Dict, Optional
|
||||||
|
|
||||||
# Scanner version for cache invalidation
|
# Scanner version for cache invalidation
|
||||||
SCANNER_VERSION = "2.0.11" # Multi-layer detection: class existence + display names
|
SCANNER_VERSION = "2.0.12" # Add dict comprehension + export list detection
|
||||||
|
|
||||||
# Cache for extract_nodes and extract_nodes_enhanced results
|
# Cache for extract_nodes and extract_nodes_enhanced results
|
||||||
_extract_nodes_cache: Dict[str, Set[str]] = {}
|
_extract_nodes_cache: Dict[str, Set[str]] = {}
|
||||||
@ -552,12 +552,22 @@ def extract_nodes_enhanced(
|
|||||||
if exists:
|
if exists:
|
||||||
phase5_nodes.add(node_name)
|
phase5_nodes.add(node_name)
|
||||||
|
|
||||||
# Union all results (FIX: Scanner 2.0.9 bug + Scanner 2.0.10 bug)
|
# Phase 6: Dict comprehension pattern (NEW in 2.0.12)
|
||||||
|
# Detects: NODE_CLASS_MAPPINGS = {cls.__name__: cls for cls in to_export}
|
||||||
|
# Example: TobiasGlaubach/ComfyUI-TG_PyCode
|
||||||
|
phase6_nodes = _fallback_dict_comprehension(code_text, file_path)
|
||||||
|
|
||||||
|
# Phase 7: Import-based class names for dict comprehension (NEW in 2.0.12)
|
||||||
|
# Detects imported classes that are added to export lists
|
||||||
|
phase7_nodes = _fallback_import_class_names(code_text, file_path)
|
||||||
|
|
||||||
|
# Union all results (FIX: Scanner 2.0.9 bug + Scanner 2.0.10 bug + Scanner 2.0.12 dict comp)
|
||||||
# 2.0.9: Used early return which missed Phase 3 nodes
|
# 2.0.9: Used early return which missed Phase 3 nodes
|
||||||
# 2.0.10: Only checked registrations, missed classes referenced in display names
|
# 2.0.10: Only checked registrations, missed classes referenced in display names
|
||||||
all_nodes = phase1_nodes | phase2_nodes | phase3_nodes | phase4_nodes | phase5_nodes
|
# 2.0.12: Added dict comprehension and import-based class detection
|
||||||
|
all_nodes = phase1_nodes | phase2_nodes | phase3_nodes | phase4_nodes | phase5_nodes | phase6_nodes | phase7_nodes
|
||||||
|
|
||||||
# Phase 6: Empty dict detector (logging only, doesn't add nodes)
|
# Phase 8: Empty dict detector (logging only, doesn't add nodes)
|
||||||
if not all_nodes:
|
if not all_nodes:
|
||||||
_fallback_empty_dict_detector(code_text, file_path, verbose)
|
_fallback_empty_dict_detector(code_text, file_path, verbose)
|
||||||
|
|
||||||
@ -644,6 +654,152 @@ def _fallback_item_assignment(code_text: str) -> Set[str]:
|
|||||||
return nodes
|
return nodes
|
||||||
|
|
||||||
|
|
||||||
|
def _fallback_dict_comprehension(code_text: str, file_path: Optional[Path] = None) -> Set[str]:
|
||||||
|
"""
|
||||||
|
Detect dict comprehension pattern with __name__ attribute access.
|
||||||
|
|
||||||
|
Pattern:
|
||||||
|
NODE_CLASS_MAPPINGS = {cls.__name__: cls for cls in to_export}
|
||||||
|
NODE_CLASS_MAPPINGS = {c.__name__: c for c in [ClassA, ClassB]}
|
||||||
|
|
||||||
|
This function detects dict comprehension assignments to NODE_CLASS_MAPPINGS
|
||||||
|
and extracts class names from the iterable (list literal or variable reference).
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
Set of class names extracted from the dict comprehension
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
with warnings.catch_warnings():
|
||||||
|
warnings.filterwarnings('ignore', category=SyntaxWarning)
|
||||||
|
parsed = ast.parse(code_text)
|
||||||
|
except:
|
||||||
|
return set()
|
||||||
|
|
||||||
|
nodes = set()
|
||||||
|
export_lists = {} # Track list variables and their contents
|
||||||
|
|
||||||
|
# First pass: collect list assignments (to_export = [...], exports = [...])
|
||||||
|
for node in ast.walk(parsed):
|
||||||
|
if isinstance(node, ast.Assign):
|
||||||
|
for target in node.targets:
|
||||||
|
if isinstance(target, ast.Name):
|
||||||
|
var_name = target.id
|
||||||
|
# Check for list literal
|
||||||
|
if isinstance(node.value, ast.List):
|
||||||
|
class_names = set()
|
||||||
|
for elt in node.value.elts:
|
||||||
|
if isinstance(elt, ast.Name):
|
||||||
|
class_names.add(elt.id)
|
||||||
|
export_lists[var_name] = class_names
|
||||||
|
|
||||||
|
# Handle augmented assignment: to_export += [...]
|
||||||
|
elif isinstance(node, ast.AugAssign):
|
||||||
|
if isinstance(node.target, ast.Name) and isinstance(node.op, ast.Add):
|
||||||
|
var_name = node.target.id
|
||||||
|
if isinstance(node.value, ast.List):
|
||||||
|
class_names = set()
|
||||||
|
for elt in node.value.elts:
|
||||||
|
if isinstance(elt, ast.Name):
|
||||||
|
class_names.add(elt.id)
|
||||||
|
if var_name in export_lists:
|
||||||
|
export_lists[var_name].update(class_names)
|
||||||
|
else:
|
||||||
|
export_lists[var_name] = class_names
|
||||||
|
|
||||||
|
# Second pass: find NODE_CLASS_MAPPINGS dict comprehension
|
||||||
|
for node in ast.walk(parsed):
|
||||||
|
if isinstance(node, ast.Assign):
|
||||||
|
for target in node.targets:
|
||||||
|
if isinstance(target, ast.Name) and target.id in ['NODE_CLASS_MAPPINGS', 'NODE_CONFIG']:
|
||||||
|
# Check for dict comprehension
|
||||||
|
if isinstance(node.value, ast.DictComp):
|
||||||
|
dictcomp = node.value
|
||||||
|
|
||||||
|
# Check if key is cls.__name__ pattern
|
||||||
|
key = dictcomp.key
|
||||||
|
if isinstance(key, ast.Attribute) and key.attr == '__name__':
|
||||||
|
# Get the iterable from the first generator
|
||||||
|
for generator in dictcomp.generators:
|
||||||
|
iter_node = generator.iter
|
||||||
|
|
||||||
|
# Case 1: Inline list [ClassA, ClassB, ...]
|
||||||
|
if isinstance(iter_node, ast.List):
|
||||||
|
for elt in iter_node.elts:
|
||||||
|
if isinstance(elt, ast.Name):
|
||||||
|
nodes.add(elt.id)
|
||||||
|
|
||||||
|
# Case 2: Variable reference (to_export, exports, etc.)
|
||||||
|
elif isinstance(iter_node, ast.Name):
|
||||||
|
var_name = iter_node.id
|
||||||
|
if var_name in export_lists:
|
||||||
|
nodes.update(export_lists[var_name])
|
||||||
|
|
||||||
|
return nodes
|
||||||
|
|
||||||
|
|
||||||
|
def _fallback_import_class_names(code_text: str, file_path: Optional[Path] = None) -> Set[str]:
|
||||||
|
"""
|
||||||
|
Extract class names from imports that are added to export lists.
|
||||||
|
|
||||||
|
Pattern:
|
||||||
|
from .module import ClassA, ClassB
|
||||||
|
to_export = [ClassA, ClassB]
|
||||||
|
NODE_CLASS_MAPPINGS = {cls.__name__: cls for cls in to_export}
|
||||||
|
|
||||||
|
This is a complementary fallback that works with _fallback_dict_comprehension
|
||||||
|
to resolve import-based node registrations.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
Set of imported class names that appear in export-like contexts
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
with warnings.catch_warnings():
|
||||||
|
warnings.filterwarnings('ignore', category=SyntaxWarning)
|
||||||
|
parsed = ast.parse(code_text)
|
||||||
|
except:
|
||||||
|
return set()
|
||||||
|
|
||||||
|
# Collect imported names
|
||||||
|
imported_names = set()
|
||||||
|
for node in ast.walk(parsed):
|
||||||
|
if isinstance(node, ast.ImportFrom):
|
||||||
|
for alias in node.names:
|
||||||
|
name = alias.asname if alias.asname else alias.name
|
||||||
|
imported_names.add(name)
|
||||||
|
|
||||||
|
# Check if these names appear in list assignments that feed into NODE_CLASS_MAPPINGS
|
||||||
|
export_candidates = set()
|
||||||
|
has_dict_comp_mapping = False
|
||||||
|
|
||||||
|
for node in ast.walk(parsed):
|
||||||
|
# Check for dict comprehension NODE_CLASS_MAPPINGS
|
||||||
|
if isinstance(node, ast.Assign):
|
||||||
|
for target in node.targets:
|
||||||
|
if isinstance(target, ast.Name) and target.id == 'NODE_CLASS_MAPPINGS':
|
||||||
|
if isinstance(node.value, ast.DictComp):
|
||||||
|
has_dict_comp_mapping = True
|
||||||
|
|
||||||
|
# Collect list contents
|
||||||
|
if isinstance(node, ast.Assign):
|
||||||
|
if isinstance(node.value, ast.List):
|
||||||
|
for elt in node.value.elts:
|
||||||
|
if isinstance(elt, ast.Name) and elt.id in imported_names:
|
||||||
|
export_candidates.add(elt.id)
|
||||||
|
|
||||||
|
# Handle augmented assignment
|
||||||
|
elif isinstance(node, ast.AugAssign):
|
||||||
|
if isinstance(node.value, ast.List):
|
||||||
|
for elt in node.value.elts:
|
||||||
|
if isinstance(elt, ast.Name) and elt.id in imported_names:
|
||||||
|
export_candidates.add(elt.id)
|
||||||
|
|
||||||
|
# Only return if there's a dict comprehension mapping
|
||||||
|
if has_dict_comp_mapping:
|
||||||
|
return export_candidates
|
||||||
|
|
||||||
|
return set()
|
||||||
|
|
||||||
|
|
||||||
def _extract_repo_name(file_path: Path) -> str:
|
def _extract_repo_name(file_path: Path) -> str:
|
||||||
"""
|
"""
|
||||||
Extract repository name from file path.
|
Extract repository name from file path.
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user