Skip to content

Commit 388ee14

Browse files
Yuxin-Qiaosteipete
andauthored
Fix Antigravity quota summary lanes (#1726)
* Fix Antigravity quota summary lanes * Align Antigravity icon lanes with quota summary ranking * fix: align Antigravity ranking with rendered lanes --------- Co-authored-by: Peter Steinberger <steipete@gmail.com>
1 parent 929d55a commit 388ee14

7 files changed

Lines changed: 115 additions & 53 deletions

File tree

Sources/CodexBar/IconRemainingResolver.swift

Lines changed: 1 addition & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,7 @@ import CodexBarCore
33
enum IconRemainingResolver {
44
private static let visibleZeroPercent = 0.0001
55
private static let antigravityQuotaSummaryWindowIDPrefix = "antigravity-quota-summary-"
6-
private static let antigravityGeminiQuotaBucketIDPrefix = "gemini-"
7-
// Antigravity quota summaries currently expose exact 5-hour session and weekly buckets for the compact icon.
6+
// Antigravity quota summaries expose exact 5-hour session and weekly buckets for the compact icon.
87
private static let sessionWindowMinutes = 5 * 60
98
private static let weeklyWindowMinutes = 7 * 24 * 60
109

@@ -38,13 +37,6 @@ enum IconRemainingResolver {
3837
} ?? []
3938
guard !quotaSummaryWindows.isEmpty else { return nil }
4039

41-
let geminiWindows = quotaSummaryWindows.filter(Self.isAntigravityGeminiQuotaSummaryWindow)
42-
// The Antigravity menu-bar icon represents Gemini quotas. If any Gemini cadence is present,
43-
// keep missing Gemini lanes empty instead of silently borrowing Claude + GPT quota.
44-
if !geminiWindows.isEmpty {
45-
return self.antigravityQuotaSummaryPair(in: geminiWindows.filter(\.usageKnown))
46-
?? (primary: nil, secondary: nil)
47-
}
4840
return self.antigravityQuotaSummaryPair(in: quotaSummaryWindows.filter(\.usageKnown))
4941
}
5042

@@ -58,15 +50,6 @@ enum IconRemainingResolver {
5850
return (primary: session, secondary: weekly)
5951
}
6052

61-
private static func isAntigravityGeminiQuotaSummaryWindow(_ window: NamedRateWindow) -> Bool {
62-
self.antigravityQuotaSummaryBucketID(for: window)?.hasPrefix(self.antigravityGeminiQuotaBucketIDPrefix) == true
63-
}
64-
65-
private static func antigravityQuotaSummaryBucketID(for window: NamedRateWindow) -> String? {
66-
guard window.id.hasPrefix(self.antigravityQuotaSummaryWindowIDPrefix) else { return nil }
67-
return String(window.id.dropFirst(self.antigravityQuotaSummaryWindowIDPrefix.count))
68-
}
69-
7053
/// Returns the highest-usage window for an exact Antigravity compact-icon cadence.
7154
private static func mostConstrainedWindow(in windows: [NamedRateWindow], windowMinutes: Int) -> RateWindow? {
7255
windows

Sources/CodexBar/UsageStore+HighestUsage.swift

Lines changed: 26 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -30,10 +30,7 @@ extension UsageStore {
3030
private func menuBarMetricWindowForHighestUsage(provider: UsageProvider, snapshot: UsageSnapshot) -> RateWindow? {
3131
let effectivePreference = self.settings.menuBarMetricPreference(for: provider, snapshot: snapshot)
3232
if provider == .antigravity, effectivePreference == .automatic {
33-
let windows = IconRemainingResolver.resolvedWindows(snapshot: snapshot, style: .antigravity)
34-
return [windows.primary, windows.secondary]
35-
.compactMap(\.self)
36-
.max(by: { $0.usedPercent < $1.usedPercent })
33+
return Self.mostConstrainedAntigravityQuotaSummaryWindow(snapshot: snapshot)
3734
}
3835
return MenuBarMetricWindowResolver.rateWindow(
3936
preference: effectivePreference,
@@ -56,9 +53,9 @@ extension UsageStore {
5653
return percents.allSatisfy { $0 >= 100 }
5754
}
5855
if provider == .antigravity, effectivePreference == .automatic {
59-
let windows = IconRemainingResolver.resolvedWindows(snapshot: snapshot, style: .antigravity)
60-
let percents = [windows.primary?.usedPercent, windows.secondary?.usedPercent].compactMap(\.self)
61-
return percents.allSatisfy { $0 >= 100 }
56+
let windows = Self.antigravityRenderedQuotaSummaryWindows(snapshot: snapshot)
57+
guard !windows.isEmpty else { return true }
58+
return windows.allSatisfy { $0.usedPercent >= 100 }
6259
}
6360
if provider == .copilot,
6461
effectivePreference == .automatic,
@@ -82,4 +79,26 @@ extension UsageStore {
8279

8380
return true
8481
}
82+
83+
private nonisolated static func mostConstrainedAntigravityQuotaSummaryWindow(
84+
snapshot: UsageSnapshot)
85+
-> RateWindow?
86+
{
87+
let windows = self.antigravityRenderedQuotaSummaryWindows(snapshot: snapshot)
88+
guard !windows.isEmpty else { return nil }
89+
90+
let usableWindows = windows.filter { $0.usedPercent < 100 }
91+
if let maxUsable = usableWindows.max(by: { $0.usedPercent < $1.usedPercent }) {
92+
return maxUsable
93+
}
94+
return windows.max(by: { $0.usedPercent < $1.usedPercent })
95+
}
96+
97+
private nonisolated static func antigravityRenderedQuotaSummaryWindows(
98+
snapshot: UsageSnapshot)
99+
-> [RateWindow]
100+
{
101+
let windows = IconRemainingResolver.resolvedWindows(snapshot: snapshot, style: .antigravity)
102+
return [windows.primary, windows.secondary].compactMap(\.self)
103+
}
85104
}

Sources/CodexBarCore/Providers/Antigravity/AntigravityStatusProbe.swift

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -299,11 +299,25 @@ public struct AntigravityStatusSnapshot: Sendable {
299299

300300
private static func displayTitle(forQuotaGroup group: AntigravityQuotaSummaryGroup) -> String {
301301
let title = group.displayName.trimmingCharacters(in: .whitespacesAndNewlines)
302+
let lowercasedTitle = title.lowercased()
303+
if lowercasedTitle.contains("gemini") {
304+
return "Gemini"
305+
}
306+
if lowercasedTitle.contains("claude") || lowercasedTitle.contains("gpt") {
307+
return "Claude/GPT"
308+
}
302309
return title.isEmpty ? "Quota" : title
303310
}
304311

305312
private static func displayTitle(forQuotaBucket bucket: AntigravityQuotaSummaryBucket) -> String {
306-
bucket.displayName
313+
switch self.quotaBucketKind(for: bucket) {
314+
case .session:
315+
"5-hour"
316+
case .weekly:
317+
"weekly"
318+
case .other:
319+
bucket.displayName
320+
}
307321
}
308322

309323
private static func windowMinutes(forQuotaBucket bucket: AntigravityQuotaSummaryBucket) -> Int? {

Tests/CodexBarTests/AntigravityQuotaSummaryTests.swift

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -37,10 +37,10 @@ struct AntigravityQuotaSummaryTests {
3737
"antigravity-quota-summary-3p-weekly",
3838
])
3939
#expect(windows.map(\.title) == [
40-
"Gemini Models Five Hour Limit",
41-
"Gemini Models Weekly Limit",
42-
"Claude and GPT models Five Hour Limit",
43-
"Claude and GPT models Weekly Limit",
40+
"Gemini 5-hour",
41+
"Gemini weekly",
42+
"Claude/GPT 5-hour",
43+
"Claude/GPT weekly",
4444
])
4545
#expect(windows.map(\.window.windowMinutes) == [300, 10080, 300, 10080])
4646
#expect(windows.map { $0.window.remainingPercent.rounded() } == [91, 82, 73, 64])

Tests/CodexBarTests/CodexbarTests.swift

Lines changed: 44 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -101,9 +101,9 @@ struct CodexBarTests {
101101
let windows = IconRemainingResolver.resolvedWindows(snapshot: snapshot, style: .antigravity)
102102

103103
#expect(windows.primary?.windowMinutes == 300)
104-
#expect(windows.primary?.remainingPercent == 3)
104+
#expect(windows.primary?.remainingPercent == 2)
105105
#expect(windows.secondary?.windowMinutes == 10080)
106-
#expect(windows.secondary?.remainingPercent == 16)
106+
#expect(windows.secondary?.remainingPercent == 1)
107107
}
108108

109109
@Test
@@ -139,7 +139,7 @@ struct CodexBarTests {
139139
}
140140

141141
@Test
142-
func `antigravity quota summary icon prefers gemini ids over display titles`() {
142+
func `antigravity quota summary icon uses most constrained quota summary lanes`() {
143143
let snapshot = UsageSnapshot(
144144
primary: nil,
145145
secondary: nil,
@@ -166,12 +166,12 @@ struct CodexBarTests {
166166

167167
let windows = IconRemainingResolver.resolvedWindows(snapshot: snapshot, style: .antigravity)
168168

169-
#expect(windows.primary?.remainingPercent == 60)
170-
#expect(windows.secondary?.remainingPercent == 70)
169+
#expect(windows.primary?.remainingPercent == 2)
170+
#expect(windows.secondary?.remainingPercent == 1)
171171
}
172172

173173
@Test
174-
func `antigravity quota summary icon does not borrow missing gemini weekly from claude gpt`() {
174+
func `antigravity quota summary icon can pair gemini session with claude gpt weekly`() {
175175
let snapshot = UsageSnapshot(
176176
primary: nil,
177177
secondary: nil,
@@ -191,11 +191,11 @@ struct CodexBarTests {
191191
let windows = IconRemainingResolver.resolvedWindows(snapshot: snapshot, style: .antigravity)
192192

193193
#expect(windows.primary?.remainingPercent == 60)
194-
#expect(windows.secondary == nil)
194+
#expect(windows.secondary?.remainingPercent == 1)
195195
}
196196

197197
@Test
198-
func `antigravity quota summary icon treats unknown gemini rows as present`() {
198+
func `antigravity quota summary icon ignores unknown rows while ranking known lanes`() {
199199
let snapshot = UsageSnapshot(
200200
primary: nil,
201201
secondary: nil,
@@ -216,7 +216,42 @@ struct CodexBarTests {
216216
let windows = IconRemainingResolver.resolvedWindows(snapshot: snapshot, style: .antigravity)
217217

218218
#expect(windows.primary == nil)
219-
#expect(windows.secondary == nil)
219+
#expect(windows.secondary?.remainingPercent == 1)
220+
}
221+
222+
@Test
223+
func `antigravity used icon percent matches constrained claude gpt lane`() {
224+
let snapshot = UsageSnapshot(
225+
primary: nil,
226+
secondary: nil,
227+
tertiary: nil,
228+
extraRateWindows: [
229+
NamedRateWindow(
230+
id: "antigravity-quota-summary-gemini-5h",
231+
title: "Gemini Session",
232+
window: RateWindow(usedPercent: 20, windowMinutes: 300, resetsAt: nil, resetDescription: nil)),
233+
NamedRateWindow(
234+
id: "antigravity-quota-summary-gemini-weekly",
235+
title: "Gemini Weekly",
236+
window: RateWindow(usedPercent: 30, windowMinutes: 10080, resetsAt: nil, resetDescription: nil)),
237+
NamedRateWindow(
238+
id: "antigravity-quota-summary-3p-5h",
239+
title: "Claude + GPT Session",
240+
window: RateWindow(usedPercent: 95, windowMinutes: 300, resetsAt: nil, resetDescription: nil)),
241+
NamedRateWindow(
242+
id: "antigravity-quota-summary-3p-weekly",
243+
title: "Claude + GPT Weekly",
244+
window: RateWindow(usedPercent: 40, windowMinutes: 10080, resetsAt: nil, resetDescription: nil)),
245+
],
246+
updatedAt: Date())
247+
248+
let percents = IconRemainingResolver.resolvedPercents(
249+
snapshot: snapshot,
250+
style: .antigravity,
251+
showUsed: true)
252+
253+
#expect(percents.primary == 95)
254+
#expect(percents.secondary == 40)
220255
}
221256

222257
@Test

Tests/CodexBarTests/StatusItemAnimationSignatureTests.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -138,7 +138,7 @@ struct StatusItemAnimationSignatureTests {
138138

139139
#expect(signature.contains("provider=antigravity"))
140140
#expect(signature.contains("style=combined"))
141-
#expect(signature.contains("primary=99.000"))
141+
#expect(signature.contains("primary=98.000"))
142142
#expect(signature.contains("weekly=1.000"))
143143
}
144144

Tests/CodexBarTests/UsageStoreHighestUsageTests.swift

Lines changed: 24 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -242,11 +242,13 @@ struct UsageStoreHighestUsageTests {
242242
#expect(highest?.provider == .codex)
243243
#expect(highest?.usedPercent == 70)
244244
}
245+
}
245246

247+
extension UsageStoreHighestUsageTests {
246248
@Test
247-
func `automatic metric ranks antigravity by rendered gemini quota summary lanes`() {
249+
func `automatic metric ranks antigravity by rendered quota summary lanes across groups`() {
248250
let settings = SettingsStore(
249-
configStore: testConfigStore(suiteName: "UsageStoreHighestUsageTests-antigravity-rendered-gemini"),
251+
configStore: testConfigStore(suiteName: "UsageStoreHighestUsageTests-antigravity-all-summary"),
250252
zaiTokenStore: NoopZaiTokenStore(),
251253
syntheticTokenStore: NoopSyntheticTokenStore())
252254
settings.refreshFrequency = .manual
@@ -271,17 +273,26 @@ struct UsageStoreHighestUsageTests {
271273
secondary: nil,
272274
updatedAt: Date()),
273275
provider: .codex)
276+
let antigravity = self.antigravityQuotaSummarySnapshot(
277+
geminiSessionUsed: 10,
278+
geminiWeeklyUsed: 20,
279+
otherSessionUsed: 95,
280+
otherWeeklyUsed: 90)
281+
let unknownCadence = NamedRateWindow(
282+
id: "antigravity-quota-summary-future-daily",
283+
title: "Future daily lane",
284+
window: RateWindow(
285+
usedPercent: 99,
286+
windowMinutes: 24 * 60,
287+
resetsAt: nil,
288+
resetDescription: nil))
274289
store._setSnapshotForTesting(
275-
self.antigravityQuotaSummarySnapshot(
276-
geminiSessionUsed: 10,
277-
geminiWeeklyUsed: 20,
278-
otherSessionUsed: 95,
279-
otherWeeklyUsed: 90),
290+
antigravity.with(extraRateWindows: (antigravity.extraRateWindows ?? []) + [unknownCadence]),
280291
provider: .antigravity)
281292

282293
var highest = store.providerWithHighestUsage()
283-
#expect(highest?.provider == .codex)
284-
#expect(highest?.usedPercent == 80)
294+
#expect(highest?.provider == .antigravity)
295+
#expect(highest?.usedPercent == 95)
285296

286297
store._setSnapshotForTesting(
287298
self.antigravityQuotaSummarySnapshot(
@@ -306,9 +317,9 @@ struct UsageStoreHighestUsageTests {
306317
}
307318

308319
@Test
309-
func `automatic metric keeps antigravity when one rendered lane has quota`() {
320+
func `automatic metric skips exhausted antigravity quota summary lanes when another remains usable`() {
310321
let settings = SettingsStore(
311-
configStore: testConfigStore(suiteName: "UsageStoreHighestUsageTests-antigravity-all-100"),
322+
configStore: testConfigStore(suiteName: "UsageStoreHighestUsageTests-antigravity-summary-usable"),
312323
zaiTokenStore: NoopZaiTokenStore(),
313324
syntheticTokenStore: NoopSyntheticTokenStore())
314325
settings.refreshFrequency = .manual
@@ -340,8 +351,8 @@ struct UsageStoreHighestUsageTests {
340351
store._setSnapshotForTesting(antigravitySnapshot, provider: .antigravity)
341352

342353
let highest = store.providerWithHighestUsage()
343-
#expect(highest?.provider == .antigravity)
344-
#expect(highest?.usedPercent == 100)
354+
#expect(highest?.provider == .codex)
355+
#expect(highest?.usedPercent == 80)
345356
}
346357

347358
@Test

0 commit comments

Comments
 (0)