2 Commits

Author SHA1 Message Date
librelad
dd1264e335 ui(spa): stamp initial history entry + close live buses on pagehide so back-button works like a real SPA
Two reasons the back button was unreliable:

1. The very first history entry (the URL the user landed on) had
   state: null because handleInitialRoute() called navigate(path,
   false), and the pushState branch only ran when addToHistory=true.
   When the user later pushState'd forward and then hit back, the
   popstate handler's guard "e.state && e.state.route" was false on
   the initial entry, so it silently did nothing — back appeared
   broken. Now navigate() replaceState's the current entry whenever
   addToHistory=false, so the initial entry (and any back-compat
   URL rewrite) always carries its route. The popstate handler also
   now falls back to window.location when state.route is missing,
   so third-party history manipulation can't break us.

2. Open SSE streams (LiveSystem, taskEventBus, services-manager log
   tails) block the browser's back-forward cache. Without BFCache,
   back has to fully re-mount the page instead of restoring it
   instantly the way Amazon/GitHub feel. Now pagehide closes every
   live bus we own, and pageshow(persisted=true) reopens them when
   the page is restored from BFCache. Log tails aren't auto-resumed
   — Resume overlay handles that if the user comes back to a
   services tab.

Public surface added: LiveSystem.pause()/resume() and
ServicesManager.pauseStreams(). TaskEventBus already had stop()/
start(). The legacy-URL rewrite in handleAppDetail also now
replaceState's with { route: canonical } instead of {} so the
stamp is consistent across all internal history updates.

Signed-off-by: librelad <librelad@digitalangels.vip>
2026-05-27 23:28:25 +01:00
librelad
9f7ad8f177 feat(system): live 1 Hz SSE stream behind admin gauges + dashboard tile
Adds /api/system/stream — a Server-Sent Events feed driven by a single
per-process ticker that reads /proc directly and splices in the latest
host-side metrics.json each second. Subscribers share the connection so
N open tabs cost one ticker, and the ticker pauses entirely when nobody
is listening.

Frontend gets a singleton LiveSystem EventSource manager with auto-
reconnect, Page-Visibility integration (closes on tab hide), and last-
sample replay for late subscribers. Admin -> System gauges and the
dashboard memory + disk tile now tick at 1 Hz; trend charts and the
per-app table keep their 30 s poll because the underlying files only
regenerate once a minute.

Also adds /api/system/history as a thin range-query wrapper over the
existing 24 h JSON ring buffer — the binary ring backend will slot in
behind it in the next phase without changing the response shape.

Signed-off-by: librelad <librelad@digitalangels.vip>
2026-05-27 20:17:58 +01:00