feat(dashboard): split monolithic CSS into common + per-page bundles
dashboard.css was 2500+ lines covering every page. Split into four
separate esbuild entry points:
- common.css — @hive/shared imports + shared typography, badges,
buttons, inbox/compose, side panel, diff panel,
path linkification, logs-header chrome (shared by
flow + logs)
- dashboard.css — dashboard-only styles (tabs, container rows,
approvals, questions, permissions, schedules, etc.)
- flow.css — flow page chrome (frosted header, composer, inbox pill)
- logs.css — log viewer sub-tabs (journal-*, build-logs-*)
Each HTML file now loads common.css + its own page-specific bundle.
build.mjs updated to produce all four CSS outputs. All builds pass.
Closes #1056
This commit is contained in:
parent
ed6f40ba40
commit
ea5115c3a6
8 changed files with 949 additions and 1136 deletions
165
frontend/packages/dashboard/src/flow.css
Normal file
165
frontend/packages/dashboard/src/flow.css
Normal file
|
|
@ -0,0 +1,165 @@
|
|||
/* ─── /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 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);
|
||||
--flow-frost-blur: blur(12px) saturate(140%);
|
||||
}
|
||||
|
||||
body.flow-shell {
|
||||
/* Override the dashboard's centred-max-width layout — flow owns
|
||||
the whole viewport. */
|
||||
max-width: none;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
height: 100vh;
|
||||
overflow: hidden;
|
||||
background:
|
||||
radial-gradient(ellipse 80% 60% at 50% 0%,
|
||||
rgba(203, 166, 247, 0.06) 0%,
|
||||
transparent 60%),
|
||||
var(--bg);
|
||||
}
|
||||
|
||||
/* 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. */
|
||||
body.flow-shell .dashboard-chrome.flow-chrome {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
z-index: 30;
|
||||
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);
|
||||
}
|
||||
/* 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);
|
||||
background: var(--bg);
|
||||
border-color: var(--purple-dim);
|
||||
box-shadow: 0 -2px 12px -4px rgba(203, 166, 247, 0.4);
|
||||
}
|
||||
/* 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 header pills. */
|
||||
.flow-pill {
|
||||
position: fixed;
|
||||
top: calc(var(--flow-header-h) + 0.8em);
|
||||
right: 1em;
|
||||
z-index: 25;
|
||||
background: var(--bg-elev);
|
||||
border: 1px solid var(--purple-dim);
|
||||
color: var(--fg);
|
||||
font-family: inherit;
|
||||
font-size: 0.85em;
|
||||
letter-spacing: 0.04em;
|
||||
border-radius: 999px;
|
||||
padding: 0.3em 0.8em;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 0.5em;
|
||||
cursor: pointer;
|
||||
box-shadow: 0 4px 14px rgba(0, 0, 0, 0.35);
|
||||
transition: border-color 0.15s ease, box-shadow 0.15s ease, color 0.15s ease;
|
||||
}
|
||||
.flow-pill:hover {
|
||||
border-color: var(--purple);
|
||||
color: var(--purple);
|
||||
box-shadow: 0 0 12px -2px var(--purple);
|
||||
}
|
||||
.flow-pill-icon { font-size: 1.05em; line-height: 1; }
|
||||
.flow-pill-label { color: var(--muted); }
|
||||
.flow-pill-count {
|
||||
background: rgba(250, 179, 135, 0.18);
|
||||
color: var(--amber);
|
||||
border-radius: 999px;
|
||||
padding: 0 0.5em;
|
||||
min-width: 1.6em;
|
||||
text-align: center;
|
||||
font-weight: bold;
|
||||
font-variant-numeric: tabular-nums;
|
||||
}
|
||||
|
||||
.flow-main {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
overflow: hidden;
|
||||
}
|
||||
/* flow.html now has a slim header (back link only) instead of the full
|
||||
tabbar — reduce the top padding to match the smaller header height. */
|
||||
body.flow-shell .flow-main-slim {
|
||||
padding-top: calc(var(--flow-header-h) + 0.4em);
|
||||
}
|
||||
.flow-main .terminal-wrap {
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
border: 0;
|
||||
background: transparent;
|
||||
box-shadow: none;
|
||||
border-radius: 0;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
.flow-main .live {
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
height: auto;
|
||||
max-height: none;
|
||||
padding-top: calc(var(--flow-header-h) + 0.8em);
|
||||
padding-bottom: calc(var(--flow-composer-h) + 0.8em);
|
||||
scroll-padding-top: calc(var(--flow-header-h) + 0.8em);
|
||||
scroll-padding-bottom: calc(var(--flow-composer-h) + 0.8em);
|
||||
overflow: auto;
|
||||
}
|
||||
/* Tail pill (↓ N new): bottom offset clears the floating composer.
|
||||
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;
|
||||
}
|
||||
|
||||
.flow-composer {
|
||||
position: fixed;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
z-index: 30;
|
||||
min-height: var(--flow-composer-h);
|
||||
background: var(--flow-frost-bg);
|
||||
-webkit-backdrop-filter: var(--flow-frost-blur);
|
||||
backdrop-filter: var(--flow-frost-blur);
|
||||
border-top: 1px solid var(--purple-dim);
|
||||
box-shadow: 0 -6px 18px rgba(0, 0, 0, 0.35);
|
||||
padding: 0.4em 1em;
|
||||
}
|
||||
|
||||
/* Hidden inbox section on the flow page — the renderInbox path
|
||||
wants `#inbox-section` in the DOM (legacy contract), but we
|
||||
surface the messages via the pill/flyout instead. */
|
||||
.flow-inbox-headless { display: none !important; }
|
||||
Loading…
Reference in a new issue