feat: add rich run timeline UI with collapsible cards and jump-to-message

- Add RunTimeline component with vertical event timeline, collapsible run
  cards, per-kind icons, status animations, and agent lane badges
- Add runTimelineFormatting helpers for timestamps, durations, labels,
  content truncation, and consecutive thinking-event collapsing
- Integrate Timeline section into ActivityPanel between Agents and Tools
- Wire jump-to-message in App.tsx using DOM data-message-id attributes
  on ChatPane messages with smooth scroll and temporary highlight ring
- Add comprehensive unit tests for all formatting helpers
- Update HANDOVER.md to document completed frontend implementation

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This commit is contained in:
David Kaya
2026-03-24 00:17:14 +01:00
co-authored by Copilot
parent b742941559
commit 0d3f1cbd50
7 changed files with 640 additions and 5 deletions
+19 -1
View File
@@ -1,5 +1,5 @@
import { useMemo, type ReactNode } from 'react';
import { Activity, Server, Code, Sparkles, Users } from 'lucide-react';
import { Activity, Clock, Server, Code, Sparkles, Users } from 'lucide-react';
import {
buildAgentActivityRows,
@@ -9,6 +9,7 @@ import {
type AgentActivityRow,
type SessionActivityState,
} from '@renderer/lib/sessionActivity';
import { RunTimeline } from '@renderer/components/RunTimeline';
import { inferProvider } from '@shared/domain/models';
import type { OrchestrationMode, PatternAgentDefinition, PatternDefinition } from '@shared/domain/pattern';
import {
@@ -158,6 +159,7 @@ interface ActivityPanelProps {
activity?: SessionActivityState;
lspProfiles: LspProfileDefinition[];
mcpServers: McpServerDefinition[];
onJumpToMessage?: (messageId: string) => void;
onUpdateSessionTooling: (selection: SessionToolingSelection) => void;
pattern: PatternDefinition;
projectIsScratchpad: boolean;
@@ -168,6 +170,7 @@ export function ActivityPanel({
activity,
lspProfiles,
mcpServers,
onJumpToMessage,
onUpdateSessionTooling,
pattern,
projectIsScratchpad,
@@ -228,6 +231,21 @@ export function ActivityPanel({
)}
</div>
{/* ── Run timeline section ─────────────────────────── */}
<div className="mb-4">
<SectionHeader>
<Clock className="size-3" />
<span>Timeline</span>
{session.runs.length > 0 && (
<span className="rounded-full bg-zinc-800 px-1.5 py-0.5 text-[9px] tabular-nums text-zinc-500">
{session.runs.length}
</span>
)}
</SectionHeader>
<RunTimeline onJumpToMessage={onJumpToMessage} runs={session.runs} />
</div>
{/* ── Tools section ────────────────────────────────── */}
<div>
<SectionHeader>