From 89213cb13b5becc65330c9fe9ecd990fff11dc7a Mon Sep 17 00:00:00 2001 From: iamtoruk Date: Sun, 13 Sep 2026 12:50:25 -0700 Subject: [PATCH] menubar: one set of quota colour bands, 60/70/95, for the flame, popover and dock --- CHANGELOG.md | 1 + .../CodeBurnMenubar/Data/QuotaSummary.swift | 14 +++++++------- .../CodeBurnMenubar/Views/CapacityDockView.swift | 16 +++++++++------- .../QuotaSummaryHeadlineTests.swift | 12 ++++++------ 4 files changed, 23 insertions(+), 20 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e06d97c3..70412631 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,6 +14,7 @@ - **The Capacity Dock gauge can report the short usage window instead of the weekly one, without expanding the dock.** The resting rail shows one number per provider, and that number was always the weekly (else monthly) billing window. Clicking the provider already resting in the rail now switches its gauge to the provider's short rolling window โ€” Claude's 5-hour limit, Codex's 5-hour or daily window, any `Hourly`, `Daily` or session row an adapter reports โ€” and clicking again switches back. The choice is stored per provider under `CodeBurnCapacityDockGlanceWindows`, so Claude can sit on its 5-hour window while Codex stays weekly, and it survives relaunch. A per-model row such as `Weekly ยท Opus` is never read as a short window, a provider that reports only one window keeps the plain click-to-pin behaviour, and a stored horizon the provider stops reporting falls back to the window it does report rather than blanking the gauge to `--`. The rail's geometry is untouched, VoiceOver and keyboard users get the switch as a named action on the provider cell with the window named in the cell's value, and Escape or a click outside still unpins the dock. (#1243) ### Changed +- **Quota colours use one set of bands everywhere: green under 60%, yellow to 70%, orange to 95%, red from 95%.** The flame, the popover rows and the Capacity Dock rings each had their own thresholds; the dock now reads the same severity the flame does. - **The Capacity Dock card and the popover quota rows no longer print a pace verdict under each window.** The "On pace" / "Lasts until reset" caption from #1314 added a third line to every window cell and made the card read as clutter; the cells are back to percent, label and countdown, up to three windows share one row again with a fourth wrapping to a second, and the pace math still feeds the tooltip and the agent-tab card. ### Fixed diff --git a/mac/Sources/CodeBurnMenubar/Data/QuotaSummary.swift b/mac/Sources/CodeBurnMenubar/Data/QuotaSummary.swift index 9a5cb265..cd186a0d 100644 --- a/mac/Sources/CodeBurnMenubar/Data/QuotaSummary.swift +++ b/mac/Sources/CodeBurnMenubar/Data/QuotaSummary.swift @@ -79,17 +79,17 @@ struct QuotaSummary: Equatable { /// to "you're over" (red) โ€” matches what the user expects from a warning /// indicator in the menu bar. static func severity(for percent: Double) -> Severity { - if percent >= 0.9 { return .danger } - if percent >= 0.75 { return .critical } - if percent >= 0.5 { return .warning } + if percent >= 0.95 { return .danger } + if percent >= 0.7 { return .critical } + if percent >= 0.6 { return .warning } return .normal } enum Severity { - case normal // <50% green - case warning // 50-75% yellow - case critical // 75-90% orange - case danger // >=90% red + case normal // <60% green + case warning // 60-70% yellow + case critical // 70-95% orange + case danger // >=95% red } /// The glance value (percent + color) for Capacity Dock. Every provider is diff --git a/mac/Sources/CodeBurnMenubar/Views/CapacityDockView.swift b/mac/Sources/CodeBurnMenubar/Views/CapacityDockView.swift index 183c814a..b77f1e3c 100644 --- a/mac/Sources/CodeBurnMenubar/Views/CapacityDockView.swift +++ b/mac/Sources/CodeBurnMenubar/Views/CapacityDockView.swift @@ -225,14 +225,16 @@ enum CapacityDockGlance { static let todayHeight: CGFloat = sectionPadTop + captionLine + pillGap + todayContentHeight + sectionPadBottom - /// Four bands, matching the rail's own sense of escalation: comfortable, - /// watch it, nearly out, over. Rings start green because a ring with no - /// colour reads as broken. + /// The menu-bar flame's bands, so the rail and the flame never disagree + /// about a window. Rings start green because a ring with no colour reads + /// as broken. static func severityColor(_ fraction: Double) -> Color { - if fraction >= 0.9 { return .red } - if fraction >= 0.8 { return .orange } - if fraction >= 0.7 { return .yellow } - return .green + switch QuotaSummary.severity(for: fraction) { + case .normal: .green + case .warning: .yellow + case .critical: .orange + case .danger: .red + } } /// Share of the pill the tint covers. Anything outside 0...1 is a bad diff --git a/mac/Tests/CodeBurnMenubarTests/QuotaSummaryHeadlineTests.swift b/mac/Tests/CodeBurnMenubarTests/QuotaSummaryHeadlineTests.swift index c5d3e38e..42d0b6eb 100644 --- a/mac/Tests/CodeBurnMenubarTests/QuotaSummaryHeadlineTests.swift +++ b/mac/Tests/CodeBurnMenubarTests/QuotaSummaryHeadlineTests.swift @@ -84,11 +84,11 @@ struct QuotaSummaryHeadlineTests { @Test("severity steps at the 0.50 / 0.75 / 0.90 band boundaries") func severityBoundaries() { - #expect(QuotaSummary.severity(for: 0.49) == .normal) - #expect(QuotaSummary.severity(for: 0.50) == .warning) - #expect(QuotaSummary.severity(for: 0.74) == .warning) - #expect(QuotaSummary.severity(for: 0.75) == .critical) - #expect(QuotaSummary.severity(for: 0.89) == .critical) - #expect(QuotaSummary.severity(for: 0.90) == .danger) + #expect(QuotaSummary.severity(for: 0.59) == .normal) + #expect(QuotaSummary.severity(for: 0.60) == .warning) + #expect(QuotaSummary.severity(for: 0.69) == .warning) + #expect(QuotaSummary.severity(for: 0.70) == .critical) + #expect(QuotaSummary.severity(for: 0.94) == .critical) + #expect(QuotaSummary.severity(for: 0.95) == .danger) } }