From f69ba1f32aa1704f40bf48074ae883d1a046d94d Mon Sep 17 00:00:00 2001 From: teernisse Date: Fri, 30 Jan 2026 23:04:04 -0500 Subject: [PATCH] Wire progress events through session viewer and fix request race condition MessageBubble: - Accept progressEvents and progressEnabled props - Render ProgressBadge below tool_call content when progress data exists - Normalize left padding from mixed pl-5/px-4 to consistent px-5 SessionViewer: - Thread toolProgress map and progressEnabled flag through to messages - Look up progress events by toolUseId for each tool_call message - Fix hash-anchor scroll firing on every filter toggle by tracking whether scroll has occurred per session load (hashScrolledRef) - Increase vertical spacing on time gap dividers and header area App: - Derive progressEnabled from hook_progress category filter state - Pass toolProgress and progressEnabled to SessionViewer - Optimize sensitiveCount to compute across all session messages (not filtered subset) to avoid re-running 37 regex patterns on every filter toggle - Tighten redaction selection badge gap from 2 to 1.5 useSession: - Add AbortController to loadSession to cancel stale in-flight requests when user rapidly switches sessions - Only clear loading state if the completing request is still current - Ignore AbortError exceptions from cancelled fetches Co-Authored-By: Claude Opus 4.5 --- src/client/app.tsx | 14 +++++++--- src/client/components/MessageBubble.tsx | 14 +++++++--- src/client/components/SessionViewer.tsx | 34 ++++++++++++++++++++++--- src/client/hooks/useSession.ts | 21 ++++++++++++--- 4 files changed, 69 insertions(+), 14 deletions(-) diff --git a/src/client/app.tsx b/src/client/app.tsx index 8177f43..bd6ac7e 100644 --- a/src/client/app.tsx +++ b/src/client/app.tsx @@ -54,9 +54,13 @@ export function App() { return filters.filterMessages(currentSession.messages); }, [currentSession, filters.filterMessages]); + const progressEnabled = filters.enabledCategories.has("hook_progress"); + + // Count across all session messages (not just filtered) — recompute only on session change. + // This avoids re-running 37 regex patterns whenever filter toggles change. const sensitiveCount = useMemo( - () => countSensitiveMessages(filteredMessages), - [filteredMessages] + () => countSensitiveMessages(currentSession?.messages || []), + [currentSession?.messages] ); // Track which filtered-message indices match the search query @@ -224,7 +228,7 @@ export function App() { {/* Main */}
-
+
{/* Left spacer — mirrors right side width to keep search centered */}
@@ -239,7 +243,7 @@ export function App() { onPrev={goToPrevMatch} /> {filters.selectedForRedaction.size > 0 && ( -
+
{filters.selectedForRedaction.size} selected @@ -285,6 +289,8 @@ export function App() { onToggleRedactionSelection={filters.toggleRedactionSelection} autoRedactEnabled={filters.autoRedactEnabled} focusedIndex={activeFocusIndex} + toolProgress={currentSession?.toolProgress} + progressEnabled={progressEnabled} />
diff --git a/src/client/components/MessageBubble.tsx b/src/client/components/MessageBubble.tsx index d52c302..8566e53 100644 --- a/src/client/components/MessageBubble.tsx +++ b/src/client/components/MessageBubble.tsx @@ -5,6 +5,7 @@ import { CATEGORY_COLORS } from "../lib/constants"; import { renderMarkdown, highlightSearchText } from "../lib/markdown"; import { redactMessage } from "../../shared/sensitive-redactor"; import { escapeHtml } from "../../shared/escape-html"; +import { ProgressBadge } from "./ProgressBadge"; interface Props { message: ParsedMessage; @@ -13,6 +14,8 @@ interface Props { selectedForRedaction: boolean; onToggleRedactionSelection: () => void; autoRedactEnabled: boolean; + progressEvents?: ParsedMessage[]; + progressEnabled?: boolean; } /** @@ -29,6 +32,8 @@ export function MessageBubble({ selectedForRedaction, onToggleRedactionSelection, autoRedactEnabled, + progressEvents, + progressEnabled, }: Props) { const colors = CATEGORY_COLORS[message.category]; const label = CATEGORY_LABELS[message.category]; @@ -110,7 +115,7 @@ export function MessageBubble({
{/* Header bar */} -
+
{isCollapsible && (