Dual-percent menu bar, response cache, and pruning pass - #10
Merged
Conversation
Deletes UsageChartView, Downsampler, CSVExporter (plus tests and integration coverage) as part of the 2.0 simplification — the chart surfaced buggy projections and the export button had no home in the new row-list layout. History recording remains so burn-rate math still feeds the popover.
UsageDetailView picks one hero (enterprise Monthly Spend, consumer 5-Hour → 7-Day, or "No active limit" fallback) and renders the rest as tight rows (label · bar · %) separated by faint dividers. Hero uses .ultraThinMaterial with a 0.5pt stroke and 8pt radius; sub-rows drop reset-time and dollar meta to stay legible at narrow widths.
Adds ProfileResponse model + PlanTier derivation, fetches profile via /api/oauth/profile after first successful usage fetch, and surfaces account/plan info in a new Settings Account tab.
Consumer plans now see both session and weekly usage at a glance: two inline percentages, each independently colored against the existing warning/critical thresholds. Enterprise keeps its ring + credit/percent display. Introduces MenuBarMetrics in Core with a view-agnostic MenuBarColorSemantic enum so the Core target stays SwiftUI-free.
NotificationCoordinator is now @mainactor and owns a bounded in-memory ring buffer that feeds BurnRateCalculator directly. Drops historyPoints/historyService/historyStore from UsageViewModel and App.swift, and removes the now-unused NotificationCoordinatorProtocol (there was no external implementer). HistoryService and UsageHistoryStore remain on disk as dead code until Unit 3 deletes them alongside the GRDB dependency. Integration tests are slimmed to cover only the pipelines that outlive history.
Removes the now-unreferenced HistoryService and UsageHistoryStore (and their tests) plus the GRDB dependency from Package.swift and project.yml. UsageDataPoint moves to its own file as a plain Sendable value type — no database conformances, no retention window, no disk. Leftover ~/.config/claude-usage/history.db files from earlier installs are intentionally ignored: the migration cost outweighs the few KB of orphan bytes, and the cache path introduced in Unit 5 lives under Application Support so it does not collide.
Strips the nl/de/fr/es/pt-BR/pt-PT translations from Localizable.xcstrings and mirrors that in project.yml and Info.plist's CFBundleLocalizations. LocalizedStringKey usage in Swift source stays intact so re-introducing a locale later is a data change, not a code change.
…e age Hydrate the UI from disk on launch so the menu bar isn't "--" for the first poll cycle, and skip the immediate network fetch when the cached response is still within one polling interval old.
…vals Drop bullets for features this pass removed (usage history, CSV export, multi-language) and document the new dual-percent menubar, response cache location, and launch behavior.
On slower CI runners, capturing countAtStop before stopPolling() let an in-flight fetch from the 100ms interval complete between the two lines, making the subsequent "no more fetches" assertion fail. Stop first, then let the cancelled task drain, then read the counter.
4 tasks
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Adopts the one meaningful UX idea from
adntgv/claude-usage-systray— a dual-percent consumer menu bar — and uses the visit as an excuse to delete accumulated carrying costs.New:
5h% · 7d%inline, each number color-coded independently by warning/critical thresholds. Enterprise display is unchanged.~/Library/Application Support/cc-stats/last-usage.json: hydrates the menu bar instantly on launch and defers the first automatic poll when the cached response is still within one polling interval old. Rapid restarts no longer stack API calls.NotificationCoordinator: preserves 5-hour burn-rate notifications without persistent history.Removed:
HistoryService,UsageHistoryStore, and the GRDB dependency — dead weight since the history chart was removed in7fdeb6f.Testing
swift buildclean.swift test— 142 tests in 15 suites pass functionally.MenuBarMetricsTests— 13 tests for extraction + color semantics.UsageResponseCacheTests— round-trip, missing file, corrupt file, clear.UsageViewModelTests— cache hydration on init, deferred first fetch with fresh cache, immediate fetch with stale cache, cache write on success, cache clear on signOut.NotificationCoordinatorTests— ring buffer accumulation.Known pre-existing flake (unrelated to this PR): three
KeychainReaderTests(isSignedOut ...) share aUserDefaultskey and race under swift-testing's default parallel execution. Flakes ~3/5 runs regardless of this PR. Worth a follow-up to serialize the suite or inject a scratchUserDefaults.Post-Deploy Monitoring & Validation
No additional operational monitoring required: client-side macOS app with no server component. Users who launch the app will see the new dual-percent label on consumer plans. If the cache file is corrupt or missing on first launch post-upgrade, the app falls back to today's immediate-fetch behavior — no user-visible regression.
🤖 Generated with Claude Opus 4.7 via Claude Code