Simplify terminal message shape to a uniform TermMsg

Move terminal-row classification server-side into a new
hive-agent/src/term_msg.rs, replacing the old JSON-mutation
enrich()/stamped-field approach in stream_enrich.rs with one
uniform wire shape: {icon?, level: debug|info|warn|error, summary,
body?, body_format?: markdown|diff, coalesce_key?}. No more per-row
`kind` tag or raw claude-JSON passthrough — every row is the same
shape, with structural identity carried by icon + summary text
instead of a CSS class per row kind.

hive-agent/src/web_ui/stream.rs's history + SSE endpoints now both
call term_msg::classify() and serve TermEnvelope{ts, seq?, msgs}
frames; events that classify to zero rows (agent-state changes,
drop-noise) never reach the wire.

Frontend: classifyEvent.ts collapses from a large per-tool dispatch
tree to a thin TermMsg -> StreamRow adapter. streamRow.ts/Row.tsx
drop the now-dead meta/childText fields. terminal.css switches from
a dozen-odd per-row-kind classes to four level-based color rules.
Expand/collapse of a bodied row is now a uniform client-side
decision (the operator's preference), no server-side per-tool
override.

docs/terminal-rendering.md rewritten to match.
This commit is contained in:
iris 2026-08-30 21:11:22 +02:00
commit 5eefaa951d
13 changed files with 886 additions and 628 deletions

View file

@ -97,55 +97,34 @@
display: inline-block;
width: 1.4em;
}
/* Row-kind colours. Pages register renderers that emit these classes;
any class no page emits is just dead CSS, which is fine. Turn-framing
classes carry their signal entirely on the coloured border-left rule
no bold, no top/bottom margins, no background tint. The chrome was
overweight for what's just a "this is a boundary" marker. */
.live .turn-start { color: var(--amber); border-left-color: var(--amber); }
/* turn-body is a child block under turn-start carrying the wake-prompt
body; reset text-indent so wrapped content stays under its own column
instead of pulling back into the parent's prefix. */
.live .turn-body { color: var(--fg); text-indent: 0; margin-top: 0.15em; }
/* Any child block (markdown body, nested details) resets the parent
row's hanging indent so the content lays out from column 0 of the
body area. */
.live .row .md, .live .row > details { text-indent: 0; }
.live .turn-end-ok { color: var(--green); border-left-color: var(--green); }
.live .turn-end-fail { color: var(--red); border-left-color: var(--red); }
/* Wall-clock time (+ duration on turn-end) appended to the turn-start /
turn-end rows. Dim + smaller so the boundary glyph stays the focus and
the timestamp reads as metadata. */
.live .turn-time { color: var(--muted); font-size: 0.85em; margin-left: 0.5em; }
.live .text { color: var(--fg); }
.live .thinking { color: var(--muted); font-style: italic; }
.live .tool-use { color: var(--cyan); }
.live .tool-result { color: var(--muted); }
.live .tool-result.error { color: var(--red); }
.live .tool-result-block.error { color: var(--red); }
.live .result { color: var(--green); }
.live .note { color: var(--muted); }
/* Distinguish stderr lines (orange) and operator-initiated notes
(mauve, lightly emphasised) from ambient harness chatter so the
eye picks out anomalies + operator actions in the scrollback. */
.live .note.stderr { color: var(--amber); }
.live .note.op { color: var(--purple); font-style: italic; }
/* The .sys catch-all fires when renderStream landed an event shape it
couldn't classify. Make it visually loud so silently-dropped event
types surface for follow-up. */
.live .sys { color: var(--amber); }
.live .unread-badge {
color: var(--amber);
font-weight: normal;
margin-left: 0.6em;
font-size: 0.85em;
text-shadow: 0 0 6px color-mix(in srgb, var(--amber) 55%, transparent);
animation: badge-pulse 1.4s ease-in-out infinite;
}
/* Row colours, keyed by severity `level` (hive-agent's `term_msg.rs`),
not by row kind any more mara's terminal-message redesign dropped
the server-side `kind` tag (turn-start/tool-use/tool-result/etc) in
favour of one uniform shape, `{icon, level, summary, body,
body_format, coalesce_key}`. What used to be a dozen-odd per-kind
classes (`.turn-start`, `.tool-use`, `.tool-result.error`, `.sys`, )
is now four: the four severities every row already carries. Structural
identity (this is a turn boundary, this is a tool call) is carried by
the row's icon (``, `🔧`, ``, ) and summary text instead of colour
see docs/terminal-rendering.md. */
.live .level-debug { color: var(--muted); }
.live .level-info { color: var(--fg); }
.live .level-warn { color: var(--amber); border-left-color: var(--amber); }
.live .level-error { color: var(--red); border-left-color: var(--red); }
/* `badge-pulse` itself is no longer used by any terminal row (the
turn-start `unread` count it animated is gone see term_msg.rs's
module doc), but agent.css's `.state-badge.state-thinking`/
`.state-compacting` badges still reuse this keyframe via
`@import "@hive/shared/terminal.css"` keep the definition here, drop
only the terminal-specific `.unread-badge` selector that used it. */
@keyframes badge-pulse {
0%, 100% { opacity: 1; text-shadow: 0 0 6px color-mix(in srgb, var(--amber) 55%, transparent); }
50% { opacity: 0.7; text-shadow: 0 0 14px color-mix(in srgb, var(--amber) 95%, transparent); }
}
/* Any child block (markdown body, nested details) resets the parent
row's hanging indent so the content lays out from column 0 of the
body area. */
.live .row .md, .live .row > details { text-indent: 0; }
/* "↓ N new" pill: shown when new rows arrive while the operator is
scrolled up; click to jump to bottom. Positioned by the wrapper's
`position: relative` (terminal-wrap supplies it; pages that skip the