agents constantly emit pointer strings to /agents/<n>/state/foo.md since broker bodies cap at 1 KiB. now those tokens linkify in the message flow, question bodies, answer text, and operator inbox; clicking expands an inline <details> that lazy-fetches via the new /api/state-file?path=... endpoint. endpoint allow-list: per-agent state dirs + shared docs, both in their container-mount form (/agents/<n>/state, /shared) and host form (/var/lib/hyperhive/...). 1 MiB read cap; canonicalises before the prefix check so `..` / symlinks can't escape. legacy bare `/state/...` is deliberately not matched — ambiguous from the host's perspective (we'd need to know which agent the message references to translate). agents should use the qualified form going forward.
684 lines
19 KiB
CSS
684 lines
19 KiB
CSS
/* Palette + base body typography live in hive-fr0nt::BASE_CSS, prepended
|
|
to this stylesheet by `serve_css` at runtime. */
|
|
body {
|
|
max-width: 70em;
|
|
margin: 1.5em auto;
|
|
padding: 0 1.5em;
|
|
}
|
|
.banner {
|
|
text-align: center;
|
|
margin: 0 0 1em 0;
|
|
font-size: 0.95em;
|
|
overflow-x: auto;
|
|
background: linear-gradient(
|
|
90deg,
|
|
var(--purple-dim) 0%,
|
|
var(--purple) 50%,
|
|
var(--purple-dim) 100%
|
|
);
|
|
background-size: 200% 100%;
|
|
background-position: 50% 0;
|
|
-webkit-background-clip: text;
|
|
background-clip: text;
|
|
color: transparent;
|
|
filter: drop-shadow(0 0 6px rgba(203, 166, 247, 0.45));
|
|
}
|
|
.banner.active {
|
|
animation: banner-shimmer 1.8s linear infinite;
|
|
}
|
|
@keyframes banner-shimmer {
|
|
from { background-position: 200% 0; }
|
|
to { background-position: -100% 0; }
|
|
}
|
|
h1, h2 {
|
|
color: var(--purple);
|
|
text-transform: uppercase;
|
|
letter-spacing: 0.15em;
|
|
margin-top: 2em;
|
|
text-shadow: 0 0 8px rgba(203, 166, 247, 0.4);
|
|
}
|
|
.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 rgba(137, 220, 235, 0.5);
|
|
}
|
|
a:hover {
|
|
color: var(--fg);
|
|
text-shadow: 0 0 12px rgba(137, 220, 235, 0.9);
|
|
}
|
|
.role {
|
|
display: inline-block;
|
|
margin-left: 0.4em;
|
|
padding: 0.05em 0.5em;
|
|
border: 1px solid;
|
|
border-radius: 2px;
|
|
font-size: 0.8em;
|
|
letter-spacing: 0.1em;
|
|
text-transform: uppercase;
|
|
}
|
|
.role-m1nd { color: var(--pink); border-color: var(--pink); background: rgba(245, 194, 231, 0.08); }
|
|
.role-ag3nt { color: var(--amber); border-color: var(--amber); background: rgba(250, 179, 135, 0.08); }
|
|
/* Container rows: identity + meta on a flowing first line, action
|
|
buttons grouped on a second. Pending rows dim everything except
|
|
the pending-state indicator. */
|
|
.containers { display: flex; flex-direction: column; gap: 0.4em; }
|
|
.container-row {
|
|
padding: 0.6em 0.8em;
|
|
border: 1px solid var(--border);
|
|
border-radius: 4px;
|
|
background: rgba(24, 24, 37, 0.55);
|
|
transition: opacity 200ms ease, border-color 200ms ease;
|
|
}
|
|
.container-row.pending {
|
|
border-color: var(--amber);
|
|
background: rgba(250, 179, 135, 0.05);
|
|
}
|
|
.container-row.pending .actions { opacity: 0.4; pointer-events: none; }
|
|
.container-row .head {
|
|
display: flex;
|
|
align-items: center;
|
|
flex-wrap: wrap;
|
|
gap: 0.5em;
|
|
margin-bottom: 0.4em;
|
|
}
|
|
.container-row .head .name {
|
|
font-size: 1.05em;
|
|
font-weight: bold;
|
|
}
|
|
.container-row .head .meta { margin-left: auto; }
|
|
.container-row .actions {
|
|
display: flex;
|
|
flex-wrap: wrap;
|
|
gap: 0.4em;
|
|
}
|
|
.container-row .actions form.inline { display: inline-block; margin: 0; }
|
|
.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 rgba(250, 179, 135, 0.5);
|
|
}
|
|
.badge-muted {
|
|
color: var(--muted); border-color: var(--purple-dim);
|
|
background: rgba(127, 132, 156, 0.08);
|
|
}
|
|
.container-row.tombstone {
|
|
border-style: dashed;
|
|
background: rgba(24, 24, 37, 0.35);
|
|
opacity: 0.85;
|
|
}
|
|
.container-row.tombstone .name { color: var(--muted); }
|
|
/* Per-container journald viewer: collapsed by default, fetches
|
|
lazily on expand. The output is in monospace inside a bordered
|
|
<pre>; controls (unit select + refresh) sit above. */
|
|
.journal {
|
|
margin-top: 0.5em;
|
|
font-size: 0.85em;
|
|
}
|
|
.journal > summary {
|
|
cursor: pointer;
|
|
color: var(--muted);
|
|
letter-spacing: 0.05em;
|
|
}
|
|
.journal > summary:hover { color: var(--cyan); }
|
|
.journal .journal-body {
|
|
margin-top: 0.4em;
|
|
padding-top: 0.4em;
|
|
border-top: 1px dashed var(--border);
|
|
}
|
|
.journal-controls {
|
|
display: flex;
|
|
gap: 0.5em;
|
|
margin-bottom: 0.4em;
|
|
align-items: center;
|
|
}
|
|
.journal-unit {
|
|
font-family: inherit;
|
|
font-size: 0.9em;
|
|
background: var(--bg-elev);
|
|
color: var(--fg);
|
|
border: 1px solid var(--border);
|
|
padding: 0.2em 0.4em;
|
|
}
|
|
.journal-refresh { font-size: 0.75em; padding: 0.15em 0.5em; }
|
|
.journal-output {
|
|
margin: 0;
|
|
background: #11111b;
|
|
color: var(--fg);
|
|
border: 1px solid var(--purple-dim);
|
|
padding: 0.5em 0.7em;
|
|
max-height: 24em;
|
|
overflow: auto;
|
|
font-size: 0.85em;
|
|
line-height: 1.4;
|
|
white-space: pre;
|
|
word-break: normal;
|
|
}
|
|
|
|
/* Notification controls — sit between the banner and the
|
|
containers section. Hidden by JS when notifications are
|
|
unsupported, denied, or already in the right state. */
|
|
/* Port-collision banner: appears above the containers list when
|
|
two sub-agents hash to the same web UI port. Critical — without
|
|
resolution, one of the harnesses will restart-loop on
|
|
AddrInUse. */
|
|
.port-conflict {
|
|
background: rgba(243, 139, 168, 0.08);
|
|
border: 1px solid var(--red);
|
|
color: var(--red);
|
|
padding: 0.5em 0.8em;
|
|
margin-bottom: 0.6em;
|
|
border-radius: 4px;
|
|
text-shadow: 0 0 6px rgba(243, 139, 168, 0.4);
|
|
animation: questions-pulse 2.4s ease-in-out infinite;
|
|
}
|
|
.port-conflict strong { color: var(--red); }
|
|
|
|
.notif-row {
|
|
display: flex;
|
|
gap: 0.5em;
|
|
align-items: center;
|
|
margin: 0.5em 0;
|
|
font-size: 0.85em;
|
|
}
|
|
.btn-notif {
|
|
font-family: inherit;
|
|
font-size: 0.85em;
|
|
background: transparent;
|
|
color: var(--cyan);
|
|
border: 1px solid var(--cyan);
|
|
padding: 0.2em 0.7em;
|
|
border-radius: 999px;
|
|
cursor: pointer;
|
|
text-shadow: 0 0 4px currentColor;
|
|
}
|
|
.btn-notif:hover {
|
|
background: rgba(137, 220, 235, 0.1);
|
|
box-shadow: 0 0 10px -2px currentColor;
|
|
}
|
|
|
|
.pending-state {
|
|
color: var(--amber);
|
|
font-size: 0.85em;
|
|
letter-spacing: 0.08em;
|
|
text-transform: uppercase;
|
|
text-shadow: 0 0 6px rgba(250, 179, 135, 0.55);
|
|
animation: badge-pulse 1.6s ease-in-out infinite;
|
|
}
|
|
@keyframes badge-pulse {
|
|
0%, 100% { opacity: 1; }
|
|
50% { opacity: 0.7; }
|
|
}
|
|
.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; }
|
|
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;
|
|
}
|
|
.approvals .row { display: flex; align-items: center; flex-wrap: wrap; gap: 0.4em; }
|
|
.approvals form.inline { display: inline; margin-left: 0.4em; }
|
|
.approval-description { font-size: 0.85em; color: var(--fg-dim, #888); margin: 0.2em 0 0.4em 1.2em; }
|
|
.approval-tabs {
|
|
display: flex;
|
|
gap: 0.4em;
|
|
margin: 0.6em 0 0.4em;
|
|
}
|
|
.approval-tab {
|
|
background: transparent;
|
|
border: 1px solid var(--border);
|
|
color: var(--muted);
|
|
font: inherit;
|
|
font-size: 0.85em;
|
|
letter-spacing: 0.08em;
|
|
padding: 0.25em 0.9em;
|
|
cursor: pointer;
|
|
transition: color 0.15s ease, border-color 0.15s ease, background 0.15s ease;
|
|
}
|
|
.approval-tab:hover { color: var(--fg); }
|
|
.approval-tab.active {
|
|
color: var(--purple);
|
|
border-color: var(--purple);
|
|
background: rgba(203, 166, 247, 0.08);
|
|
text-shadow: 0 0 4px currentColor;
|
|
}
|
|
.approvals-history .status { font-size: 0.85em; padding: 0 0.5em; }
|
|
.status-approved { color: var(--green); }
|
|
.status-denied { color: var(--red); }
|
|
.status-failed { color: var(--amber); }
|
|
.glyph-approved { color: var(--green); }
|
|
.glyph-denied { color: var(--red); }
|
|
.glyph-failed { color: var(--amber); }
|
|
.meta-inputs {
|
|
list-style: none;
|
|
padding: 0;
|
|
margin: 0 0 0.8em;
|
|
display: grid;
|
|
gap: 0.2em;
|
|
}
|
|
.meta-inputs li {
|
|
padding: 0.25em 0.6em;
|
|
border: 1px solid var(--border);
|
|
background: rgba(24, 24, 37, 0.6);
|
|
}
|
|
.meta-inputs label {
|
|
display: flex;
|
|
align-items: baseline;
|
|
gap: 0.5em;
|
|
cursor: pointer;
|
|
font-size: 0.9em;
|
|
}
|
|
.meta-input-name { color: var(--amber); font-weight: bold; }
|
|
.meta-input-rev { color: var(--muted); }
|
|
.meta-input-ts { color: var(--muted); font-size: 0.85em; }
|
|
.meta-input-url {
|
|
color: var(--muted);
|
|
font-size: 0.85em;
|
|
margin-left: auto;
|
|
overflow: hidden;
|
|
text-overflow: ellipsis;
|
|
white-space: nowrap;
|
|
}
|
|
.btn-meta-update {
|
|
background: rgba(203, 166, 247, 0.12);
|
|
border: 1px solid var(--purple);
|
|
color: var(--purple);
|
|
text-shadow: 0 0 4px currentColor;
|
|
padding: 0.3em 1em;
|
|
font: inherit;
|
|
font-size: 0.85em;
|
|
letter-spacing: 0.08em;
|
|
cursor: pointer;
|
|
transition: box-shadow 0.15s ease, background 0.15s ease;
|
|
}
|
|
.btn-meta-update:hover:not([disabled]) {
|
|
background: rgba(203, 166, 247, 0.22);
|
|
box-shadow: 0 0 10px -2px currentColor;
|
|
}
|
|
.btn-meta-update[disabled] {
|
|
opacity: 0.35;
|
|
cursor: not-allowed;
|
|
}
|
|
.history-note {
|
|
margin-left: 1.8em;
|
|
margin-top: 0.2em;
|
|
color: var(--muted);
|
|
font-size: 0.85em;
|
|
white-space: pre-wrap;
|
|
word-break: break-word;
|
|
}
|
|
ul form.inline { display: inline-block; }
|
|
.btn {
|
|
font-family: inherit;
|
|
font-weight: bold;
|
|
text-transform: uppercase;
|
|
letter-spacing: 0.1em;
|
|
background: transparent;
|
|
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: rgba(205, 214, 244, 0.06);
|
|
text-shadow: 0 0 10px currentColor;
|
|
box-shadow: 0 0 10px -2px currentColor;
|
|
}
|
|
.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; }
|
|
.btn-talk { color: var(--cyan); border-color: var(--cyan); }
|
|
.btn-spawn { color: var(--amber); border-color: var(--amber); }
|
|
.spawnform { display: flex; gap: 0.6em; align-items: stretch; margin: 0.5em 0; }
|
|
.spawnform input {
|
|
font-family: inherit;
|
|
font-size: 1em;
|
|
background: var(--bg-elev);
|
|
color: var(--fg);
|
|
border: 1px solid var(--border);
|
|
padding: 0.4em 0.6em;
|
|
flex: 1;
|
|
}
|
|
.spawnform input::placeholder { color: var(--muted); }
|
|
.spawnform input:focus { outline: 1px solid var(--purple); }
|
|
.role-pending { color: var(--amber); border-color: var(--amber); }
|
|
.btn-inline {
|
|
font-family: inherit;
|
|
background: transparent;
|
|
cursor: pointer;
|
|
margin-left: 0.4em;
|
|
}
|
|
.btn-inline:hover { background: rgba(255, 184, 77, 0.1); }
|
|
.kind {
|
|
display: inline-block;
|
|
margin-left: 0.4em;
|
|
padding: 0.05em 0.5em;
|
|
border: 1px solid var(--purple-dim);
|
|
color: var(--purple-dim);
|
|
border-radius: 2px;
|
|
font-size: 0.75em;
|
|
letter-spacing: 0.1em;
|
|
text-transform: uppercase;
|
|
}
|
|
.kind-spawn { color: var(--amber); border-color: var(--amber); }
|
|
.spinner {
|
|
display: inline-block;
|
|
animation: spin 1s linear infinite;
|
|
color: var(--amber);
|
|
}
|
|
@keyframes spin { from { transform: rotate(0deg); } to { transform: rotate(360deg); } }
|
|
.talkform {
|
|
display: flex;
|
|
gap: 0.6em;
|
|
align-items: stretch;
|
|
margin-top: 0.5em;
|
|
}
|
|
.talkform select, .talkform input {
|
|
font-family: inherit;
|
|
font-size: 1em;
|
|
background: var(--bg-elev);
|
|
color: var(--fg);
|
|
border: 1px solid var(--border);
|
|
padding: 0.4em 0.6em;
|
|
}
|
|
.talkform select { color: var(--amber); }
|
|
.talkform input { flex: 1; }
|
|
.talkform input::placeholder { color: var(--muted); }
|
|
.talkform input:focus, .talkform select:focus { outline: 1px solid var(--purple); }
|
|
details { margin-top: 0.5em; }
|
|
summary {
|
|
cursor: pointer;
|
|
color: var(--muted);
|
|
font-size: 0.85em;
|
|
text-transform: uppercase;
|
|
letter-spacing: 0.1em;
|
|
}
|
|
summary:hover { color: var(--purple); }
|
|
.diff {
|
|
background: var(--bg-elev);
|
|
border: 1px solid var(--border);
|
|
padding: 0.8em;
|
|
margin-top: 0.4em;
|
|
overflow-x: auto;
|
|
font-size: 0.85em;
|
|
line-height: 1.4;
|
|
color: var(--muted);
|
|
white-space: pre;
|
|
}
|
|
.diff span { display: block; }
|
|
.diff .diff-add { color: var(--green); }
|
|
.diff .diff-del { color: var(--red); }
|
|
.diff .diff-hunk { color: var(--cyan); }
|
|
.diff .diff-file { color: var(--purple); font-weight: bold; }
|
|
.diff .diff-ctx { color: var(--fg); }
|
|
.questions {
|
|
background: var(--bg-elev);
|
|
border: 1px solid var(--amber);
|
|
box-shadow: 0 0 12px -4px var(--amber);
|
|
padding: 0.6em 0.9em;
|
|
animation: questions-pulse 2.4s ease-in-out infinite;
|
|
}
|
|
@keyframes questions-pulse {
|
|
0%, 100% { box-shadow: 0 0 12px -4px rgba(250, 179, 135, 0.55); }
|
|
50% { box-shadow: 0 0 22px -2px rgba(250, 179, 135, 0.95); }
|
|
}
|
|
/* Path linkification — agents drop pointer strings into messages
|
|
constantly; clicking the anchor expands a sibling <details> that
|
|
lazy-loads from /api/state-file. */
|
|
.path-link {
|
|
color: var(--blue, #89b4fa);
|
|
text-decoration: underline dotted;
|
|
cursor: pointer;
|
|
}
|
|
.path-link:hover { color: var(--amber); }
|
|
.path-preview {
|
|
margin: 0.2em 0 0.4em 1.5em;
|
|
border-left: 2px solid var(--border);
|
|
padding-left: 0.6em;
|
|
}
|
|
.path-preview > summary {
|
|
cursor: pointer;
|
|
color: var(--muted);
|
|
font-size: 0.85em;
|
|
list-style: none;
|
|
user-select: none;
|
|
}
|
|
.path-preview > summary::marker { content: ''; }
|
|
.path-preview-body {
|
|
background: var(--bg);
|
|
border: 1px solid var(--border);
|
|
padding: 0.5em 0.7em;
|
|
margin: 0.3em 0 0;
|
|
max-height: 30em;
|
|
overflow: auto;
|
|
white-space: pre-wrap;
|
|
word-break: break-word;
|
|
font-size: 0.85em;
|
|
color: var(--fg);
|
|
}
|
|
|
|
/* Filter chip row above the questions list. The active chip lights
|
|
up amber to match the rest of the dashboard's selection accents. */
|
|
.questions-filters {
|
|
display: flex;
|
|
flex-wrap: wrap;
|
|
gap: 0.3em;
|
|
margin-bottom: 0.5em;
|
|
}
|
|
.q-filter-chip {
|
|
background: var(--bg);
|
|
color: var(--muted);
|
|
border: 1px solid var(--border);
|
|
border-radius: 999px;
|
|
padding: 0.15em 0.7em;
|
|
font: inherit;
|
|
font-size: 0.85em;
|
|
cursor: pointer;
|
|
}
|
|
.q-filter-chip:hover { color: var(--fg); }
|
|
.q-filter-chip.active {
|
|
color: var(--amber);
|
|
border-color: var(--amber);
|
|
}
|
|
/* Peer (agent-to-agent) question rows get a left rule + dim
|
|
target-name styling so they read distinctly from operator-bound
|
|
threads at a glance. */
|
|
.questions li.question-peer {
|
|
border-left: 2px solid var(--mauve, #cba6f7);
|
|
padding-left: 0.6em;
|
|
}
|
|
.questions .msg-to-peer { color: var(--mauve, #cba6f7); }
|
|
/* The override button on peer threads picks up a non-default colour
|
|
so the operator notices they're answering on someone's behalf. */
|
|
.btn-override { background: var(--mauve, #cba6f7) !important; color: var(--bg) !important; }
|
|
.questions li.question {
|
|
padding: 0.4em 0;
|
|
border-bottom: 1px solid var(--border);
|
|
}
|
|
.questions li.question:last-child { border-bottom: 0; }
|
|
.questions .q-head { font-size: 0.9em; }
|
|
.questions .q-ttl {
|
|
color: var(--amber);
|
|
margin-left: 0.4em;
|
|
font-size: 0.95em;
|
|
letter-spacing: 0.05em;
|
|
}
|
|
.questions .q-body {
|
|
color: var(--fg);
|
|
margin: 0.3em 0;
|
|
white-space: pre-wrap;
|
|
word-break: break-word;
|
|
}
|
|
.qform {
|
|
display: flex;
|
|
flex-direction: column;
|
|
gap: 0.5em;
|
|
margin-top: 0.4em;
|
|
}
|
|
.qform .q-options {
|
|
display: flex;
|
|
flex-direction: column;
|
|
gap: 0.25em;
|
|
background: var(--bg);
|
|
border: 1px solid var(--border);
|
|
border-radius: 4px;
|
|
padding: 0.4em 0.6em;
|
|
}
|
|
.qform .q-option label { cursor: pointer; user-select: none; }
|
|
.qform .q-option input { margin-right: 0.4em; accent-color: var(--amber); }
|
|
.qform .q-free { display: flex; }
|
|
.qform .q-free input {
|
|
flex: 1;
|
|
font-family: inherit;
|
|
font-size: 1em;
|
|
background: var(--bg);
|
|
color: var(--fg);
|
|
border: 1px solid var(--border);
|
|
padding: 0.4em 0.6em;
|
|
}
|
|
.qform .q-free input::placeholder { color: var(--muted); }
|
|
.qform .q-free input:focus { outline: 1px solid var(--amber); }
|
|
.qform button { align-self: flex-start; }
|
|
.qform-cancel { margin-top: 0.3em; }
|
|
.q-history {
|
|
margin-top: 0.8em;
|
|
border: 1px solid var(--border);
|
|
border-radius: 4px;
|
|
padding: 0.4em 0.7em;
|
|
}
|
|
.q-history summary { cursor: pointer; color: var(--muted); font-size: 0.9em; user-select: none; }
|
|
.questions-answered {
|
|
border: none;
|
|
box-shadow: none;
|
|
animation: none;
|
|
padding: 0;
|
|
margin-top: 0.5em;
|
|
}
|
|
.question-answered { opacity: 0.7; }
|
|
.question-answered .q-body { color: var(--muted); margin-bottom: 0.15em; }
|
|
.q-answer { font-size: 0.9em; color: var(--green, #a6e3a1); padding: 0.1em 0 0.4em 0; }
|
|
.q-answer-text { font-style: italic; }
|
|
.inbox {
|
|
background: var(--bg-elev);
|
|
border: 1px solid var(--border);
|
|
padding: 0.5em 0.8em;
|
|
max-height: 24em;
|
|
overflow-y: auto;
|
|
}
|
|
.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` (see
|
|
hive-fr0nt::TERMINAL_CSS). The msgrow / msg-* rules below are
|
|
dashboard-specific: each broker event becomes a grid of timestamp +
|
|
arrow + from/sep/to + body inside the `.row` shell. */
|
|
.live .msgrow { display: grid; grid-template-columns: auto auto auto auto auto 1fr; gap: 0.6em; align-items: baseline; padding: 0.1em 0; }
|
|
.live .msgrow.sent .msg-arrow { color: var(--cyan); }
|
|
.live .msgrow.delivered .msg-arrow { color: var(--green); }
|
|
.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; }
|
|
/* Compose box sits inside `.terminal-wrap`, below the `.live` log. The
|
|
dashed separator mirrors the agent terminal's prompt divider. */
|
|
.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;
|
|
border: none;
|
|
outline: none;
|
|
color: var(--fg);
|
|
font: inherit;
|
|
font-size: 0.85em;
|
|
line-height: 1.5;
|
|
resize: none;
|
|
overflow: hidden;
|
|
min-height: 1.5em;
|
|
caret-color: var(--purple);
|
|
}
|
|
.op-compose-input::placeholder { color: var(--muted); }
|
|
.op-compose-suggest {
|
|
position: absolute;
|
|
bottom: 100%;
|
|
left: 0.8em;
|
|
margin-bottom: 0.2em;
|
|
background: rgba(24, 24, 37, 0.95);
|
|
border: 1px solid var(--border);
|
|
font-size: 0.85em;
|
|
min-width: 12em;
|
|
max-height: 12em;
|
|
overflow-y: auto;
|
|
z-index: 10;
|
|
}
|
|
.op-compose-suggest .item {
|
|
padding: 0.2em 0.8em;
|
|
cursor: pointer;
|
|
color: var(--fg);
|
|
}
|
|
.op-compose-suggest .item.active,
|
|
.op-compose-suggest .item:hover {
|
|
background: rgba(203, 166, 247, 0.18);
|
|
color: var(--purple);
|
|
}
|
|
footer {
|
|
margin-top: 4em;
|
|
text-align: center;
|
|
color: var(--muted);
|
|
font-size: 0.9em;
|
|
}
|
|
footer a { color: var(--purple); }
|