137 lines · 6.6 KB · Swift
RawDownload
import AppKitimport CodexBarCoreimport SwiftUI extension StatusItemController {    /// Smart update: rebuild everything below the provider switcher while keeping the switcher view intact.    struct MenuUpdateContext {        let provider: UsageProvider?        let currentProvider: UsageProvider        let switcherSelection: ProviderSwitcherSelection        let menuWidth: CGFloat        let codexAccountDisplay: CodexAccountMenuDisplay?        let tokenAccountDisplay: TokenAccountMenuDisplay?        let openAIContext: OpenAIWebContext        let descriptor: MenuDescriptor    }     /// Smart update: rebuild everything below the provider switcher while keeping the switcher view intact.    func updateMenuContentPreservingSwitcher(        _ menu: NSMenu,        context: MenuUpdateContext)    {        self.performMenuMutationWithoutAnimation {            let contentStartIndex = self.providerSwitcherContentStartIndex(in: menu)            if let switcherView = menu.items.first?.view as? ProviderSwitcherView {                switcherView.updateSelection(context.switcherSelection)                switcherView.updateQuotaIndicators()            }            let outgoingSelection = self.lastMergedMenuContentSelection            let isSelectionSwitch = outgoingSelection != nil && outgoingSelection != context.switcherSelection            let enabledProviders = self.store.enabledProvidersForDisplay()             if isSelectionSwitch,               let outgoingSelection,               let cachedItems = self.reusableMergedSwitcherContent(                   for: context.switcherSelection,                   in: menu,                   menuWidth: context.menuWidth,                   codexAccountDisplay: context.codexAccountDisplay,                   tokenAccountDisplay: context.tokenAccountDisplay)            {                // Park the outgoing payloads for an equally instant switch-back. Compatible                // menu-item shells stay attached, avoiding the empty intermediate layout that                // AppKit can visibly render when the whole content block is removed first.                let outgoingCodexAccountDisplay = self.lastCodexAccountMenuDisplay                let outgoingTokenAccountDisplay = self.lastTokenAccountMenuDisplay                self.rememberMergedSwitcherState(enabledProviders, context.switcherSelection)                let displacedItems = self.replaceMenuContentKeepingRowsVisible(                    menu,                    fromIndex: contentStartIndex,                    with: cachedItems)                self.cacheMergedSwitcherContent(                    displacedItems,                    in: menu,                    selection: outgoingSelection,                    context: MergedSwitcherContentCacheContext(                        menuWidth: context.menuWidth,                        codexAccountDisplay: outgoingCodexAccountDisplay,                        tokenAccountDisplay: outgoingTokenAccountDisplay,                        contentVersion: nil))                self.lastCodexAccountMenuDisplay = context.codexAccountDisplay                self.lastTokenAccountMenuDisplay = context.tokenAccountDisplay                self.cacheVisibleMergedSwitcherContent(                    in: menu,                    selection: context.switcherSelection,                    contentStartIndex: contentStartIndex,                    menuWidth: context.menuWidth,                    contentVersion: self.menuContentVersion)                return            }             // Rebuild path (data tick, or switch whose incoming tab must be built): recycle            // the outgoing hosting views and reconcile in place when the row skeleton is            // unchanged, so an open tracked menu sees content mutations instead of item            // churn. The fresh content is built into a detached scratch menu while its            // interaction closures capture the live menu they will serve.            let shapes = self.menuContentShapes(in: menu, fromIndex: contentStartIndex)            self.harvestRecyclableMenuCardViews(                in: menu,                fromIndex: contentStartIndex,                displacedSelection: outgoingSelection,                preserveHighlightedItem: true)            defer { self.clearMenuCardViewRecyclePool() }            self.rememberMergedSwitcherState(enabledProviders, context.switcherSelection)            let scratch = NSMenu()            scratch.autoenablesItems = false            self.addSwitcherScopedMenuContent(into: scratch, captureMenu: menu, context: context)            self.reconcileMenuContent(menu, fromIndex: contentStartIndex, shapes: shapes, with: scratch)            self.cacheVisibleMergedSwitcherContent(                in: menu,                selection: context.switcherSelection,                contentStartIndex: contentStartIndex,                menuWidth: context.menuWidth,                contentVersion: self.menuContentVersion)        }    }     /// Adds everything below the provider switcher (account switchers, card content, and    /// actionable sections) to `target`, which may be a detached scratch menu; interaction    /// closures always capture `captureMenu`, the live menu the rows will serve.    private func addSwitcherScopedMenuContent(        into target: NSMenu,        captureMenu: NSMenu,        context: MenuUpdateContext)    {        self.addCodexAccountSwitcherIfNeeded(            to: target,            display: context.codexAccountDisplay,            width: context.menuWidth,            captureMenu: captureMenu)        self.lastCodexAccountMenuDisplay = context.codexAccountDisplay        self.addTokenAccountSwitcherIfNeeded(            to: target,            display: context.tokenAccountDisplay,            width: context.menuWidth,            captureMenu: captureMenu)        self.lastTokenAccountMenuDisplay = context.tokenAccountDisplay         let menuContext = MenuCardContext(            currentProvider: context.currentProvider,            selectedProvider: context.provider,            menuWidth: context.menuWidth,            codexAccountDisplay: context.codexAccountDisplay,            tokenAccountDisplay: context.tokenAccountDisplay,            openAIContext: context.openAIContext)        self.addPrimaryMenuContent(            to: target,            context: menuContext,            switcherSelection: context.switcherSelection,            captureMenu: captureMenu)        self.addActionableSections(            context.descriptor.sections,            to: target,            width: context.menuWidth,            captureMenu: captureMenu)    }}