From 05b37715443e82643c2d692799f10d9a2cc435f7 Mon Sep 17 00:00:00 2001 From: David Kaya Date: Wed, 15 Apr 2026 09:11:38 +0200 Subject: [PATCH] fix: accurate elapsed timing and UX polish for activity panel - Fix useElapsedTimer to use completedAt instead of Date.now() for finished runs, preventing wildly inaccurate durations (e.g. 9802m) when reopening old sessions - Extract formatElapsedMs as a shared pure formatter; reuse in SubagentActivityCard - Standardize icon column alignment across all activity row types using consistent w-4 + h-[18px] icon containers - Redesign collapsed header: status dot with pulse animation, bolder status label, tabular-nums on elapsed time, cleaner hierarchy - Polish expanded stream: timeline spine connecting activity items, more prominent intent dividers with pill-style background, tightened vertical rhythm - Add unit tests for formatElapsedMs Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .../components/chat/SubagentActivityCard.tsx | 12 +-- .../components/chat/TurnActivityPanel.tsx | 80 +++++++++++-------- src/renderer/hooks/useElapsedTimer.ts | 45 +++++++---- src/renderer/styles.css | 17 ++++ tests/renderer/useElapsedTimer.test.ts | 37 +++++++++ 5 files changed, 137 insertions(+), 54 deletions(-) create mode 100644 tests/renderer/useElapsedTimer.test.ts diff --git a/src/renderer/components/chat/SubagentActivityCard.tsx b/src/renderer/components/chat/SubagentActivityCard.tsx index 8593f4d..42e3f70 100644 --- a/src/renderer/components/chat/SubagentActivityCard.tsx +++ b/src/renderer/components/chat/SubagentActivityCard.tsx @@ -5,12 +5,12 @@ import type { ActiveSubagent } from '@renderer/lib/subagentTracker'; const COMPLETION_GRACE_MS = 3000; -function formatElapsed(startedAt: string): string { - const seconds = Math.floor((Date.now() - new Date(startedAt).getTime()) / 1000); - if (seconds < 60) return `${seconds}s`; - const minutes = Math.floor(seconds / 60); - const remainder = seconds % 60; - return `${minutes}m ${remainder}s`; +import { formatElapsedMs } from '@renderer/hooks/useElapsedTimer'; + +function formatElapsed(startedAt: string, endedAt?: string): string { + const endMs = endedAt ? new Date(endedAt).getTime() : Date.now(); + const durationMs = endMs - new Date(startedAt).getTime(); + return formatElapsedMs(durationMs); } function StatusIcon({ status }: { status: ActiveSubagent['status'] }) { diff --git a/src/renderer/components/chat/TurnActivityPanel.tsx b/src/renderer/components/chat/TurnActivityPanel.tsx index 11d4ce7..e3a49ab 100644 --- a/src/renderer/components/chat/TurnActivityPanel.tsx +++ b/src/renderer/components/chat/TurnActivityPanel.tsx @@ -153,12 +153,12 @@ function ActivityTimelineEventRow({ event }: { event: RunTimelineEventRecord }) const isTerminal = event.kind === 'run-completed' || event.kind === 'run-cancelled' || event.kind === 'run-failed'; return ( -
-
+
+
- + {label} @@ -231,14 +231,14 @@ function GroupedToolCallRow({ toolName, events }: { toolName: string; events: Ru