import CodexBarCore import Foundation enum MenuBarIconStyle: String, CaseIterable { case critters case bars case iconAndPercent var label: String { switch self { case .critters: L("menu_bar_style_critters") case .bars: L("menu_bar_style_bars") case .iconAndPercent: L("menu_bar_style_icon_percent") } } } enum SwitcherRowsOption: String, CaseIterable { case icons case progress var label: String { switch self { case .icons: L("switcher_rows_icons") case .progress: L("switcher_rows_progress") } } } enum UsageBarsFillOption: String, CaseIterable { case remaining case used var label: String { switch self { case .remaining: L("usage_bars_fill_remaining") case .used: L("usage_bars_fill_used") } } } enum ResetTimesOption: String, CaseIterable { case countdown case clock var label: String { switch self { case .countdown: L("reset_times_countdown") case .clock: L("reset_times_clock") } } } enum ConfettiCelebrationOption: String, CaseIterable { case off case session case weekly case both var label: String { switch self { case .off: L("confetti_option_off") case .session: L("confetti_option_session") case .weekly: L("confetti_option_weekly") case .both: L("confetti_option_both") } } } enum CostSummaryOption: String, CaseIterable { case off case inlineSummary case costSubmenu case both var label: String { switch self { case .off: L("cost_summary_off") case .inlineSummary: CostSummaryDisplayStyle.inlineSummary.label case .costSubmenu: CostSummaryDisplayStyle.costSubmenu.label case .both: CostSummaryDisplayStyle.both.label } } } extension SettingsStore { var menuBarIconStyle: MenuBarIconStyle { get { if self.menuBarShowsBrandIconWithPercent { return .iconAndPercent } return self.menuBarHidesCritters ? .bars : .critters } set { switch newValue { case .critters: self.menuBarShowsBrandIconWithPercent = false self.menuBarHidesCritters = false case .bars: self.menuBarShowsBrandIconWithPercent = false self.menuBarHidesCritters = true case .iconAndPercent: self.menuBarShowsBrandIconWithPercent = true } } } var switcherRowsOption: SwitcherRowsOption { get { self.switcherShowsIcons ? .icons : .progress } set { self.switcherShowsIcons = newValue == .icons } } var usageBarsFillOption: UsageBarsFillOption { get { self.usageBarsShowUsed ? .used : .remaining } set { self.usageBarsShowUsed = newValue == .used } } var resetTimesOption: ResetTimesOption { get { self.resetTimesShowAbsolute ? .clock : .countdown } set { self.resetTimesShowAbsolute = newValue == .clock } } var confettiCelebrationOption: ConfettiCelebrationOption { get { switch (self.confettiOnSessionLimitResetsEnabled, self.confettiOnWeeklyLimitResetsEnabled) { case (false, false): .off case (true, false): .session case (false, true): .weekly case (true, true): .both } } set { self.confettiOnSessionLimitResetsEnabled = newValue == .session || newValue == .both self.confettiOnWeeklyLimitResetsEnabled = newValue == .weekly || newValue == .both } } var costSummaryOption: CostSummaryOption { get { guard self.costUsageEnabled else { return .off } switch self.costSummaryDisplayStyle { case .inlineSummary: return .inlineSummary case .costSubmenu: return .costSubmenu case .both: return .both } } set { switch newValue { case .off: self.costUsageEnabled = false case .inlineSummary: self.costSummaryDisplayStyle = .inlineSummary self.costUsageEnabled = true case .costSubmenu: self.costSummaryDisplayStyle = .costSubmenu self.costUsageEnabled = true case .both: self.costSummaryDisplayStyle = .both self.costUsageEnabled = true } } } func menuBarMetricPreference(for provider: UsageProvider) -> MenuBarMetricPreference { if Self.isBalanceOnlyProvider(provider), provider != .mistral { return .automatic } if provider == .mistral { let raw = self.menuBarMetricPreferencesRaw[provider.rawValue] ?? "" let preference = MenuBarMetricPreference(rawValue: raw) ?? .automatic switch preference { case .automatic, .monthlyPlan: return preference case .primary, .secondary, .primaryAndSecondary, .tertiary, .extraUsage, .average: return .automatic } } if provider == .openrouter { let raw = self.menuBarMetricPreferencesRaw[provider.rawValue] ?? "" let preference = MenuBarMetricPreference(rawValue: raw) ?? .automatic switch preference { case .automatic, .primary: return preference case .secondary, .primaryAndSecondary, .average, .tertiary, .extraUsage, .monthlyPlan: return .automatic } } let raw = self.menuBarMetricPreferencesRaw[provider.rawValue] ?? "" let preference = MenuBarMetricPreference(rawValue: raw) ?? .automatic if preference == .average, !self.menuBarMetricSupportsAverage(for: provider) { return .automatic } if preference == .primaryAndSecondary, !self.menuBarMetricSupportsPrimaryAndSecondary(for: provider) { return .automatic } if preference == .tertiary, !self.menuBarMetricSupportsTertiary(for: provider) { return .automatic } if preference == .extraUsage, !self.menuBarMetricSupportsExtraUsage(for: provider) { return .automatic } if preference == .monthlyPlan { return .automatic } return preference } func setMenuBarMetricPreference(_ preference: MenuBarMetricPreference, for provider: UsageProvider) { if Self.isBalanceOnlyProvider(provider), provider != .mistral { self.menuBarMetricPreferencesRaw[provider.rawValue] = MenuBarMetricPreference.automatic.rawValue return } if provider == .mistral { switch preference { case .automatic, .monthlyPlan: self.menuBarMetricPreferencesRaw[provider.rawValue] = preference.rawValue case .primary, .secondary, .primaryAndSecondary, .tertiary, .extraUsage, .average: self.menuBarMetricPreferencesRaw[provider.rawValue] = MenuBarMetricPreference.automatic.rawValue } return } if provider == .openrouter { switch preference { case .automatic, .primary: self.menuBarMetricPreferencesRaw[provider.rawValue] = preference.rawValue case .secondary, .primaryAndSecondary, .average, .tertiary, .extraUsage, .monthlyPlan: self.menuBarMetricPreferencesRaw[provider.rawValue] = MenuBarMetricPreference.automatic.rawValue } return } if preference == .primaryAndSecondary, !self.menuBarMetricSupportsPrimaryAndSecondary(for: provider) { self.menuBarMetricPreferencesRaw[provider.rawValue] = MenuBarMetricPreference.automatic.rawValue return } if preference == .tertiary, !self.menuBarMetricSupportsTertiary(for: provider) { self.menuBarMetricPreferencesRaw[provider.rawValue] = MenuBarMetricPreference.automatic.rawValue return } if preference == .extraUsage, !self.menuBarMetricSupportsExtraUsage(for: provider) { self.menuBarMetricPreferencesRaw[provider.rawValue] = MenuBarMetricPreference.automatic.rawValue return } if preference == .monthlyPlan { self.menuBarMetricPreferencesRaw[provider.rawValue] = MenuBarMetricPreference.automatic.rawValue return } self.menuBarMetricPreferencesRaw[provider.rawValue] = preference.rawValue } func menuBarMetricSupportsAverage(for provider: UsageProvider) -> Bool { provider == .gemini } func menuBarMetricSupportsPrimaryAndSecondary(for provider: UsageProvider) -> Bool { provider == .codex || provider == .claude } func menuBarMetricSupportsTertiary(for provider: UsageProvider) -> Bool { provider == .cursor || provider == .perplexity || provider == .zai } func menuBarMetricSupportsTertiary(for provider: UsageProvider, snapshot: UsageSnapshot?) -> Bool { if provider == .cursor || provider == .zai { return snapshot?.tertiary != nil } return self.menuBarMetricSupportsTertiary(for: provider) } func menuBarMetricSupportsExtraUsage(for provider: UsageProvider) -> Bool { provider == .cursor || provider == .claude } func menuBarMetricSupportsExtraUsage(for provider: UsageProvider, snapshot: UsageSnapshot?) -> Bool { guard self.menuBarMetricSupportsExtraUsage(for: provider) else { return false } guard let cost = snapshot?.providerCost else { return false } return cost.limit > 0 } func menuBarMetricPreference(for provider: UsageProvider, snapshot: UsageSnapshot?) -> MenuBarMetricPreference { let preference = self.menuBarMetricPreference(for: provider) if preference == .tertiary, !self.menuBarMetricSupportsTertiary(for: provider, snapshot: snapshot) { return .automatic } if preference == .extraUsage, !self.menuBarMetricSupportsExtraUsage(for: provider, snapshot: snapshot) { return .automatic } return preference } func isCostUsageEffectivelyEnabled(for provider: UsageProvider) -> Bool { self.costUsageEnabled && ProviderDescriptorRegistry.descriptor(for: provider).tokenCost.supportsTokenCost } var resetTimeDisplayStyle: ResetTimeDisplayStyle { self.resetTimesShowAbsolute ? .absolute : .countdown } static func isBalanceOnlyProvider(_ provider: UsageProvider) -> Bool { switch provider { case .deepseek, .mistral, .kimik2, .moonshot, .poe, .crossmodel: true default: false } } }