Drop the H0M3 tile hub and the core page. The dashboard is now the index; kept state is its fifth tab. A pages menu in the chrome links flow, stats, API docs and the swarm UI. The swarm apex URL comes from services.hyperhive.swarm.domain via HIVE_SWARM_PUBLIC_URL and the new swarm_public_url field of the state snapshot.
646 lines
18 KiB
CSS
646 lines
18 KiB
CSS
/* 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";
|
|
/* Shared pill/chip/badge shape (the "extract pill component" issue,
|
|
resolved as shared CSS rather than a JS component) — see pill.css
|
|
itself for the classnames it covers. */
|
|
@import "@hive/shared/pill.css";
|
|
|
|
/* ─── global typography ─────────────────────────────────────────────
|
|
Element-level rules shared across every page. 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 ─────────────────────────────────────────────────
|
|
Semantic colour variants only — shape comes from `hive-pill-sm`
|
|
(shared with the agent UI's pill/chip family, @hive/shared/pill.css),
|
|
carried directly in the markup alongside each of these classnames
|
|
(agents.js/kept.js/builds.js). Used on container rows and build-log
|
|
rows. */
|
|
.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-loose-ends {
|
|
color: var(--purple);
|
|
border-color: var(--purple);
|
|
text-shadow: 0 0 6px color-mix(in srgb, var(--purple) 40%, transparent);
|
|
}
|
|
/* Paused agent: turn loop parked, container still up. Clickable (btn-inline):
|
|
clicking sends POST /api/resume/{name} so the badge doubles as a resume button. */
|
|
.badge-paused {
|
|
color: var(--yellow);
|
|
border-color: var(--yellow);
|
|
background: color-mix(in srgb, var(--yellow) 10%, transparent);
|
|
}
|
|
/* Active Claude model badge on dashboard container rows. */
|
|
.badge-model {
|
|
color: var(--blue);
|
|
border-color: var(--blue);
|
|
opacity: 0.8;
|
|
}
|
|
/* 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. */
|
|
.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-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;
|
|
}
|
|
/* Same yellow as .badge-paused (the paused-state pill) — pause enters
|
|
that state, so the trigger and the resulting badge read as one
|
|
colour, not two unrelated ones. Resume reads as green like
|
|
.btn-start: both are "go" actions, back to the turn loop running. */
|
|
.btn-pause {
|
|
color: var(--yellow);
|
|
border-color: var(--yellow);
|
|
font-size: 0.75em;
|
|
padding: 0.15em 0.5em;
|
|
margin-left: 0.6em;
|
|
}
|
|
.btn-resume {
|
|
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/dashboard.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);
|
|
}
|
|
|
|
/* ─── 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. */
|
|
|
|
/* `#msgflow` is a shared `.live` pane inside `.terminal-wrap`. The
|
|
msgrow / msg-* rules below power 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, applied config) opens in
|
|
a drawer that swipes in from the right — used by both /flow.html and
|
|
the dashboard. The drawer chrome itself (backdrop, drawer, resize
|
|
handle, header, title, close button) is now the shared
|
|
`<hive-side-panel>` element (@hive/shared/side-panel.js); what's left
|
|
here is the drag cursor-override escape hatch (has to be a global,
|
|
light-DOM rule — it reaches every element on the page during a drag,
|
|
not just the shared element's own shadow tree) and the styling for
|
|
the panel's own content types, which the shared element's shadow tree
|
|
can never reach (see @hive/shared/side-panel/hive-side-panel.css's
|
|
header comment). The `hive-side-panel .md …` rules below use the
|
|
element's own tag name as the selector root — no compatibility class
|
|
needed, the tag name already uniquely identifies the light-DOM
|
|
instance `common.js`'s `sidePanel` creates. */
|
|
body.side-panel-resizing {
|
|
user-select: none;
|
|
}
|
|
body.side-panel-resizing * {
|
|
cursor: ew-resize !important;
|
|
}
|
|
hive-side-panel .md {
|
|
color: var(--fg);
|
|
line-height: 1.5;
|
|
}
|
|
hive-side-panel .md > :first-child {
|
|
margin-top: 0;
|
|
}
|
|
hive-side-panel .md > :last-child {
|
|
margin-bottom: 0;
|
|
}
|
|
hive-side-panel .md p {
|
|
margin: 0.5em 0;
|
|
}
|
|
hive-side-panel .md h1,
|
|
hive-side-panel .md h2,
|
|
hive-side-panel .md h3,
|
|
hive-side-panel .md h4 {
|
|
color: var(--purple);
|
|
margin: 0.9em 0 0.4em;
|
|
}
|
|
hive-side-panel .md code {
|
|
background: var(--bg);
|
|
border: 1px solid var(--border);
|
|
border-radius: 2px;
|
|
padding: 0.1em 0.3em;
|
|
font-size: 0.9em;
|
|
}
|
|
hive-side-panel .md pre {
|
|
background: var(--crust);
|
|
border: 1px solid var(--border);
|
|
padding: 0.5em 0.7em;
|
|
overflow-x: auto;
|
|
margin: 0.5em 0;
|
|
}
|
|
hive-side-panel .md pre code {
|
|
background: none;
|
|
border: none;
|
|
padding: 0;
|
|
}
|
|
hive-side-panel .md a {
|
|
color: var(--cyan);
|
|
}
|
|
hive-side-panel .md ul,
|
|
hive-side-panel .md ol {
|
|
margin: 0.4em 0;
|
|
padding-left: 1.5em;
|
|
}
|
|
hive-side-panel .md blockquote {
|
|
border-left: 3px solid var(--purple-dim);
|
|
padding-left: 0.8em;
|
|
margin: 0.4em 0;
|
|
color: var(--muted);
|
|
}
|
|
hive-side-panel .md table {
|
|
border-collapse: collapse;
|
|
margin: 0.5em 0;
|
|
}
|
|
hive-side-panel .md th,
|
|
hive-side-panel .md td {
|
|
border: 1px solid var(--border);
|
|
padding: 0.2em 0.5em;
|
|
}
|
|
|
|
/* ─── page-content gutter ─────────────────────────────────────────────
|
|
Inverted-gutter layout: the dashboard keeps `<body>` full-bleed
|
|
and wraps 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 (index.html); FL0W / ST4TS
|
|
are full-bleed with their own `.<page>-main` padding. */
|
|
.page-content {
|
|
padding: 0 1.5em;
|
|
}
|
|
|
|
/* ─── sticky top region ───────────────────────────────────────────────
|
|
One sticky container per page holding the warning banner above the
|
|
page chrome (tab bar / page header). common.js (ensureStickyTop)
|
|
wraps the page's chrome in this on first render and injects the banner
|
|
as its first child, so the banner + chrome stack in a single sticky
|
|
context — pinned together — instead of two separate `top:0` stickies
|
|
colliding (the banner used to overlay the tab bar). */
|
|
.sticky-top {
|
|
position: sticky;
|
|
top: 0;
|
|
z-index: 50;
|
|
}
|
|
|
|
/* ─── server warnings banner ──────────────────────────────────────────
|
|
Generic top-of-page strip, injected as the first child of `.sticky-top`
|
|
by common.js (renderServerWarnings) on every page. One row per warning;
|
|
colour comes from the per-warning `level` (warn = amber, crit = red).
|
|
Stickiness lives on the `.sticky-top` wrapper, not here. The bar spans
|
|
the full width edge-to-edge; the dashboard is full-bleed at the
|
|
body level (their 1.5em gutter lives on an inner `.page-content`
|
|
wrapper), so the banner is full-width for free. */
|
|
.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) — 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;
|
|
}
|