const gib = (b) => (b == null ? "?" : (b / 1073741824).toFixed(1)); const pct = (n) => (n == null ? "?" : `${Math.round(n)}%`); const up = (s) => { if (s == null) return "?"; const d = Math.floor(s / 86400); const h = Math.floor((s % 86400) / 3600); return d ? `${d}d ${h}h` : `${h}h`; }; const ago = (ts) => { if (!ts) return "—"; const d = Math.max(0, Math.floor(Date.now() / 1000 - ts)); if (d < 3600) return `${Math.floor(d / 60)}m`; if (d < 86400) return `${Math.floor(d / 3600)}h`; return `${Math.floor(d / 86400)}d`; }; const ACTIVE = ["running", "busy", "starting"]; function metric(label, value) { const row = document.createElement("div"); row.className = "metric"; const k = document.createElement("span"); k.className = "k"; k.textContent = label; const v = document.createElement("span"); v.className = "v num"; v.textContent = value; row.append(k, v); return row; } export default { title: "Build VM", interval: 10, mount(el) { el.innerHTML = `
`; }, update(state, el) { const box = el.querySelector(".sbb"); box.textContent = ""; if (state.offline) { box.textContent = "build VM unreachable"; return; } const s = state.stats ?? {}; const p = s.packages ?? {}; const sr = s.slackrepo ?? {}; const jobs = state.jobs ?? []; const running = jobs.find((j) => ACTIVE.includes(j.status)); const now = document.createElement("div"); now.className = "sbb-now"; now.textContent = running ? `● ${running.kind || "job"} running` : "idle"; now.classList.toggle("warn", Boolean(running)); box.append(now); const overlays = Object.entries(p).filter(([k]) => k !== "total" && k !== "shadowed"); const results = Object.entries(sr.results ?? {}); box.append( metric("CPU · load", `${pct(s.cpu)} · ${s.load ? s.load[0].toFixed(2) : "?"}`), metric("Uptime", up(s.uptime)), metric("Memory", `${gib((s.mem?.total ?? 0) - (s.mem?.available ?? 0))} / ${gib(s.mem?.total)} GiB`), metric("Disk free", `${gib(s.disk?.free)} / ${gib(s.disk?.total)} GiB`), metric("Packages", `${p.total ?? "?"}${overlays.length ? " · " + overlays.map(([k, v]) => `${k} ${v}`).join(" · ") : ""}`), metric("Shadowed · orphans", `${p.shadowed ?? "?"} · ${s.orphans ?? "?"}`), metric("Built · results", `${sr.built ?? "?"}${results.length ? " · " + results.map(([k, v]) => `${v} ${k}`).join(" · ") : ""}`), metric("Overlay pulls", Object.entries(s.sync ?? {}).map(([k, v]) => `${k} ${ago(v)}`).join(" · ") || "—"), ); }, };