Skip to content

Commit d7a3535

Browse files
acailicclaude
andcommitted
feat: UX polish for Trace tab, Inspect tab, and cross-tab design system
- Trace tab: enhanced session cards with accent borders, hover animations, status indicator dots, replay value pills, replay control grouping, breakpoint stop banner, detail rail section headers, search focus rings - Inspect tab: panel visual hierarchy with panel--accent modifier, section dividers (Analysis, Monitoring, Intelligence), inspector labels, DecisionTree panel polish, coordination and failure cluster accent colors - Global: compact header, spacing scale CSS variables (--space-*), panel base style refinement, empty state polish, snappier transitions - Per-feature GIFs: decision tree, checkpoint replay, failure clustering, multi-agent coordination, and full walkthrough demo Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1 parent 3384580 commit d7a3535

11 files changed

Lines changed: 482 additions & 101 deletions
3.4 MB
Loading
4.14 MB
Loading
4.68 MB
Loading
5.51 MB
Loading

frontend/src/App.css

Lines changed: 422 additions & 26 deletions
Large diffs are not rendered by default.

frontend/src/App.tsx

Lines changed: 41 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -83,9 +83,6 @@ function App() {
8383
} = useSessionStore()
8484

8585
// Local state for items not yet moved to the store
86-
const rollingSummaryData: RollingSummary | null = null
87-
const policyShifts: PolicyShift[] = []
88-
const failureClusters: FailureCluster[] = []
8986

9087
useEffect(() => {
9188
let ignore = false
@@ -470,8 +467,8 @@ function App() {
470467
return (
471468
<div className="app-shell">
472469
<header className="hero">
473-
<div style={{ display: 'flex', alignItems: 'center', gap: 14 }}>
474-
<Logo size={32} />
470+
<div style={{ display: 'flex', alignItems: 'center', gap: 12 }}>
471+
<Logo size={24} />
475472
<div>
476473
<h1>Peaky Peek</h1>
477474
</div>
@@ -526,10 +523,22 @@ function App() {
526523
</div>
527524

528525
<div className="inspect-grid">
526+
{/* Analysis Group: Inspectors + Conversation + Comparison */}
527+
<div className="inspect-section-divider">
528+
<span className="inspect-section-label">Analysis</span>
529+
</div>
530+
529531
<section className="panel panel--primary">
530532
<div className="inspectors-grid">
531-
<ToolInspector event={toolEvent} />
532-
<LLMViewer request={llmRequest} response={llmResponse} />
533+
<div className="inspector-wrapper">
534+
<span className="inspector-label">Tool Inspector</span>
535+
<ToolInspector event={toolEvent} />
536+
</div>
537+
<div className="inspector-separator" />
538+
<div className="inspector-wrapper">
539+
<span className="inspector-label">LLM Viewer</span>
540+
<LLMViewer request={llmRequest} response={llmResponse} />
541+
</div>
533542
</div>
534543
</section>
535544

@@ -553,13 +562,17 @@ function App() {
553562
/>
554563
</section>
555564

565+
{/* Monitoring Group: Live Dashboard + Checkpoints + Alerts */}
566+
<div className="inspect-section-divider">
567+
<span className="inspect-section-label">Monitoring</span>
568+
</div>
569+
556570
<section className="panel panel--secondary">
557571
<LiveDashboard
558572
session={currentSession}
559573
events={mergedSessionEvents}
560574
checkpoints={bundle?.checkpoints ?? []}
561575
liveSummary={liveSummary}
562-
rollingSummaryData={rollingSummaryData}
563576
isConnected={streamConnected}
564577
liveEventCount={liveEvents.length}
565578
onSelectEvent={handleInspectEvent}
@@ -653,17 +666,28 @@ function App() {
653666
loading={driftLoading}
654667
/>
655668
<PolicyDiffView
656-
policyShifts={policyShifts}
669+
policyShifts={[]}
657670
onSelectEvent={handleInspectEvent}
658671
/>
659-
<FailureClusterPanel
660-
clusters={failureClusters}
661-
onSelectSession={setSelectedSessionId}
662-
selectedSessionId={selectedSessionId}
663-
analysisClusters={bundle?.analysis.failure_clusters ?? []}
664-
events={mergedSessionEvents}
665-
/>
666-
<MultiAgentCoordinationPanel bundle={bundle} />
672+
673+
{/* Intelligence Group: Drift + Policy + Failure Clusters + Coordination */}
674+
<div className="inspect-section-divider">
675+
<span className="inspect-section-label">Intelligence</span>
676+
</div>
677+
678+
<section className="panel panel--accent failure-cluster-panel">
679+
<FailureClusterPanel
680+
clusters={[]}
681+
onSelectSession={setSelectedSessionId}
682+
selectedSessionId={selectedSessionId}
683+
analysisClusters={bundle?.analysis.failure_clusters ?? []}
684+
events={mergedSessionEvents}
685+
/>
686+
</section>
687+
688+
<section className="panel panel--accent coordination-panel">
689+
<MultiAgentCoordinationPanel bundle={bundle} />
690+
</section>
667691
</div>
668692
</section>
669693
</main>

frontend/src/components/EmptyState.css

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -4,17 +4,17 @@
44
align-items: center;
55
justify-content: center;
66
text-align: center;
7-
padding: 3rem 2rem;
8-
gap: 0.75rem;
7+
padding: 4rem 2rem;
8+
gap: 1rem;
99
max-width: 420px;
1010
margin: 0 auto;
1111
background: linear-gradient(135deg, color-mix(in oklch, var(--panel), var(--accent) 3%), color-mix(in oklch, var(--panel), var(--bg-strong) 6%));
12-
border-radius: 1.2rem;
12+
border-radius: 1.4rem;
1313
}
1414

1515
.empty-state-icon {
16-
font-size: 2.5rem;
17-
opacity: 0.5;
16+
font-size: 3rem;
17+
opacity: 0.6;
1818
line-height: 1;
1919
animation: float 3s ease-in-out infinite;
2020
}
@@ -29,17 +29,17 @@
2929
}
3030

3131
.empty-state-title {
32-
font-size: 1.1rem;
32+
font-size: 1.25rem;
3333
font-weight: 600;
3434
margin: 0;
35-
color: var(--text-primary, #e0e0e0);
35+
color: var(--text);
3636
}
3737

3838
.empty-state-description {
39-
font-size: 0.85rem;
40-
color: var(--text-secondary, #888);
39+
font-size: 0.9rem;
40+
color: var(--muted);
4141
margin: 0;
42-
line-height: 1.5;
42+
line-height: 1.6;
4343
}
4444

4545
.empty-state-steps {

frontend/src/components/LiveDashboard.tsx

Lines changed: 2 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import type { Checkpoint, LiveSummary, RollingSummary, Session, TraceEvent } from '../types'
2-
import { formatEventHeadline } from '../utils/formatting'
2+
import { computeCheckpointDelta, formatEventHeadline, formatMetricLabel, latestOf } from '../utils/formatting'
33

44
interface LiveDashboardProps {
55
session: Session | null
@@ -12,20 +12,6 @@ interface LiveDashboardProps {
1212
onSelectEvent: (eventId: string) => void
1313
}
1414

15-
function latestOf(events: TraceEvent[], eventTypes: string[]): TraceEvent | null {
16-
for (let index = events.length - 1; index >= 0; index -= 1) {
17-
const event = events[index]
18-
if (eventTypes.includes(event.event_type)) {
19-
return event
20-
}
21-
}
22-
return null
23-
}
24-
25-
function formatMetricLabel(key: string): string {
26-
return key.replaceAll('_', ' ').replace(/\b\w/g, (c) => c.toUpperCase())
27-
}
28-
2915
interface StabilityIndicatorProps {
3016
alertCount: number
3117
hasOscillation: boolean
@@ -87,14 +73,7 @@ export function LiveDashboard({
8773
const metrics = rollingSummaryData?.metrics ?? {}
8874
const metricEntries = Object.entries(metrics)
8975

90-
function computeCheckpointDelta(): { stateDelta: number; memoryDelta: number } | null {
91-
if (!latestCheckpoint || !previousCheckpoint) return null
92-
const stateDelta = Object.keys(latestCheckpoint.state).length - Object.keys(previousCheckpoint.state).length
93-
const memoryDelta = Object.keys(latestCheckpoint.memory).length - Object.keys(previousCheckpoint.memory).length
94-
return { stateDelta, memoryDelta }
95-
}
96-
97-
const checkpointDelta = computeCheckpointDelta()
76+
const checkpointDelta = computeCheckpointDelta(latestCheckpoint, previousCheckpoint)
9877

9978
return (
10079
<div className="live-dashboard">

frontend/src/components/LiveSummaryPanel.tsx

Lines changed: 2 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import type { Checkpoint, LiveSummary, RollingSummary, Session, TraceEvent } from '../types'
2-
import { formatEventHeadline } from '../utils/formatting'
2+
import { computeCheckpointDelta, formatEventHeadline, formatMetricLabel, latestOf } from '../utils/formatting'
33

44
interface LiveSummaryPanelProps {
55
session: Session | null
@@ -12,20 +12,6 @@ interface LiveSummaryPanelProps {
1212
onSelectEvent: (eventId: string) => void
1313
}
1414

15-
function latestOf(events: TraceEvent[], eventTypes: string[]): TraceEvent | null {
16-
for (let index = events.length - 1; index >= 0; index -= 1) {
17-
const event = events[index]
18-
if (eventTypes.includes(event.event_type)) {
19-
return event
20-
}
21-
}
22-
return null
23-
}
24-
25-
function formatMetricLabel(key: string): string {
26-
return key.replaceAll('_', ' ').replace(/\b\w/g, (c) => c.toUpperCase())
27-
}
28-
2915
export function LiveSummaryPanel({
3016
session,
3117
events,
@@ -55,14 +41,7 @@ export function LiveSummaryPanel({
5541
const metrics = rollingSummaryData?.metrics ?? {}
5642
const metricEntries = Object.entries(metrics)
5743

58-
function computeCheckpointDelta(): { stateDelta: number; memoryDelta: number } | null {
59-
if (!latestCheckpoint || !previousCheckpoint) return null
60-
const stateDelta = Object.keys(latestCheckpoint.state).length - Object.keys(previousCheckpoint.state).length
61-
const memoryDelta = Object.keys(latestCheckpoint.memory).length - Object.keys(previousCheckpoint.memory).length
62-
return { stateDelta, memoryDelta }
63-
}
64-
65-
const checkpointDelta = computeCheckpointDelta()
44+
const checkpointDelta = computeCheckpointDelta(latestCheckpoint, previousCheckpoint)
6645

6746
return (
6847
<div className="live-summary-panel">

frontend/src/components/SessionRail.tsx

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -70,11 +70,14 @@ export function SessionRail() {
7070
setSelectedEventId(null)
7171
}}
7272
>
73-
<span className="session-name">{session.agent_name}</span>
73+
<div className="session-card-header">
74+
<span className="session-name">{session.agent_name}</span>
75+
<span className={`session-status-dot ${session.status === 'error' ? 'error' : session.status === 'completed' ? 'success' : 'pending'}`} />
76+
</div>
7477
<span className="session-framework">{session.framework}</span>
7578
<span className="session-status">{session.status}</span>
7679
<div className="session-card-metrics">
77-
<span>Replay {(session.replay_value ?? 0).toFixed(2)}</span>
80+
<span className="replay-value-badge">Replay {(session.replay_value ?? 0).toFixed(2)}</span>
7881
<span className={`retention-pill ${session.retention_tier ?? 'downsampled'}`}>
7982
{session.retention_tier ?? 'downsampled'}
8083
</span>

0 commit comments

Comments
 (0)