diff --git a/apps/macos/Sources/Clawdbot/MenuSessionsInjector.swift b/apps/macos/Sources/Clawdbot/MenuSessionsInjector.swift index ad0487893..6aab7910e 100644 --- a/apps/macos/Sources/Clawdbot/MenuSessionsInjector.swift +++ b/apps/macos/Sources/Clawdbot/MenuSessionsInjector.swift @@ -1,4 +1,5 @@ import AppKit +import Observation import SwiftUI @MainActor @@ -18,6 +19,7 @@ final class MenuSessionsInjector: NSObject, NSMenuDelegate { private var isMenuOpen = false private var lastKnownMenuWidth: CGFloat? private var menuOpenWidth: CGFloat? + private var isObservingControlChannel = false private var cachedSnapshot: SessionStoreSnapshot? private var cachedErrorText: String? @@ -50,6 +52,7 @@ final class MenuSessionsInjector: NSObject, NSMenuDelegate { self.loadTask = Task { await self.refreshCache(force: true) } } + self.startControlChannelObservation() self.nodesStore.start() } @@ -96,6 +99,50 @@ final class MenuSessionsInjector: NSObject, NSMenuDelegate { self.cancelPreviewTasks() } + private func startControlChannelObservation() { + guard !self.isObservingControlChannel else { return } + self.isObservingControlChannel = true + self.observeControlChannelState() + } + + private func observeControlChannelState() { + withObservationTracking { + _ = ControlChannel.shared.state + } onChange: { [weak self] in + Task { @MainActor [weak self] in + guard let self else { return } + self.handleControlChannelStateChange() + self.observeControlChannelState() + } + } + } + + private func handleControlChannelStateChange() { + guard self.isMenuOpen, let menu = self.statusItem?.menu else { return } + self.loadTask?.cancel() + self.loadTask = Task { [weak self, weak menu] in + guard let self, let menu else { return } + await self.refreshCache(force: true) + await self.refreshUsageCache(force: true) + await self.refreshCostUsageCache(force: true) + await MainActor.run { + guard self.isMenuOpen else { return } + self.inject(into: menu) + self.injectNodes(into: menu) + } + } + + self.nodesLoadTask?.cancel() + self.nodesLoadTask = Task { [weak self, weak menu] in + guard let self, let menu else { return } + await self.nodesStore.refresh() + await MainActor.run { + guard self.isMenuOpen else { return } + self.injectNodes(into: menu) + } + } + } + func menuNeedsUpdate(_ menu: NSMenu) { self.originalDelegate?.menuNeedsUpdate?(menu) }