From 03e51e0ec2b6d2a2835d37667dfd6b0b383f57f3 Mon Sep 17 00:00:00 2001 From: Neil <4138956+nwparker@users.noreply.github.com> Date: Fri, 29 May 2026 07:38:56 -0700 Subject: [PATCH] Fix dock unread badge for detected worktrees Automatic agent completion and permission attention can mark a worktree unread before it is present in worktreesByRepo. Include detectedWorktreesByRepo in the unread badge count so the macOS Dock badge follows the same unread state as the in-app markers. --- src/renderer/src/hooks/useUnreadDockBadge.ts | 1 + .../src/lib/unread-badge-count.test.ts | 30 ++++++++++++++++++- src/renderer/src/lib/unread-badge-count.ts | 14 ++++++++- 3 files changed, 43 insertions(+), 2 deletions(-) diff --git a/src/renderer/src/hooks/useUnreadDockBadge.ts b/src/renderer/src/hooks/useUnreadDockBadge.ts index 1f6674a9824..5fa6b8876f8 100644 --- a/src/renderer/src/hooks/useUnreadDockBadge.ts +++ b/src/renderer/src/hooks/useUnreadDockBadge.ts @@ -6,6 +6,7 @@ export function useUnreadDockBadge(): void { const unreadCount = useAppStore((state) => getUnreadBadgeCount({ worktreesByRepo: state.worktreesByRepo, + detectedWorktreesByRepo: state.detectedWorktreesByRepo, tabsByWorktree: state.tabsByWorktree, unreadTerminalTabs: state.unreadTerminalTabs }) diff --git a/src/renderer/src/lib/unread-badge-count.test.ts b/src/renderer/src/lib/unread-badge-count.test.ts index fdf20dfab30..60cf5b3173b 100644 --- a/src/renderer/src/lib/unread-badge-count.test.ts +++ b/src/renderer/src/lib/unread-badge-count.test.ts @@ -1,5 +1,5 @@ import { describe, expect, it } from 'vitest' -import type { TerminalTab, Worktree } from '../../../shared/types' +import type { DetectedWorktreeListResult, TerminalTab, Worktree } from '../../../shared/types' import { getUnreadBadgeCount } from './unread-badge-count' function worktree(id: string, isUnread: boolean): Worktree { @@ -10,6 +10,23 @@ function tab(id: string): TerminalTab { return { id } as TerminalTab } +function detectedWorktree(id: string, isUnread: boolean): DetectedWorktreeListResult { + return { + repoId: 'repo', + authoritative: true, + source: 'git', + worktrees: [ + { + ...worktree(id, isUnread), + repoId: 'repo', + ownership: 'orca-managed', + selectedCheckout: false, + visible: true + } + ] + } as DetectedWorktreeListResult +} + describe('getUnreadBadgeCount', () => { it('counts unread worktrees', () => { expect( @@ -40,4 +57,15 @@ describe('getUnreadBadgeCount', () => { }) ).toBe(2) }) + + it('counts automatic unread markers on detected worktrees', () => { + expect( + getUnreadBadgeCount({ + worktreesByRepo: { repo: [] }, + detectedWorktreesByRepo: { repo: detectedWorktree('wt-detected', true) }, + tabsByWorktree: {}, + unreadTerminalTabs: {} + }) + ).toBe(1) + }) }) diff --git a/src/renderer/src/lib/unread-badge-count.ts b/src/renderer/src/lib/unread-badge-count.ts index 79cd2ffa36b..7841618cfa2 100644 --- a/src/renderer/src/lib/unread-badge-count.ts +++ b/src/renderer/src/lib/unread-badge-count.ts @@ -1,11 +1,13 @@ -import type { TerminalTab, Worktree } from '../../../shared/types' +import type { DetectedWorktreeListResult, TerminalTab, Worktree } from '../../../shared/types' export function getUnreadBadgeCount({ worktreesByRepo, + detectedWorktreesByRepo = {}, tabsByWorktree, unreadTerminalTabs }: { worktreesByRepo: Record + detectedWorktreesByRepo?: Record tabsByWorktree: Record unreadTerminalTabs: Record }): number { @@ -19,6 +21,16 @@ export function getUnreadBadgeCount({ } } + // Why: automatic agent attention can land while a worktree is only present + // in detectedWorktreesByRepo; the Dock badge must mirror that unread marker. + for (const result of Object.values(detectedWorktreesByRepo)) { + for (const worktree of result.worktrees) { + if (worktree.isUnread) { + unreadWorktreeIds.add(worktree.id) + } + } + } + const unreadTabIds = new Set(Object.keys(unreadTerminalTabs)) if (unreadTabIds.size === 0) { return unreadWorktreeIds.size