Skip to content

Dual-percent menu bar, response cache, and pruning pass - #10

Merged
nkootstra merged 10 commits into
mainfrom
feat/redesign
Apr 24, 2026
Merged

Dual-percent menu bar, response cache, and pruning pass#10
nkootstra merged 10 commits into
mainfrom
feat/redesign

Conversation

@nkootstra

Copy link
Copy Markdown
Owner

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:

  • Dual-percent menu bar: consumer plans render 5h% · 7d% inline, each number color-coded independently by warning/critical thresholds. Enterprise display is unchanged.
  • Persistent response cache at ~/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.
  • In-memory burn-rate ring buffer in 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 in 7fdeb6f.
  • Six locale translations (nl, de, fr, es, pt-BR, pt-PT). English-only going forward.
  • Usage history display, CSV export.

Testing

  • swift build clean.
  • swift test — 142 tests in 15 suites pass functionally.
  • New coverage:
    • 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.
  • Manual: launched in dev mode, menu bar renders dual-percent for consumer plans.

Known pre-existing flake (unrelated to this PR): three KeychainReaderTests (isSignedOut ...) share a UserDefaults key 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 scratch UserDefaults.

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

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.
@nkootstra
nkootstra merged commit 7b75954 into main Apr 24, 2026
3 checks passed
@nkootstra
nkootstra deleted the feat/redesign branch April 24, 2026 06:32
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant