From 85d7bea7ea16aa89c119af633ba49910bb3db4b3 Mon Sep 17 00:00:00 2001 From: AgentSeal Date: Sat, 18 Apr 2026 05:07:36 -0700 Subject: [PATCH] feat(mac): hide agent tabs when fewer than two providers have spend The tab strip was visible for everyone regardless of which tools they actually run, which produced a row of All + one provider for Claude-only users and a row of All + zeros for users on exotic stacks. Hide the whole row until a second provider has real spend, matching the behavior the GNOME extension ships with. Also expand ProviderFilter to include every provider the CLI supports (OpenCode and Pi were missing) so their tabs appear when those tools produce sessions. The CLI already emits pi and opencode in the payload's providers map; the Mac app just wasn't offering a tab for them. visibleFilters now filters on value > 0 instead of key presence, because the CLI includes zero-cost entries for discovered-but-unused providers and we don't want those rendering as blank tabs. --- mac/Sources/CodeBurnMenubar/AppStore.swift | 4 ++++ .../CodeBurnMenubar/Views/AgentTabStrip.swift | 13 ++++++++++++- .../CodeBurnMenubar/Views/MenuBarContent.swift | 16 +++++++++++++--- 3 files changed, 29 insertions(+), 4 deletions(-) diff --git a/mac/Sources/CodeBurnMenubar/AppStore.swift b/mac/Sources/CodeBurnMenubar/AppStore.swift index cd26d76..db3e2da 100644 --- a/mac/Sources/CodeBurnMenubar/AppStore.swift +++ b/mac/Sources/CodeBurnMenubar/AppStore.swift @@ -213,6 +213,8 @@ enum ProviderFilter: String, CaseIterable, Identifiable { case codex = "Codex" case cursor = "Cursor" case copilot = "Copilot" + case opencode = "OpenCode" + case pi = "Pi" var id: String { rawValue } @@ -224,6 +226,8 @@ enum ProviderFilter: String, CaseIterable, Identifiable { case .codex: "codex" case .cursor: "cursor" case .copilot: "copilot" + case .opencode: "opencode" + case .pi: "pi" } } } diff --git a/mac/Sources/CodeBurnMenubar/Views/AgentTabStrip.swift b/mac/Sources/CodeBurnMenubar/Views/AgentTabStrip.swift index 4feb180..885bdcc 100644 --- a/mac/Sources/CodeBurnMenubar/Views/AgentTabStrip.swift +++ b/mac/Sources/CodeBurnMenubar/Views/AgentTabStrip.swift @@ -33,7 +33,16 @@ struct AgentTabStrip: View { } private var visibleFilters: [ProviderFilter] { - let activeKeys = Set(allProvidersToday.current.providers.keys.map { $0.lowercased() }) + // Only surface providers that actually spent money in the all-provider view. + // The CLI includes zero-cost providers in the payload for completeness, so + // filtering on value > 0 keeps the tab row honest and avoids showing tabs + // for tools the user hasn't run yet. + let activeKeys = Set( + allProvidersToday.current.providers + .filter { $0.value > 0 } + .keys + .map { $0.lowercased() } + ) return ProviderFilter.allCases.filter { filter in if filter == .all { return true } return activeKeys.contains(filter.rawValue.lowercased()) @@ -87,6 +96,8 @@ extension ProviderFilter { case .codex: return Theme.categoricalCodex case .cursor: return Theme.categoricalCursor case .copilot: return Color(red: 0x6D/255.0, green: 0x8F/255.0, blue: 0xA6/255.0) + case .opencode: return Color(red: 0x5B/255.0, green: 0x83/255.0, blue: 0x5B/255.0) + case .pi: return Color(red: 0xB2/255.0, green: 0x6B/255.0, blue: 0x3D/255.0) } } } diff --git a/mac/Sources/CodeBurnMenubar/Views/MenuBarContent.swift b/mac/Sources/CodeBurnMenubar/Views/MenuBarContent.swift index b6c550d..3c08c27 100644 --- a/mac/Sources/CodeBurnMenubar/Views/MenuBarContent.swift +++ b/mac/Sources/CodeBurnMenubar/Views/MenuBarContent.swift @@ -11,9 +11,10 @@ struct MenuBarContent: View { Divider() - AgentTabStrip() - - Divider() + if showAgentTabs { + AgentTabStrip() + Divider() + } ZStack { ScrollView(.vertical, showsIndicators: false) { @@ -63,6 +64,15 @@ struct MenuBarContent: View { return store.payload.current.cost <= 0 && store.payload.current.calls == 0 } + /// Only show the tab row when two or more providers have non-zero spend. One + /// provider means the tabs are redundant (the All tab already shows it); zero + /// providers means the popover has nothing to filter. + private var showAgentTabs: Bool { + let payload = store.todayPayload ?? store.payload + let active = payload.current.providers.values.filter { $0 > 0 } + return active.count >= 2 + } + } private struct EmptyProviderState: View {