diff --git a/src/contexts/GitStatusContext.tsx b/src/contexts/GitStatusContext.tsx index b21e62115..54b7c5a54 100644 --- a/src/contexts/GitStatusContext.tsx +++ b/src/contexts/GitStatusContext.tsx @@ -31,7 +31,7 @@ interface GitStatusProviderProps { * - Max 5 concurrent git status checks to prevent bash process explosion */ // Configuration - enabled by default, no env variables needed -const GIT_STATUS_INTERVAL_MS = 3000; // 3 seconds +const GIT_STATUS_INTERVAL_MS = 3000; // 3 seconds - interactive updates const MAX_CONCURRENT_GIT_OPS = 5; // Fetch configuration - aggressive intervals for fresh data diff --git a/src/hooks/useUnreadTracking.ts b/src/hooks/useUnreadTracking.ts index 63032d3a1..102567cab 100644 --- a/src/hooks/useUnreadTracking.ts +++ b/src/hooks/useUnreadTracking.ts @@ -73,13 +73,14 @@ export function useUnreadTracking( }, [selectedWorkspace?.workspaceId, workspaceStates, markAsRead]); // Calculate unread status for all workspaces + const unreadStatusRef = useRef>(new Map()); const unreadStatus = useMemo(() => { - const result = new Map(); + const next = new Map(); for (const [workspaceId, state] of workspaceStates) { // Streaming workspaces are never unread if (state.canInterrupt) { - result.set(workspaceId, false); + next.set(workspaceId, false); continue; } @@ -92,10 +93,26 @@ export function useUnreadTracking( msg.type !== "user" && msg.type !== "history-hidden" && (msg.timestamp ?? 0) > lastRead ); - result.set(workspaceId, hasUnread); + next.set(workspaceId, hasUnread); } - return result; + // Return previous Map reference if nothing actually changed to keep identity stable + const prev = unreadStatusRef.current; + if (prev.size === next.size) { + let same = true; + for (const [k, v] of next) { + if (prev.get(k) !== v) { + same = false; + break; + } + } + if (same) { + return prev; + } + } + + unreadStatusRef.current = next; + return next; }, [workspaceStates, lastReadMap]); // Manual toggle function for clicking the indicator