175 lines · 7.1 KB · Swift
RawDownload
import AppKitimport CodexBarCoreimport QuartzCore extension StatusItemController {    private static let defaultDeferredMenuInteractionRefreshDelay: Duration = .milliseconds(250)    private static let slowMenuOperationThreshold: TimeInterval = 0.15    private static let slowChartRenderThreshold: TimeInterval = 0.050     #if DEBUG    private static var deferredMenuInteractionRefreshDelayForTesting: Duration = .milliseconds(250)     static func setDeferredMenuInteractionRefreshDelayForTesting(_ delay: Duration) {        self.deferredMenuInteractionRefreshDelayForTesting = delay    }     static func resetDeferredMenuInteractionRefreshDelayForTesting() {        self.deferredMenuInteractionRefreshDelayForTesting = self.defaultDeferredMenuInteractionRefreshDelay    }    #endif     private static var deferredMenuInteractionRefreshDelay: Duration {        #if DEBUG        deferredMenuInteractionRefreshDelayForTesting        #else        defaultDeferredMenuInteractionRefreshDelay        #endif    }     struct MenuOperationTrace {        let operation: String        let startedAt: CFTimeInterval    }     /// Pairs the slow-operation timing log with a watchdog breadcrumb so a hang during    /// the operation is attributed to it even when the operation never finishes logging.    func beginMenuOperationTrace(        _ operation: String,        breadcrumb: @autoclosure () -> String) -> MenuOperationTrace    {        MainThreadActivityBreadcrumb.push(breadcrumb())        return MenuOperationTrace(operation: operation, startedAt: CACurrentMediaTime())    }     func endMenuOperationTrace(_ trace: MenuOperationTrace, menu: NSMenu, provider: UsageProvider?) {        MainThreadActivityBreadcrumb.pop()        self.logMenuOperationDurationIfSlow(            trace.operation,            startedAt: trace.startedAt,            menu: menu,            provider: provider)    }     func logMenuOperationDurationIfSlow(        _ operation: String,        startedAt: CFTimeInterval,        menu: NSMenu,        provider: UsageProvider?)    {        let elapsed = CACurrentMediaTime() - startedAt        guard elapsed >= Self.slowMenuOperationThreshold else { return }        self.menuLogger.warning(            "slow menu operation",            metadata: [                "operation": operation,                "durationMs": String(format: "%.1f", elapsed * 1000),                "items": "\(menu.items.count)",                "provider": provider?.rawValue ?? "nil",                "openMenus": "\(self.openMenus.count)",                "storeRefreshing": self.store.isRefreshing ? "1" : "0",            ])    }     func logChartRenderDurationIfSlow(_ label: String, startedAt: CFTimeInterval) {        let elapsed = CACurrentMediaTime() - startedAt        guard elapsed >= Self.slowChartRenderThreshold else { return }        self.menuLogger.warning(            "slow chart render",            metadata: [                "section": label,                "durationMs": String(format: "%.1f", elapsed * 1000),            ])    }     func deferMenuInteractionRefreshIfNeeded(providers: [UsageProvider]) {        guard !self.store.isRefreshing else { return }        self.deferredMenuInteractionRefreshProviders.formUnion(providers)    }     func clearSatisfiedDeferredMenuInteractionRefreshes(for providers: [UsageProvider]) {        for provider in providers            where !self.store.needsUsageRefreshRetry(for: provider)        {            self.deferredMenuInteractionRefreshProviders.remove(provider)        }    }     func deferOpenAIDashboardRefreshUntilMenuCloses(reason: String) {        if let existingReason = self.deferredOpenAIDashboardRefreshReason {            self.deferredOpenAIDashboardRefreshReason = "\(existingReason), \(reason)"        } else {            self.deferredOpenAIDashboardRefreshReason = reason        }    }     func cancelDeferredMenuInteractionRefreshTask() {        self.deferredMenuInteractionRefreshTask?.cancel()        self.deferredMenuInteractionRefreshTask = nil    }     func scheduleDeferredMenuInteractionRefreshIfNeeded(delay: Duration? = nil) {        guard self.openMenus.isEmpty else { return }        guard self.deferredMenuInteractionRefreshPending || self.deferredOpenAIDashboardRefreshReason != nil else {            return        }        guard !self.hasPreparedForAppShutdown else { return }         self.cancelDeferredMenuInteractionRefreshTask()        let delay = delay ?? Self.deferredMenuInteractionRefreshDelay        self.deferredMenuInteractionRefreshTask = Task { @MainActor [weak self] in            try? await Task.sleep(for: delay)            guard let self, !Task.isCancelled else { return }            guard self.openMenus.isEmpty else {                self.deferredMenuInteractionRefreshTask = nil                return            }            let pendingProviders = self.deferredMenuInteractionRefreshProviders            let hasProviderRefreshInFlight = pendingProviders.contains {                self.store.refreshingProviders.contains($0)            }            guard !self.store.isRefreshing,                  !self.store.hasForcedRefreshEnrichmentInFlight,                  !hasProviderRefreshInFlight            else {                self.deferredMenuInteractionRefreshTask = nil                self.scheduleDeferredMenuInteractionRefreshIfNeeded(                    delay: Self.defaultDeferredMenuInteractionRefreshDelay)                return            }            self.clearSatisfiedDeferredMenuInteractionRefreshes(for: Array(pendingProviders))            let shouldRefreshStore = self.deferredMenuInteractionRefreshPending            let openAIDashboardRefreshReason = self.deferredOpenAIDashboardRefreshReason            guard shouldRefreshStore || openAIDashboardRefreshReason != nil else {                self.deferredMenuInteractionRefreshTask = nil                return            }            guard !self.hasPreparedForAppShutdown else {                self.deferredMenuInteractionRefreshTask = nil                return            }            self.deferredMenuInteractionRefreshTask = nil            self.deferredMenuInteractionRefreshProviders.removeAll()            self.deferredOpenAIDashboardRefreshReason = nil            #if DEBUG            self.onDeferredMenuInteractionRefreshForTesting?()            #endif            if shouldRefreshStore {                await self.performStoreRefresh(                    forceTokenUsage: false,                    refreshOpenMenusWhenComplete: false,                    interaction: .background)                guard !Task.isCancelled else { return }            }            if let openAIDashboardRefreshReason {                guard self.openMenus.isEmpty else {                    self.deferOpenAIDashboardRefreshUntilMenuCloses(reason: openAIDashboardRefreshReason)                    return                }                // Keep menu-originated automatic dashboard refreshes non-interactive:                // opening a menu is not consent to show macOS Keychain prompts.                self.store.requestOpenAIDashboardRefreshIfStale(reason: openAIDashboardRefreshReason)            }        }    }}