hyperhive/frontend/packages/dashboard/src/common.css
iris d14d79ce3b fix(frontend): give the server-warning bar an opaque background (#1572)
The server-warning rows composited their level tint (amber/red) over
`transparent`, so only 16-18% of the colour was opaque. Because the bar
is `position: sticky; top: 0`, page content scrolled up behind it showed
through the see-through background. Composite the tint over the elevated
surface grey (--bg-elev) instead, so the bar reads as a solid grey strip
with the level tint on top — no bleed-through. Affects every page that
shows the banner (the flow page is where it was spotted).
2026-06-09 13:32:50 +02:00

568 lines
19 KiB
CSS
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/* Shared Catppuccin palette + body typography + terminal pane styles.
Bundled in front of the page-specific rules via esbuild. */
@import "@hive/shared/base.css";
@import "@hive/shared/terminal.css";
@import "@hive/shared/tabs.css";
@import "@hive/shared/chrome.css";
/* ─── global typography ─────────────────────────────────────────────
Element-level rules shared across all three pages (index, flow,
logs). These must not reference page-specific chrome classes. */
h1, h2 {
color: var(--purple);
text-transform: uppercase;
letter-spacing: 0.15em;
margin-top: 2em;
text-shadow: 0 0 8px color-mix(in srgb, var(--purple) 40%, transparent);
}
.divider {
color: var(--purple-dim);
overflow: hidden;
white-space: nowrap;
margin-bottom: 0.5em;
}
ul { list-style: none; padding-left: 0; }
li { padding: 0.5em 0; }
.glyph { color: var(--purple); margin-right: 0.5em; }
a {
color: var(--cyan);
text-decoration: none;
font-weight: bold;
text-shadow: 0 0 4px color-mix(in srgb, var(--cyan) 50%, transparent);
}
a:hover {
color: var(--fg);
text-shadow: 0 0 12px color-mix(in srgb, var(--cyan) 90%, transparent);
}
code {
color: var(--amber);
background: var(--bg-elev);
padding: 0.1em 0.4em;
border: 1px solid var(--border);
border-radius: 2px;
font-size: 0.9em;
}
/* ─── shared inline labels ──────────────────────────────────────────
.meta / .id / .agent / .empty appear in message rows, build-log
toolbars, and side-panel content rendered by common.js. */
.meta { color: var(--muted); font-size: 0.85em; margin-left: 0.4em; }
.id { color: var(--pink); font-weight: bold; margin-right: 0.4em; }
.agent { color: var(--amber); font-weight: bold; margin-right: 0.6em; }
.empty { color: var(--muted); font-style: italic; }
/* ─── status badges ─────────────────────────────────────────────────
.badge base + semantic colour variants. Used on container rows
(tabs.js) and build-log rows (logs.js). */
.badge {
display: inline-block;
padding: 0.05em 0.5em;
border: 1px solid;
border-radius: 2px;
font-size: 0.75em;
letter-spacing: 0.08em;
text-transform: uppercase;
}
.badge-warn {
color: var(--amber); border-color: var(--amber);
text-shadow: 0 0 6px color-mix(in srgb, var(--amber) 50%, transparent);
}
.badge-rate-limited {
color: var(--red); border-color: var(--red);
text-shadow: 0 0 6px color-mix(in srgb, var(--red) 50%, transparent);
}
.badge-muted {
color: var(--muted); border-color: var(--purple-dim);
background: color-mix(in srgb, var(--muted) 8%, transparent);
}
.badge-reminder {
color: var(--cyan); border-color: var(--cyan);
text-shadow: 0 0 6px color-mix(in srgb, var(--cyan) 40%, transparent);
}
.badge-loose-ends {
color: var(--purple); border-color: var(--purple);
text-shadow: 0 0 6px color-mix(in srgb, var(--purple) 40%, transparent);
}
/* Context-window usage badges on dashboard container rows. */
.badge-ctx-ok {
color: var(--green); border-color: var(--green);
opacity: 0.85;
}
.badge-ctx-caution {
color: var(--amber); border-color: var(--amber);
text-shadow: 0 0 6px color-mix(in srgb, var(--amber) 50%, transparent);
}
.badge-ctx-warn {
color: var(--red); border-color: var(--red);
text-shadow: 0 0 6px color-mix(in srgb, var(--red) 50%, transparent);
}
.badge-ok { background: color-mix(in srgb, var(--green) 12%, transparent); color: var(--green); border-color: var(--green); }
.badge-fail { background: color-mix(in srgb, var(--red) 12%, transparent); color: var(--red); border-color: var(--red); }
.badge-running { background: color-mix(in srgb, var(--amber) 12%, transparent); color: var(--amber); border-color: var(--amber); }
/* ─── buttons ───────────────────────────────────────────────────────
.btn base + semantic colour/size modifiers. Logs page uses
.btn-restart for the ↻ refresh buttons; dashboard uses all variants. */
.btn {
font-family: inherit;
font-weight: bold;
text-transform: uppercase;
letter-spacing: 0.1em;
background: transparent;
/* Strip browser UA button chrome (solid fill, system text colour).
Without this, native <button> + <input type="submit"> elements
ignore our background:transparent and colour in some browsers. */
-webkit-appearance: none;
appearance: none;
border: 1px solid;
padding: 0.25em 0.8em;
cursor: pointer;
text-shadow: 0 0 4px currentColor;
box-shadow: 0 0 0 0 currentColor;
transition: box-shadow 0.15s ease;
}
.btn:hover {
background: color-mix(in srgb, var(--fg) 6%, transparent);
text-shadow: 0 0 10px currentColor;
box-shadow: 0 0 10px -2px currentColor;
}
.btn:disabled,
.btn[disabled] {
opacity: 0.32;
cursor: not-allowed;
text-shadow: none;
box-shadow: none;
}
.btn:disabled:hover,
.btn[disabled]:hover {
background: transparent;
text-shadow: none;
box-shadow: none;
}
.btn-approve { color: var(--green); border-color: var(--green); }
.btn-deny { color: var(--red); border-color: var(--red); }
.btn-destroy { color: var(--red); border-color: var(--red); font-size: 0.75em; padding: 0.15em 0.5em; margin-left: 0.6em; }
.btn-rebuild { color: var(--amber); border-color: var(--amber); font-size: 0.75em; padding: 0.15em 0.5em; margin-left: 0.6em; }
.btn-restart { color: var(--cyan); border-color: var(--cyan); font-size: 0.75em; padding: 0.15em 0.5em; margin-left: 0.6em; }
.btn-stop { color: var(--pink); border-color: var(--pink); font-size: 0.75em; padding: 0.15em 0.5em; margin-left: 0.6em; }
.btn-start { color: var(--green); border-color: var(--green); font-size: 0.75em; padding: 0.15em 0.5em; margin-left: 0.6em; }
/* M0V3 affordance (selection bar) — mauve reads as "structural
change" rather than the destructive red / amber chrome of
destroy / rebuild. See docs/web-ui.md::Selection bar. */
.btn-move { color: var(--purple); border-color: var(--purple); font-size: 0.75em; padding: 0.15em 0.5em; margin-left: 0.6em; }
.btn-talk { color: var(--cyan); border-color: var(--cyan); }
.btn-spawn { color: var(--amber); border-color: var(--amber); }
.btn-fire-now { color: var(--purple); border-color: var(--purple); }
/* Post-fire flash: report renders directly on the button for ~1.5s
so the operator sees ok/failed/missing/consumed counts inline
without a modal. Green when at least one ok; muted otherwise. */
.btn-fire-now-flashed {
color: var(--green);
border-color: var(--green);
text-shadow: 0 0 6px currentColor;
box-shadow: 0 0 8px -2px currentColor;
}
/* 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); border-color: var(--yellow); }
/* ─── file-preview / diff panel (common.js Panel) ───────────────────
Side-panel content rendered by openFilePanel / buildTabbedPreview
in common.js; all three pages load the Panel singleton. */
.diff-panel { display: flex; flex-direction: column; gap: 0.6em; }
.diff-base-tabs { display: flex; flex-wrap: wrap; gap: 0.4em; }
.diff-base-tab {
background: transparent;
border: 1px solid var(--border);
color: var(--muted);
font: inherit;
font-size: 0.85em;
padding: 0.2em 0.7em;
cursor: pointer;
}
.diff-base-tab:hover { color: var(--fg); }
.diff-base-tab.active {
color: var(--purple);
border-color: var(--purple);
background: color-mix(in srgb, var(--purple) 8%, transparent);
}
/* Image / tabbed file preview */
.preview-host { margin-top: 0.5em; }
.img-preview {
display: block;
max-width: 100%;
height: auto;
margin: 0 auto;
border: 1px solid var(--border);
border-radius: 4px;
/* checkerboard so transparent regions of the image read clearly */
background: repeating-conic-gradient(var(--border) 0% 25%, var(--bg) 0% 50%) 50% / 18px 18px;
}
/* Path linkification — agents drop pointer strings into messages;
clicking the anchor opens the file in the side panel. */
.path-link {
color: var(--blue);
text-decoration: underline dotted;
cursor: pointer;
}
.path-link:hover { color: var(--amber); }
/* File-preview body — rendered inside the side panel. */
.path-preview-body {
background: var(--bg);
border: 1px solid var(--border);
padding: 0.5em 0.7em;
margin: 0;
white-space: pre-wrap;
word-break: break-word;
font-size: 0.85em;
color: var(--fg);
}
/* page chrome (.page-header / .page-back / .page-title — the standalone
pages' sticky back-link bar) moved to @hive/shared/chrome.css, imported
at the top of this file. */
/* ─── operator inbox + message rows ────────────────────────────────
.inbox and .msg-* are rendered by common.js (renderInbox) and
flow.js — both the dashboard side panel and the /flow.html page
display broker messages. */
.inbox {
background: var(--bg-elev);
border: 1px solid var(--border);
padding: 0.5em 0.8em;
/* No max-height cap — let the inbox grow to fill the
side-panel-body which already scrolls. */
}
.inbox li {
padding: 0.25em 0;
border-bottom: 1px solid var(--border);
display: grid;
grid-template-columns: auto auto auto 1fr;
gap: 0.5em;
align-items: baseline;
}
.inbox li:last-child { border-bottom: 0; }
.inbox .msg-ts { color: var(--muted); font-size: 0.85em; }
.inbox .msg-from { color: var(--amber); }
.inbox .msg-sep { color: var(--muted); }
.inbox .msg-body { color: var(--fg); white-space: pre-wrap; word-break: break-word; }
/* `#msgflow` is a shared `.live` pane inside `.terminal-wrap`. The
msgrow / msg-* rules below power both the dashboard CALL tab and
the /flow.html full-page terminal. */
.live .msgrow {
display: flex;
flex-wrap: wrap;
align-items: baseline;
gap: 0.5em;
padding: 0.1em 0;
text-indent: 0;
}
.live .msgrow .msg-body {
flex: 1 1 100%;
min-width: 0;
}
.live .msgrow.sent .msg-arrow { color: var(--cyan); }
.live .msgrow.delivered .msg-arrow { color: var(--green); }
/* Reply-thread rendering: indented border-left + muted reply tag. */
.live .msgrow.msg-reply {
padding-left: 1.2em;
border-left: 2px solid var(--border);
margin-left: 0.6em;
}
.msg-reply-tag {
color: var(--muted);
font-size: 0.8em;
white-space: nowrap;
order: -1;
}
.msg-reply-tag a {
color: var(--muted);
text-shadow: none;
font-weight: normal;
}
.msg-reply-tag a:hover { color: var(--fg); }
@keyframes msg-highlight-fade {
from { background: color-mix(in srgb, var(--purple) 18%, transparent); }
to { background: transparent; }
}
.msg-highlight { animation: msg-highlight-fade 1.5s ease-out forwards; }
.msg-ts { color: var(--muted); font-size: 0.85em; }
.msg-arrow { font-weight: bold; }
.msg-from { color: var(--amber); }
.msg-sep { color: var(--muted); }
.msg-to { color: var(--pink); }
.msg-body { color: var(--fg); white-space: pre-wrap; word-break: break-word; }
/* ─── operator compose box ──────────────────────────────────────────
Sits inside `.terminal-wrap` on both /flow.html and the dashboard
CALL tab. Dashed top separator mirrors the agent terminal prompt. */
.op-compose {
position: relative;
display: flex;
align-items: flex-start;
gap: 0.6em;
padding: 0.55em 0.8em;
border-top: 1px dashed var(--purple-dim);
}
.op-compose-prompt {
color: var(--purple);
text-shadow: 0 0 4px currentColor;
font-weight: bold;
white-space: nowrap;
user-select: none;
padding-top: 0.15em;
}
.op-compose-input {
flex: 1;
background: transparent;
color: var(--fg);
border: none;
font-family: inherit;
font-size: 1em;
line-height: 1.4;
resize: none;
overflow: hidden;
max-height: 8em;
padding: 0;
}
.op-compose-input:focus { outline: none; }
.op-compose-input::placeholder { color: var(--muted); }
.op-compose-suggest {
position: absolute;
left: 3em;
bottom: 100%;
z-index: 50;
background: var(--bg-elev);
border: 1px solid var(--purple-dim);
border-radius: 4px;
box-shadow: 0 4px 16px rgba(0, 0, 0, 0.45);
list-style: none;
margin: 0;
padding: 0.3em 0;
min-width: 12em;
max-height: 14em;
overflow-y: auto;
}
.op-compose-suggest .item {
padding: 0.35em 0.9em;
cursor: pointer;
font-size: 0.9em;
color: var(--muted);
}
.op-compose-suggest .item.active,
.op-compose-suggest .item:hover {
background: var(--border);
color: var(--purple);
}
/* ─── side panel ─────────────────────────────────────────────────
Long content (file previews, diffs, journald, applied config)
opens in a drawer that swipes in from the right — used by both
/flow.html and the dashboard. */
.side-panel {
position: fixed;
inset: 0;
z-index: 200;
pointer-events: none;
}
.side-panel-backdrop {
position: absolute;
inset: 0;
background: color-mix(in srgb, var(--crust) 55%, transparent);
opacity: 0;
transition: opacity 200ms ease;
}
.side-panel-drawer {
position: absolute;
top: 0;
right: 0;
bottom: 0;
width: min(42em, 95vw);
background: var(--bg-elev);
border-left: 1px solid var(--purple-dim);
display: flex;
flex-direction: column;
transform: translateX(100%);
transition: transform 220ms ease;
overflow: hidden;
}
.side-panel.open { pointer-events: auto; }
.side-panel.open .side-panel-backdrop { opacity: 1; }
.side-panel.open .side-panel-drawer { transform: translateX(0); }
.side-panel-resize {
position: absolute;
left: 0;
top: 0;
bottom: 0;
width: 6px;
cursor: ew-resize;
opacity: 0;
transition: opacity 120ms;
background: var(--purple-dim);
}
.side-panel-resize:hover,
body.side-panel-resizing .side-panel-resize {
opacity: 1;
}
body.side-panel-resizing .side-panel-resize {
background: var(--purple);
}
body.side-panel-resizing {
user-select: none;
}
body.side-panel-resizing * { cursor: ew-resize !important; }
.side-panel-head {
display: flex;
align-items: center;
gap: 0.6em;
padding: 0.6em 1em;
border-bottom: 1px solid var(--border);
flex: none;
}
.side-panel-title {
flex: 1;
font-size: 0.88em;
color: var(--muted);
letter-spacing: 0.04em;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.side-panel-close {
display: flex;
align-items: center;
justify-content: center;
background: none;
border: 1px solid var(--border);
border-radius: 3px;
color: var(--muted);
font-size: 1em;
line-height: 1;
padding: 0.15em 0.4em;
cursor: pointer;
transition: border-color 0.15s ease, color 0.15s ease;
}
.side-panel-close:hover { border-color: var(--red); color: var(--red); }
.side-panel-body {
flex: 1;
overflow-y: auto;
padding: 0.8em 1em;
font-size: 0.88em;
}
.side-panel-body .md { color: var(--fg); line-height: 1.5; }
.side-panel-body .md > :first-child { margin-top: 0; }
.side-panel-body .md > :last-child { margin-bottom: 0; }
.side-panel-body .md p { margin: 0.5em 0; }
.side-panel-body .md h1,
.side-panel-body .md h2,
.side-panel-body .md h3,
.side-panel-body .md h4 { color: var(--purple); margin: 0.9em 0 0.4em; }
.side-panel-body .md code {
background: var(--bg);
border: 1px solid var(--border);
border-radius: 2px;
padding: 0.1em 0.3em;
font-size: 0.9em;
}
.side-panel-body .md pre {
background: var(--crust);
border: 1px solid var(--border);
padding: 0.5em 0.7em;
overflow-x: auto;
margin: 0.5em 0;
}
.side-panel-body .md pre code { background: none; border: none; padding: 0; }
.side-panel-body .md a { color: var(--cyan); }
.side-panel-body .md ul,
.side-panel-body .md ol { margin: 0.4em 0; padding-left: 1.5em; }
.side-panel-body .md blockquote {
border-left: 3px solid var(--purple-dim);
padding-left: 0.8em;
margin: 0.4em 0;
color: var(--muted);
}
.side-panel-body .md table { border-collapse: collapse; margin: 0.5em 0; }
.side-panel-body .md th,
.side-panel-body .md td {
border: 1px solid var(--border);
padding: 0.2em 0.5em;
}
/* ─── page-content gutter ─────────────────────────────────────────────
Inverted-gutter layout: the dashboard + H0M3 keep `<body>` full-bleed
and wrap their padded content in a single `.page-content` div that
carries the horizontal gutter. Full-width chrome (the tab strip, the
server-warnings banner, the footer) then spans edge-to-edge for free,
with no negative-margin breakout hacks. Only matches on pages that opt
in by adding the wrapper (dashboard.html, index.html); FL0W / L0GS /
ST4TS / S3TT1NGS are full-bleed with their own `.<page>-main` padding. */
.page-content { padding: 0 1.5em; }
/* ─── server warnings banner ──────────────────────────────────────────
Generic top-of-page strip, injected at the top of <body> by
common.js (renderServerWarnings) on every page. One row per warning;
colour comes from the per-warning `level` (warn = amber, crit = red).
`position: sticky; top: 0` keeps it pinned above the page chrome so an
operator sees it on any page no matter how far they've scrolled. */
.server-warnings {
position: sticky;
top: 0;
z-index: 50;
}
/* The bar is prepended as a direct child of <body> and spans the full
width edge-to-edge. The dashboard + H0M3 are full-bleed at the body
level — their 1.5em horizontal gutter lives on an inner `.page-content`
wrapper (see below), so the banner is full-width for free with no
breakout. FL0W + L0GS are full-bleed too. */
.server-warnings[hidden] { display: none; }
.server-warn {
text-align: center;
padding: 0.4em 1em;
font-size: 0.85em;
font-weight: bold;
letter-spacing: 0.02em;
}
/* Tint composited over the opaque elevated-surface grey (not transparent):
the bar is `position: sticky`, so a see-through background would let
page content scroll through behind it. */
.server-warn-warn {
color: var(--amber);
background: color-mix(in srgb, var(--amber) 16%, var(--bg-elev));
border-bottom: 1px solid var(--amber);
}
.server-warn-crit {
color: var(--red);
background: color-mix(in srgb, var(--red) 18%, var(--bg-elev));
border-bottom: 1px solid var(--red);
}
/* ─── stats table ─────────────────────────────────────────────────
Shared right-aligned numeric table. Used by /stats.html (busiest
agents) and the dashboard SYST3M C0NT41N3R L04D table — moved here
from dashboard.css when ST4TS became its own page. */
.hive-stats-table {
width: 100%;
border-collapse: collapse;
font-size: 0.85rem;
margin-bottom: 8px;
}
.hive-stats-table th,
.hive-stats-table td {
border-bottom: 1px solid var(--border);
padding: 5px 8px;
text-align: right;
}
.hive-stats-table th:first-child,
.hive-stats-table td:first-child {
text-align: left;
}
.hive-stats-table th {
color: var(--muted);
font-weight: normal;
}
.hive-stats-table td.num {
font-variant-numeric: tabular-nums;
}