diff --git a/api/main.py b/api/main.py index 5b1794f..97a7102 100644 --- a/api/main.py +++ b/api/main.py @@ -257,6 +257,11 @@ class DashboardHandler(BaseHTTPRequestHandler): self.send_response(200) self.send_header("Content-Type", "text/html; charset=utf-8") self.send_header("Content-Length", str(len(body))) + # never cache the dashboard shell โ€” a stale cached page was serving old + # JS/CSS, so pushed fixes (and live-vs-cached behaviour) didn't reach the + # browser. The page is tiny and the data is fetched separately anyway. + self.send_header("Cache-Control", "no-store, no-cache, must-revalidate") + self.send_header("Pragma", "no-cache") self.end_headers() self.wfile.write(body) diff --git a/api/static/dashboard.html b/api/static/dashboard.html index 2d92ec2..a89ddbe 100644 --- a/api/static/dashboard.html +++ b/api/static/dashboard.html @@ -793,13 +793,18 @@ function setConn(ok, msg) { $("stamp").textContent = msg; } function startStream() { + // Render immediately from a plain fetch (the same reliable path the date + // selector uses) so LIVE shows data even if EventSource never connects in + // this browser/proxy. SSE then layers on for low-latency pushes. + pollOnce(); + if (typeof EventSource === "undefined") { startPolling(); return; } try { es = new EventSource("/api/stream"); es.addEventListener("snapshot", (ev) => { try { render(JSON.parse(ev.data)); setConn(true, "live ยท " + new Date().toLocaleTimeString()); } - catch (e) {} + catch (e) { console.error("render failed:", e); setConn(false, "render error (see console)"); } }); - es.onerror = () => { setConn(false, "stream lost โ€” polling"); es.close(); es = null; startPolling(); }; + es.onerror = () => { setConn(false, "stream lost โ€” polling"); if (es) es.close(); es = null; startPolling(); }; } catch (e) { startPolling(); } } async function pollOnce() {