784 lines · 31.2 KB · Swift
RawDownload
import AppKitimport CodexBarCoreimport QuartzCore extension StatusItemController {    private static let loadingPercentEpsilon = 0.0001    private static let blinkActiveTickInterval: Duration = .milliseconds(75)    private static let blinkIdleFallbackInterval: Duration = .seconds(1)     func needsMenuBarIconAnimation() -> Bool {        if self.shouldMergeIcons {            let primaryProvider = self.primaryProviderForUnifiedIcon()            return self.shouldAnimate(provider: primaryProvider)        }        return UsageProvider.allCases.contains { self.shouldAnimate(provider: $0) }    }     func updateBlinkingState() {        // During the loading animation, blink ticks can overwrite the animated menu bar icon and cause flicker.        if self.needsMenuBarIconAnimation() {            self.stopBlinking()            return        }         let blinkingEnabled = self.isBlinkingAllowed()        // Use display list so merged-mode visibility stays consistent with shouldMergeIcons.        let displayProviders = self.store.enabledProvidersForDisplay()        let anyEnabled = !displayProviders.isEmpty || self.store.debugForceAnimation        let anyVisible = UsageProvider.allCases.contains { self.isVisible($0) }        let mergeIcons = self.shouldMergeIcons        let shouldBlink = mergeIcons ? anyEnabled : anyVisible        if blinkingEnabled, shouldBlink {            if self.blinkTask == nil {                self.seedBlinkStatesIfNeeded()                self.blinkTask = Task { [weak self] in                    while !Task.isCancelled {                        let delay = await MainActor.run {                            self?.blinkTickSleepDuration(now: Date()) ?? Self.blinkIdleFallbackInterval                        }                        try? await Task.sleep(for: delay)                        await MainActor.run { self?.tickBlink() }                    }                }            }        } else {            self.stopBlinking()        }    }     private func seedBlinkStatesIfNeeded() {        let now = Date()        for provider in UsageProvider.allCases where self.blinkStates[provider] == nil {            self.blinkStates[provider] = BlinkState(nextBlink: now.addingTimeInterval(BlinkState.randomDelay()))        }    }     private func stopBlinking() {        self.blinkTask?.cancel()        self.blinkTask = nil        self.blinkAmounts.removeAll()        let phase: Double? = self.needsMenuBarIconAnimation() ? self.animationPhase : nil        if self.shouldMergeIcons {            self.applyIcon(phase: phase)        } else {            for provider in UsageProvider.allCases {                self.applyIcon(for: provider, phase: phase)            }        }    }     private func blinkTickSleepDuration(now: Date) -> Duration {        let mergeIcons = self.shouldMergeIcons        var nextWakeAt: Date?         for provider in UsageProvider.allCases {            let shouldRender = mergeIcons ? self.isEnabled(provider) : self.isVisible(provider)            guard shouldRender, !self.shouldAnimate(provider: provider, mergeIcons: mergeIcons) else { continue }             let state = self                .blinkStates[provider] ?? BlinkState(nextBlink: now.addingTimeInterval(BlinkState.randomDelay()))            if state.blinkStart != nil {                return Self.blinkActiveTickInterval            }             let candidate: Date = state.pendingSecondStart ?? state.nextBlink            if let current = nextWakeAt {                if candidate < current {                    nextWakeAt = candidate                }            } else {                nextWakeAt = candidate            }        }         guard let nextWakeAt else { return Self.blinkIdleFallbackInterval }        let delay = nextWakeAt.timeIntervalSince(now)        if delay <= 0 { return Self.blinkActiveTickInterval }        return .seconds(delay)    }     private func tickBlink(now: Date = .init()) {        guard self.isBlinkingAllowed(at: now) else {            self.stopBlinking()            return        }         let blinkDuration: TimeInterval = 0.36        let doubleBlinkChance = 0.18        let doubleDelayRange: ClosedRange<TimeInterval> = 0.22...0.34        // Cache merge state once per tick to avoid repeated enabled-provider lookups.        let mergeIcons = self.shouldMergeIcons         for provider in UsageProvider.allCases {            let shouldRender = mergeIcons ? self.isEnabled(provider) : self.isVisible(provider)            guard shouldRender, !self.shouldAnimate(provider: provider, mergeIcons: mergeIcons) else {                self.clearMotion(for: provider)                continue            }             var state = self                .blinkStates[provider] ?? BlinkState(nextBlink: now.addingTimeInterval(BlinkState.randomDelay()))             if let pendingSecond = state.pendingSecondStart, now >= pendingSecond {                state.blinkStart = now                state.pendingSecondStart = nil            }             if let start = state.blinkStart {                let elapsed = now.timeIntervalSince(start)                if elapsed >= blinkDuration {                    state.blinkStart = nil                    if let pending = state.pendingSecondStart, now < pending {                        // Wait for the planned double-blink.                    } else {                        state.pendingSecondStart = nil                        state.nextBlink = now.addingTimeInterval(BlinkState.randomDelay())                    }                    self.clearMotion(for: provider)                } else {                    let progress = max(0, min(elapsed / blinkDuration, 1))                    let symmetric = progress < 0.5 ? progress * 2 : (1 - progress) * 2                    let eased = pow(symmetric, 2.2) // slightly punchier than smoothstep                    self.assignMotion(amount: CGFloat(eased), for: provider, effect: state.effect)                }            } else if now >= state.nextBlink {                state.blinkStart = now                state.effect = self.randomEffect(for: provider)                if state.effect == .blink, Double.random(in: 0...1) < doubleBlinkChance {                    state.pendingSecondStart = now.addingTimeInterval(Double.random(in: doubleDelayRange))                }                self.clearMotion(for: provider)            } else {                self.clearMotion(for: provider)            }             self.blinkStates[provider] = state            if !mergeIcons {                self.applyIcon(for: provider, phase: nil)            }        }        if mergeIcons {            let phase: Double? = self.needsMenuBarIconAnimation() ? self.animationPhase : nil            self.applyIcon(phase: phase)        }    }     private func blinkAmount(for provider: UsageProvider) -> CGFloat {        guard self.isBlinkingAllowed() else { return 0 }        return self.blinkAmounts[provider] ?? 0    }     private func wiggleAmount(for provider: UsageProvider) -> CGFloat {        guard self.isBlinkingAllowed() else { return 0 }        return self.wiggleAmounts[provider] ?? 0    }     private func tiltAmount(for provider: UsageProvider) -> CGFloat {        guard self.isBlinkingAllowed() else { return 0 }        return self.tiltAmounts[provider] ?? 0    }     private func assignMotion(amount: CGFloat, for provider: UsageProvider, effect: MotionEffect) {        switch effect {        case .blink:            self.blinkAmounts[provider] = amount            self.wiggleAmounts[provider] = 0            self.tiltAmounts[provider] = 0        case .wiggle:            self.wiggleAmounts[provider] = amount            self.blinkAmounts[provider] = 0            self.tiltAmounts[provider] = 0        case .tilt:            self.tiltAmounts[provider] = amount            self.blinkAmounts[provider] = 0            self.wiggleAmounts[provider] = 0        }    }     private func clearMotion(for provider: UsageProvider) {        self.blinkAmounts[provider] = 0        self.wiggleAmounts[provider] = 0        self.tiltAmounts[provider] = 0    }     private func randomEffect(for provider: UsageProvider) -> MotionEffect {        if provider == .claude {            Bool.random() ? .blink : .wiggle        } else {            Bool.random() ? .blink : .tilt        }    }     private func isBlinkingAllowed(at date: Date = .init()) -> Bool {        if self.settings.randomBlinkEnabled { return true }        if let until = self.blinkForceUntil, until > date { return true }        self.blinkForceUntil = nil        return false    }     // swiftlint:disable function_body_length    @discardableResult    func applyIcon(phase: Double?) -> Bool {        guard let button = self.statusItem.button else { return false }         let style = self.store.iconStyle        let showUsed = self.settings.usageBarsShowUsed        let showBrandPercent = self.settings.menuBarShowsBrandIconWithPercent        let primaryProvider = self.primaryProviderForUnifiedIcon()        let snapshot = self.store.snapshot(for: primaryProvider)         // IconRenderer treats these values as a left-to-right "progress fill" percentage; depending on the        // user setting we pass either "percent left" or "percent used".        let resolved = snapshot.map {            IconRemainingResolver.resolvedPercents(                snapshot: $0,                style: style,                showUsed: showUsed)        }        var primary = resolved?.primary        var weekly = resolved?.secondary        if showUsed,           primaryProvider == .warp,           let remaining = snapshot?.secondary?.remainingPercent,           remaining <= 0        {            // Preserve Warp "no bonus/exhausted bonus" layout even in show-used mode.            weekly = 0        }        if showUsed,           primaryProvider == .warp,           let remaining = snapshot?.secondary?.remainingPercent,           remaining > 0,           weekly == 0        {            // In show-used mode, `0` means "unused", not "missing". Keep the weekly lane present.            weekly = Self.loadingPercentEpsilon        }        let codexProjection = self.store.codexConsumerProjectionIfNeeded(            for: primaryProvider,            surface: .menuBar,            snapshotOverride: snapshot,            now: snapshot?.updatedAt ?? Date())        var credits: Double? = codexProjection?.menuBarFallback == .creditsBalance            ? self.store.codexMenuBarCreditsRemaining(                snapshotOverride: snapshot,                now: snapshot?.updatedAt ?? Date())            : nil        var stale = self.store.isStale(provider: primaryProvider)        var morphProgress: Double?         let needsAnimation = self.needsMenuBarIconAnimation()        if let phase, needsAnimation {            var pattern = self.animationPattern            if style == .combined, pattern == .unbraid {                pattern = .cylon            }            if pattern == .unbraid {                morphProgress = pattern.value(phase: phase) / 100                primary = nil                weekly = nil                credits = nil                stale = false            } else {                // Keep loading animation layout stable: IconRenderer uses `weeklyRemaining > 0` to switch layouts,                // so hitting an exact 0 would flip between "normal" and "weekly exhausted" rendering.                primary = max(pattern.value(phase: phase), Self.loadingPercentEpsilon)                weekly = max(pattern.value(phase: phase + pattern.secondaryOffset), Self.loadingPercentEpsilon)                credits = nil                stale = false            }        }         let blink: CGFloat = style == .combined ? 0 : self.blinkAmount(for: primaryProvider)        let wiggle: CGFloat = style == .combined ? 0 : self.wiggleAmount(for: primaryProvider)        let tilt: CGFloat = style == .combined ? 0 : self.tiltAmount(for: primaryProvider) * .pi / 28         let statusIndicator: ProviderStatusIndicator = {            for provider in self.store.enabledProvidersForDisplay() {                let indicator = self.store.statusIndicator(for: provider)                if indicator.hasIssue { return indicator }            }            return .none        }()        let debugDouble: (Double?) -> String = { value in            guard let value else { return "nil" }            return String(format: "%.3f", value)        }         if showBrandPercent,           let brand = ProviderBrandIcon.image(for: primaryProvider)        {            let displayText = self.menuBarDisplayText(for: primaryProvider, snapshot: snapshot)            let signature = [                "mode=brandPercent",                "provider=\(primaryProvider.rawValue)",                "style=\(String(describing: style))",                "primary=\(debugDouble(primary))",                "weekly=\(debugDouble(weekly))",                "credits=\(debugDouble(credits))",                "stale=\(stale ? "1" : "0")",                "status=\(statusIndicator.rawValue)",                "text=\(displayText ?? "nil")",                "anim=\(needsAnimation ? "1" : "0")",            ].joined(separator: "|")            if self.shouldSkipMergedIconRender(signature) {                return true            }            self.setButtonImage(brand, for: button)            self.setButtonTitle(displayText, for: button)            return false        }         if Self.shouldUseOpenRouterBrandFallback(provider: primaryProvider, snapshot: snapshot),           let brand = ProviderBrandIcon.image(for: primaryProvider)        {            let signature = [                "mode=openRouterFallback",                "provider=\(primaryProvider.rawValue)",                "style=\(String(describing: style))",                "primary=\(debugDouble(primary))",                "weekly=\(debugDouble(weekly))",                "credits=\(debugDouble(credits))",                "stale=\(stale ? "1" : "0")",                "status=\(statusIndicator.rawValue)",                "anim=\(needsAnimation ? "1" : "0")",            ].joined(separator: "|")            if self.shouldSkipMergedIconRender(signature) {                return true            }            self.setButtonTitle(nil, for: button)            self.setButtonImage(                Self.brandImageWithStatusOverlay(brand: brand, statusIndicator: statusIndicator),                for: button)            return false        }         self.setButtonTitle(nil, for: button)        if let morphProgress {            let signature = [                "mode=morph",                "provider=\(primaryProvider.rawValue)",                "style=\(String(describing: style))",                "morph=\(debugDouble(morphProgress))",                "status=\(statusIndicator.rawValue)",                "anim=\(needsAnimation ? "1" : "0")",            ].joined(separator: "|")            if self.shouldSkipMergedIconRender(signature) {                return true            }            let image = IconRenderer.makeMorphIcon(progress: morphProgress, style: style)            self.setButtonImage(image, for: button)        } else {            let signature = [                "mode=icon",                "provider=\(primaryProvider.rawValue)",                "style=\(String(describing: style))",                "primary=\(debugDouble(primary))",                "weekly=\(debugDouble(weekly))",                "credits=\(debugDouble(credits))",                "stale=\(stale ? "1" : "0")",                "status=\(statusIndicator.rawValue)",                "blink=\(debugDouble(Double(blink)))",                "wiggle=\(debugDouble(Double(wiggle)))",                "tilt=\(debugDouble(Double(tilt)))",                "anim=\(needsAnimation ? "1" : "0")",            ].joined(separator: "|")            if self.shouldSkipMergedIconRender(signature) {                return true            }            let image = IconRenderer.makeIcon(                primaryRemaining: primary,                weeklyRemaining: weekly,                creditsRemaining: credits,                stale: stale,                style: style,                blink: blink,                wiggle: wiggle,                tilt: tilt,                statusIndicator: statusIndicator)            self.setButtonImage(image, for: button)        }        return false    }     // swiftlint:enable function_body_length     private func shouldSkipMergedIconRender(_ signature: String) -> Bool {        guard self.shouldMergeIcons else { return false }        if self.lastAppliedMergedIconRenderSignature == signature {            return true        }        self.lastAppliedMergedIconRenderSignature = signature        return false    }     func applyIcon(for provider: UsageProvider, phase: Double?) {        guard let button = self.statusItems[provider]?.button else { return }        let snapshot = self.store.snapshot(for: provider)        // IconRenderer treats these values as a left-to-right "progress fill" percentage; depending on the        // user setting we pass either "percent left" or "percent used".        let showUsed = self.settings.usageBarsShowUsed        let showBrandPercent = self.settings.menuBarShowsBrandIconWithPercent        let style: IconStyle = self.store.style(for: provider)         if showBrandPercent,           let brand = ProviderBrandIcon.image(for: provider)        {            let displayText = self.menuBarDisplayText(for: provider, snapshot: snapshot)            self.setButtonImage(brand, for: button)            self.setButtonTitle(displayText, for: button)            return        }         if Self.shouldUseOpenRouterBrandFallback(provider: provider, snapshot: snapshot),           let brand = ProviderBrandIcon.image(for: provider)        {            self.setButtonTitle(nil, for: button)            self.setButtonImage(                Self.brandImageWithStatusOverlay(                    brand: brand,                    statusIndicator: self.store.statusIndicator(for: provider)),                for: button)            return        }        let resolved = snapshot.map {            IconRemainingResolver.resolvedPercents(                snapshot: $0,                style: style,                showUsed: showUsed)        }        var primary = resolved?.primary        var weekly = resolved?.secondary        if showUsed,           provider == .warp,           let remaining = snapshot?.secondary?.remainingPercent,           remaining <= 0        {            // Preserve Warp "no bonus/exhausted bonus" layout even in show-used mode.            weekly = 0        }        if showUsed,           provider == .warp,           let remaining = snapshot?.secondary?.remainingPercent,           remaining > 0,           weekly == 0        {            // In show-used mode, `0` means "unused", not "missing". Keep the weekly lane present.            weekly = Self.loadingPercentEpsilon        }        let codexProjection = self.store.codexConsumerProjectionIfNeeded(            for: provider,            surface: .menuBar,            snapshotOverride: snapshot,            now: snapshot?.updatedAt ?? Date())        var credits: Double? = codexProjection?.menuBarFallback == .creditsBalance            ? self.store.codexMenuBarCreditsRemaining(                snapshotOverride: snapshot,                now: snapshot?.updatedAt ?? Date())            : nil        var stale = self.store.isStale(provider: provider)        var morphProgress: Double?         if let phase, self.shouldAnimate(provider: provider) {            var pattern = self.animationPattern            if provider == .claude, pattern == .unbraid {                pattern = .cylon            }            if pattern == .unbraid {                morphProgress = pattern.value(phase: phase) / 100                primary = nil                weekly = nil                credits = nil                stale = false            } else {                // Keep loading animation layout stable: IconRenderer switches layouts at `weeklyRemaining == 0`.                primary = max(pattern.value(phase: phase), Self.loadingPercentEpsilon)                weekly = max(pattern.value(phase: phase + pattern.secondaryOffset), Self.loadingPercentEpsilon)                credits = nil                stale = false            }

Lines 1–500 of 784.