blob: ed19c60310ba99630a33fed8ebbc469791f4af39 (
plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
|
// The shell: discover modules, mount each panel, poll its state.
// A module is static/modules/<name>.js exporting default {title, interval,
// mount(el), update(state, el)}. A missing or broken panel is skipped.
const grid = document.getElementById("grid");
const refreshers = [];
async function mountModule(name) {
try {
const panel = (await import(`./modules/${name}.js`)).default;
const el = document.createElement("section");
el.className = "panel";
el.dataset.module = name;
const title = document.createElement("h2");
title.textContent = panel.title ?? name;
el.append(title);
grid.append(el);
panel.mount?.(el);
const poll = async () => {
try {
const r = await fetch(`/api/${name}`);
if (r.ok) panel.update?.(await r.json(), el);
} catch {
// keep the last state; the next tick retries
}
};
refreshers.push(poll);
await poll();
setInterval(poll, (panel.interval ?? 1) * 1000);
} catch {
// a broken panel must not take down the rest of the dashboard
}
}
async function boot() {
const r = await fetch("/api/modules");
if (!r.ok) return;
for (const { name } of await r.json()) await mountModule(name);
}
document.addEventListener("visibilitychange", () => {
if (document.visibilityState === "visible") refreshers.forEach((fn) => fn());
});
boot();
|