aboutsummaryrefslogtreecommitdiffstats
path: root/static
diff options
context:
space:
mode:
Diffstat (limited to 'static')
-rw-r--r--static/deck.css6
-rw-r--r--static/modules/sbbuilder.js96
2 files changed, 59 insertions, 43 deletions
diff --git a/static/deck.css b/static/deck.css
index 40d6ac8..8b2416c 100644
--- a/static/deck.css
+++ b/static/deck.css
@@ -131,8 +131,10 @@ body {
.music .time { font-size: 12px; color: var(--udt-subtext0); white-space: nowrap; }
.music .controls { display: flex; flex-wrap: wrap; gap: 8px; }
-.sbb .sbb-summary { margin-bottom: 8px; font-size: 12px; }
-.sbb .row.job .kind { flex: 1; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
+.sbb .sbb-now { margin-bottom: 8px; font-size: 12px; font-weight: 600; }
+.sbb .metric { display: flex; justify-content: space-between; gap: 12px; padding: 3px 0; font-size: 13px; }
+.sbb .metric .k { color: var(--udt-subtext0); flex: none; }
+.sbb .metric .v { text-align: right; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.grid-stack-item .ui-resizable-se { width: 22px; height: 22px; right: 2px; bottom: 2px; opacity: .45; }
.grid-stack-item:hover .ui-resizable-se { opacity: 1; }
diff --git a/static/modules/sbbuilder.js b/static/modules/sbbuilder.js
index f1c574d..02493af 100644
--- a/static/modules/sbbuilder.js
+++ b/static/modules/sbbuilder.js
@@ -1,58 +1,72 @@
-function chipClass(status) {
- if (status === "ok") return "good";
- if (status === "failed" || status === "lost" || status === "killed") return "bad";
- if (status === "running" || status === "busy" || status === "starting") return "warn";
- return "muted";
-}
+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`;
+};
-function when(ts) {
- if (!ts) return "";
- return new Date(ts * 1000).toLocaleTimeString([], { hour: "2-digit", minute: "2-digit" });
-}
+const ACTIVE = ["running", "busy", "starting"];
-function summary(jobs) {
- const running = jobs.filter((j) => ["running", "busy", "starting"].includes(j.status)).length;
- const failed = jobs.filter((j) => ["failed", "lost", "killed"].includes(j.status)).length;
- const parts = [];
- if (running) parts.push(`${running} running`);
- if (failed) parts.push(`${failed} failed`);
- if (!parts.length) parts.push(jobs.length ? `${jobs.length} recent` : "idle");
- return parts.join(" · ");
+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: 5,
+ interval: 10,
mount(el) {
- el.innerHTML = `<div class="sbb"><div class="sbb-summary muted"></div><div class="sbb-list"></div></div>`;
+ el.innerHTML = `<div class="sbb"></div>`;
},
update(state, el) {
- const sum = el.querySelector(".sbb-summary");
- const list = el.querySelector(".sbb-list");
+ const box = el.querySelector(".sbb");
+ box.textContent = "";
if (state.offline) {
- sum.textContent = "";
- list.textContent = "build VM unreachable";
+ box.textContent = "build VM unreachable";
return;
}
+ const s = state.stats ?? {};
+ const p = s.packages ?? {};
+ const sr = s.slackrepo ?? {};
const jobs = state.jobs ?? [];
- sum.textContent = summary(jobs);
- list.textContent = "";
- for (const job of jobs.slice(0, 8)) {
- const row = document.createElement("div");
- row.className = "row job";
- const kind = document.createElement("span");
- kind.className = "kind";
- kind.textContent = job.kind || "job";
- const time = document.createElement("span");
- time.className = "muted num";
- time.textContent = when(job.created);
- const status = document.createElement("span");
- status.className = chipClass(job.status);
- status.textContent = job.status;
- row.append(kind, time, status);
- list.append(row);
- }
+
+ 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(" · ") || "—"),
+ );
},
};