diff --git a/CHANGELOG.md b/CHANGELOG.md index 1377cd8..ef0b4a0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,10 @@ ## Unreleased +### Added + +- Admin option *ComfyUI Manager in its own sidebar tab*: a **Manager** tab in the left bar (Open Manager, Custom Nodes Manager) replaces the top-bar button, for accounts with the *Manager* permission + ### Changed - Other plugins' top-bar buttons (rgthree-comfy, Crystools...) are hidden without the *Manager* permission, like ComfyUI Manager's. Their settings kept on the server, which apply to everyone, need *Manager* too diff --git a/docs/settings.md b/docs/settings.md index ff3a4f8..0fc5d12 100644 --- a/docs/settings.md +++ b/docs/settings.md @@ -28,6 +28,7 @@ Admins change these under **Admin → Server settings**. They are stored in `rig | `broadcast_execution` | `true` | Send progress and previews to everyone, not only whoever queued | | `hide_comfy_account` | `false` | Hide ComfyUI's Comfy.org sign-in button and dialog | | `hide_comfy_file_tabs` | `true` | Hide other workflow browsers: ComfyUI's Workflows and Apps sidebar tabs and Pixaroma's Workflows panel. RigShare's Files tab replaces them and their shortcuts open it | +| `manager_tab` | `false` | Show ComfyUI Manager as a Manager tab in the left sidebar instead of a top-bar button (only for accounts with *Manager*) | | `hide_api_templates` | `false` | Remove templates that need paid API nodes from the template browser | | `api_protection.enabled` | `true` | Server-side checks on queue, interrupt, free and upload calls | | `api_protection.trusted_ips` | `["127.0.0.1", "::1"]` | Addresses or networks that skip login and checks | diff --git a/rigshare_core/hub.py b/rigshare_core/hub.py index b9e169b..0a06d74 100644 --- a/rigshare_core/hub.py +++ b/rigshare_core/hub.py @@ -417,6 +417,7 @@ class Hub: return {"name": cfg.get("server_name"), "allow_guests": cfg.get("allow_guests"), "hide_comfy_account": bool(cfg.get("hide_comfy_account")), "hide_comfy_file_tabs": bool(cfg.get("hide_comfy_file_tabs", True)), + "manager_tab": bool(cfg.get("manager_tab")), "hide_api_templates": bool(cfg.get("hide_api_templates"))} async def broadcast_server(self): diff --git a/rigshare_core/store.py b/rigshare_core/store.py index 74cf399..5e74be9 100644 --- a/rigshare_core/store.py +++ b/rigshare_core/store.py @@ -49,6 +49,8 @@ DEFAULT_CONFIG = { "hide_comfy_account": False, # ComfyUI's own Workflows and Apps sidebar tabs duplicate RigShare's Files tab. "hide_comfy_file_tabs": True, + # ComfyUI Manager in its own sidebar tab instead of the top-bar button. + "manager_tab": False, # Remove templates that need paid API nodes from the template browser. "hide_api_templates": False, # Rooms nobody opened for this many days are forgotten. diff --git a/tests/python/test_server.py b/tests/python/test_server.py index 9770647..0d07dbe 100644 --- a/tests/python/test_server.py +++ b/tests/python/test_server.py @@ -579,3 +579,16 @@ def test_other_plugins_server_routes(run): assert await status("GET", "/pixaroma/api/workflows/index", admin) == 200 assert await status("GET", "/pixaroma/api/fonts/list", alice) == 200, "the rest of Pixaroma works" run(scenario()) + + +def test_manager_tab_option(run): + async def scenario(): + async with rig_server() as rig: + admin = await rig.setup_admin() + sock = await rig.ws("boss") + assert rig.hub.server_info()["manager_tab"] is False, "off by default" + r = await rig.http.patch(rig.url("/rigshare/api/config"), json={"manager_tab": True}, headers=admin) + assert r.status == 200 + assert (await sock.until("server"))["server"]["manager_tab"] is True + await sock.close() + run(scenario()) diff --git a/web/rigshare/main.js b/web/rigshare/main.js index 088f7da..745f854 100644 --- a/web/rigshare/main.js +++ b/web/rigshare/main.js @@ -104,6 +104,48 @@ function installManagerGuard(client) { setInterval(tagPluginButtons, 1000); } +// ComfyUI Manager in its own sidebar tab (admin option): its menu and the custom +// nodes manager open from there, and the top-bar button goes away. +const MANAGER_TAB_ID = "rigshare-manager"; +function installManagerTab(client) { + let registered = false; + const run = (id) => app.extensionManager?.command?.execute(id); + const panel = h("div", { class: "rs-panel rs-manager-panel" }, + h("div", { class: "rs-header" }, + h("div", { class: "rs-title" }, h("i", { class: "pi pi-th-large rs-files-logo" }), + h("div", { class: "rs-title-text" }, h("div", { class: "rs-title-name" }, "ComfyUI Manager"), + h("div", { class: "rs-status" }, "Custom nodes, models, updates")))), + h("div", { class: "rs-body" }, + h("section", { class: "rs-section" }, + h("button", { class: "rs-btn rs-btn-primary rs-block-btn", onclick: () => run("Comfy.Manager.Menu.ToggleVisibility") }, + h("i", { class: "pi pi-th-large" }), "Open Manager"), + h("p", { class: "rs-muted rs-small" }, "Install missing nodes, update everything, the model manager, snapshots and restart."), + h("button", { class: "rs-btn rs-block-btn", onclick: () => run("Comfy.Manager.CustomNodesManager.ToggleVisibility") }, + h("i", { class: "pi pi-server" }), "Custom Nodes Manager"), + h("p", { class: "rs-muted rs-small" }, "Browse, install, update, disable or remove custom nodes.")))); + const apply = () => { + const manager = app.extensionManager; + const hasManager = (manager?.command?.commands ?? []).some((c) => c.id === "Comfy.Manager.Menu.ToggleVisibility"); + const want = !!client.server?.manager_tab && client.online && !!client.perms.manager && hasManager; + document.body.classList.toggle("rs-manager-tab", !!client.server?.manager_tab); + if (want && !registered) { + manager.registerSidebarTab({ + id: MANAGER_TAB_ID, icon: "pi pi-th-large", title: "Manager", tooltip: "ComfyUI Manager", + label: "Manager", type: "custom", + render: (el) => { el.classList.add("rs-host"); el.replaceChildren(panel); }, + }); + registered = true; + } else if (!want && registered) { + (manager.unregisterSidebarTab ?? manager.sidebarTab?.unregisterSidebarTab)?.(MANAGER_TAB_ID); + registered = false; + } + }; + client.on("server", apply); + client.on("self", apply); + client.on("status", apply); + setTimeout(apply, 2500); // Manager registers its commands during setup +} + // Comfy.org account: hide the top-bar sign-in button and refuse the sign-in // dialogs when the admin turned it off. const SIGNIN_DIALOGS = ["global-signin", "api-nodes-signin"]; @@ -330,6 +372,7 @@ app.registerExtension({ installManagerGuard(client); installComfyAccountGuard(client); installFileTabsGuard(client); + installManagerTab(client); installSaveDialog(app, api, client, sync); presence.install(); diff --git a/web/rigshare/panel.js b/web/rigshare/panel.js index 02fdb27..29aae0a 100644 --- a/web/rigshare/panel.js +++ b/web/rigshare/panel.js @@ -834,6 +834,7 @@ export class RigSharePanel { protect: box(cfg.api_protection?.enabled), hideAccount: box(cfg.hide_comfy_account), hideFileTabs: box(cfg.hide_comfy_file_tabs ?? true), + managerTab: box(cfg.manager_tab), hideApiTemplates: box(cfg.hide_api_templates), trusted: text((cfg.api_protection?.trusted_ips || []).join(", "), "127.0.0.1, 172.17.0.0/16"), interval: h("input", { class: "rs-input rs-num", type: "number", min: 0, value: Math.round((cfg.snapshot_interval_sec ?? 300) / 60) }), @@ -852,6 +853,7 @@ export class RigSharePanel { broadcast_execution: f.broadcast.checked, hide_comfy_account: f.hideAccount.checked, hide_comfy_file_tabs: f.hideFileTabs.checked, + manager_tab: f.managerTab.checked, hide_api_templates: f.hideApiTemplates.checked, api_protection: { enabled: f.protect.checked, trusted_ips: f.trusted.value.split(/[\s,]+/).filter(Boolean) }, snapshot_interval_sec: Math.max(0, +f.interval.value || 0) * 60, @@ -866,6 +868,7 @@ export class RigSharePanel { line(f.broadcast, "Share execution progress", "Everyone sees progress and previews, not only whoever queued"), line(f.protect, "Enforce permissions on the ComfyUI API", "Blocks queueing, interrupting and uploads for users without permission"), line(f.hideAccount, "Hide the Comfy.org account login", "Removes ComfyUI's own sign-in button and dialog (used for paid API nodes)"), + line(f.managerTab, "ComfyUI Manager in its own sidebar tab", "A Manager tab in the left bar replaces the top-bar button, for accounts with the Manager permission"), line(f.hideFileTabs, "Hide other workflow browsers", "ComfyUI's Workflows and Apps tabs, and Pixaroma's Workflows panel: RigShare's Files tab replaces them and their shortcuts open Files"), line(f.hideApiTemplates, "Hide API templates", "Removes templates that need paid API nodes from the template browser"), h("label", { class: "rs-label" }, "Trusted IPs / networks (skip login and checks)"), f.trusted, diff --git a/web/rigshare/styles.js b/web/rigshare/styles.js index 31288c1..c12678f 100644 --- a/web/rigshare/styles.js +++ b/web/rigshare/styles.js @@ -111,6 +111,10 @@ body.rs-hide-comfy-account [data-testid="login-button-popover"] { display: none body.rs-no-manager .rs-plugin-bar { display: none !important; } /* Pixaroma's workflow browser button: RigShare's Files tab replaces it */ body.rs-hide-file-browsers .pixwb-group-btn { display: none !important; } +/* ComfyUI Manager moved to its own sidebar tab (admin option): no top-bar button */ +body.rs-manager-tab .comfyui-button-group:has(> [title="ComfyUI Manager"]), +body.rs-manager-tab [title="ComfyUI Manager"] { display: none !important; } +.rs-block-btn { width: 100%; justify-content: center; padding: 9px 12px; margin-top: 4px; } /* ComfyUI Manager hidden for accounts without the "manager" permission */ body.rs-no-manager .comfyui-button-group:has(> [title="ComfyUI Manager"]), body.rs-no-manager [title="ComfyUI Manager"],