312 lines
11 KiB
Swift
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
|
|
}
|
|
}
|
|
}
|