diff --git a/Sources/CodexBar/InlineUsageDashboardContent.swift b/Sources/CodexBar/InlineUsageDashboardContent.swift index 51b8eb891f..9e58ab07cf 100644 --- a/Sources/CodexBar/InlineUsageDashboardContent.swift +++ b/Sources/CodexBar/InlineUsageDashboardContent.swift @@ -98,7 +98,9 @@ extension UsageMenuCardView.Model { return [L("Select a DeepSeek Chrome profile in Settings.")] } } - guard input.showOptionalCreditsAndExtraUsage else { return nil } + guard input.tokenCostInlineDashboardEnabled, + input.showOptionalCreditsAndExtraUsage + else { return nil } guard let usage = input.snapshot?.deepseekUsage else { if input.snapshot?.deepseekDetailedUsageState == .webSessionRequired { return [L("Sign in to DeepSeek Platform in Chrome for detailed usage.")] @@ -240,6 +242,7 @@ extension UsageMenuCardView.Model { } if input.provider == .deepseek, !input.isRefreshing, + input.tokenCostInlineDashboardEnabled, input.showOptionalCreditsAndExtraUsage, let usage = input.snapshot?.deepseekUsage, !usage.daily.isEmpty diff --git a/Sources/CodexBar/Providers/Shared/ProviderTokenAccountSelection.swift b/Sources/CodexBar/Providers/Shared/ProviderTokenAccountSelection.swift index 5e98253091..c8f56173b0 100644 --- a/Sources/CodexBar/Providers/Shared/ProviderTokenAccountSelection.swift +++ b/Sources/CodexBar/Providers/Shared/ProviderTokenAccountSelection.swift @@ -13,7 +13,9 @@ enum ProviderTokenAccountSelection { settings: SettingsStore, override: TokenAccountOverride?) -> ProviderTokenAccount? { - if let override, override.provider == provider { return override.account } + if let override, override.provider == provider { + return override.account + } return settings.effectiveSelectedTokenAccount(for: provider) } @@ -23,11 +25,9 @@ enum ProviderTokenAccountSelection { settings: SettingsStore, override: TokenAccountOverride?) -> Bool { - guard settings.showOptionalCreditsAndExtraUsage else { return false } - guard provider == .deepseek, - let override, - override.provider == provider - else { return true } + guard provider == .deepseek else { return settings.showOptionalCreditsAndExtraUsage } + guard settings.costUsageEnabled, settings.showOptionalCreditsAndExtraUsage else { return false } + guard let override, override.provider == provider else { return true } return settings.selectedTokenAccount(for: provider)?.id == override.account.id } } diff --git a/Sources/CodexBar/SettingsStore+TokenCost.swift b/Sources/CodexBar/SettingsStore+TokenCost.swift index 354485615d..c4881e95d6 100644 --- a/Sources/CodexBar/SettingsStore+TokenCost.swift +++ b/Sources/CodexBar/SettingsStore+TokenCost.swift @@ -3,7 +3,11 @@ import Foundation extension SettingsStore { func costSummaryShowsInlineDashboard(for provider: UsageProvider) -> Bool { - self.isCostUsageEffectivelyEnabled(for: provider) && + // DeepSeek has no cost submenu, so any enabled cost-summary style falls back to inline. + if provider == .deepseek { + return self.costUsageEnabled + } + return self.isCostUsageEffectivelyEnabled(for: provider) && self.costSummaryDisplayStyle.showsInlineSummary } @@ -67,8 +71,12 @@ extension SettingsStore { .appendingPathComponent("archived_sessions", isDirectory: true) }() - if hasAnyJsonl(in: codexRoot) { return true } - if let archivedCodexRoot, hasAnyJsonl(in: archivedCodexRoot) { return true } + if hasAnyJsonl(in: codexRoot) { + return true + } + if let archivedCodexRoot, hasAnyJsonl(in: archivedCodexRoot) { + return true + } let claudeRoots: [URL] = { if let env = env["CLAUDE_CONFIG_DIR"]?.trimmingCharacters(in: .whitespacesAndNewlines), diff --git a/Tests/CodexBarTests/MenuCardDeepSeekTests.swift b/Tests/CodexBarTests/MenuCardDeepSeekTests.swift index 47fee0791c..ecda9be11b 100644 --- a/Tests/CodexBarTests/MenuCardDeepSeekTests.swift +++ b/Tests/CodexBarTests/MenuCardDeepSeekTests.swift @@ -90,7 +90,7 @@ struct MenuCardDeepSeekTests { } @Test - func `model hides optional deepseek usage when extras disabled`() throws { + func `model hides deepseek usage when extras are disabled despite cost summary enabled`() throws { let now = Date() let metadata = try #require(ProviderDefaults.metadata[.deepseek]) let snapshot = Self.makeSnapshot(now: now, usageSummary: Self.sampleDeepSeekSummary(now: now)) @@ -110,7 +110,7 @@ struct MenuCardDeepSeekTests { lastError: nil, usageBarsShowUsed: false, resetTimeDisplayStyle: .countdown, - tokenCostUsageEnabled: false, + tokenCostUsageEnabled: true, showOptionalCreditsAndExtraUsage: false, hidePersonalInfo: false, now: now)) @@ -120,7 +120,37 @@ struct MenuCardDeepSeekTests { } @Test - func `model explains unavailable optional deepseek usage`() throws { + func `model shows deepseek usage when cost summary and extras are enabled`() throws { + let now = Date() + let metadata = try #require(ProviderDefaults.metadata[.deepseek]) + let snapshot = Self.makeSnapshot(now: now, usageSummary: Self.sampleDeepSeekSummary(now: now)) + + let model = UsageMenuCardView.Model.make(.init( + provider: .deepseek, + metadata: metadata, + snapshot: snapshot, + credits: nil, + creditsError: nil, + dashboard: nil, + dashboardError: nil, + tokenSnapshot: nil, + tokenError: nil, + account: AccountInfo(email: nil, plan: nil), + isRefreshing: false, + lastError: nil, + usageBarsShowUsed: false, + resetTimeDisplayStyle: .countdown, + tokenCostUsageEnabled: true, + showOptionalCreditsAndExtraUsage: true, + hidePersonalInfo: false, + now: now)) + + #expect(model.inlineUsageDashboard?.accessibilityLabel == "DeepSeek this month token usage trend") + #expect(model.usageNotes.contains { $0.contains("Today:") }) + } + + @Test + func `model explains unavailable deepseek usage when cost summary is enabled`() throws { let now = Date() let metadata = try #require(ProviderDefaults.metadata[.deepseek]) let snapshot = Self.makeSnapshot(now: now) @@ -140,7 +170,7 @@ struct MenuCardDeepSeekTests { lastError: nil, usageBarsShowUsed: false, resetTimeDisplayStyle: .countdown, - tokenCostUsageEnabled: false, + tokenCostUsageEnabled: true, showOptionalCreditsAndExtraUsage: true, hidePersonalInfo: false, now: now)) @@ -170,7 +200,7 @@ struct MenuCardDeepSeekTests { lastError: nil, usageBarsShowUsed: false, resetTimeDisplayStyle: .countdown, - tokenCostUsageEnabled: false, + tokenCostUsageEnabled: true, showOptionalCreditsAndExtraUsage: true, hidePersonalInfo: false, now: now)) @@ -203,7 +233,7 @@ struct MenuCardDeepSeekTests { lastError: nil, usageBarsShowUsed: false, resetTimeDisplayStyle: .countdown, - tokenCostUsageEnabled: false, + tokenCostUsageEnabled: true, showOptionalCreditsAndExtraUsage: true, hidePersonalInfo: false, now: now)) @@ -213,7 +243,7 @@ struct MenuCardDeepSeekTests { } @Test - func `browser only sign in remains visible when optional usage is hidden`() throws { + func `browser only sign in remains visible when cost summary is disabled`() throws { let now = Date() let metadata = try #require(ProviderDefaults.metadata[.deepseek]) let snapshot = DeepSeekUsageSnapshot( @@ -272,7 +302,7 @@ struct MenuCardDeepSeekTests { lastError: nil, usageBarsShowUsed: false, resetTimeDisplayStyle: .countdown, - tokenCostUsageEnabled: false, + tokenCostUsageEnabled: true, showOptionalCreditsAndExtraUsage: true, hidePersonalInfo: false, now: now)) @@ -282,7 +312,7 @@ struct MenuCardDeepSeekTests { } @Test - func `model shows optional deepseek usage when extras enabled`() throws { + func `model hides deepseek usage when cost summary is disabled despite extras enabled`() throws { let now = Date() let metadata = try #require(ProviderDefaults.metadata[.deepseek]) let snapshot = Self.makeSnapshot(now: now, usageSummary: Self.sampleDeepSeekSummary(now: now)) @@ -307,7 +337,7 @@ struct MenuCardDeepSeekTests { hidePersonalInfo: false, now: now)) - #expect(model.inlineUsageDashboard?.accessibilityLabel == "DeepSeek this month token usage trend") - #expect(model.usageNotes.contains { $0.contains("Today:") }) + #expect(model.inlineUsageDashboard == nil) + #expect(model.usageNotes.isEmpty) } } diff --git a/Tests/CodexBarTests/ProviderSettingsDescriptorTests.swift b/Tests/CodexBarTests/ProviderSettingsDescriptorTests.swift index 5a1cb1fae5..5a3c4c6125 100644 --- a/Tests/CodexBarTests/ProviderSettingsDescriptorTests.swift +++ b/Tests/CodexBarTests/ProviderSettingsDescriptorTests.swift @@ -787,8 +787,11 @@ extension ProviderSettingsDescriptorTests { } @Test - func `deepseek detailed usage runs only for the active api token account`() throws { + func `deepseek detailed usage requires cost extras and active api token account`() throws { let fixture = try self.makeSettingsFixture(suite: "ProviderSettingsDescriptorTests-deepseek-account-usage") + fixture.settings.showOptionalCreditsAndExtraUsage = true + fixture.settings.costSummaryOption = .inlineSummary + #expect(fixture.settings.costSummaryShowsInlineDashboard(for: .deepseek)) fixture.settings.addTokenAccount(provider: .deepseek, label: "Personal", token: "token-1") fixture.settings.addTokenAccount(provider: .deepseek, label: "Work", token: "token-2") let accounts = fixture.settings.tokenAccounts(for: .deepseek) @@ -803,6 +806,30 @@ extension ProviderSettingsDescriptorTests { provider: .deepseek, settings: fixture.settings, override: TokenAccountOverride(provider: .deepseek, account: inactive))) + fixture.settings.costSummaryOption = .costSubmenu + #expect(fixture.settings.costSummaryShowsInlineDashboard(for: .deepseek)) + #expect(ProviderTokenAccountSelection.shouldIncludeOptionalUsage( + provider: .deepseek, + settings: fixture.settings, + override: TokenAccountOverride(provider: .deepseek, account: active))) + fixture.settings.costSummaryOption = .both + #expect(fixture.settings.costSummaryShowsInlineDashboard(for: .deepseek)) + fixture.settings.costSummaryOption = .off + #expect(!fixture.settings.costSummaryShowsInlineDashboard(for: .deepseek)) + #expect(!ProviderTokenAccountSelection.shouldIncludeOptionalUsage( + provider: .deepseek, + settings: fixture.settings, + override: TokenAccountOverride(provider: .deepseek, account: active))) + fixture.settings.showOptionalCreditsAndExtraUsage = false + #expect(!ProviderTokenAccountSelection.shouldIncludeOptionalUsage( + provider: .codex, + settings: fixture.settings, + override: nil)) + fixture.settings.costSummaryOption = .inlineSummary + #expect(!ProviderTokenAccountSelection.shouldIncludeOptionalUsage( + provider: .deepseek, + settings: fixture.settings, + override: TokenAccountOverride(provider: .deepseek, account: active))) } @Test