Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
51 changes: 1 addition & 50 deletions Sources/CodexBar/StatusItemController+Animation.swift
Original file line number Diff line number Diff line change
Expand Up @@ -223,7 +223,6 @@ extension StatusItemController {
return false
}

// swiftlint:disable function_body_length
@discardableResult
func applyIcon(phase: Double?) -> Bool {
guard let button = self.statusItem.button else { return false }
Expand Down Expand Up @@ -336,30 +335,6 @@ extension StatusItemController {
return false
}

if Self.shouldUseOpenRouterBrandFallback(provider: primaryProvider, snapshot: snapshot),
let brand = ProviderBrandIcon.image(for: primaryProvider)
{
let signature = [
"mode=openRouterFallback",
"provider=\(primaryProvider.rawValue)",
"style=\(String(describing: style))",
"primary=\(debugDouble(primary))",
"weekly=\(debugDouble(weekly))",
"credits=\(debugDouble(credits))",
"stale=\(stale ? "1" : "0")",
"status=\(statusIndicator.rawValue)",
"anim=\(needsAnimation ? "1" : "0")",
].joined(separator: "|")
if self.shouldSkipMergedIconRender(signature) {
return true
}
self.setButtonTitle(nil, for: button)
self.setButtonImage(
Self.brandImageWithStatusOverlay(brand: brand, statusIndicator: statusIndicator),
for: button)
return false
}

self.setButtonTitle(nil, for: button)
if let morphProgress {
let signature = [
Expand Down Expand Up @@ -408,8 +383,6 @@ extension StatusItemController {
return false
}

// swiftlint:enable function_body_length

private func shouldSkipMergedIconRender(_ signature: String) -> Bool {
guard self.shouldMergeIcons else {
self.lastAppliedMergedIconRenderSignature = signature
Expand Down Expand Up @@ -440,17 +413,7 @@ extension StatusItemController {
return
}

if Self.shouldUseOpenRouterBrandFallback(provider: provider, snapshot: snapshot),
let brand = ProviderBrandIcon.image(for: provider)
{
self.setButtonTitle(nil, for: button)
self.setButtonImage(
Self.brandImageWithStatusOverlay(
brand: brand,
statusIndicator: self.store.statusIndicator(for: provider)),
for: button)
return
}
// OpenRouter always gets a meter here — the brand-logo fallback was removed on purpose.
let resolved = snapshot.map {
IconRemainingResolver.resolvedPercents(
snapshot: $0,
Expand Down Expand Up @@ -775,18 +738,6 @@ extension StatusItemController {
}
}

nonisolated static func shouldUseOpenRouterBrandFallback(
provider: UsageProvider,
snapshot: UsageSnapshot?) -> Bool
{
guard provider == .openrouter,
let openRouterUsage = snapshot?.openRouterUsage
else {
return false
}
return openRouterUsage.keyQuotaStatus == .noLimitConfigured
}

nonisolated static func brandImageWithStatusOverlay(
brand: NSImage,
statusIndicator: ProviderStatusIndicator) -> NSImage
Expand Down
133 changes: 133 additions & 0 deletions Tests/CodexBarTests/StatusItemAnimationTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import Testing

@MainActor
@Suite(.serialized)
// swiftlint:disable:next type_body_length
struct StatusItemAnimationTests {
private func maxAlpha(in rep: NSBitmapImageRep) -> CGFloat {
var maxAlpha: CGFloat = 0
Expand Down Expand Up @@ -247,6 +248,138 @@ struct StatusItemAnimationTests {
#expect(alpha < 0.6)
}

@Test
func `open router without key limit uses meter icon when brand percent is disabled`() {
let settings = SettingsStore(
configStore: testConfigStore(suiteName: "StatusItemAnimationTests-openrouter-no-limit-meter"),
zaiTokenStore: NoopZaiTokenStore(),
syntheticTokenStore: NoopSyntheticTokenStore())
settings.statusChecksEnabled = false
settings.refreshFrequency = .manual
settings.mergeIcons = false
settings.menuBarShowsBrandIconWithPercent = false

let registry = ProviderRegistry.shared
if let openRouterMeta = registry.metadata[.openrouter] {
settings.setProviderEnabled(provider: .openrouter, metadata: openRouterMeta, enabled: true)
}
settings.openRouterAPIToken = "or-token"

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())
defer { controller.releaseStatusItemsForTesting() }

let snapshot = OpenRouterUsageSnapshot(
totalCredits: 50,
totalUsage: 45,
balance: 5,
usedPercent: 90,
keyDataFetched: true,
keyLimit: nil,
keyUsage: nil,
rateLimit: nil,
updatedAt: Date()).toUsageSnapshot()

store._setSnapshotForTesting(snapshot, provider: .openrouter)
store._setErrorForTesting(nil, provider: .openrouter)

controller.applyIcon(for: .openrouter, phase: nil)

guard let image = controller.statusItems[.openrouter]?.button?.image else {
#expect(Bool(false))
return
}

#expect(image.size.width == 18)
#expect(image.size.height == 18)
#expect(snapshot.openRouterUsage?.keyQuotaStatus == .noLimitConfigured)
#expect(controller.statusItems[.openrouter]?.button?.title.isEmpty == true)
#expect(MenuBarDisplayText.percentText(window: snapshot.primary, showUsed: false) == nil)

// With no key limit, the primary bar has no fill — just the dim track.
// A brand logo would be fully opaque here; the track is not.
let rep = image.representations.compactMap { $0 as? NSBitmapImageRep }.first(where: {
$0.pixelsWide == 36 && $0.pixelsHigh == 36
})
#expect(rep != nil)
if let rep {
let alpha = (rep.colorAt(x: 8, y: 25) ?? .clear).alphaComponent
#expect(alpha < 0.5)
}
}

@Test
func `open router key data not fetched still uses meter icon when brand percent is disabled`() {
let settings = SettingsStore(
configStore: testConfigStore(suiteName: "StatusItemAnimationTests-openrouter-no-fetch-meter"),
zaiTokenStore: NoopZaiTokenStore(),
syntheticTokenStore: NoopSyntheticTokenStore())
settings.statusChecksEnabled = false
settings.refreshFrequency = .manual
settings.mergeIcons = false
settings.menuBarShowsBrandIconWithPercent = false

let registry = ProviderRegistry.shared
if let openRouterMeta = registry.metadata[.openrouter] {
settings.setProviderEnabled(provider: .openrouter, metadata: openRouterMeta, enabled: true)
}
settings.openRouterAPIToken = "or-token"

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())
defer { controller.releaseStatusItemsForTesting() }

let snapshot = OpenRouterUsageSnapshot(
totalCredits: 50,
totalUsage: 45,
balance: 5,
usedPercent: 90,
keyDataFetched: false,
keyLimit: nil,
keyUsage: nil,
rateLimit: nil,
updatedAt: Date()).toUsageSnapshot()

store._setSnapshotForTesting(snapshot, provider: .openrouter)
store._setErrorForTesting(nil, provider: .openrouter)

controller.applyIcon(for: .openrouter, phase: nil)

guard let image = controller.statusItems[.openrouter]?.button?.image else {
#expect(Bool(false))
return
}

#expect(image.size.width == 18)
#expect(image.size.height == 18)
#expect(snapshot.openRouterUsage?.keyQuotaStatus == .unavailable)

// Even with no key data, OpenRouter still renders a meter rather than the brand logo.
// A brand logo would be fully opaque here; the unfilled track is not.
let rep = image.representations.compactMap { $0 as? NSBitmapImageRep }.first(where: {
$0.pixelsWide == 36 && $0.pixelsHigh == 36
})
#expect(rep != nil)
if let rep {
let alpha = (rep.colorAt(x: 8, y: 25) ?? .clear).alphaComponent
#expect(alpha < 0.5)
}
}

@Test
func `menu bar percent uses configured metric`() {
let settings = SettingsStore(
Expand Down
55 changes: 0 additions & 55 deletions Tests/CodexBarTests/StatusItemControllerMenuTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -96,61 +96,6 @@ struct StatusItemControllerMenuTests {
#expect(percent == 76)
}

@Test
func `open router brand fallback enabled when no key limit configured`() {
let snapshot = OpenRouterUsageSnapshot(
totalCredits: 50,
totalUsage: 45,
balance: 5,
usedPercent: 90,
keyDataFetched: true,
keyLimit: nil,
keyUsage: nil,
rateLimit: nil,
updatedAt: Date()).toUsageSnapshot()

#expect(StatusItemController.shouldUseOpenRouterBrandFallback(
provider: .openrouter,
snapshot: snapshot))
#expect(MenuBarDisplayText.percentText(window: snapshot.primary, showUsed: false) == nil)
}

@Test
func `open router brand fallback disabled when key quota fetch unavailable`() {
let snapshot = OpenRouterUsageSnapshot(
totalCredits: 50,
totalUsage: 45,
balance: 5,
usedPercent: 90,
keyDataFetched: false,
keyLimit: nil,
keyUsage: nil,
rateLimit: nil,
updatedAt: Date()).toUsageSnapshot()

#expect(!StatusItemController.shouldUseOpenRouterBrandFallback(
provider: .openrouter,
snapshot: snapshot))
}

@Test
func `open router brand fallback disabled when key quota available`() {
let snapshot = OpenRouterUsageSnapshot(
totalCredits: 50,
totalUsage: 45,
balance: 5,
usedPercent: 90,
keyLimit: 20,
keyUsage: 2,
rateLimit: nil,
updatedAt: Date()).toUsageSnapshot()

#expect(!StatusItemController.shouldUseOpenRouterBrandFallback(
provider: .openrouter,
snapshot: snapshot))
#expect(snapshot.primary?.usedPercent == 10)
}

@Test
@MainActor
func `menu card width stays at base width when menu accessories are present`() {
Expand Down