mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-25 12:33:04 +00:00
feat(macos): add Dock menu shortcuts
This commit is contained in:
49
apps/macos/Sources/OpenClaw/AppNavigationActions.swift
Normal file
49
apps/macos/Sources/OpenClaw/AppNavigationActions.swift
Normal file
@@ -0,0 +1,49 @@
|
||||
import AppKit
|
||||
|
||||
@MainActor
|
||||
enum AppNavigationActions {
|
||||
static func openDashboard() {
|
||||
NSApp.activate(ignoringOtherApps: true)
|
||||
if DashboardManager.shared.showConfiguredWindowIfPossible() {
|
||||
return
|
||||
}
|
||||
Task { @MainActor in
|
||||
if DashboardManager.shared.showConfiguredWindowIfPossible() {
|
||||
return
|
||||
}
|
||||
do {
|
||||
try await DashboardManager.shared.show()
|
||||
} catch {
|
||||
DashboardManager.shared.showFailure(error)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static func openChat() {
|
||||
NSApp.activate(ignoringOtherApps: true)
|
||||
Task { @MainActor in
|
||||
let sessionKey = await WebChatManager.shared.preferredSessionKey()
|
||||
WebChatManager.shared.show(sessionKey: sessionKey)
|
||||
}
|
||||
}
|
||||
|
||||
static func toggleCanvas() {
|
||||
NSApp.activate(ignoringOtherApps: true)
|
||||
Task { @MainActor in
|
||||
if AppStateStore.shared.canvasPanelVisible {
|
||||
CanvasManager.shared.hideAll()
|
||||
} else {
|
||||
let sessionKey = await GatewayConnection.shared.mainSessionKey()
|
||||
_ = try? CanvasManager.shared.show(sessionKey: sessionKey, path: nil)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static func openSettings(tab: SettingsTab = .general) {
|
||||
SettingsTabRouter.request(tab)
|
||||
SettingsWindowOpener.shared.open()
|
||||
DispatchQueue.main.async {
|
||||
NotificationCenter.default.post(name: .openclawSelectSettingsTab, object: tab)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -173,19 +173,7 @@ struct OpenClawApp: App {
|
||||
private func openDashboardWindow() {
|
||||
HoverHUDController.shared.setSuppressed(true)
|
||||
self.isMenuPresented = false
|
||||
if DashboardManager.shared.showConfiguredWindowIfPossible() {
|
||||
return
|
||||
}
|
||||
Task { @MainActor in
|
||||
if DashboardManager.shared.showConfiguredWindowIfPossible() {
|
||||
return
|
||||
}
|
||||
do {
|
||||
try await DashboardManager.shared.show()
|
||||
} catch {
|
||||
DashboardManager.shared.showFailure(error)
|
||||
}
|
||||
}
|
||||
AppNavigationActions.openDashboard()
|
||||
}
|
||||
|
||||
@MainActor
|
||||
@@ -250,6 +238,59 @@ final class AppDelegate: NSObject, NSApplicationDelegate {
|
||||
private let webChatAutoLogger = Logger(subsystem: "ai.openclaw", category: "Chat")
|
||||
let updaterController: UpdaterProviding = makeUpdaterController()
|
||||
|
||||
func applicationDockMenu(_: NSApplication) -> NSMenu? {
|
||||
let menu = NSMenu()
|
||||
menu.autoenablesItems = false
|
||||
menu.addItem(self.dockMenuItem(
|
||||
title: "Open Dashboard",
|
||||
systemImage: "gauge",
|
||||
action: #selector(self.openDashboardFromDockMenu(_:))))
|
||||
menu.addItem(self.dockMenuItem(
|
||||
title: "Open Chat",
|
||||
systemImage: "bubble.left.and.bubble.right",
|
||||
action: #selector(self.openChatFromDockMenu(_:))))
|
||||
let canvasTitle = AppStateStore.shared.canvasPanelVisible ? "Close Canvas" : "Open Canvas"
|
||||
let canvasItem = self.dockMenuItem(
|
||||
title: canvasTitle,
|
||||
systemImage: "rectangle.inset.filled.on.rectangle",
|
||||
action: #selector(self.toggleCanvasFromDockMenu(_:)))
|
||||
canvasItem.isEnabled = AppStateStore.shared.canvasEnabled
|
||||
menu.addItem(canvasItem)
|
||||
menu.addItem(.separator())
|
||||
menu.addItem(self.dockMenuItem(
|
||||
title: "Settings…",
|
||||
systemImage: "gearshape",
|
||||
action: #selector(self.openSettingsFromDockMenu(_:))))
|
||||
return menu
|
||||
}
|
||||
|
||||
private func dockMenuItem(title: String, systemImage: String, action: Selector) -> NSMenuItem {
|
||||
let item = NSMenuItem(title: title, action: action, keyEquivalent: "")
|
||||
item.target = self
|
||||
item.image = NSImage(systemSymbolName: systemImage, accessibilityDescription: title)
|
||||
return item
|
||||
}
|
||||
|
||||
@objc
|
||||
private func openDashboardFromDockMenu(_: Any?) {
|
||||
AppNavigationActions.openDashboard()
|
||||
}
|
||||
|
||||
@objc
|
||||
private func openChatFromDockMenu(_: Any?) {
|
||||
AppNavigationActions.openChat()
|
||||
}
|
||||
|
||||
@objc
|
||||
private func toggleCanvasFromDockMenu(_: Any?) {
|
||||
AppNavigationActions.toggleCanvas()
|
||||
}
|
||||
|
||||
@objc
|
||||
private func openSettingsFromDockMenu(_: Any?) {
|
||||
AppNavigationActions.openSettings()
|
||||
}
|
||||
|
||||
func application(_: NSApplication, open urls: [URL]) {
|
||||
Task { @MainActor in
|
||||
for url in urls {
|
||||
|
||||
@@ -111,28 +111,19 @@ struct MenuContent: View {
|
||||
self.voiceWakeMicMenu
|
||||
}
|
||||
Divider()
|
||||
Link(destination: URL(string: "openclaw://dashboard")!) {
|
||||
Button {
|
||||
AppNavigationActions.openDashboard()
|
||||
} label: {
|
||||
Label("Open Dashboard", systemImage: "gauge")
|
||||
}
|
||||
Button {
|
||||
Task { @MainActor in
|
||||
let sessionKey = await WebChatManager.shared.preferredSessionKey()
|
||||
WebChatManager.shared.show(sessionKey: sessionKey)
|
||||
}
|
||||
AppNavigationActions.openChat()
|
||||
} label: {
|
||||
Label("Open Chat", systemImage: "bubble.left.and.bubble.right")
|
||||
}
|
||||
if self.state.canvasEnabled {
|
||||
Button {
|
||||
Task { @MainActor in
|
||||
if self.state.canvasPanelVisible {
|
||||
CanvasManager.shared.hideAll()
|
||||
} else {
|
||||
let sessionKey = await GatewayConnection.shared.mainSessionKey()
|
||||
// Don't force a navigation on re-open: preserve the current web view state.
|
||||
_ = try? CanvasManager.shared.show(sessionKey: sessionKey, path: nil)
|
||||
}
|
||||
}
|
||||
AppNavigationActions.toggleCanvas()
|
||||
} label: {
|
||||
Label(
|
||||
self.state.canvasPanelVisible ? "Close Canvas" : "Open Canvas",
|
||||
@@ -330,12 +321,7 @@ struct MenuContent: View {
|
||||
}
|
||||
|
||||
private func open(tab: SettingsTab) {
|
||||
SettingsTabRouter.request(tab)
|
||||
NSApp.activate(ignoringOtherApps: true)
|
||||
self.openSettings()
|
||||
DispatchQueue.main.async {
|
||||
NotificationCenter.default.post(name: .openclawSelectSettingsTab, object: tab)
|
||||
}
|
||||
AppNavigationActions.openSettings(tab: tab)
|
||||
}
|
||||
|
||||
private var macNodeStatus: (label: String, color: Color)? {
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import AppKit
|
||||
import SwiftUI
|
||||
import Testing
|
||||
@testable import OpenClaw
|
||||
@@ -38,4 +39,15 @@ struct MenuContentSmokeTests {
|
||||
let view = MenuContent(state: state, updater: nil)
|
||||
_ = view.body
|
||||
}
|
||||
|
||||
@Test func `dock menu exposes primary shortcuts`() throws {
|
||||
let delegate = AppDelegate()
|
||||
let menu = try #require(delegate.applicationDockMenu(NSApplication.shared))
|
||||
let titles = menu.items.map(\.title)
|
||||
|
||||
#expect(titles.contains("Open Dashboard"))
|
||||
#expect(titles.contains("Open Chat"))
|
||||
#expect(titles.contains("Open Canvas") || titles.contains("Close Canvas"))
|
||||
#expect(titles.contains("Settings…"))
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user