Files
xiaodoudou-ComfyUI-RigShare/web/rigshare/server-widget.js
T
Doudou d7b96ecd4d ComfyUI RigShare 1.0.0
Multi-user collaboration for the current ComfyUI frontend, continued
from ComfyUI-Nexus by daxcay (MIT).

- Login page before ComfyUI loads, first-run admin setup, JWT sessions
- Personal API keys; server-side permission checks (edit, queue, manager, admin)
- Every workflow shared as a live room, with snapshots and history
- Per-workflow access lists (view / edit per person)
- Conflict-free concurrent edits (id remapping, per-id link merges)
- Presence: who is online, cursors, selections, follow mode
- Chat with unread badge, blinking icon and sound
- Server monitor (CPU, RAM, GPUs, queue) with a floating widget
- Admin options to hide the Comfy.org login and API templates
- Docs, changelog, screenshots and icon
2026-09-25 13:24:09 +00:00

100 lines
3.9 KiB
JavaScript

// Optional floating server monitor: a small draggable card over the canvas.
import { h, storage } from "./ui.js";
const KEY_ON = "rigshare.serverFloat";
const KEY_POS = "rigshare.serverFloatPos";
function mini(label, pct, text) {
const p = Math.max(0, Math.min(100, pct || 0));
const level = p > 90 ? "hot" : p > 70 ? "warm" : "";
return h("div", { class: "rs-mini", title: `${label}: ${text}` },
h("span", { class: "rs-mini-label" }, label),
h("span", { class: "rs-mini-track" }, h("span", { class: `rs-meter-fill ${level}`, style: `width:${p}%` })),
h("span", { class: "rs-mini-text" }, text));
}
export class ServerWidget {
constructor(client, onChange) {
this.client = client;
this.onChange = onChange;
this.enabled = storage.get(KEY_ON) === "1";
this.el = null;
client.on("stats", () => this.render());
if (this.enabled) this.show();
}
toggle() {
this.enabled = !this.enabled;
storage.set(KEY_ON, this.enabled ? "1" : null);
this.enabled ? this.show() : this.hide();
this.onChange?.();
}
show() {
if (this.el) return;
this.body = h("div", { class: "rs-float-body" });
const head = h("div", { class: "rs-float-head" },
h("i", { class: "pi pi-server" }), h("span", { class: "rs-grow" }, "Server"),
h("button", { class: "rs-float-close", title: "Close", onclick: () => this.toggle() }, h("i", { class: "pi pi-times" })));
this.el = h("div", { class: "rs-float rs-panel-vars" }, head, this.body);
let pos = null;
try { pos = JSON.parse(storage.get(KEY_POS)); } catch { /* default */ }
this.place(pos?.x ?? window.innerWidth - 300, pos?.y ?? 70);
document.body.append(this.el);
this.drag(head);
this.render();
}
hide() {
this.el?.remove();
this.el = null;
}
place(x, y) {
const w = this.el.offsetWidth || 260;
const hgt = this.el.offsetHeight || 120;
x = Math.max(4, Math.min(window.innerWidth - w - 4, x));
y = Math.max(4, Math.min(window.innerHeight - hgt - 4, y));
this.el.style.left = `${x}px`;
this.el.style.top = `${y}px`;
return { x, y };
}
drag(handle) {
handle.addEventListener("pointerdown", (e) => {
if (e.target.closest("button")) return;
e.preventDefault();
const startX = e.clientX - this.el.offsetLeft;
const startY = e.clientY - this.el.offsetTop;
const move = (ev) => this.place(ev.clientX - startX, ev.clientY - startY);
const up = () => {
window.removeEventListener("pointermove", move);
window.removeEventListener("pointerup", up);
storage.set(KEY_POS, JSON.stringify({ x: this.el.offsetLeft, y: this.el.offsetTop }));
};
window.addEventListener("pointermove", move);
window.addEventListener("pointerup", up);
});
}
render() {
if (!this.el) return;
const st = this.client.stats;
if (!st) {
this.body.replaceChildren(h("div", { class: "rs-muted rs-small" }, "Waiting for data…"));
return;
}
const rows = [];
if (st.cpu != null) rows.push(mini("CPU", st.cpu, `${Math.round(st.cpu)}%`));
if (st.ram_total) rows.push(mini("RAM", (st.ram_used / st.ram_total) * 100, `${Math.round(st.ram_used)}G`));
for (const g of st.gpus || []) {
rows.push(mini(`GPU${g.index}`, g.util, `${g.util}%${g.temp != null ? ` ${g.temp}°` : ""}`));
rows.push(mini(`VRAM${g.index}`, (g.vram_used / g.vram_total) * 100, `${g.vram_used.toFixed(1)}G`));
}
const q = st.queue;
if (q) rows.push(h("div", { class: "rs-mini-queue" }, q.running || q.pending ? `${q.running} running · ${q.pending} queued` : "queue idle"));
this.body.replaceChildren(...rows);
}
}