Files
DemonGatanjieu-Anomalous_Mo…/web/modules/ui_pack_usage.js
T
DemonGatanjieuandClaude Opus 5.5 396b2a1489 Move the node packs not used lately to Settings → Node packs
Node packs are ComfyUI's extensions, not material, so looking for them
under the models page's Tidy tab felt odd. Tidy keeps the models and a
line leading to the new Settings tab; both pages share the check and
its head (usage_view.js), ui_pack_usage.js draws the packs.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-14 22:25:04 +08:00

127 lines
6.0 KiB
JavaScript

/**
* Settings → Node packs: the node packs not used lately (usage_view.js runs the check). Packs
* are only listed, each with a way into ComfyUI Manager, which disables or removes them. Packs
* that also add to the page are set apart: their nodes going unused does not say the pack is.
* A pack the user says they still use ("I still use it") leaves the groups for a list of its own.
*/
import { app } from "../../../scripts/app.js";
import { translate as t } from './locales.js';
import { keepPack } from './model_placement.js';
import { button, daysSince, el, lastUsed, notUsedSince, usageSection } from './usage_view.js';
// ComfyUI lists the Manager's commands even without the Manager; its own routes tell it is there,
// and which one: the Manager 4 (ComfyUI's own command) or 3 (the command its script adds, whose
// node list has a search box this page can fill).
const MANAGERS = [
['/api/v2/manager/version', ['Comfy.Manager.CustomNodesManager.ShowCustomNodesMenu'], null],
['/api/manager/version', ['Comfy.Manager.CustomNodesManager.ToggleVisibility', 'Comfy.Manager.CustomNodesManager.ShowLegacyCustomNodesMenu'], 'input.cn-manager-keywords'],
];
const SEARCH_WAIT_MS = 6000;
/** The installed Manager: { ids: commands opening its node pack list, search: its search box } or null. */
async function findManager() {
for (const [url, ids, search] of MANAGERS) {
try {
if ((await fetch(url)).ok) return { ids, search };
} catch (_) { /* not this Manager version */ }
}
return null;
}
/** Types `words` into the Manager's search box once its node list shows; false when it never does. */
async function searchManager(selector, words) {
const until = Date.now() + SEARCH_WAIT_MS;
while (Date.now() < until) {
const box = [...document.querySelectorAll(selector)].find(node => node.offsetParent !== null);
if (box) {
box.value = words;
box.dispatchEvent(new Event('input', { bubbles: true }));
return true;
}
await new Promise(resolve => setTimeout(resolve, 150));
}
return false;
}
/** ComfyUI Manager's node pack list, searched for `pack` where the Manager allows it (its name
* is also copied, to paste where it does not); the browser steps aside for it. */
async function openManager(owner, pack) {
const manager = await findManager();
if (!manager) {
alert(t('tidyUsageNoManager'));
return;
}
try {
await navigator.clipboard?.writeText(pack.name);
} catch (_) { /* no clipboard here: the name is on the page */ }
const commands = app.extensionManager?.command;
owner.close?.();
for (const id of manager.ids) {
try {
if (commands?.commands?.some?.(command => command.id === id) === false) continue;
await commands.execute(id);
if (manager.search) await searchManager(manager.search, pack.words || pack.name);
return;
} catch (_) { /* not this ComfyUI's command: the next one */ }
}
alert(t('tidyUsageNoManager'));
}
function packRow(owner, item, now, redraw) {
const row = el('div', 'anomalous-usage-row is-pack');
const body = el('div', 'anomalous-usage-body');
const facts = [item.nodes ? t('tidyUsageNodes', { count: item.nodes }) : t('tidyUsageNoNodes')];
if (item.added) facts.push(t('tidyUsageInstalled', { days: daysSince(item.added, now) }));
body.append(el('strong', 'anomalous-usage-pack', item.name), el('div', 'anomalous-tidy-file', facts.join(' · ')));
if (item.nodes) body.append(el('div', 'anomalous-usage-when', lastUsed(item, now)));
if (item.saved) body.append(el('div', 'anomalous-usage-saved', t('tidyUsageSaved', { count: item.saved })));
if (item.feeds?.length) body.append(el('div', 'anomalous-usage-saved', t('tidyUsageFeeds', { packs: item.feeds.join(', ') })));
const actions = el('div', 'anomalous-usage-actions');
const mark = button('anomalous-scan-row-btn', t(item.kept ? 'tidyUsageUnkeep' : 'tidyUsageKeep'), async () => {
mark.disabled = true;
try {
await keepPack(item.name, !item.kept);
item.kept = !item.kept;
redraw();
} catch (error) {
console.warn('[AMB] Tidy: could not mark a node pack.', error);
alert(t('tidyUsageKeepFailed'));
mark.disabled = false;
}
});
actions.append(mark);
if (!item.kept) actions.append(button('anomalous-scan-row-btn', t('tidyUsageFind'), () => openManager(owner, item)));
row.append(body, actions);
return row;
}
function packGroups(owner, section, view, redraw) {
const { packs, now } = view.data;
const rows = (items) => items.map(item => packRow(owner, item, now, redraw));
const group = (titleKey, leadKey, items) => {
const head = el('div', 'anomalous-tidy-step');
head.append(el('h3', '', t(titleKey)));
section.append(head, el('p', 'anomalous-tidy-why', t(leadKey)), ...rows(items));
};
const idle = packs.filter(item => item.nodes && !item.kept && notUsedSince(item, view.filter, now));
const nodesOnly = idle.filter(item => !item.web);
section.append(el('p', 'anomalous-tidy-why', t('tidyUsagePacksLead')));
if (!nodesOnly.length) section.append(el('div', 'anomalous-tidy-done', t('tidyUsagePacksNone')));
else section.append(...rows(nodesOnly));
const withPage = idle.filter(item => item.web);
if (withPage.length) group('tidyUsageUiPacks', 'tidyUsageUiPacksLead', withPage);
const pageOnly = packs.filter(item => !item.nodes && !item.kept);
if (pageOnly.length) group('tidyUsagePageOnly', 'tidyUsagePageOnlyLead', pageOnly);
const kept = packs.filter(item => item.kept);
if (kept.length) group('tidyUsageKept', 'tidyUsageKeptLead', kept);
}
/** Settings → Node packs; `redraw()` draws the settings page again. */
export function renderPackUsage(owner, redraw) {
return usageSection(owner, {
titleKey: 'tidyUsagePacksTitle', leadKey: 'tidyUsageLead', redraw,
body: (section, view) => packGroups(owner, section, view, redraw),
});
}