feat(macos): hover HUD for activity

This commit is contained in:
Peter Steinberger
2025-12-19 00:03:58 +01:00
parent 0c06276b48
commit 47510e2912
8 changed files with 512 additions and 70 deletions

View File

@@ -22,6 +22,11 @@ struct ClawdisApp: App {
self.statusItem?.button?.highlight(self.isPanelVisible)
}
@MainActor
private func updateHoverHUDSuppression() {
HoverHUDController.shared.setSuppressed(self.isMenuPresented || self.isPanelVisible)
}
init() {
_state = State(initialValue: AppStateStore.shared)
}
@@ -44,6 +49,7 @@ struct ClawdisApp: App {
self.applyStatusItemAppearance(paused: self.state.isPaused)
self.installStatusItemMouseHandler(for: item)
self.menuInjector.install(into: item)
self.updateHoverHUDSuppression()
}
.onChange(of: self.state.isPaused) { _, paused in
self.applyStatusItemAppearance(paused: paused)
@@ -65,6 +71,7 @@ struct ClawdisApp: App {
.windowResizability(.contentSize)
.onChange(of: self.isMenuPresented) { _, _ in
self.updateStatusHighlight()
self.updateHoverHUDSuppression()
}
}
@@ -80,6 +87,7 @@ struct ClawdisApp: App {
WebChatManager.shared.onPanelVisibilityChanged = { [self] visible in
self.isPanelVisible = visible
self.updateStatusHighlight()
self.updateHoverHUDSuppression()
}
CanvasManager.shared.onPanelVisibilityChanged = { [self] visible in
self.state.canvasPanelVisible = visible
@@ -88,12 +96,21 @@ struct ClawdisApp: App {
let handler = StatusItemMouseHandlerView()
handler.translatesAutoresizingMaskIntoConstraints = false
handler.onLeftClick = { [self] in self.toggleWebChatPanel() }
handler.onLeftClick = { [self] in
HoverHUDController.shared.dismiss(reason: "statusItemClick")
self.toggleWebChatPanel()
}
handler.onRightClick = { [self] in
HoverHUDController.shared.dismiss(reason: "statusItemRightClick")
WebChatManager.shared.closePanel()
self.isMenuPresented = true
self.updateStatusHighlight()
}
handler.onHoverChanged = { [self] inside in
HoverHUDController.shared.statusItemHoverChanged(
inside: inside,
anchorProvider: { [self] in self.statusButtonScreenFrame() })
}
button.addSubview(handler)
NSLayoutConstraint.activate([
@@ -106,6 +123,7 @@ struct ClawdisApp: App {
@MainActor
private func toggleWebChatPanel() {
HoverHUDController.shared.setSuppressed(true)
self.isMenuPresented = false
WebChatManager.shared.togglePanel(
sessionKey: WebChatManager.shared.preferredSessionKey(),
@@ -138,6 +156,8 @@ struct ClawdisApp: App {
private final class StatusItemMouseHandlerView: NSView {
var onLeftClick: (() -> Void)?
var onRightClick: (() -> Void)?
var onHoverChanged: ((Bool) -> Void)?
private var tracking: NSTrackingArea?
override func mouseDown(with event: NSEvent) {
if let onLeftClick {
@@ -151,6 +171,29 @@ private final class StatusItemMouseHandlerView: NSView {
self.onRightClick?()
// Do not call super; menu will be driven by isMenuPresented binding.
}
override func updateTrackingAreas() {
super.updateTrackingAreas()
if let tracking {
self.removeTrackingArea(tracking)
}
let options: NSTrackingArea.Options = [
.mouseEnteredAndExited,
.activeAlways,
.inVisibleRect,
]
let area = NSTrackingArea(rect: self.bounds, options: options, owner: self, userInfo: nil)
self.addTrackingArea(area)
self.tracking = area
}
override func mouseEntered(with event: NSEvent) {
self.onHoverChanged?(true)
}
override func mouseExited(with event: NSEvent) {
self.onHoverChanged?(false)
}
}
final class AppDelegate: NSObject, NSApplicationDelegate {