1016 lines · 42.0 KB · Swift
RawDownload
import AppKitimport CodexBarCoreimport Observationimport QuartzCore // MARK: - Status item controller (AppKit-hosted icons, SwiftUI popovers) @MainActorprotocol StatusItemControlling: AnyObject {    func openMenuFromShortcut()    func runLoginFlowFromSettings(provider: UsageProvider) async    func celebrationOriginPoint(for provider: UsageProvider?) -> CGPoint?    func trimRebuildableCachesForMemoryPressure() -> MemoryPressureCacheTrimSummary    #if DEBUG    func seedRebuildableCachesForMemoryPressureProof()    #endif    func prepareForAppShutdown()} extension StatusItemControlling {    func celebrationOriginPoint(for provider: UsageProvider?) -> CGPoint? {        nil    }     func trimRebuildableCachesForMemoryPressure() -> MemoryPressureCacheTrimSummary {        MemoryPressureCacheTrimSummary()    }     #if DEBUG    func seedRebuildableCachesForMemoryPressureProof() {}    #endif     func prepareForAppShutdown() {}} struct NativeHighlightDeferredMenuRebuild {    let provider: UsageProvider?} @MainActorfinal class StatusItemController: NSObject, NSMenuDelegate, StatusItemControlling {    // Disable SwiftUI menu cards + menu refresh work in tests to avoid swiftpm-testing-helper crashes.    static var menuCardRenderingEnabled = !SettingsStore.isRunningTests    private static let defaultMenuRefreshEnabled = !SettingsStore.isRunningTests    private(set) static var menuRefreshEnabled = !SettingsStore.isRunningTests    static let quotaWarningFlashDuration: TimeInterval = 60    private nonisolated static let statusItemAccessibilityTitle = "CodexBar"    private nonisolated static let debugStatusItemAccessibilityTitle = "CodexBar Debug"    private nonisolated static let statusItemAccessibilityIdentifierPrefix = "CodexBar.StatusItem"    private nonisolated static let mergedLegacyDefaultItemIndex = 0     enum StatusItemIdentity {        case merged        case provider(UsageProvider)         var autosaveName: String {            switch self {            case .merged:                "codexbar-merged"            case let .provider(provider):                "codexbar-\(provider.rawValue)"            }        }         var accessibilityIdentifier: String {            switch self {            case .merged:                StatusItemController.statusItemAccessibilityIdentifierPrefix            case let .provider(provider):                "\(StatusItemController.statusItemAccessibilityIdentifierPrefix).\(provider.rawValue)"            }        }    }     nonisolated static func isDebugApp(bundleIdentifier: String?) -> Bool {        bundleIdentifier?.contains(".debug") == true    }     nonisolated static func statusItemAccessibilityTitle(isDebugApp: Bool) -> String {        isDebugApp ? self.debugStatusItemAccessibilityTitle : self.statusItemAccessibilityTitle    }     #if DEBUG    var menuRefreshEnabledOverrideForTesting: Bool?    #endif     typealias Factory =        @MainActor (            UsageStore,            SettingsStore,            AccountInfo,            UpdaterProviding,            PreferencesSelection,            ManagedCodexAccountCoordinator,            CodexAccountPromotionCoordinator)        -> StatusItemControlling    // swiftlint:disable:next function_parameter_count    static func makeDefaultController(        store: UsageStore,        settings: SettingsStore,        account: AccountInfo,        updater: UpdaterProviding,        selection: PreferencesSelection,        managedCodexAccountCoordinator: ManagedCodexAccountCoordinator,        codexAccountPromotionCoordinator: CodexAccountPromotionCoordinator)        -> StatusItemControlling    {        StatusItemController(            store: store,            settings: settings,            account: account,            updater: updater,            preferencesSelection: selection,            managedCodexAccountCoordinator: managedCodexAccountCoordinator,            codexAccountPromotionCoordinator: codexAccountPromotionCoordinator)    }     static let defaultFactory: Factory = StatusItemController.makeDefaultController     static var factory: Factory = StatusItemController.defaultFactory     let store: UsageStore    let settings: SettingsStore    let agentSessions: AgentSessionsStore    lazy var menuCardRefreshMonitor = self.makeMenuCardRefreshMonitor()     let account: AccountInfo    let updater: UpdaterProviding    let managedCodexAccountCoordinator: ManagedCodexAccountCoordinator    let codexAccountPromotionCoordinator: CodexAccountPromotionCoordinator    let statusBar: NSStatusBar    let menuCardRenderingEnabledForController: Bool    let menuRefreshEnabledForController: Bool    var statusItem: NSStatusItem    var statusItems: [UsageProvider: NSStatusItem] = [:]    /// App intent survives Tahoe changing `NSStatusItem.isVisible` after Control Center rejects its scene.    var expectedVisibleStatusItemAutosaveNames: Set<String> = []    var lastMenuProvider: UsageProvider?    var menuProviders: [ObjectIdentifier: UsageProvider] = [:]    var menuSession = MenuSessionCoordinator<ObjectIdentifier>()    var menuReadinessSignatures: [ObjectIdentifier: String] = [:]    let hostedSubviewRenderSignatures = NSMapTable<NSMenu, HostedSubviewRenderSignatureBox>.weakToStrongObjects()    /// Persistent Refresh rows are weakly tracked so their enabled state can change during menu tracking.    let persistentRefreshItems = NSHashTable<NSMenuItem>.weakObjects()    var menuCardHeightCache: [MenuCardHeightCacheKey: CGFloat] = [:]    var measuredStandardMenuWidthCache: [String: CGFloat] = [:]    var lastMenuAdjunctReadinessSignature = ""    var lastMenuAdjunctReadinessBaselineVersion = 0    var rootOpenHandledMenuObservationSignature: String?    var mergedMenu: NSMenu?    var providerMenus: [UsageProvider: NSMenu] = [:]    var fallbackMenu: NSMenu?    var openMenus: [ObjectIdentifier: NSMenu] = [:]    var menuRefreshTasks: [ObjectIdentifier: Task<Void, Never>] = [:]    /// Manual refreshes tracked per scope so refreshing one provider neither greys out nor blocks    /// a manual refresh of another. `.global` covers the all-providers refresh (⌘R / merged overview).    var manualRefreshTasks: [ManualRefreshScope: Task<Void, Never>] = [:]     var closedMenuRebuildTasks: [ObjectIdentifier: Task<Void, Never>] = [:]    var closedMenuRebuildRequests = MenuRebuildRequestRegistry<ObjectIdentifier>()    var openMenuRebuildTasks: [ObjectIdentifier: Task<Void, Never>] = [:]    var openMenuRebuildRequests = MenuRebuildRequestRegistry<ObjectIdentifier>()    var menuIdentitySignatures: [ObjectIdentifier: String] = [:]    var codexAccountMenuProjectionRevalidationTask: Task<Void, Never>?    var openMenuRebuildsClosingHostedSubviewMenus: Set<ObjectIdentifier> = []    var parentMenuRebuildPendingAfterHostedSubviewClose = false    var deferredMenuInteractionRefreshProviders: Set<UsageProvider> = []    var deferredMenuInteractionRefreshPending: Bool {        !self.deferredMenuInteractionRefreshProviders.isEmpty    }     var deferredOpenAIDashboardRefreshReason: String?    var deferredMenuInteractionRefreshTask: Task<Void, Never>?    var highlightedMenuItems: [ObjectIdentifier: NSMenuItem] = [:]    /// Open-menu rebuilds paused so AppKit's native selection background cannot retain stale geometry.    var nativeHighlightDeferredMenuRebuilds: [ObjectIdentifier: NativeHighlightDeferredMenuRebuild] = [:]    /// Baseline resync intent survives rebuild coalescing and any native-row or hosted-submenu deferral.    var pendingMenuBaselineResyncs: Set<ObjectIdentifier> = []    var providerSwitcherShortcutEventMonitor: ProviderSwitcherShortcutEventMonitor?    var providerSwitcherShortcutMenuID: ObjectIdentifier?    var providerSwitcherPointerInteractionMenuID: ObjectIdentifier?    var pendingProviderSwitcherPointerRebuild: PendingProviderSwitcherRebuild?    var overviewScrollAccumulatedDelta: CGFloat = 0    var overviewScrollNavigationHandlerForTesting: ((OverviewScrollStep) -> Void)?    var hasPreparedForAppShutdown = false    var scheduleQuitTermination: (@escaping @MainActor () -> Void) -> Void = { operation in        DispatchQueue.main.async {            Task { @MainActor in                operation()            }        }    }     var terminateApplicationForQuit: @MainActor () -> Void = {        NSApp.terminate(nil)    }     var openMenuInvalidationRetryTask: Task<Void, Never>?    #if DEBUG    var onDelayedMenuRefreshAttemptForTesting: (() -> Void)?    var onDeferredMenuInteractionRefreshForTesting: (() -> Void)?    var onOpenMenuInvalidationRetryForTesting: (() -> Void)?    var isReleasedForTesting = false    var lastLoggedClosedMenuRebuildVersion: Int?    var _test_openMenuRefreshYieldOverride: (@MainActor () async -> Void)?    var _test_openMenuRebuildObserver: (@MainActor (NSMenu) -> Void)?    var _test_providerSwitcherMenuRebuildDebounceNanoseconds: UInt64?    var _test_codexAmbientLoginRunnerOverride:        (@MainActor (TimeInterval) async -> CodexLoginRunner.Result)?    #endif    var manualRefreshViewportRestoreState = ManualRefreshViewportRestoreState()    var blinkTask: Task<Void, Never>?    var menuBarCountdownRefreshTask: Task<Void, Never>?    var loginTask: Task<Void, Never>? {        didSet { self.refreshMenusForLoginStateChange() }    }     var creditsPurchaseWindow: OpenAICreditsPurchaseWindowController?     var activeLoginProvider: UsageProvider? {        didSet {            if oldValue != self.activeLoginProvider {                self.refreshMenusForLoginStateChange()            }        }    }     var blinkStates: [UsageProvider: BlinkState] = [:]    var blinkAmounts: [UsageProvider: CGFloat] = [:]    var wiggleAmounts: [UsageProvider: CGFloat] = [:]    var tiltAmounts: [UsageProvider: CGFloat] = [:]    var quotaWarningFlashUntil: [UsageProvider: Date] = [:]    var quotaWarningFlashTasks: [UsageProvider: Task<Void, Never>] = [:]    var blinkForceUntil: Date?    var loginPhase: LoginPhase = .idle {        didSet {            if oldValue != self.loginPhase {                self.refreshMenusForLoginStateChange()            }        }    }     let preferencesSelection: PreferencesSelection    var animationDriver: DisplayLinkDriver?    var animationPhase: Double = 0    var animationPattern: LoadingPattern = .knightRider    var animationStartedAt: Date?    private var lastConfigRevision: Int    private var lastProviderOrder: [UsageProvider]    private var lastMergeIcons: Bool    private var lastSwitcherShowsIcons: Bool    private var lastObservedUsageBarsShowUsed: Bool    var lastWidgetDisplaySettingsSignature = ""    var lastAgentSessionsEnabled: Bool    var lastAgentSessionsManualHosts: String    var lastAgentSessionsRefreshFrequency: RefreshFrequency    var lastAdaptiveActivityScanningEnabled: Bool    /// Tracks which `usageBarsShowUsed` mode the provider switcher was built with.    /// Used to decide whether we can "smart update" menu content without rebuilding the switcher.    var lastSwitcherUsageBarsShowUsed: Bool    /// Tracks whether the merged-menu switcher was built with the Overview tab visible.    /// Used to force switcher rebuilds when Overview availability toggles.    var lastSwitcherIncludesOverview: Bool = false    /// Tracks localization-sensitive labels used by the merged menu.    /// Used to force menu rebuilds when app language changes.    var lastMenuLocalizationSignature: String = ""    /// Tracks which providers the merged menu's switcher was built with, to detect when it needs full rebuild.    var lastSwitcherProviders: [UsageProvider] = []    /// Tracks which switcher tab state was used for the current merged-menu switcher instance.    var lastMergedSwitcherSelection: ProviderSwitcherSelection?    /// Tracks which provider/overview content is currently attached below the merged-menu switcher.    var lastMergedMenuContentSelection: ProviderSwitcherSelection?    /// Tracks the visible Codex account switcher contents for merged-menu smart updates.    var lastCodexAccountMenuDisplay: CodexAccountMenuDisplay?    /// Tracks the visible token account switcher contents for merged-menu smart updates.    var lastTokenAccountMenuDisplay: TokenAccountMenuDisplay?    /// Keeps detached merged-menu tab content reusable while the same menu remains open.    var mergedSwitcherContentCaches: [ObjectIdentifier: [ProviderSwitcherSelection: CachedMergedSwitcherMenuContent]]        = [:]    var preservesMergedSwitcherContentCachesDuringInvalidation = false    /// Card hosting views harvested from items about to be discarded by the current populate    /// pass, keyed by card identifier; consumed by `makeMenuCardItem` and cleared when the    /// pass finishes. Never outlives a single synchronous menu population.    var menuCardViewRecyclePool: [String: NSView] = [:]    /// Monotonic token used to ignore stale deferred provider-switcher menu rebuilds.    var providerSwitcherUpdateToken = 0    var providerSelectionUIRefreshTask: Task<Void, Never>?    var deferredMergedIconRenderAfterTracking = false    var lastAppliedMergedIconRenderSignature: String?    var lastAppliedProviderIconRenderSignatures: [UsageProvider: String] = [:]    let menuBarLayoutRenderer = MenuBarLayoutRenderer()    var lastObservedStoreIconWorkSignature: String?    var iconPerfRefreshCycleMetrics: IconPerfRefreshCycleMetrics?    var iconPerfUpdatePassActive = false    var lastKnownScreenCount: Int    var pendingScreenChangePreviousCount: Int?    var screenChangeVisibilityTask: Task<Void, Never>?    let loginLogger = CodexBarLog.logger(LogCategories.login)    let menuLogger = CodexBarLog.logger(LogCategories.app)    var selectedMenuProvider: UsageProvider? {        get { self.settings.selectedMenuProvider }        set { self.settings.selectedMenuProvider = newValue }    }     static func makeStatusItem(        statusBar: NSStatusBar,        identity: StatusItemIdentity,        defaults: UserDefaults,        legacyDefaultItemIndex: Int?,        onCreated: ((NSStatusItem) -> Void)? = nil)        -> NSStatusItem    {        MenuBarStatusItemPlacementPreflight.prepare(            defaults: defaults,            autosaveName: identity.autosaveName,            legacyDefaultItemIndex: legacyDefaultItemIndex)        let item = statusBar.statusItem(withLength: NSStatusItem.variableLength)        onCreated?(item)        item.autosaveName = identity.autosaveName        if let button = item.button {            let title = self.statusItemAccessibilityTitle(                isDebugApp: self.isDebugApp(bundleIdentifier: Bundle.main.bundleIdentifier))            // Ensure the icon is rendered at 1:1 without resampling (crisper edges for template images).            button.imageScaling = .scaleNone            button.setAccessibilityIdentifier(identity.accessibilityIdentifier)            button.setAccessibilityTitle(title)        }        return item    }     struct BlinkState {        var nextBlink: Date        var blinkStart: Date?        var pendingSecondStart: Date?        var effect: MotionEffect = .blink         static func randomDelay() -> TimeInterval {            Double.random(in: 3...12)        }    }     enum MotionEffect {        case blink        case wiggle        case tilt    }     enum LoginPhase {        case idle        case requesting        case waitingBrowser    }     func menuBarMetricWindow(for provider: UsageProvider, snapshot: UsageSnapshot?, now: Date = Date()) -> RateWindow? {        if provider == .codex {            return self.codexMenuBarMetricWindow(snapshot: snapshot, now: now)        }        return MenuBarMetricWindowResolver.rateWindow(            preference: self.settings.menuBarMetricPreference(for: provider, snapshot: snapshot),            provider: provider,            snapshot: snapshot,            supportsAverage: self.settings.menuBarMetricSupportsAverage(for: provider),            antigravityPrioritizeExhaustedQuotas: self.settings.antigravityPrioritizeExhaustedQuotas,            now: now)    }     private func codexMenuBarMetricWindow(snapshot: UsageSnapshot?, now: Date) -> RateWindow? {        guard let snapshot else { return nil }        return self.store.codexMenuBarMetricWindow(snapshot: snapshot, now: now)    }     init(        store: UsageStore,        settings: SettingsStore,        account: AccountInfo,        updater: UpdaterProviding,        preferencesSelection: PreferencesSelection,        managedCodexAccountCoordinator: ManagedCodexAccountCoordinator =            ManagedCodexAccountCoordinator(),        codexAccountPromotionCoordinator: CodexAccountPromotionCoordinator? = nil,        statusBar: NSStatusBar = .system,        menuCardRenderingEnabled: Bool = StatusItemController.menuCardRenderingEnabled,        menuRefreshEnabled: Bool = StatusItemController.menuRefreshEnabled,        observeProviderConfigNotifications: Bool = !SettingsStore.isRunningTests)    {        if SettingsStore.isRunningTests {            _ = NSApplication.shared        }        self.store = store        self.settings = settings        self.agentSessions = AgentSessionsStore(settings: settings)        self.account = account        self.updater = updater        self.preferencesSelection = preferencesSelection        self.managedCodexAccountCoordinator = managedCodexAccountCoordinator        self.codexAccountPromotionCoordinator =            codexAccountPromotionCoordinator                ?? CodexAccountPromotionCoordinator(                    settingsStore: settings,                    usageStore: store,                    managedAccountCoordinator: managedCodexAccountCoordinator)        self.lastConfigRevision = settings.configRevision        self.lastProviderOrder = settings.providerOrder        self.lastMergeIcons = settings.mergeIcons        self.lastSwitcherShowsIcons = settings.switcherShowsIcons        self.lastObservedUsageBarsShowUsed = settings.usageBarsShowUsed        self.lastAgentSessionsEnabled = settings.agentSessionsEnabled        self.lastAgentSessionsManualHosts = settings.agentSessionsManualHosts        self.lastAgentSessionsRefreshFrequency = settings.refreshFrequency        self.lastAdaptiveActivityScanningEnabled = settings.adaptiveActivityScanningEnabled        self.lastSwitcherUsageBarsShowUsed = settings.usageBarsShowUsed        self.menuCardRenderingEnabledForController = menuCardRenderingEnabled        self.menuRefreshEnabledForController = menuRefreshEnabled        let repairedStatusItemVisibilityKeys = MenuBarStatusItemDefaultsRepair            .repairHiddenVisibilityDefaultsIfNeeded(defaults: settings.userDefaults)        self.statusBar = statusBar        self.statusItem = Self.makeStatusItem(            statusBar: statusBar,            identity: .merged,            defaults: settings.userDefaults,            legacyDefaultItemIndex: Self.mergedLegacyDefaultItemIndex)        self.lastKnownScreenCount = NSScreen.screens.count        // Status items for individual providers are now created lazily in updateVisibility()        super.init()        if !repairedStatusItemVisibilityKeys.isEmpty {            self.menuLogger.info(                "Repaired hidden macOS status-item visibility defaults",                metadata: ["keys": repairedStatusItemVisibilityKeys.joined(separator: ",")])        }        self.lastMenuAdjunctReadinessSignature = self.menuAdjunctReadinessSignature()        self.lastMenuAdjunctReadinessBaselineVersion = self.menuSession.contentVersion        self.lastWidgetDisplaySettingsSignature = self.widgetDisplaySettingsSignature()        self.wireBindings()        self.wireAgentSessionUpdates()        if !SettingsStore.isRunningTests {            self.agentSessions.start()        }        self.updateVisibility()        self.updateIcons()        self.scheduleCodexAccountMenuProjectionRevalidationIfNeeded(            for: self.store.enabledProvidersForDisplay())        self.scheduleStartupStatusItemVisibilityCheck()        NotificationCenter.default.addObserver(            self,            selector: #selector(self.handleDebugReplayNotification(_:)),            name: .codexbarDebugReplayAllAnimations,            object: nil)        NotificationCenter.default.addObserver(            self,            selector: #selector(self.handleDebugBlinkNotification),            name: .codexbarDebugBlinkNow,            object: nil)        NotificationCenter.default.addObserver(            self,            selector: #selector(self.handleQuotaWarningPosted(_:)),            name: .codexbarQuotaWarningDidPost,            object: nil)        if observeProviderConfigNotifications {            NotificationCenter.default.addObserver(                self,                selector: #selector(self.handleProviderConfigDidChange),                name: .codexbarProviderConfigDidChange,                object: nil)        }        NotificationCenter.default.addObserver(            self,            selector: #selector(self.handleScreenParametersDidChange(_:)),            name: NSApplication.didChangeScreenParametersNotification,            object: nil)        self.observeMenuBarTimeEnvironmentChanges()    }     convenience init(        store: UsageStore,        settings: SettingsStore,        account: AccountInfo,        updater: UpdaterProviding,        preferencesSelection: PreferencesSelection,        statusBar: NSStatusBar = .system,        menuCardRenderingEnabled: Bool = StatusItemController.menuCardRenderingEnabled,        menuRefreshEnabled: Bool = StatusItemController.menuRefreshEnabled,        observeProviderConfigNotifications: Bool = !SettingsStore.isRunningTests)    {        self.init(            store: store,            settings: settings,            account: account,            updater: updater,            preferencesSelection: preferencesSelection,            managedCodexAccountCoordinator: ManagedCodexAccountCoordinator(),            codexAccountPromotionCoordinator: nil,            statusBar: statusBar,            menuCardRenderingEnabled: menuCardRenderingEnabled,            menuRefreshEnabled: menuRefreshEnabled,            observeProviderConfigNotifications: observeProviderConfigNotifications)    }     private func wireBindings() {        self.observeStoreChanges()        self.observeStoreIconChanges()

Lines 1–500 of 1016.