feat(macos): windowed chat mode + Talk Mode crash fix (#36983)
Replace the menu bar NSPopover with a proper resizable NSWindow for the chat interface. Left-click the menu bar icon to toggle the window. - Chat opens as a standard macOS window (titled, closable, resizable, miniaturizable) instead of a borderless popover anchored to the menu bar - Window stays open when clicking desktop, enabling drag-and-drop workflows - Add WebChatManager.toggleWindow() and closeWindow() for menu bar toggle - Fix Talk Mode crash on launch (#36983): defer TalkModeController.setEnabled() to next run loop to prevent Swift exclusivity violation in AppState.init()
This commit is contained in:
parent
709c730e2a
commit
76433b27a2
@ -338,7 +338,12 @@ final class AppState {
|
||||
|
||||
if !self.isPreview {
|
||||
Task { await VoiceWakeRuntime.shared.refresh(state: self) }
|
||||
Task { await TalkModeController.shared.setEnabled(self.talkEnabled) }
|
||||
// Defer TalkModeController init to next run loop iteration to prevent
|
||||
// re-entrant access to AppStateStore.shared during init (#36983).
|
||||
let savedTalkEnabled = self.talkEnabled
|
||||
DispatchQueue.main.async {
|
||||
Task { await TalkModeController.shared.setEnabled(savedTalkEnabled) }
|
||||
}
|
||||
}
|
||||
|
||||
self.isInitializing = false
|
||||
|
||||
@ -154,8 +154,13 @@ struct OpenClawApp: App {
|
||||
handler.onRightClick = { [self] in
|
||||
HoverHUDController.shared.dismiss(reason: "statusItemRightClick")
|
||||
WebChatManager.shared.closePanel()
|
||||
self.isMenuPresented = true
|
||||
self.updateStatusHighlight()
|
||||
WebChatManager.shared.closeWindow()
|
||||
// Deactivate the app briefly so MenuBarExtra menu can appear
|
||||
// (SwiftUI menu won't show while an NSWindow is key)
|
||||
DispatchQueue.main.async {
|
||||
self.isMenuPresented = true
|
||||
self.updateStatusHighlight()
|
||||
}
|
||||
}
|
||||
handler.onHoverChanged = { [self] inside in
|
||||
HoverHUDController.shared.statusItemHoverChanged(
|
||||
@ -178,9 +183,7 @@ struct OpenClawApp: App {
|
||||
self.isMenuPresented = false
|
||||
Task { @MainActor in
|
||||
let sessionKey = await WebChatManager.shared.preferredSessionKey()
|
||||
WebChatManager.shared.togglePanel(
|
||||
sessionKey: sessionKey,
|
||||
anchorProvider: { [self] in self.statusButtonScreenFrame() })
|
||||
WebChatManager.shared.toggleWindow(sessionKey: sessionKey)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -59,6 +59,21 @@ final class WebChatManager {
|
||||
controller.show()
|
||||
}
|
||||
|
||||
/// Toggle the chat window (show/hide). Used by menu bar left-click.
|
||||
func toggleWindow(sessionKey: String) {
|
||||
self.closePanel()
|
||||
if let controller = self.windowController, self.windowSessionKey == sessionKey {
|
||||
if controller.isVisible {
|
||||
controller.close()
|
||||
self.onPanelVisibilityChanged?(false)
|
||||
return
|
||||
}
|
||||
controller.show()
|
||||
return
|
||||
}
|
||||
self.show(sessionKey: sessionKey)
|
||||
}
|
||||
|
||||
func togglePanel(sessionKey: String, anchorProvider: @escaping () -> NSRect?) {
|
||||
if let controller = self.panelController {
|
||||
if self.panelSessionKey != sessionKey {
|
||||
@ -93,6 +108,11 @@ final class WebChatManager {
|
||||
self.panelController?.close()
|
||||
}
|
||||
|
||||
func closeWindow() {
|
||||
self.windowController?.close()
|
||||
self.onPanelVisibilityChanged?(false)
|
||||
}
|
||||
|
||||
func preferredSessionKey() async -> String {
|
||||
if let cachedPreferredSessionKey { return cachedPreferredSessionKey }
|
||||
let key = await GatewayConnection.shared.mainSessionKey()
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user