flow: keep the dashboard tab strip on the flow page (#383)

Operator wanted the tab header visible on /flow.html so switching
tabs doesn't require navigating back to / first.

The flow page now reuses the same `<header class="dashboard-chrome">`
markup the dashboard renders, with a few tweaks:

- The SW4RM / Y3R C4LL / SYST3M tabs are cross-page links
  (`href="/#swarm"` etc.) — clicking lands on the dashboard with the
  destination tab pre-active via the hash router.
- The FL0W tab is rendered `.active.tab-link` + `aria-current="page"`
  so it reads as the current view (no clickable arrow / "go here"
  affordance — you're already here).
- Banner-thin echoes the dashboard for visual continuity.
- Notif controls cohabit with the tabs (same IDs the dashboard uses,
  so app.js's NOTIF binding picks them up unchanged).

Layout glue:
- `body.flow-shell .dashboard-chrome.flow-chrome` overrides the
  dashboard's `position: sticky` with `position: fixed` so the
  chrome stays put under flow-shell's `overflow: hidden` body
  layout, keeping the terminal full-viewport behind/beneath.
- New rule for the active FL0W tab — the `.tab-link` styling on the
  dashboard otherwise reads as a passive cross-page link; here we
  need it lit-up like a regular active tab.
- `--flow-header-h` bumped from 4.2em → 4.7em to match the natural
  height of the tab strip + banner combo. Terminal padding +
  inbox-pill top offset both derive from this variable, so they
  follow automatically.

Removed:
- Legacy `.flow-title`, `.flow-hint`, `.flow-back` CSS rules (their
  HTML counterparts are gone — the tab strip carries the
  identity now).
- The `<a class="flow-back">← d4shb04rd</a>` link and the
  `<h2 class="flow-title">` from flow.html.

## Validation

`npm run build` clean.
  dashboard.css: 38kb → 37kb (legacy rules removed, new shared-
                  chrome rules are smaller)
  flow.html:    4.4kb → 4.7kb (tab strip replaces title bar)
  app.js:       unchanged (no JS changes — the tab navigation is
                pure HTML href + cross-page hash)

Closes #383.
This commit is contained in:
iris 2026-05-24 14:23:34 +02:00 committed by Mara
parent 3abd0ba711
commit ba669d2d6c
2 changed files with 66 additions and 56 deletions

View file

@ -1290,7 +1290,12 @@ footer a { color: var(--purple); }
ergonomics without stealing terminal real estate. */
:root {
--flow-header-h: 4.2em;
/* Approximate height of the flow chrome (banner-thin + tabbar +
dashboard-chrome padding). Bumped slightly above the natural
content height so the terminal padding-top clears the bottom
border. Tweaked once after #383 took over the chrome from the
simpler title bar. */
--flow-header-h: 4.7em;
--flow-composer-h: 3.6em;
--flow-frost-bg: rgba(30, 30, 46, 0.74);
--flow-frost-blur: blur(12px) saturate(140%);
@ -1311,58 +1316,40 @@ body.flow-shell {
var(--bg);
}
.flow-header {
/* Flow chrome (#383): reuses the dashboard's `.dashboard-chrome` +
tabbar so the operator can switch tabs from the flow page without
navigating back first. The chrome must be fixed-position (vs
sticky on the dashboard) since flow-shell has `overflow: hidden`
on body and the main area absolute-positions the terminal. */
body.flow-shell .dashboard-chrome.flow-chrome {
position: fixed;
top: 0;
left: 0;
right: 0;
z-index: 30;
min-height: var(--flow-header-h);
display: flex;
align-items: center;
gap: 1em;
padding: 0.55em 1em;
margin: 0;
padding: 0.4em 0 0;
background: var(--flow-frost-bg);
-webkit-backdrop-filter: var(--flow-frost-blur);
backdrop-filter: var(--flow-frost-blur);
border-bottom: 1px solid var(--purple-dim);
box-shadow: 0 6px 18px rgba(0, 0, 0, 0.35);
flex-wrap: wrap;
}
.flow-back {
color: var(--cyan);
text-decoration: none;
font-size: 0.85em;
letter-spacing: 0.1em;
padding: 0.25em 0.6em;
border: 1px solid var(--purple-dim);
border-radius: 4px;
flex: 0 0 auto;
transition: border-color 0.15s ease, color 0.15s ease;
}
.flow-back:hover {
border-color: var(--cyan);
text-shadow: 0 0 8px rgba(137, 220, 235, 0.6);
}
.flow-title {
margin: 0;
font-size: 1.2em;
/* Active tab on the flow page is FL0W. Its `.tab-link` styling (the
tab strip's right-most entry on the dashboard, where it represents
the *link out* to /flow.html) needs to read as the active tab here.
`aria-current="page"` flags it semantically. */
body.flow-shell .tabbar .tab.active.tab-link {
color: var(--purple);
text-transform: uppercase;
letter-spacing: 0.15em;
}
.flow-hint {
margin: 0;
color: var(--muted);
font-size: 0.82em;
flex: 1 1 auto;
min-width: 0;
}
.flow-header .notif-row {
margin-left: auto;
display: flex;
gap: 0.4em;
background: var(--bg);
border-color: var(--purple-dim);
box-shadow: 0 -2px 12px -4px rgba(203, 166, 247, 0.4);
}
/* Legacy `.flow-title` / `.flow-hint` / `.flow-back` rules were
removed in #383 the flow page now uses the shared chrome with
the dashboard tab strip, no need for FL0W-specific title/hint
elements. The `.notif-row` styling lives under the shared
`.tabbar #notif-row` selector earlier in the file. */
/* Inbox pill operator inbox flyout trigger. Sits right under the
header so it stays in the operator's gaze without crowding the

View file

@ -8,22 +8,45 @@
</head>
<body class="flow-shell">
<!-- Fixed-overlay header. Frosted glass over the message flow —
backdrop-filter blur shows the scrolled chat text behind. Mirrors
the agent live page's overlay layout (#362). -->
<header class="flow-header" id="flow-header">
<a href="/" class="flow-back" title="back to dashboard">← d4shb04rd</a>
<h2 class="flow-title">◆ FL0W ◆</h2>
<p class="flow-hint">live broker tail · newest at the top · <code>@name</code> picks a recipient (sticky); <code>tab</code> completes</p>
<!-- Notif controls cohabit here (no other chrome on this page).
Same IDs as on the dashboard so app.js's NOTIF binding picks
them up unchanged. -->
<div id="notif-row" class="notif-row">
<button type="button" id="notif-enable" class="btn btn-notif" hidden>🔔 enable notifications</button>
<button type="button" id="notif-mute" class="btn btn-notif" hidden>🔕 mute</button>
<button type="button" id="notif-unmute" class="btn btn-notif" hidden>🔔 unmute</button>
<span id="notif-status" class="meta" hidden></span>
</div>
<!-- Fixed-overlay chrome with the same tab strip as the dashboard
(#383) — operator never has to navigate back to the dashboard
to switch tabs. FL0W is the current page, the SW4RM / Y3R C4LL
/ SYST3M tabs cross-link to the dashboard with the matching
hash so the destination tab is pre-active. Banner-thin echoes
the dashboard for visual continuity. -->
<header class="dashboard-chrome flow-chrome" id="flow-header">
<pre class="banner banner-thin">░▒▓█▓▒░ HYPERHIVE / HIVE-C0RE / WE ARE THE WIRED ░▒▓█▓▒░</pre>
<nav class="tabbar" id="tabbar" role="tablist">
<a class="tab" href="/#swarm" role="tab" data-tab="swarm">
<span class="tab-label">◆ SW4RM ◆</span>
<span class="tab-count" id="tab-count-swarm" hidden></span>
</a>
<a class="tab" href="/#call" role="tab" data-tab="call">
<span class="tab-label">◆ Y3R C4LL ◆</span>
<span class="tab-count tab-count-attn" id="tab-count-call" hidden></span>
</a>
<a class="tab" href="/#system" role="tab" data-tab="system">
<span class="tab-label">◆ SYST3M ◆</span>
<span class="tab-count" id="tab-count-system" hidden></span>
</a>
<a class="tab tab-link active" id="tab-flow" href="/flow.html"
aria-current="page"
title="all-agents chat — you are here">
<span class="tab-label">◆ FL0W ◆</span>
<span class="tab-count" id="tab-count-flow" hidden></span>
</a>
<!-- Notif controls cohabit with the tabs (always-on chrome).
Same IDs as on the dashboard so app.js's NOTIF binding
picks them up unchanged. -->
<div id="notif-row" class="notif-row">
<button type="button" id="notif-enable" class="btn btn-notif" hidden>🔔 enable notifications</button>
<button type="button" id="notif-mute" class="btn btn-notif" hidden>🔕 mute</button>
<button type="button" id="notif-unmute" class="btn btn-notif" hidden>🔔 unmute</button>
<span id="notif-status" class="meta" hidden></span>
</div>
</nav>
</header>
<!-- Operator inbox flyout trigger — count + click → side panel