diff --git a/docs/web-ui.md b/docs/web-ui.md index 7a6cd6d7..9f91d8c2 100644 --- a/docs/web-ui.md +++ b/docs/web-ui.md @@ -383,7 +383,14 @@ newest-first. **MESS4GE FL0W** — live broker tail wrapped in a `.terminal-wrap`. Cold load backfills the last ~200 messages from `/dashboard/history`; live frames arrive on `/dashboard/stream`. Each row is one broker -event — `sent` or `delivered` — with `from → to: body`. Sticky- +event — `sent` or `delivered` — with `from → to: body`. The row is +a `flex-wrap: wrap` container holding ts / arrow / from / sep / to +chips inline; the **body wraps to its own full-width line below** +the chips (`flex: 1 1 100%`) so the body always gets the full row +width down to the content edge — long timestamps + agent names +used to push the body ~30ch in and force awkward narrow-column +wraps. `min-width: 0` keeps `word-break: break-word` effective so +the body doesn't force the row wider than its container. Sticky- bottom auto-scroll + "↓ N new" pill. Below the stream sits a terminal-style compose box: `@name` picks the recipient (sticky via localStorage; auto-complete from the live container list, Tab/Enter diff --git a/frontend/packages/dashboard/src/dashboard.css b/frontend/packages/dashboard/src/dashboard.css index f08bb5d8..343f338f 100644 --- a/frontend/packages/dashboard/src/dashboard.css +++ b/frontend/packages/dashboard/src/dashboard.css @@ -3,19 +3,19 @@ @import "@hive/shared/base.css"; @import "@hive/shared/terminal.css"; -/* ─── tabbed dashboard chrome (#369) ──────────────────────────────── - Top-of-page sticky header with banner + tab strip. Tab routing is - hash-based; tab panes are show/hide via the `.tab-pane-active` - class. SSE stays alive across tab switches so count pills update - live on inactive tabs without losing pulse on what's happening - elsewhere. */ +/* ─── tabbed dashboard chrome ────────────────────────────────────── + Top-of-page sticky header with banner + tab strip. SSE stays + alive across tab switches so count pills update live on inactive + tabs. See docs/web-ui.md::Chrome header + Tab strip for the + routing model. */ body.dashboard-shell { - /* Full-width layout (#416 — mara: drop the 90em cap so wide screens - don't waste real estate on empty side margins). `padding: 0 1.5em - 1.5em` keeps a small gutter on the left/right so cards don't kiss - the viewport edge; `.dashboard-chrome { margin: 0 -1.5em ... }` - still pulls the chrome bar edge-to-edge through that gutter. */ + /* Full-width layout — no max-width cap so wide screens don't + waste real estate on empty side margins. `padding: 0 1.5em + 1.5em` keeps a small gutter on the left/right so cards don't + kiss the viewport edge; `.dashboard-chrome { margin: 0 -1.5em + ... }` still pulls the chrome bar edge-to-edge through that + gutter. */ margin: 0; padding: 0 1.5em 1.5em; } @@ -427,11 +427,10 @@ a:hover { color: var(--cyan); border-color: var(--cyan); text-shadow: 0 0 6px rgba(137, 220, 235, 0.4); } -/* Context-window usage badges on dashboard container rows. Thresholds - are derived per-container: yellow ≥ 50% and red ≥ 75% of the model's - context window (`ContainerView.context_window_tokens`), mirroring the - harness compaction watermarks. Falls back to fixed 100k / 150k when - the window is unknown. (issue #66) */ +/* Context-window usage badges on dashboard container rows. See + docs/web-ui.md::Container row Line 1 for the per-container + threshold derivation (yellow ≥ 50% / red ≥ 75% of the model's + context window) and fallback values. */ .badge-ctx-ok { color: var(--green); border-color: var(--green); opacity: 0.85; @@ -464,10 +463,10 @@ a:hover { /* Per-container journald viewer + applied-config viewer. Both open in the side panel and lazy-fetch on open; output is monospace inside a bordered
, controls (unit select + refresh) above. - #541: the panel-body wrapper is a column flex container that fills - the side-panel-body so thecan flex-grow into a single tall - scrollable surface instead of a short box at the top with the rest - of the panel empty. */ + The panel-body wrapper is a column flex container that fills the + side-panel-body so thecan flex-grow into a single tall + scrollable surface instead of a short box at the top with the + rest of the panel empty. */ .journal-body { display: flex; flex-direction: column; @@ -495,9 +494,9 @@ a:hover { color: var(--fg); border: 1px solid var(--purple-dim); padding: 0.5em 0.7em; - /* #541: take all leftover panel height + scroll inside the pre so - long log fetches don't push the controls off-screen. `min-height: - 0` is the canonical "let me actually flex-shrink for overflow" + /* Take all leftover panel height + scroll inside the pre so long + log fetches don't push the controls off-screen. `min-height: 0` + is the canonical "let me actually flex-shrink for overflow" escape hatch on flex children. */ flex: 1 1 0; min-height: 0; @@ -645,7 +644,8 @@ code { border-color: var(--purple); background: rgba(203, 166, 247, 0.08); } -/* Image / tabbed file preview (issues #188, #192) */ +/* Image / tabbed file preview — see docs/web-ui.md::Side panel + for the type-aware preview shapes. */ .preview-host { margin-top: 0.5em; } .img-preview { display: block; @@ -719,7 +719,7 @@ code { text-overflow: ellipsis; white-space: nowrap; } -/* Bulk select-all / -none control above the meta-inputs tree (#275). */ +/* Bulk select-all / -none control above the meta-inputs tree. */ .meta-inputs-bulk { margin: 0 0 0.5em; font-size: 0.8em; @@ -739,7 +739,7 @@ code { border-color: var(--cyan); text-shadow: 0 0 6px currentColor; } -/* Tree twig glyph prefixing a nested (sub-)input row (#275). */ +/* Tree twig glyph prefixing a nested (sub-)input row. */ .meta-input-twig { color: var(--purple-dim); margin-right: 0.1em; @@ -765,7 +765,7 @@ code { cursor: not-allowed; } /* In-progress banner for the META INPUTS panel: shown while a - dashboard-triggered meta-update runs in the background (issue #259). */ + dashboard-triggered meta-update runs in the background. */ .meta-update-running { margin: 0 0 0.7em; padding: 0.4em 0.7em; @@ -941,7 +941,7 @@ ul form.inline { display: inline-block; } text-shadow: 0 0 6px currentColor; box-shadow: 0 0 8px -2px currentColor; } -/* #474: inline edit button on each schedule row. Yellow reads as a +/* Inline edit button on each schedule row. Yellow reads as a parallel destructive-adjacent action (edit changes state, but isn't deletion). */ .btn-edit-schedule { color: var(--yellow, #f9e2af); border-color: var(--yellow, #f9e2af); } @@ -1206,10 +1206,8 @@ summary:hover { color: var(--purple); } background: var(--bg-elev); border: 1px solid var(--border); padding: 0.5em 0.8em; - /* #450: no max-height cap — let the inbox grow to fill the - side-panel-body which already scrolls (`overflow: auto`). The - pre-#450 24em cap clamped the list well short of the available - panel height even on tall viewports. */ + /* No max-height cap — let the inbox grow to fill the + side-panel-body which already scrolls. */ } .inbox li { padding: 0.25em 0; @@ -1245,17 +1243,9 @@ summary:hover { color: var(--purple); } text-indent: 0; } .live .msgrow .msg-body { - /* #485: body takes a full flex line of its own beneath the - metadata chips (ts / arrow / from / sep / to). Previously the - body sat inline with `flex: 1 1 0`, eating whatever the chips - left — which on a long timestamp + agent names + arrows meant - the body started ~30ch in and wrapped awkwardly. Pushing - `flex-basis: 100%` forces the body to wrap to its own line in - the existing `flex-wrap: wrap` row, where it can use the full - width down to the row's content edge. - `min-width: 0` still applies so `word-break: break-word` - actually kicks in instead of forcing the row wider than its - container. */ + /* Body takes a full flex line of its own beneath the metadata + chips. See docs/web-ui.md::FL0W page (MESS4GE FL0W) for the + flex-basis: 100% + min-width: 0 rationale. */ flex: 1 1 100%; min-width: 0; } @@ -1354,9 +1344,9 @@ footer { font-size: 0.9em; } footer a { color: var(--purple); } -/* Slug banner now lives at the page footer (#389 follow-up) — give - it a slim top margin so it doesn't crash into the prior content, - and bottom margin separating from the divider/link line. */ +/* Slug banner lives at the page footer — slim top margin so it + doesn't crash into the prior content, bottom margin separating + from the divider/link line. */ footer .banner-thin { margin-bottom: 0.8em; } @@ -1402,13 +1392,11 @@ footer .banner-thin { top: 0; right: 0; bottom: 0; - /* #451: width is a CSS variable so the drag handle (added by - Panel.bind) can update it live, and so localStorage-persisted - widths apply on first paint. Default min(760px, 94vw) preserves - the pre-#451 behaviour for operators who never drag. JS sets - `--side-panel-w` via inline style on the drawer; persistence - lives in localStorage (key `hyperhive:side-panel-width`), - replayed onto the var by `Panel.applyStoredWidth` at bind. */ + /* Width is a CSS variable so the drag handle can update it live + (JS sets `--side-panel-w` on the drawer) and localStorage- + persisted widths apply on first paint. Default min(760px, 94vw) + covers operators who never drag. See docs/web-ui.md::Side panel + for the drag-to-resize mechanism + localStorage key. */ width: var(--side-panel-w, min(760px, 94vw)); /* Clamp so a stored width can never push the drawer off-screen or shrink it past readability. min content width matches the @@ -1426,12 +1414,11 @@ footer .banner-thin { .side-panel.open { pointer-events: auto; } .side-panel.open .side-panel-backdrop { opacity: 1; } .side-panel.open .side-panel-drawer { transform: translateX(0); } -/* #451: drag-to-resize handle on the drawer's left edge. The handle - itself is invisible until hover/drag so it doesn't compete with the - 2px mauve `border-left` for the visual boundary. Pointer-cursor - tells the operator the edge is grabbable; the brighter glow during - drag (`body.side-panel-resizing`) is the affordance the eye - tracks. */ +/* Drag-to-resize handle on the drawer's left edge. Invisible until + hover/drag so it doesn't compete with the 2px mauve border-left + for the visual boundary. Pointer-cursor tells the operator the + edge is grabbable; the brighter glow during drag + (body.side-panel-resizing) is the affordance the eye tracks. */ .side-panel-resize { position: absolute; top: 0; @@ -1529,22 +1516,17 @@ body.side-panel-resizing * { cursor: ew-resize !important; } padding: 0.2em 0.5em; } -/* ─── /flow.html — full-page chat (#369) ─────────────────────────── - The all-agents chat surface lives on its own page so it can claim - full-viewport vibec0re styling (operator @ #369#issuecomment-3437). - Same shape as the per-agent live page (#362): frosted-glass header - at top, frosted composer docked at bottom, terminal scrolls - behind both. Operator inbox lives behind a header pill that opens - the side-panel flyout — preserves the "inbox + chat in one view" - ergonomics without stealing terminal real estate. */ +/* ─── /flow.html — full-page chat ───────────────────────────────── + See docs/web-ui.md::FL0W page for the page-vs-pane rationale + and the inbox-flyout-as-pill ergonomics. Shape mirrors the + per-agent live page (frosted-glass header + composer, full- + viewport terminal). */ :root { /* Approximate height of the flow chrome (tabbar + dashboard-chrome - padding). The banner-thin slug used to live in the chrome and - padded this up to 4.7em; with the slug moved out (#389 follow-up: - slug lives at the page footer on /, simply omitted on /flow.html - since there's no normal-flow footer position in the full-viewport - terminal), the chrome is just tabs now and shrinks accordingly. */ + padding). The slug banner lives at the page footer on /, omitted + on /flow.html since the full-viewport terminal has no normal-flow + footer position. */ --flow-header-h: 3.6em; --flow-composer-h: 3.6em; --flow-frost-bg: rgba(30, 30, 46, 0.74); @@ -1566,8 +1548,8 @@ body.flow-shell { var(--bg); } -/* Flow chrome (#383): reuses the dashboard's `.dashboard-chrome` + - tabbar so the operator can switch tabs from the flow page without +/* Flow chrome 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. */ @@ -1595,15 +1577,13 @@ body.flow-shell .tabbar .tab.active.tab-link { 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. */ +/* `.notif-row` styling lives under the shared `.tabbar #notif-row` + selector earlier in the file — the flow page reuses the dashboard + tab strip rather than carrying its own title/hint/back chrome. */ /* Inbox pill — operator inbox flyout trigger. Sits right under the header so it stays in the operator's gaze without crowding the - chat. Same shape as the agent page's pills (#362). */ + chat. Same shape as the agent page's header pills. */ .flow-pill { position: fixed; top: calc(var(--flow-header-h) + 0.8em); @@ -1672,11 +1652,10 @@ body.flow-shell .tabbar .tab.active.tab-link { overflow: auto; } /* Tail pill (↓ N new): bottom offset clears the floating composer. - z-index escapes the stacking context the .terminal-wrap's - backdrop-filter creates (issue #375) — tabs.js anchors the pill - on .flow-main now (not .terminal-wrap), so this z-index reaches - the root stacking context and properly floats above the - composer at z-index 30. */ + Pill is anchored on .flow-main (not .terminal-wrap) so the + backdrop-filter stacking context doesn't trap its z-index — see + docs/web-ui.md::Per-agent page (Terminal-wrap) for the same + gotcha on the agent page. */ .flow-main .tail-pill { bottom: calc(var(--flow-composer-h) + 0.6em); z-index: 35; @@ -1702,10 +1681,11 @@ body.flow-shell .tabbar .tab.active.tab-link { surface the messages via the pill/flyout instead. */ .flow-inbox-headless { display: none !important; } -/* ─── scheduled prompts tab (#459) ───────────────────────────────────── +/* ─── scheduled prompts tab ──────────────────────────────────────── Creation form at the top, list of queued schedule cards below. Cards show: id + source + due-in + cancel-all in the header, - the prompt body, then a targets table with per-row cancel. */ + the prompt body, then a targets table with per-row cancel. See + docs/web-ui.md::SCH3DUL3S tab. */ .schedule-edit-form { display: flex; @@ -1717,9 +1697,9 @@ body.flow-shell .tabbar .tab.active.tab-link { padding: 0.8em 1em; margin-bottom: 0.5em; } -/* #474 — inline edit form opens directly under the schedule row's - actions strip, indented slightly so it visually nests under the - row it edits. */ +/* Inline edit form opens directly under the schedule row's + actions strip, indented slightly so it visually nests under + the row it edits. */ .schedule-edit-form-wrapper { margin-top: 0.5em; padding-left: 0.5em; @@ -1782,7 +1762,7 @@ body.flow-shell .tabbar .tab.active.tab-link { justify-content: flex-end; } -/* Interval composer (#466). Preset chip row + d/h/m/s inputs + live +/* Interval composer — preset chip row + d/h/m/s inputs + live preview, so the operator never has to multiply seconds by hand. */ .schedule-interval-presets { display: flex; @@ -1826,10 +1806,9 @@ body.flow-shell .tabbar .tab.active.tab-link { } .schedule-interval-preview-oneshot { color: var(--muted); font-style: italic; } -/* #535 — schedules-as-table. One row per schedule, attribute columns +/* Schedules-as-table — one row per schedule, attribute columns on the left, one ✓/✕ column per agent in the middle, actions - column on the right. Agent column headers tilt -45° so a stack of - short agent names fits in ~28px each. */ + column on the right. See docs/web-ui.md::SCH3DUL3S tab. */ .schedules-table { width: 100%; border-collapse: collapse; @@ -1849,11 +1828,11 @@ body.flow-shell .tabbar .tab.active.tab-link { .schedules-table-id { width: 3em; } .schedules-table-body-th { min-width: 12em; } .schedules-table-actions-th { width: 7em; } -/* Tilted agent column headers (#535). Header cell is narrow (~28px) - and tall (~90px); the innerrotates -45° about its bottom-left - corner, with a translate to slide the text up alongside the cell - border. The inner carries the actual baseline so the - underline (border-bottom on the cell) aligns with the rotated text. */ +/* Tilted agent column headers — narrow (~28px) and tall (~90px); + the innerrotates -45° about its bottom-left corner, with + a translate to slide the text up alongside the cell border. The + inner carries the actual baseline so the underline + (border-bottom on the cell) aligns with the rotated text. */ .schedules-table-agent-th { width: 28px; min-width: 28px; @@ -1937,8 +1916,8 @@ body.flow-shell .tabbar .tab.active.tab-link { padding: 0.1em 0.45em; } -/* #564 — inline create row at the bottom of the schedules table. - Cells host inputs directly so the operator can fill + click + to +/* Inline create row at the bottom of the schedules table. Cells + host inputs directly so the operator can fill + click + to queue a new schedule without leaving the table view. Slightly different background tone so it reads as "this isn't a schedule yet, it's the create form". */ @@ -2040,11 +2019,11 @@ body.flow-shell .tabbar .tab.active.tab-link { border-color: var(--border); } -/* Selection bar (#443). Sticky-bottom strip that surfaces bulk - actions when ≥1 agent is selected (click the icon). Visually - echoes the flow composer's frosted-mauve treatment so the chrome - reads as part of the same vibecore family. Hidden when empty — - leaves the page footer's normal-flow position untouched. */ +/* Selection bar — sticky-bottom strip that surfaces bulk actions + when ≥1 agent is selected (click the icon). Visually echoes the + flow composer's frosted-mauve treatment so the chrome reads as + part of the same vibecore family. Hidden when empty. See + docs/web-ui.md::Selection bar for the bulk-action semantics. */ .selection-bar { position: fixed; bottom: 0; @@ -2100,11 +2079,11 @@ body.flow-shell .tabbar .tab.active.tab-link { selection set is non-empty). */ body.dashboard-shell.has-selection { padding-bottom: 4.5em; } -/* #486 — M0V3 →picker. Inline `