feat(web-ui): improve Agents page legibility and scannability

Targeted UI/UX polish on the Agents page, keeping the existing dark
aesthetic and both Overview/Live view modes:

- Add a readable --text-mute token (dark + light) and apply it to the
  summary chips, lane meta, and idle/offline status, which previously
  used the near-invisible --text-dim.
- Event feed: demote the generic "Span Started/Completed" label to a
  quiet mono category tag and promote the tool name, with a left-edge
  accent by event kind (run/span/error/session). Scoped to
  #agents-content so other pages' feeds are unaffected.
- Active-op pills: add a per-kind left accent bar (tool/subagent/run).
- Lane sparkline: raise opacity and add a gradient so it actually reads.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
William Valentin
2026-06-27 10:35:33 -07:00
parent 478c7529a7
commit 69eb87ebc9
2 changed files with 52 additions and 9 deletions
+12 -3
View File
@@ -46,6 +46,14 @@ let _agentsRenderTimer = null;
// ── Private helpers ────────────────────────────────────── // ── Private helpers ──────────────────────────────────────
function eventKindClass(eventType) {
if (eventType === 'run.start' || eventType === 'run.end') return 'evt-run';
if (eventType === 'span.start' || eventType === 'span.end') return 'evt-span';
if (eventType === 'error') return 'evt-error';
if (eventType === 'session.start' || eventType === 'session.end') return 'evt-session';
return 'evt-other';
}
function ensureAgentBucket(evt) { function ensureAgentBucket(evt) {
const identity = getAgentIdentity(evt); const identity = getAgentIdentity(evt);
if (!identity.key) return null; if (!identity.key) return null;
@@ -258,7 +266,8 @@ function renderAgentLanes() {
const opsHTML = ops.length > 0 ? `<div class="active-ops">${ops.map(op => { const opsHTML = ops.length > 0 ? `<div class="active-ops">${ops.map(op => {
const elapsed = Math.floor((Date.now() - op.startedAt) / 1000); const elapsed = Math.floor((Date.now() - op.startedAt) / 1000);
const stale = elapsed > 300; const stale = elapsed > 300;
const kindClass = op.kind === 'agent' || op.subType === 'subagent' ? ' subagent' : ''; const kindClass = op.kind === 'agent' || op.subType === 'subagent' ? ' subagent'
: op.kind === 'run' ? ' run' : '';
return ` return `
<div class="active-op${stale ? ' stale' : ''}${kindClass}"> <div class="active-op${stale ? ' stale' : ''}${kindClass}">
<span class="active-op-dot"></span> <span class="active-op-dot"></span>
@@ -276,7 +285,7 @@ function renderAgentLanes() {
const expandHTML = details ? '<button class="timeline-expand-hint" type="button">details</button>' : ''; const expandHTML = details ? '<button class="timeline-expand-hint" type="button">details</button>' : '';
return ` return `
<div class="timeline-event"> <div class="timeline-event ${eventKindClass(eventType)}">
<div class="timeline-event-header"> <div class="timeline-event-header">
${getEventIcon(eventType)} ${getEventIcon(eventType)}
<span class="timeline-event-type">${escapeHTML(getEventLabel(eventType))}</span> <span class="timeline-event-type">${escapeHTML(getEventLabel(eventType))}</span>
@@ -675,7 +684,7 @@ function renderAgentsLive() {
</div> </div>
<div class="live-run-events"> <div class="live-run-events">
${group.events.map(evt => ` ${group.events.map(evt => `
<div class="timeline-event live-event"> <div class="timeline-event live-event ${eventKindClass(getEnvelopeType(evt))}">
<div class="timeline-event-header"> <div class="timeline-event-header">
${getEventIcon(getEnvelopeType(evt))} ${getEventIcon(getEnvelopeType(evt))}
<span class="timeline-event-type">${escapeHTML(getEventLabel(getEnvelopeType(evt)))}</span> <span class="timeline-event-type">${escapeHTML(getEventLabel(getEnvelopeType(evt)))}</span>
+40 -6
View File
@@ -12,6 +12,7 @@
--text: #b8c5d4; --text: #b8c5d4;
--text-dim: #465a6e; --text-dim: #465a6e;
--text-mute: #6b7f94;
--text-bright: #e4edf5; --text-bright: #e4edf5;
--accent: #22d3ee; --accent: #22d3ee;
@@ -2147,6 +2148,7 @@ tr.expandable:hover .expand-icon::before {
--text: #3d4a5c; --text: #3d4a5c;
--text-dim: #8b9ab0; --text-dim: #8b9ab0;
--text-mute: #5c6b80;
--text-bright: #1a2332; --text-bright: #1a2332;
--accent: #0891b2; --accent: #0891b2;
@@ -2270,7 +2272,8 @@ tr.expandable:hover .expand-icon::before {
padding: 0.6rem 1rem; padding: 0.6rem 1rem;
font-family: var(--font-mono); font-family: var(--font-mono);
font-size: 0.78rem; font-size: 0.78rem;
color: var(--text-dim); color: var(--text-mute);
letter-spacing: 0.02em;
display: flex; display: flex;
align-items: center; align-items: center;
gap: 0.5rem; gap: 0.5rem;
@@ -2333,7 +2336,7 @@ tr.expandable:hover .expand-icon::before {
margin-top: 0.2rem; margin-top: 0.2rem;
font-family: var(--font-mono); font-family: var(--font-mono);
font-size: 0.68rem; font-size: 0.68rem;
color: var(--text-dim); color: var(--text-mute);
} }
/* ── Agent Lane Sparklines ────────────────────────────────── */ /* ── Agent Lane Sparklines ────────────────────────────────── */
@@ -2347,10 +2350,10 @@ tr.expandable:hover .expand-icon::before {
.agent-lane-sparkline-bar { .agent-lane-sparkline-bar {
flex: 1; flex: 1;
background: var(--accent); background: linear-gradient(to top, var(--accent), var(--accent-glow));
border-radius: 1px 1px 0 0; border-radius: 1px 1px 0 0;
opacity: 0.5; opacity: 0.8;
min-height: 1px; min-height: 2px;
} }
.agent-lane-dot { .agent-lane-dot {
@@ -2374,7 +2377,7 @@ tr.expandable:hover .expand-icon::before {
.agent-lane-status { .agent-lane-status {
font-size: 0.7rem; font-size: 0.7rem;
font-weight: 600; font-weight: 600;
color: var(--text-dim); color: var(--text-mute);
text-transform: uppercase; text-transform: uppercase;
letter-spacing: 0.06em; letter-spacing: 0.06em;
} }
@@ -2399,6 +2402,7 @@ tr.expandable:hover .expand-icon::before {
padding: 0.35rem 0.625rem; padding: 0.35rem 0.625rem;
background: var(--accent-dim); background: var(--accent-dim);
border: 1px solid rgba(34, 211, 238, 0.15); border: 1px solid rgba(34, 211, 238, 0.15);
border-left: 2px solid var(--accent);
border-radius: var(--radius); border-radius: var(--radius);
font-size: 0.75rem; font-size: 0.75rem;
animation: fadeUp 0.2s ease both; animation: fadeUp 0.2s ease both;
@@ -2406,11 +2410,17 @@ tr.expandable:hover .expand-icon::before {
[data-theme="light"] .active-op { [data-theme="light"] .active-op {
border-color: rgba(8, 145, 178, 0.2); border-color: rgba(8, 145, 178, 0.2);
border-left-color: var(--accent);
}
.active-op.run {
border-left-color: var(--accent);
} }
.active-op.subagent { .active-op.subagent {
background: rgba(167, 139, 250, 0.12); background: rgba(167, 139, 250, 0.12);
border-color: rgba(167, 139, 250, 0.22); border-color: rgba(167, 139, 250, 0.22);
border-left-color: var(--purple);
} }
.active-op.stale { .active-op.stale {
@@ -2482,6 +2492,7 @@ tr.expandable:hover .expand-icon::before {
border-radius: var(--radius); border-radius: var(--radius);
margin-bottom: 0.25rem; margin-bottom: 0.25rem;
border: 1px solid var(--border-soft); border: 1px solid var(--border-soft);
border-left: 2px solid var(--border);
background: transparent; background: transparent;
font-size: 0.82rem; font-size: 0.82rem;
} }
@@ -2494,6 +2505,29 @@ tr.expandable:hover .expand-icon::before {
font-size: 0.6rem; font-size: 0.6rem;
} }
/* ── Event-kind accents + hierarchy (scoped to Agents page) ── */
#agents-content .timeline-event.evt-run { border-left-color: var(--accent); }
#agents-content .timeline-event.evt-error { border-left-color: var(--error); }
#agents-content .timeline-event.evt-session { border-left-color: var(--success); }
#agents-content .timeline-event.evt-span { border-left-color: rgba(34, 211, 238, 0.4); }
/* Demote the generic type label to a quiet category tag; let the
tool name / preview body carry the line. */
#agents-content .timeline-event-type {
font-family: var(--font-mono);
font-size: 0.62rem;
font-weight: 600;
text-transform: uppercase;
letter-spacing: 0.06em;
color: var(--text-mute);
}
#agents-content .timeline-event-body.tool-name,
#agents-content .timeline-event-body.subagent-name {
font-size: 0.82rem;
font-weight: 500;
}
.agent-lane-events .empty-state { .agent-lane-events .empty-state {
padding: 2rem 1rem; padding: 2rem 1rem;
font-size: 0.78rem; font-size: 0.78rem;