// The shell: discover modules, mount each panel, poll its state. // A module is static/modules/.js exporting default {title, interval, // mount(el), update(state, el)}. A missing or broken panel is skipped. const grid = document.getElementById("grid"); async function mountModule(name) { let panel; try { panel = (await import(`./modules/${name}.js`)).default; } catch { return; } const el = document.createElement("section"); el.className = "panel"; el.dataset.module = name; el.innerHTML = `

${panel.title ?? name}

`; 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 } }; await poll(); setInterval(poll, (panel.interval ?? 1) * 1000); } async function boot() { const r = await fetch("/api/modules"); if (!r.ok) return; for (const { name } of await r.json()) await mountModule(name); } boot();