diff --git a/Sources/CodexBar/StatusItemController.swift b/Sources/CodexBar/StatusItemController.swift index dce63bf7..5a0a8a04 100644 --- a/Sources/CodexBar/StatusItemController.swift +++ b/Sources/CodexBar/StatusItemController.swift @@ -125,7 +125,7 @@ final class StatusItemController: NSObject, NSMenuDelegate, StatusItemControllin let usedPercent = (primary.usedPercent + secondary.usedPercent) / 2 return RateWindow(usedPercent: usedPercent, windowMinutes: nil, resetsAt: nil, resetDescription: nil) case .automatic: - if provider == .factory { + if provider == .factory || provider == .kimi { return snapshot?.secondary ?? snapshot?.primary } return snapshot?.primary ?? snapshot?.secondary diff --git a/Sources/CodexBar/UsageStore.swift b/Sources/CodexBar/UsageStore.swift index b42856c7..5164328a 100644 --- a/Sources/CodexBar/UsageStore.swift +++ b/Sources/CodexBar/UsageStore.swift @@ -314,8 +314,8 @@ final class UsageStore { for provider in self.enabledProviders() { guard let snapshot = self.snapshots[provider] else { continue } // Use the same window selection logic as menuBarPercentWindow: - // Factory uses secondary (premium) first, others use primary (session) first. - let window: RateWindow? = if provider == .factory { + // Factory and Kimi use secondary first, others use primary first. + let window: RateWindow? = if provider == .factory || provider == .kimi { snapshot.secondary ?? snapshot.primary } else { snapshot.primary ?? snapshot.secondary diff --git a/Tests/CodexBarTests/StatusItemAnimationTests.swift b/Tests/CodexBarTests/StatusItemAnimationTests.swift index e4c8a540..de7f4708 100644 --- a/Tests/CodexBarTests/StatusItemAnimationTests.swift +++ b/Tests/CodexBarTests/StatusItemAnimationTests.swift @@ -269,6 +269,45 @@ struct StatusItemAnimationTests { #expect(window?.usedPercent == 42) } + @Test + func menuBarPercentAutomaticPrefersRateLimitForKimi() { + let settings = SettingsStore( + configStore: testConfigStore(suiteName: "StatusItemAnimationTests-kimi-automatic"), + zaiTokenStore: NoopZaiTokenStore()) + settings.statusChecksEnabled = false + settings.refreshFrequency = .manual + settings.mergeIcons = true + settings.selectedMenuProvider = .kimi + settings.setMenuBarMetricPreference(.automatic, for: .kimi) + + let registry = ProviderRegistry.shared + if let kimiMeta = registry.metadata[.kimi] { + settings.setProviderEnabled(provider: .kimi, metadata: kimiMeta, enabled: true) + } + + let fetcher = UsageFetcher() + let store = UsageStore(fetcher: fetcher, browserDetection: BrowserDetection(cacheTTL: 0), settings: settings) + let controller = StatusItemController( + store: store, + settings: settings, + account: fetcher.loadAccountInfo(), + updater: DisabledUpdaterController(), + preferencesSelection: PreferencesSelection(), + statusBar: self.makeStatusBarForTesting()) + + let snapshot = UsageSnapshot( + primary: RateWindow(usedPercent: 12, windowMinutes: nil, resetsAt: nil, resetDescription: nil), + secondary: RateWindow(usedPercent: 42, windowMinutes: 300, resetsAt: nil, resetDescription: nil), + updatedAt: Date()) + + store._setSnapshotForTesting(snapshot, provider: .kimi) + store._setErrorForTesting(nil, provider: .kimi) + + let window = controller.menuBarMetricWindow(for: .kimi, snapshot: snapshot) + + #expect(window?.usedPercent == 42) + } + @Test func menuBarPercentUsesAverageForGemini() { let settings = SettingsStore( diff --git a/Tests/CodexBarTests/UsageStoreCoverageTests.swift b/Tests/CodexBarTests/UsageStoreCoverageTests.swift index 889ff4f2..32ea4285 100644 --- a/Tests/CodexBarTests/UsageStoreCoverageTests.swift +++ b/Tests/CodexBarTests/UsageStoreCoverageTests.swift @@ -71,6 +71,34 @@ struct UsageStoreCoverageTests { #expect(label.contains("openai-web")) } + @Test + func providerWithHighestUsagePrefersKimiRateLimitWindow() throws { + let settings = Self.makeSettingsStore(suite: "UsageStoreCoverageTests-kimi-highest") + let store = Self.makeUsageStore(settings: settings) + let metadata = ProviderRegistry.shared.metadata + + try settings.setProviderEnabled(provider: .codex, metadata: #require(metadata[.codex]), enabled: true) + try settings.setProviderEnabled(provider: .kimi, metadata: #require(metadata[.kimi]), enabled: true) + + let now = Date() + store._setSnapshotForTesting( + UsageSnapshot( + primary: RateWindow(usedPercent: 60, windowMinutes: nil, resetsAt: nil, resetDescription: nil), + secondary: nil, + updatedAt: now), + provider: .codex) + store._setSnapshotForTesting( + UsageSnapshot( + primary: RateWindow(usedPercent: 10, windowMinutes: nil, resetsAt: nil, resetDescription: nil), + secondary: RateWindow(usedPercent: 80, windowMinutes: 300, resetsAt: nil, resetDescription: nil), + updatedAt: now), + provider: .kimi) + + let highest = store.providerWithHighestUsage() + #expect(highest?.provider == .kimi) + #expect(highest?.usedPercent == 80) + } + @Test func providerAvailabilityAndSubscriptionDetection() { let zaiStore = InMemoryZaiTokenStore(value: "zai-token")