Peter Steinbergerchore: merge main into jetbrains43098bf10 months agoHistory
1667 lines · 67.2 KB · Swift
RawDownload
import AppKitimport CodexBarCoreimport Observationimport ServiceManagement enum RefreshFrequency: String, CaseIterable, Identifiable {    case manual    case oneMinute    case twoMinutes    case fiveMinutes    case fifteenMinutes     var id: String { self.rawValue }     var seconds: TimeInterval? {        switch self {        case .manual: nil        case .oneMinute: 60        case .twoMinutes: 120        case .fiveMinutes: 300        case .fifteenMinutes: 900        }    }     var label: String {        switch self {        case .manual: "Manual"        case .oneMinute: "1 min"        case .twoMinutes: "2 min"        case .fiveMinutes: "5 min"        case .fifteenMinutes: "15 min"        }    }} enum MenuBarMetricPreference: String, CaseIterable, Identifiable {    case automatic    case primary    case secondary    case average     var id: String { self.rawValue }     var label: String {        switch self {        case .automatic: "Automatic"        case .primary: "Primary"        case .secondary: "Secondary"        case .average: "Average"        }    }} @MainActor@Observablefinal class SettingsStore {    private static let sharedDefaults = UserDefaults(suiteName: "group.com.steipete.codexbar")    /// Persisted provider display order.    ///    /// Stored as raw `UsageProvider` strings so new providers can be appended automatically without breaking.    private(set) var providerOrderRaw: [String] {        didSet { self.userDefaults.set(self.providerOrderRaw, forKey: "providerOrder") }    }     var refreshFrequency: RefreshFrequency {        didSet { self.userDefaults.set(self.refreshFrequency.rawValue, forKey: "refreshFrequency") }    }     var launchAtLogin: Bool {        didSet {            self.userDefaults.set(self.launchAtLogin, forKey: "launchAtLogin")            LaunchAtLoginManager.setEnabled(self.launchAtLogin)        }    }     /// Hidden toggle to reveal debug-only menu items (enable via defaults write com.steipete.CodexBar debugMenuEnabled    /// -bool YES).    var debugMenuEnabled: Bool {        didSet { self.userDefaults.set(self.debugMenuEnabled, forKey: "debugMenuEnabled") }    }     /// Debug-only: disable all Keychain access and hide cookie-based web options.    var debugDisableKeychainAccess: Bool {        didSet {            self.userDefaults.set(self.debugDisableKeychainAccess, forKey: "debugDisableKeychainAccess")            Self.sharedDefaults?.set(self.debugDisableKeychainAccess, forKey: "debugDisableKeychainAccess")            KeychainAccessGate.isDisabled = self.debugDisableKeychainAccess        }    }     private var debugLoadingPatternRaw: String? {        didSet {            if let raw = self.debugLoadingPatternRaw {                self.userDefaults.set(raw, forKey: "debugLoadingPattern")            } else {                self.userDefaults.removeObject(forKey: "debugLoadingPattern")            }        }    }     var statusChecksEnabled: Bool {        didSet { self.userDefaults.set(self.statusChecksEnabled, forKey: "statusChecksEnabled") }    }     var sessionQuotaNotificationsEnabled: Bool {        didSet {            self.userDefaults.set(self.sessionQuotaNotificationsEnabled, forKey: "sessionQuotaNotificationsEnabled")        }    }     /// When enabled, progress bars show "percent used" instead of "percent left".    var usageBarsShowUsed: Bool {        didSet { self.userDefaults.set(self.usageBarsShowUsed, forKey: "usageBarsShowUsed") }    }     /// Optional: show reset times as absolute clock values instead of countdowns.    var resetTimesShowAbsolute: Bool {        didSet { self.userDefaults.set(self.resetTimesShowAbsolute, forKey: "resetTimesShowAbsolute") }    }     /// Optional: use provider branding icons with a percentage in the menu bar.    var menuBarShowsBrandIconWithPercent: Bool {        didSet {            self.userDefaults.set(self.menuBarShowsBrandIconWithPercent, forKey: "menuBarShowsBrandIconWithPercent")        }    }     /// Optional: show all token accounts stacked in the menu (otherwise show a switcher bar).    var showAllTokenAccountsInMenu: Bool {        didSet { self.userDefaults.set(self.showAllTokenAccountsInMenu, forKey: "showAllTokenAccountsInMenu") }    }     /// Optional: choose which quota window drives the menu bar percentage.    private(set) var menuBarMetricPreferencesRaw: [String: String] {        didSet { self.userDefaults.set(self.menuBarMetricPreferencesRaw, forKey: "menuBarMetricPreferences") }    }     /// Optional: show provider cost summary from local usage logs (Codex + Claude).    var costUsageEnabled: Bool {        didSet { self.userDefaults.set(self.costUsageEnabled, forKey: "tokenCostUsageEnabled") }    }     /// Optional: hide personal info (emails) in menu bar + menu content.    var hidePersonalInfo: Bool {        didSet { self.userDefaults.set(self.hidePersonalInfo, forKey: "hidePersonalInfo") }    }     var randomBlinkEnabled: Bool {        didSet { self.userDefaults.set(self.randomBlinkEnabled, forKey: "randomBlinkEnabled") }    }     /// Optional: augment Claude usage with claude.ai web API (via browser cookies),    /// incl. "Extra usage" spend.    var claudeWebExtrasEnabled: Bool {        get { self.debugDisableKeychainAccess ? false : self.claudeWebExtrasEnabledRaw }        set { self.claudeWebExtrasEnabledRaw = newValue }    }     private var claudeWebExtrasEnabledRaw: Bool {        didSet { self.userDefaults.set(self.claudeWebExtrasEnabledRaw, forKey: "claudeWebExtrasEnabled") }    }     /// Optional: show Codex credits + Claude extra usage sections in the menu UI.    var showOptionalCreditsAndExtraUsage: Bool {        didSet {            self.userDefaults.set(self.showOptionalCreditsAndExtraUsage, forKey: "showOptionalCreditsAndExtraUsage")        }    }     /// Optional: fetch OpenAI web dashboard extras for Codex (browser cookies).    var openAIWebAccessEnabled: Bool {        didSet { self.userDefaults.set(self.openAIWebAccessEnabled, forKey: "openAIWebAccessEnabled") }    }     private var codexUsageDataSourceRaw: String? {        didSet {            if let raw = self.codexUsageDataSourceRaw {                self.userDefaults.set(raw, forKey: "codexUsageDataSource")            } else {                self.userDefaults.removeObject(forKey: "codexUsageDataSource")            }        }    }     private var claudeUsageDataSourceRaw: String? {        didSet {            if let raw = self.claudeUsageDataSourceRaw {                self.userDefaults.set(raw, forKey: "claudeUsageDataSource")            } else {                self.userDefaults.removeObject(forKey: "claudeUsageDataSource")            }        }    }     private var codexCookieSourceRaw: String? {        didSet {            if let raw = self.codexCookieSourceRaw {                self.userDefaults.set(raw, forKey: "codexCookieSource")            } else {                self.userDefaults.removeObject(forKey: "codexCookieSource")            }        }    }     private var claudeCookieSourceRaw: String? {        didSet {            if let raw = self.claudeCookieSourceRaw {                self.userDefaults.set(raw, forKey: "claudeCookieSource")            } else {                self.userDefaults.removeObject(forKey: "claudeCookieSource")            }        }    }     private var cursorCookieSourceRaw: String? {        didSet {            if let raw = self.cursorCookieSourceRaw {                self.userDefaults.set(raw, forKey: "cursorCookieSource")            } else {                self.userDefaults.removeObject(forKey: "cursorCookieSource")            }        }    }     private var opencodeCookieSourceRaw: String? {        didSet {            if let raw = self.opencodeCookieSourceRaw {                self.userDefaults.set(raw, forKey: "opencodeCookieSource")            } else {                self.userDefaults.removeObject(forKey: "opencodeCookieSource")            }        }    }     private var opencodeWorkspaceIDRaw: String? {        didSet {            if let raw = self.opencodeWorkspaceIDRaw, !raw.isEmpty {                self.userDefaults.set(raw, forKey: "opencodeWorkspaceID")            } else {                self.userDefaults.removeObject(forKey: "opencodeWorkspaceID")            }        }    }     private var factoryCookieSourceRaw: String? {        didSet {            if let raw = self.factoryCookieSourceRaw {                self.userDefaults.set(raw, forKey: "factoryCookieSource")            } else {                self.userDefaults.removeObject(forKey: "factoryCookieSource")            }        }    }     private var minimaxCookieSourceRaw: String? {        didSet {            if let raw = self.minimaxCookieSourceRaw {                self.userDefaults.set(raw, forKey: "minimaxCookieSource")            } else {                self.userDefaults.removeObject(forKey: "minimaxCookieSource")            }        }    }     private var kimiCookieSourceRaw: String? {        didSet {            if let raw = self.kimiCookieSourceRaw {                self.userDefaults.set(raw, forKey: "kimiCookieSource")            } else {                self.userDefaults.removeObject(forKey: "kimiCookieSource")            }        }    }     private var augmentCookieSourceRaw: String? {        didSet {            if let raw = self.augmentCookieSourceRaw {                self.userDefaults.set(raw, forKey: "augmentCookieSource")            } else {                self.userDefaults.removeObject(forKey: "augmentCookieSource")            }        }    }     private var ampCookieSourceRaw: String? {        didSet {            if let raw = self.ampCookieSourceRaw {                self.userDefaults.set(raw, forKey: "ampCookieSource")            } else {                self.userDefaults.removeObject(forKey: "ampCookieSource")            }        }    }     /// JetBrains IDE base path for quota file lookup.    var jetbrainsIDEBasePath: String {        didSet { self.userDefaults.set(self.jetbrainsIDEBasePath, forKey: "jetbrainsIDEBasePath") }    }     /// Optional: collapse provider icons into a single menu bar item with an in-menu switcher.    var mergeIcons: Bool {        didSet { self.userDefaults.set(self.mergeIcons, forKey: "mergeIcons") }    }     /// Optional: show provider icons in the in-menu switcher.    var switcherShowsIcons: Bool {        didSet { self.userDefaults.set(self.switcherShowsIcons, forKey: "switcherShowsIcons") }    }     /// MiniMax API region (stored in UserDefaults).    var minimaxAPIRegion: MiniMaxAPIRegion {        didSet { self.userDefaults.set(self.minimaxAPIRegion.rawValue, forKey: "minimaxAPIRegion") }    }     /// z.ai API region (stored in UserDefaults).    var zaiAPIRegion: ZaiAPIRegion {        didSet { self.userDefaults.set(self.zaiAPIRegion.rawValue, forKey: "zaiAPIRegion") }    }     /// z.ai API token (stored in Keychain).    var zaiAPIToken: String {        didSet { self.schedulePersistZaiAPIToken() }    }     /// Synthetic API key (stored in Keychain).    var syntheticAPIToken: String {        didSet { self.schedulePersistSyntheticAPIToken() }    }     /// Codex OpenAI cookie header (stored in Keychain).    var codexCookieHeader: String {        didSet { self.schedulePersistCodexCookieHeader() }    }     /// Claude session cookie header (stored in Keychain).    var claudeCookieHeader: String {        didSet { self.schedulePersistClaudeCookieHeader() }    }     /// Cursor session cookie header (stored in Keychain).    var cursorCookieHeader: String {        didSet { self.schedulePersistCursorCookieHeader() }    }     /// OpenCode session cookie header (stored in Keychain).    var opencodeCookieHeader: String {        didSet { self.schedulePersistOpenCodeCookieHeader() }    }     /// Optional OpenCode workspace ID override.    var opencodeWorkspaceID: String {        get { self.opencodeWorkspaceIDRaw ?? "" }        set {            let trimmed = newValue.trimmingCharacters(in: .whitespacesAndNewlines)            self.opencodeWorkspaceIDRaw = trimmed.isEmpty ? nil : trimmed        }    }     /// Factory session cookie header (stored in Keychain).    var factoryCookieHeader: String {        didSet { self.schedulePersistFactoryCookieHeader() }    }     /// MiniMax session cookie header (stored in Keychain).    var minimaxCookieHeader: String {        didSet { self.schedulePersistMiniMaxCookieHeader() }    }     /// MiniMax API token (stored in Keychain).    var minimaxAPIToken: String {        didSet { self.schedulePersistMiniMaxAPIToken() }    }     /// Augment session cookie header (stored in Keychain).    var augmentCookieHeader: String {        didSet { self.schedulePersistAugmentCookieHeader() }    }     /// Amp session cookie header (stored in Keychain).    var ampCookieHeader: String {        didSet { self.schedulePersistAmpCookieHeader() }    }     /// Copilot API token (stored in Keychain).    var copilotAPIToken: String {        didSet { self.schedulePersistCopilotAPIToken() }    }     /// Kimi auth token (stored in Keychain).    var kimiManualCookieHeader: String {        didSet { self.schedulePersistKimiAuthToken() }    }     /// Kimi K2 API token (stored in Keychain).    var kimiK2APIToken: String {        didSet { self.schedulePersistKimiK2APIToken() }    }     /// Token accounts loaded from the local config file.    var tokenAccountsByProvider: [UsageProvider: ProviderTokenAccountData] {        didSet { self.schedulePersistTokenAccounts() }    }     private var selectedMenuProviderRaw: String? {        didSet {            if let raw = self.selectedMenuProviderRaw {                self.userDefaults.set(raw, forKey: "selectedMenuProvider")            } else {                self.userDefaults.removeObject(forKey: "selectedMenuProvider")            }        }    }     /// Optional override for the loading animation pattern, exposed via the Debug tab.    var debugLoadingPattern: LoadingPattern? {        get { self.debugLoadingPatternRaw.flatMap(LoadingPattern.init(rawValue:)) }        set {            self.debugLoadingPatternRaw = newValue?.rawValue        }    }     var selectedMenuProvider: UsageProvider? {        get { self.selectedMenuProviderRaw.flatMap(UsageProvider.init(rawValue:)) }        set {            self.selectedMenuProviderRaw = newValue?.rawValue        }    }     var resetTimeDisplayStyle: ResetTimeDisplayStyle {        self.resetTimesShowAbsolute ? .absolute : .countdown    }     var codexUsageDataSource: CodexUsageDataSource {        get { CodexUsageDataSource(rawValue: self.codexUsageDataSourceRaw ?? "") ?? .auto }        set {            self.codexUsageDataSourceRaw = newValue.rawValue        }    }     var claudeUsageDataSource: ClaudeUsageDataSource {        get { ClaudeUsageDataSource(rawValue: self.claudeUsageDataSourceRaw ?? "") ?? .auto }        set {            self.claudeUsageDataSourceRaw = newValue.rawValue            if newValue != .cli {                self.claudeWebExtrasEnabled = false            }        }    }     var codexCookieSource: ProviderCookieSource {        get {            guard !self.debugDisableKeychainAccess else { return .off }            return ProviderCookieSource(rawValue: self.codexCookieSourceRaw ?? "") ?? .auto        }        set {            self.codexCookieSourceRaw = newValue.rawValue            self.openAIWebAccessEnabled = newValue.isEnabled        }    }     var claudeCookieSource: ProviderCookieSource {        get {            guard !self.debugDisableKeychainAccess else { return .off }            return ProviderCookieSource(rawValue: self.claudeCookieSourceRaw ?? "") ?? .auto        }        set { self.claudeCookieSourceRaw = newValue.rawValue }    }     var cursorCookieSource: ProviderCookieSource {        get {            guard !self.debugDisableKeychainAccess else { return .off }            return ProviderCookieSource(rawValue: self.cursorCookieSourceRaw ?? "") ?? .auto        }        set { self.cursorCookieSourceRaw = newValue.rawValue }    }     var opencodeCookieSource: ProviderCookieSource {        get {            guard !self.debugDisableKeychainAccess else { return .off }            return ProviderCookieSource(rawValue: self.opencodeCookieSourceRaw ?? "") ?? .auto        }        set { self.opencodeCookieSourceRaw = newValue.rawValue }    }     var factoryCookieSource: ProviderCookieSource {        get {            guard !self.debugDisableKeychainAccess else { return .off }            return ProviderCookieSource(rawValue: self.factoryCookieSourceRaw ?? "") ?? .auto        }        set { self.factoryCookieSourceRaw = newValue.rawValue }    }     var minimaxCookieSource: ProviderCookieSource {        get {            guard !self.debugDisableKeychainAccess else { return .off }            return ProviderCookieSource(rawValue: self.minimaxCookieSourceRaw ?? "") ?? .auto        }        set { self.minimaxCookieSourceRaw = newValue.rawValue }    } 

Lines 1–500 of 1667.