Files
steipete--codexbar/Sources/CodexBar/SettingsStore+MenuPreferences.swift
T
2026-07-13 12:22:33 +08:00

312 lines
11 KiB
Swift

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
}
}
}