693 lines · 32.5 KB · Swift
RawDownload
import AppKitimport CodexBarCoreimport Observationimport ServiceManagement enum RefreshFrequency: String, CaseIterable, Identifiable {    case manual    case oneMinute    case twoMinutes    case fiveMinutes    case fifteenMinutes    case thirtyMinutes     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        case .thirtyMinutes: 1800        }    }     var label: String {        switch self {        case .manual: L("refresh_manual")        case .oneMinute: L("refresh_1min")        case .twoMinutes: L("refresh_2min")        case .fiveMinutes: L("refresh_5min")        case .fifteenMinutes: L("refresh_15min")        case .thirtyMinutes: L("refresh_30min")        }    }} enum MenuBarMetricPreference: String, CaseIterable, Identifiable {    case automatic    case primary    case secondary    case tertiary    case extraUsage    case average    case monthlyPlan     var id: String {        self.rawValue    }     var label: String {        switch self {        case .automatic: L("metric_pref_automatic")        case .primary: L("metric_pref_primary")        case .secondary: L("metric_pref_secondary")        case .tertiary: L("metric_pref_tertiary")        case .extraUsage: L("metric_pref_extra_usage")        case .average: L("metric_pref_average")        case .monthlyPlan: L("metric_mistral_monthly_plan")        }    }} enum KiroMenuBarDisplayMode: String, CaseIterable, Identifiable {    case automatic    case hidden    case creditsLeft    case percentLeft    case creditsAndPercent    case usedAndTotal    case overageCreditsWhenExhausted    case overageCostWhenExhausted    case overageCreditsAndCostWhenExhausted     var id: String {        self.rawValue    }     var label: String {        switch self {        case .automatic: "Automatic"        case .hidden: "Hidden"        case .creditsLeft: "Credits left"        case .percentLeft: "Percent left"        case .creditsAndPercent: "Credits + percent"        case .usedAndTotal: "Used / total"        case .overageCreditsWhenExhausted: "Overage credits at zero"        case .overageCostWhenExhausted: "Overage cost at zero"        case .overageCreditsAndCostWhenExhausted: "Overage credits + cost at zero"        }    }} enum MultiAccountMenuLayout: String, CaseIterable, Identifiable {    case segmented    case stacked     var id: String {        self.rawValue    }     var label: String {        switch self {        case .segmented: L("multi_account_layout_segmented")        case .stacked: L("multi_account_layout_stacked")        }    }} struct CachedCodexAccountReconciliationSnapshot {    let activeSource: CodexActiveSource    let loadedAt: Date    let snapshot: CodexAccountReconciliationSnapshot} struct CachedCodexAccountMenuProjection: Equatable {    let activeSource: CodexActiveSource    let loadedAt: Date    let projection: CodexVisibleAccountProjection} enum CodexAccountMenuProjectionRevalidationResult: Equatable {    case skipped    case discarded    case unchanged    case updated} @MainActor@Observablefinal class SettingsStore {    static let sharedDefaults = AppGroupSupport.sharedDefaults()    static let mergedOverviewProviderLimit = 3    static let productionCodexAccountReconciliationSnapshotCacheInterval: TimeInterval = 2    static let isRunningTests: Bool = {        let env = ProcessInfo.processInfo.environment        if env["XCTestConfigurationFilePath"] != nil { return true }        if env["TESTING_LIBRARY_VERSION"] != nil { return true }        if env["SWIFT_TESTING"] != nil { return true }        return NSClassFromString("XCTestCase") != nil    }()     #if DEBUG    static var codexAccountReconciliationSnapshotCacheIntervalOverrideForTesting: TimeInterval?    #endif     @ObservationIgnored let userDefaults: UserDefaults    @ObservationIgnored let configStore: CodexBarConfigStore    @ObservationIgnored let antigravityOAuthCredentialsStore: AntigravityOAuthCredentialsStore    @ObservationIgnored var config: CodexBarConfig    @ObservationIgnored var configPersistTask: Task<Void, Never>?    @ObservationIgnored var configLoading = false    @ObservationIgnored var tokenAccountsLoaded = false    @ObservationIgnored var cachedCodexAccountReconciliationSnapshot:        CachedCodexAccountReconciliationSnapshot?    @ObservationIgnored var cachedCodexAccountMenuProjection: CachedCodexAccountMenuProjection?    @ObservationIgnored var codexAccountReconciliationGeneration: UInt = 0    #if DEBUG    @ObservationIgnored var _test_codexAccountSnapshotLoader:        (@Sendable (CodexActiveSource) -> CodexAccountReconciliationSnapshot)?    #endif    @ObservationIgnored var mergedMenuLastSelectedWasOverviewStorage = false    @ObservationIgnored var selectedMenuProviderRawStorage: String?    var defaultsState: SettingsDefaultsState    var configRevision: Int = 0    var providerOrder: [UsageProvider] = []    var providerEnablement: [UsageProvider: Bool] = [:]     static func shouldBridgeSharedDefaults(for userDefaults: UserDefaults) -> Bool {        if !self.isRunningTests { return true }        if userDefaults === UserDefaults.standard { return true }        if let shared = sharedDefaults, userDefaults === shared { return true }        return false    }     init(        userDefaults: UserDefaults = .standard,        configStore: CodexBarConfigStore = CodexBarConfigStore(),        zaiTokenStore: any ZaiTokenStoring = KeychainZaiTokenStore(),        syntheticTokenStore: any SyntheticTokenStoring = KeychainSyntheticTokenStore(),        codexCookieStore: any CookieHeaderStoring = KeychainCookieHeaderStore(            account: "codex-cookie",            promptKind: .codexCookie),        claudeCookieStore: any CookieHeaderStoring = KeychainCookieHeaderStore(            account: "claude-cookie",            promptKind: .claudeCookie),        cursorCookieStore: any CookieHeaderStoring = KeychainCookieHeaderStore(            account: "cursor-cookie",            promptKind: .cursorCookie),        opencodeCookieStore: any CookieHeaderStoring = KeychainCookieHeaderStore(            account: "opencode-cookie",            promptKind: .opencodeCookie),        factoryCookieStore: any CookieHeaderStoring = KeychainCookieHeaderStore(            account: "factory-cookie",            promptKind: .factoryCookie),        minimaxCookieStore: any MiniMaxCookieStoring = KeychainMiniMaxCookieStore(),        minimaxAPITokenStore: any MiniMaxAPITokenStoring = KeychainMiniMaxAPITokenStore(),        kimiTokenStore: any KimiTokenStoring = KeychainKimiTokenStore(),        kimiK2TokenStore: any KimiK2TokenStoring = KeychainKimiK2TokenStore(),        augmentCookieStore: any CookieHeaderStoring = KeychainCookieHeaderStore(            account: "augment-cookie",            promptKind: .augmentCookie),        ampCookieStore: any CookieHeaderStoring = KeychainCookieHeaderStore(            account: "amp-cookie",            promptKind: .ampCookie),        copilotTokenStore: any CopilotTokenStoring = KeychainCopilotTokenStore(),        tokenAccountStore: any ProviderTokenAccountStoring = FileTokenAccountStore(),        antigravityOAuthCredentialsStore: AntigravityOAuthCredentialsStore = AntigravityOAuthCredentialsStore(),        performInitialProviderDetection: Bool = !SettingsStore.isRunningTests)    {        let appGroupID = AppGroupSupport.currentGroupID()        let appGroupMigration: AppGroupSupport.MigrationResult        if Self.isRunningTests {            appGroupMigration = AppGroupSupport.migrateLegacyDataIfNeeded(standardDefaults: userDefaults)        } else {            Self.scheduleAppGroupMigration()            appGroupMigration = AppGroupSupport.MigrationResult(status: .targetUnavailable)        }        let sharedDefaultsAvailable = Self.sharedDefaults != nil        if !Self.isRunningTests {            CodexBarLog.logger(LogCategories.settings).info(                "App group resolved",                metadata: [                    "groupID": appGroupID,                    "sharedDefaultsAvailable": sharedDefaultsAvailable ? "1" : "0",                    "migrationStatus": appGroupMigration.status.rawValue,                    "migratedSnapshot": appGroupMigration.copiedSnapshot ? "1" : "0",                    "migratedDefaults": "\(appGroupMigration.copiedDefaults)",                ])        }         if userDefaults.object(forKey: "openAIWebAccessEnabled") == nil,           let legacyOpenAIWebAccess = userDefaults.object(forKey: "openAIWebAccess") as? Bool        {            userDefaults.set(legacyOpenAIWebAccess, forKey: "openAIWebAccessEnabled")        }        let hasStoredOpenAIWebAccessPreference = userDefaults.object(forKey: "openAIWebAccessEnabled") != nil        let hadExistingConfig = (try? configStore.load()) != nil        let legacyStores = CodexBarConfigMigrator.LegacyStores(            zaiTokenStore: zaiTokenStore,            syntheticTokenStore: syntheticTokenStore,            codexCookieStore: codexCookieStore,            claudeCookieStore: claudeCookieStore,            cursorCookieStore: cursorCookieStore,            opencodeCookieStore: opencodeCookieStore,            factoryCookieStore: factoryCookieStore,            minimaxCookieStore: minimaxCookieStore,            minimaxAPITokenStore: minimaxAPITokenStore,            kimiTokenStore: kimiTokenStore,            kimiK2TokenStore: kimiK2TokenStore,            augmentCookieStore: augmentCookieStore,            ampCookieStore: ampCookieStore,            copilotTokenStore: copilotTokenStore,            tokenAccountStore: tokenAccountStore)        let config = CodexBarConfigMigrator.loadOrMigrate(            configStore: configStore,            userDefaults: userDefaults,            stores: legacyStores)        self.userDefaults = userDefaults        self.configStore = configStore        self.antigravityOAuthCredentialsStore = antigravityOAuthCredentialsStore        self.config = config        self.configLoading = true        let defaultsState = Self.loadDefaultsState(userDefaults: userDefaults)        self.defaultsState = defaultsState        self.mergedMenuLastSelectedWasOverviewStorage = defaultsState.mergedMenuLastSelectedWasOverview        self.selectedMenuProviderRawStorage = defaultsState.selectedMenuProviderRaw        self.updateProviderState(config: config)        self.configLoading = false        CodexBarLog.setFileLoggingEnabled(self.debugFileLoggingEnabled)        userDefaults.removeObject(forKey: "showCodexUsage")        userDefaults.removeObject(forKey: "showClaudeUsage")        LaunchAtLoginManager.setEnabled(self.launchAtLogin)        if performInitialProviderDetection {            self.runInitialProviderDetectionIfNeeded()        }        self.ensureAlibabaProviderAutoEnabledIfNeeded()        self.applyTokenCostDefaultIfNeeded()        if self.claudeUsageDataSource != .cli {            if Self.isRunningTests {                self.claudeWebExtrasEnabled = false            } else {                self.defaultsState.claudeWebExtrasEnabledRaw = false            }        }        let resolvedOpenAIWebAccessEnabled = if hasStoredOpenAIWebAccessPreference {            self.defaultsState.openAIWebAccessEnabled        } else {            Self.inferredInitialOpenAIWebAccessEnabled(                config: config,                hadExistingConfig: hadExistingConfig)        }        if Self.isRunningTests {            self.openAIWebAccessEnabled = resolvedOpenAIWebAccessEnabled        } else {            self.defaultsState.openAIWebAccessEnabled = resolvedOpenAIWebAccessEnabled        }        KeychainAccessGate.isDisabled = self.debugDisableKeychainAccess    }} extension SettingsStore {    private static func scheduleAppGroupMigration() {        Task.detached(priority: .utility) {            let result = AppGroupSupport.migrateLegacyDataIfNeeded()            CodexBarLog.logger(LogCategories.settings).info(                "App group migration completed",                metadata: [                    "migrationStatus": result.status.rawValue,                    "migratedSnapshot": result.copiedSnapshot ? "1" : "0",                    "migratedDefaults": "\(result.copiedDefaults)",                ])        }    }     private static func inferredInitialOpenAIWebAccessEnabled(        config: CodexBarConfig,        hadExistingConfig: Bool) -> Bool    {        guard let codex = config.providerConfig(for: .codex) else { return false }        if let cookieSource = codex.cookieSource { return cookieSource.isEnabled }        if codex.sanitizedCookieHeader != nil { return true }        return hadExistingConfig    }     private static func loadDefaultsState(userDefaults: UserDefaults) -> SettingsDefaultsState {        let refreshDefault = userDefaults.string(forKey: "refreshFrequency")            .flatMap(RefreshFrequency.init(rawValue:))        let refreshFrequency = refreshDefault ?? .fiveMinutes        if Self.isRunningTests, refreshDefault == nil {            userDefaults.set(refreshFrequency.rawValue, forKey: "refreshFrequency")        }        let launchAtLogin = userDefaults.object(forKey: "launchAtLogin") as? Bool ?? false        let debugMenuEnabled = userDefaults.object(forKey: "debugMenuEnabled") as? Bool ?? false        let debugDisableKeychainAccess = Self.loadDebugDisableKeychainAccess(userDefaults: userDefaults)        let debugFileLoggingEnabled = userDefaults.object(forKey: "debugFileLoggingEnabled") as? Bool ?? false        let debugLogLevelRaw = userDefaults.string(forKey: "debugLogLevel") ?? CodexBarLog.Level.verbose.rawValue        if Self.isRunningTests, userDefaults.string(forKey: "debugLogLevel") == nil {            userDefaults.set(debugLogLevelRaw, forKey: "debugLogLevel")        }        let debugLoadingPatternRaw = userDefaults.string(forKey: "debugLoadingPattern")        let debugKeepCLISessionsAlive = userDefaults.object(forKey: "debugKeepCLISessionsAlive") as? Bool ?? false        let statusChecksEnabled = userDefaults.object(forKey: "statusChecksEnabled") as? Bool ?? true        let sessionQuotaDefault = userDefaults.object(forKey: "sessionQuotaNotificationsEnabled") as? Bool        let sessionQuotaNotificationsEnabled = sessionQuotaDefault ?? true        if Self.isRunningTests, sessionQuotaDefault == nil {            userDefaults.set(true, forKey: "sessionQuotaNotificationsEnabled")        }        let quotaWarnings = Self.loadQuotaWarningDefaults(userDefaults: userDefaults)        let quotaWarningMarkersVisibleDefault = userDefaults.object(forKey: "quotaWarningMarkersVisible") as? Bool        let quotaWarningMarkersVisible = quotaWarningMarkersVisibleDefault ?? true        if Self.isRunningTests, quotaWarningMarkersVisibleDefault == nil {            userDefaults.set(true, forKey: "quotaWarningMarkersVisible")        }        let weeklyProgressWorkDays = userDefaults.object(forKey: "weeklyProgressWorkDays") as? Int        let usageBarsShowUsed = userDefaults.object(forKey: "usageBarsShowUsed") as? Bool ?? false        let resetTimesShowAbsolute = userDefaults.object(forKey: "resetTimesShowAbsolute") as? Bool ?? false        let providerChangelogLinksEnabled = userDefaults.object(            forKey: "providerChangelogLinksEnabled") as? Bool ?? false        let menuBarShowsBrandIconWithPercent = userDefaults.object(            forKey: "menuBarShowsBrandIconWithPercent") as? Bool ?? false        let menuBarHidesCritters = userDefaults.object(forKey: "menuBarHidesCritters") as? Bool ?? false        let menuBarDisplayModeRaw = userDefaults.string(forKey: "menuBarDisplayMode")            ?? MenuBarDisplayMode.percent.rawValue        let kiroMenuBarDisplayModeRaw = userDefaults.string(forKey: "kiroMenuBarDisplayMode")            ?? KiroMenuBarDisplayMode.automatic.rawValue        let historicalTrackingEnabled = userDefaults.object(forKey: "historicalTrackingEnabled") as? Bool ?? false        let multiAccountMenuLayoutRaw = Self.loadMultiAccountMenuLayoutRaw(userDefaults: userDefaults)        let resolvedPreferences = Self.loadMenuBarMetricPreferences(userDefaults: userDefaults)        let copilotBudgetExtrasEnabled = userDefaults.object(forKey: "copilotBudgetExtrasEnabled") as? Bool ?? false        let copilotIconSecondaryWindowIDRaw = Self.loadCopilotIconSecondaryWindowIDRaw(userDefaults: userDefaults)        let costUsageEnabled = userDefaults.object(forKey: "tokenCostUsageEnabled") as? Bool ?? false        let rawCostUsageHistoryDays = userDefaults.object(forKey: "tokenCostUsageHistoryDays") as? Int ?? 30        let costUsageHistoryDays = max(1, min(365, rawCostUsageHistoryDays))        let hidePersonalInfo = userDefaults.object(forKey: "hidePersonalInfo") as? Bool ?? false        let randomBlinkEnabled = userDefaults.object(forKey: "randomBlinkEnabled") as? Bool ?? false        let confettiOnWeeklyLimitResetsEnabled = userDefaults.object(            forKey: "confettiOnWeeklyLimitResetsEnabled") as? Bool ?? false        let menuBarShowsHighestUsage = userDefaults.object(forKey: "menuBarShowsHighestUsage") as? Bool ?? false        let claudeOAuthKeychainPromptModeRaw = userDefaults.string(forKey: "claudeOAuthKeychainPromptMode")        let claudeOAuthKeychainReadStrategyRaw = userDefaults.string(forKey: "claudeOAuthKeychainReadStrategy")        let claudeWebExtrasEnabledRaw = userDefaults.object(forKey: "claudeWebExtrasEnabled") as? Bool ?? false        let creditsExtrasDefault = userDefaults.object(forKey: "showOptionalCreditsAndExtraUsage") as? Bool        let showOptionalCreditsAndExtraUsage = creditsExtrasDefault ?? true        if Self.isRunningTests, creditsExtrasDefault == nil {            userDefaults.set(true, forKey: "showOptionalCreditsAndExtraUsage")        }        let openAIWebAccessDefault = userDefaults.object(forKey: "openAIWebAccessEnabled") as? Bool        let openAIWebAccessEnabled = openAIWebAccessDefault ?? false        if Self.isRunningTests, openAIWebAccessDefault == nil {            userDefaults.set(false, forKey: "openAIWebAccessEnabled")        }        let openAIWebBatterySaverDefault = userDefaults.object(forKey: "openAIWebBatterySaverEnabled") as? Bool        let openAIWebBatterySaverEnabled = openAIWebBatterySaverDefault ?? false        if Self.isRunningTests, openAIWebBatterySaverDefault == nil {            userDefaults.set(false, forKey: "openAIWebBatterySaverEnabled")        }        let providerStorageFootprintsDefault = userDefaults.object(forKey: "providerStorageFootprintsEnabled") as? Bool        let providerStorageFootprintsEnabled = providerStorageFootprintsDefault ?? false        if Self.isRunningTests, providerStorageFootprintsDefault == nil {            userDefaults.set(false, forKey: "providerStorageFootprintsEnabled")        }        let jetbrainsIDEBasePath = userDefaults.string(forKey: "jetbrainsIDEBasePath") ?? ""        let mergeIcons = userDefaults.object(forKey: "mergeIcons") as? Bool ?? true        let switcherShowsIcons = userDefaults.object(forKey: "switcherShowsIcons") as? Bool ?? true        let mergedMenuLastSelectedWasOverview = userDefaults.object(            forKey: "mergedMenuLastSelectedWasOverview") as? Bool ?? false        let mergedOverviewSelectedProvidersRaw = userDefaults.array(            forKey: "mergedOverviewSelectedProviders") as? [String] ?? []        let selectedMenuProviderRaw = userDefaults.string(forKey: "selectedMenuProvider")        let providerDetectionCompleted = userDefaults.object(forKey: "providerDetectionCompleted") as? Bool ?? false        let providersSortedAlphabetically = userDefaults.object(            forKey: "providersSortedAlphabetically") as? Bool ?? false        let appLanguageRaw = userDefaults.string(forKey: "appLanguage")        return SettingsDefaultsState(            refreshFrequency: refreshFrequency,            launchAtLogin: launchAtLogin,            debugMenuEnabled: debugMenuEnabled,            debugDisableKeychainAccess: debugDisableKeychainAccess,            debugFileLoggingEnabled: debugFileLoggingEnabled,            debugLogLevelRaw: debugLogLevelRaw,            debugLoadingPatternRaw: debugLoadingPatternRaw,            debugKeepCLISessionsAlive: debugKeepCLISessionsAlive,            statusChecksEnabled: statusChecksEnabled,            sessionQuotaNotificationsEnabled: sessionQuotaNotificationsEnabled,            quotaWarningNotificationsEnabled: quotaWarnings.notificationsEnabled,            quotaWarningThresholdsRaw: quotaWarnings.thresholdsRaw,            quotaWarningSessionThresholdsRaw: quotaWarnings.sessionThresholdsRaw,            quotaWarningWeeklyThresholdsRaw: quotaWarnings.weeklyThresholdsRaw,            quotaWarningSessionEnabled: quotaWarnings.sessionEnabled,            quotaWarningWeeklyEnabled: quotaWarnings.weeklyEnabled,            quotaWarningSoundEnabled: quotaWarnings.soundEnabled,            quotaWarningMarkersVisible: quotaWarningMarkersVisible,            weeklyProgressWorkDays: weeklyProgressWorkDays,            usageBarsShowUsed: usageBarsShowUsed,            resetTimesShowAbsolute: resetTimesShowAbsolute,            providerChangelogLinksEnabled: providerChangelogLinksEnabled,            menuBarShowsBrandIconWithPercent: menuBarShowsBrandIconWithPercent,            menuBarHidesCritters: menuBarHidesCritters,            menuBarDisplayModeRaw: menuBarDisplayModeRaw,            kiroMenuBarDisplayModeRaw: kiroMenuBarDisplayModeRaw,            historicalTrackingEnabled: historicalTrackingEnabled,            multiAccountMenuLayoutRaw: multiAccountMenuLayoutRaw,            menuBarMetricPreferencesRaw: resolvedPreferences,            copilotBudgetExtrasEnabled: copilotBudgetExtrasEnabled,            copilotIconSecondaryWindowIDRaw: copilotIconSecondaryWindowIDRaw,            costUsageEnabled: costUsageEnabled,            costUsageHistoryDays: costUsageHistoryDays,            hidePersonalInfo: hidePersonalInfo,            randomBlinkEnabled: randomBlinkEnabled,            confettiOnWeeklyLimitResetsEnabled: confettiOnWeeklyLimitResetsEnabled,            menuBarShowsHighestUsage: menuBarShowsHighestUsage,            claudeOAuthKeychainPromptModeRaw: claudeOAuthKeychainPromptModeRaw,            claudeOAuthKeychainReadStrategyRaw: claudeOAuthKeychainReadStrategyRaw,            claudeWebExtrasEnabledRaw: claudeWebExtrasEnabledRaw,            showOptionalCreditsAndExtraUsage: showOptionalCreditsAndExtraUsage,            openAIWebAccessEnabled: openAIWebAccessEnabled,            openAIWebBatterySaverEnabled: openAIWebBatterySaverEnabled,            providerStorageFootprintsEnabled: providerStorageFootprintsEnabled,            jetbrainsIDEBasePath: jetbrainsIDEBasePath,            mergeIcons: mergeIcons,            switcherShowsIcons: switcherShowsIcons,            mergedMenuLastSelectedWasOverview: mergedMenuLastSelectedWasOverview,            mergedOverviewSelectedProvidersRaw: mergedOverviewSelectedProvidersRaw,            selectedMenuProviderRaw: selectedMenuProviderRaw,            providerDetectionCompleted: providerDetectionCompleted,            providersSortedAlphabetically: providersSortedAlphabetically,            appLanguageRaw: appLanguageRaw,            terminalAppRaw: userDefaults.string(forKey: "terminalApp"))    }     private static func loadMenuBarMetricPreferences(userDefaults: UserDefaults) -> [String: String] {        let storedPreferences = userDefaults.dictionary(forKey: "menuBarMetricPreferences") as? [String: String] ?? [:]        let preferences: [String: String] = if !storedPreferences.isEmpty {            storedPreferences        } else if let menuBarMetricRaw = userDefaults.string(forKey: "menuBarMetricPreference"),                  let legacyPreference = MenuBarMetricPreference(rawValue: menuBarMetricRaw)        {            Dictionary(                uniqueKeysWithValues: UsageProvider.allCases.map { ($0.rawValue, legacyPreference.rawValue) })        } else {            [:]        }         let migrationKey = "antigravityTwoPoolMetricPreferenceMigrated"        guard !userDefaults.bool(forKey: migrationKey) else { return preferences }         // Tagged builds through v0.35 used primary=Claude, secondary=Gemini Pro,        // and tertiary=Gemini Flash. Remap those meanings once to the two-pool schema.        var migrated = preferences        switch MenuBarMetricPreference(rawValue: migrated[UsageProvider.antigravity.rawValue] ?? "") {        case .primary:            migrated[UsageProvider.antigravity.rawValue] = MenuBarMetricPreference.secondary.rawValue        case .secondary:            migrated[UsageProvider.antigravity.rawValue] = MenuBarMetricPreference.primary.rawValue        case .tertiary:            migrated[UsageProvider.antigravity.rawValue] = MenuBarMetricPreference.primary.rawValue

Lines 1–500 of 693.