From 0548369b83511c62839d5f11ef11bdf421fd1979 Mon Sep 17 00:00:00 2001 From: Morten Trydal Date: Mon, 16 Mar 2026 22:58:54 +0100 Subject: [PATCH] Add dock icon badge for pending completion count Sets NSApp.dockTile.badgeLabel at every pending-count mutation site (enqueue, agent start, activate/close surface, load/delete workspace) so the dock icon always reflects the live pending completion count. --- .../Workspaces/WorkspaceManager+Completions.swift | 8 ++++++++ .../Workspaces/WorkspaceManager+SurfaceOperations.swift | 2 ++ .../Workspaces/WorkspaceManager+WorkspaceLifecycle.swift | 3 +++ 3 files changed, 13 insertions(+) diff --git a/Sources/Shellraiser/Services/Workspaces/WorkspaceManager+Completions.swift b/Sources/Shellraiser/Services/Workspaces/WorkspaceManager+Completions.swift index f6211f5..64a1925 100644 --- a/Sources/Shellraiser/Services/Workspaces/WorkspaceManager+Completions.swift +++ b/Sources/Shellraiser/Services/Workspaces/WorkspaceManager+Completions.swift @@ -43,6 +43,7 @@ extension WorkspaceManager { workspaceName: workspace.name ) } + updateDockBadge() } else if !payload.isEmpty { surfaceManager.setAgentType( workspaceId: workspaceId, @@ -124,6 +125,7 @@ extension WorkspaceManager { persistence: persistence ) completionNotifications.removeNotifications(for: event.surfaceId) + updateDockBadge() guard event.agentType != .codex else { return } markSurfaceBusy(event.surfaceId) case .completed: @@ -228,6 +230,12 @@ extension WorkspaceManager { } } + /// Updates the dock icon badge to reflect the current pending completion count. + func updateDockBadge() { + let count = pendingCompletionTargets().count + NSApp.dockTile.badgeLabel = count > 0 ? "\(count)" : nil + } + /// Rebuilds the next FIFO sequence cursor from persisted surface metadata. func synchronizePendingCompletionCursor() { let highestSequence = workspaces diff --git a/Sources/Shellraiser/Services/Workspaces/WorkspaceManager+SurfaceOperations.swift b/Sources/Shellraiser/Services/Workspaces/WorkspaceManager+SurfaceOperations.swift index d330ed7..b84727e 100644 --- a/Sources/Shellraiser/Services/Workspaces/WorkspaceManager+SurfaceOperations.swift +++ b/Sources/Shellraiser/Services/Workspaces/WorkspaceManager+SurfaceOperations.swift @@ -38,6 +38,7 @@ extension WorkspaceManager { persistence: persistence ) completionNotifications.removeNotifications(for: surfaceId) + updateDockBadge() GhosttyRuntime.shared.endSearch(surfaceId: surfaceId) GhosttyRuntime.shared.releaseSurface(surfaceId: surfaceId) clearBusySurface(surfaceId) @@ -75,6 +76,7 @@ extension WorkspaceManager { markRecentlyHandled(surfaceId: surfaceId) } completionNotifications.removeNotifications(for: surfaceId) + updateDockBadge() GhosttyRuntime.shared.focusSurfaceHost(surfaceId: surfaceId) if let workspace = workspace(id: workspaceId), diff --git a/Sources/Shellraiser/Services/Workspaces/WorkspaceManager+WorkspaceLifecycle.swift b/Sources/Shellraiser/Services/Workspaces/WorkspaceManager+WorkspaceLifecycle.swift index 309a4f7..066b87e 100644 --- a/Sources/Shellraiser/Services/Workspaces/WorkspaceManager+WorkspaceLifecycle.swift +++ b/Sources/Shellraiser/Services/Workspaces/WorkspaceManager+WorkspaceLifecycle.swift @@ -1,3 +1,4 @@ +import AppKit import Foundation /// Workspace lifecycle and selection flows for the shared manager. @@ -17,6 +18,7 @@ extension WorkspaceManager { persistence: persistence ) synchronizePendingCompletionCursor() + updateDockBadge() refreshFocusedWorkspaceGitBranches() hasLoadedPersistedWorkspaces = true } @@ -69,6 +71,7 @@ extension WorkspaceManager { GhosttyRuntime.shared.releaseSurface(surfaceId: $0) clearGitBranch(surfaceId: $0) } + updateDockBadge() } /// Requests deletion for a workspace and asks for confirmation when live terminals exist.