Files
Claude 3adebb731f Rename FunPack to FunUI (5.0.3 Classy Chopper): /fun endpoint, wink logo, load-time migration of old names
Old project/workflow names and browser settings migrate on load; /funpack redirects to /fun.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016acRss69eH1235eRdFTZHY
2026-10-25 13:11:03 +00:00

64 lines
4.1 KiB
JavaScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
// Custom Nodes: install, update and remove ComfyUI node packs. Three git operations, not a catalogue: you supply the URL.
// Packs register when ComfyUI starts, so every change ends with "restart needed", never a restart on its own.
import { composer as c } from "../../composer/composer.js";
export const packs = (app) => function mount() {
const page = c.region.stack({ gap: "md" });
let nodes = [], root = "", error = "", busy = "", checked = {}, dirty = false;
const tell = (text) => c.toast.warn({ text });
async function refresh() {
try { const r = await app.api.packs(); nodes = r.nodes || []; root = r.root || ""; error = ""; } catch (err) { nodes = []; error = err.message; }
draw();
}
async function run(key, what, work) {
if (busy) return;
busy = key; draw();
try {
const res = await work();
const req = res && res.requirements;
if (req && req.ran && !req.ok) tell(`${what}, but installing its dependencies failed: ${req.detail || ""}`);
dirty = true;
delete checked[key];
} catch (err) { tell(`${what} failed: ${err.message}`); }
busy = ""; await refresh();
}
const install = async () => {
const url = await c.modal.prompt({ title: "Add a custom node", label: "Repository git URL", placeholder: "https://github.com/owner/repo", confirmLabel: "Install" }).result;
if (url && url.trim()) run("__install__", `Installing ${url.trim()}`, () => app.api.pack("install", { url: url.trim() }));
};
const check = async () => {
busy = "__check__"; draw();
try { checked = (await app.api.pack("check")).checked || {}; } catch (err) { tell(`Could not check for updates: ${err.message}`); }
busy = ""; draw();
};
const remove = async (n) => {
if (await c.modal.dialogue({ title: "Remove node pack", message: `Delete ${root}/${n.name} and everything in it? This cannot be undone.`, tone: "danger", confirmLabel: "Delete" }).result)
run(n.name, `Removing ${n.name}`, () => app.api.pack("remove", { name: n.name }));
};
function row(n) {
const chk = checked[n.name], behind = chk && chk.checked && chk.behind > 0;
const bits = [n.is_funui ? "FunUI itself" : null, n.git ? `${n.branch || "?"} · ${n.commit || "?"}` : "not a git checkout",
chk && chk.checked ? (behind ? `${chk.behind} behind` : chk.ahead > 0 ? `${chk.ahead} ahead of origin` : "up to date") : chk && chk.reason ? `not compared — ${chk.reason}` : null].filter(Boolean);
const acts = n.is_funui ? c.text.sm({ text: "" }) : c.toolbar.default({ items: [
c.button.sm({ label: behind ? `Update (${chk.behind})` : "Update", tone: behind ? "primary" : "ghost", disabled: Boolean(busy) || !n.git, onClick: () => run(n.name, `Updating ${n.name}`, () => app.api.pack("update", { name: n.name })) }),
c.button.sm({ label: "Remove", tone: "danger", disabled: Boolean(busy), onClick: () => remove(n) })] });
return c.settingsRow.default({ label: n.name, hint: bits.join(" — "), control: acts });
}
function draw() {
page.set([
c.toolbar.default({ items: [c.button.sm({ label: "+ Add node", tone: "primary", disabled: Boolean(busy), onClick: install }),
c.button.sm({ label: busy === "__check__" ? "Checking…" : "Check for updates", tone: "ghost", disabled: Boolean(busy), title: "Fetch each pack's origin and report how far behind it is", onClick: check }),
busy && busy !== "__check__" ? c.text.sm({ text: busy === "__install__" ? "Cloning and installing…" : `Working on ${busy}…` }) : null].filter(Boolean) }),
dirty ? c.banner.info({ text: "Node packs are registered when ComfyUI starts, so these changes are not live yet.", action: { label: "Restart ComfyUI", onClick: () => app.maintenance.restart() } }) : null,
error ? c.banner.warn({ text: `Could not read custom_nodes: ${error}` }) : null,
!error && !nodes.length ? c.emptyState.default({ icon: "⧉", title: "No node packs", hint: "No custom node packs installed." }) : null,
...nodes.map(row),
root ? c.hint.default({ text: root }) : null,
].filter(Boolean));
}
draw(); refresh();
return { node: page.node, destroy: () => page.node.remove() };
};