feat(dashboard): serve H0M3 at /, relocate dashboard to /dashboard.html (#1464)

Step 2 of the nav restructure: make the H0M3 menu hub the landing page
at / and move the dashboard SPA to /dashboard.html, with every surface
linking back to the hub.

Mechanism (frontend-only, no host-side change — confirmed against the
ServeDir route table): the dashboard's ServeDir fallback serves
index.html at / via append_index_html_on_directories, and there is no
SPA path-routing catch-all to break. The dashboard SPA is served as the
plain file dashboard.html so it never shadows the exact-match
/dashboard/stream + /dashboard/history SSE routes registered before the
fallback.

- Swap the HTML entry files: the H0M3 page becomes index.html (loads
  home.js → served at /), and the dashboard SPA becomes dashboard.html
  (loads tabs.js → served at /dashboard.html). build.mjs copies the new
  set; JS bundle names are unchanged (referenced by absolute /static/
  paths, independent of the HTML filename).
- H0M3 Dashboard tile now points at /dashboard.html.
- The dashboard gains a "← home" back-link in its chrome; flow.html and
  logs.html relabel their back-link from "← dashboard" to "← home"
  (href stays / — which is the hub now). Pages link to the hub, not to
  each other.
- Agent page (app.js + stats.js): the "↑ dashboard" link now targets
  /dashboard.html. The API base (rebuild / answer-question /
  mark-all-read POSTs) stays the origin root, unchanged.
- Comment-only: tabs.js / flow.js / common.js references to the
  dashboard's old index.html filename updated to dashboard.html.

Note for review: git renders the file swap as a deleted home.html + an
added dashboard.html + a heavily-modified index.html, because index.html
exists on both sides with swapped content. It's a content swap, not a
rewrite — the built dist/ is verified (index.html→home.js, dashboard.html
→tabs.js).

Deferred to a follow-up: removing the FL0W / L0GS / M4TR1X "→" page-links
from the dashboard tab strip (touches tabs.js gating/overflow), and the
shared reusable chrome component (a later step).
This commit is contained in:
iris 2026-06-07 20:49:33 +02:00 committed by mara
commit 8ddab401f9
12 changed files with 479 additions and 449 deletions

View file

@ -1,4 +1,4 @@
// /index.html entry point: tab renderers + tab routing + refreshState
// /dashboard.html entry point: tab renderers + tab routing + refreshState
// + notification deltas. Reads /api/state on cold load and after every
// async-form submit; live updates run through `applyXxx` mutation
// handlers triggered by the dashboard event stream (subscribed via
@ -8,7 +8,7 @@
// Pure helpers (DOM, side panel, OS notifications, path linkification)
// live in `./common.js`; the flow-page surface (operator inbox, broker
// terminal, @-mention composer) lives in `./flow.js` — `flow.html` and
// `index.html` each load their own bundle.
// `dashboard.html` each load their own bundle.
import { marked } from 'marked';
import {
@ -838,7 +838,7 @@ window.marked = marked;
function renderContainers(s) {
const root = $('containers-section');
// #containers-section only exists on /index.html. tabs.js is the
// #containers-section only exists on /dashboard.html. tabs.js is the
// bundle for that page only (flow.html loads flow.js instead).
// Belt-and-suspenders for any future page that adds tabs.js
// without a #containers-section — matches the
@ -1287,7 +1287,7 @@ window.marked = marked;
function renderTombstones(s) {
const root = $('tombstones-section');
// #tombstones-section only lives on /index.html (SYST3M tab);
// #tombstones-section only lives on /dashboard.html (SYST3M tab);
// no-op on /flow.html and any other page that loads the shared
// bundle without the dashboard's tab panes.
if (!root) return;
@ -1814,7 +1814,7 @@ window.marked = marked;
function renderQuestions() {
const root = $('questions-section');
// #questions-section only lives on /index.html (Y3R C4LL tab);
// #questions-section only lives on /dashboard.html (Y3R C4LL tab);
// no-op when the section is missing. `question_added` /
// `question_resolved` SSE events route through here.
if (!root) return;
@ -2173,7 +2173,7 @@ window.marked = marked;
function renderApprovals() {
const root = $('approvals-section');
// #approvals-section only lives on /index.html (Y3R C4LL tab);
// #approvals-section only lives on /dashboard.html (Y3R C4LL tab);
// no-op elsewhere — `approval_added` / `approval_resolved` SSE
// events route through here on every page that loads the bundle.
if (!root) return;
@ -3990,7 +3990,7 @@ window.marked = marked;
// lost during the disconnect window (same pattern as flow.js's
// onStreamOpen).
//
// Both /index.html and /flow.html subscribe to `/dashboard/stream`
// Both /dashboard.html and /flow.html subscribe to `/dashboard/stream`
// and filter client-side — the dashboard ignores broker traffic
// and the inbox ignores mutation events.
const MUTATION_HANDLERS = {