mirror of
https://github.com/openclaw/openclaw.git
synced 2026-08-23 10:55:31 -06:00
b8a6ea12cc
* refactor(macos): remove the custom menu bar hover card The status item showed two hover affordances at once: the native AppKit tooltip and a custom SwiftUI panel rendering an "Idle / No recent activity" card. Keep the native tooltip and drop the custom HUD. The HUD was the only consumer of the status item's hover tracking, so the NSTrackingArea and onHoverChanged plumbing in StatusItemMouseRouter goes with it; click routing is unchanged. WorkActivityStore.lastToolLabel had no other reader either. * chore(i18n): refresh native i18n inventory after hover card removal Deleting HoverHUD.swift shifted the MenuBar.swift line numbers that apps/.i18n/native-source.json records. Regenerated with `pnpm native:i18n:baseline`; only line numbers change, no strings added or removed. * refactor(macos): drop the orphaned anchored chat panel Removing the hover card left WebChatManager.togglePanel() without a caller, and it was the only thing that created the menu-bar-anchored chat popover. Delete that presentation path: togglePanel/panelHidden, the panel controller state, WebChatPresentation, WebChatPanel, presentAnchored and its dismiss monitor, and WindowPlacement.anchoredBelowFrame. Chat is unaffected as a window - WebChatManager.show() still backs the Dock menu, --chat and deep links. The status item highlight that tracked chat window visibility is preserved, renamed to onChatWindowVisibilityChanged now that no panel exists to confuse it with. Periphery reports no unused code. * chore: drop CHANGELOG edit from this PR AGENTS.md: CHANGELOG.md is release-only and release generation derives it from merged PRs. Release-note context lives in the PR body instead. * chore: restore CHANGELOG to the branch base Correct the previous commit, which restored CHANGELOG.md from current origin/main and so pulled in an unrelated entry added after this branch forked. Reset to the merge-base content: this PR now touches no changelog.
142 lines
4.9 KiB
Swift
142 lines
4.9 KiB
Swift
import AppKit
|
|
import Testing
|
|
@testable import OpenClaw
|
|
|
|
@Suite(.serialized)
|
|
@MainActor
|
|
struct StatusItemMouseRouterTests {
|
|
@Test func `installed monitor consumes the target event before native dispatch`() throws {
|
|
let monitorToken = NSObject()
|
|
var installedMask: NSEvent.EventTypeMask = []
|
|
var installedHandler: StatusItemMouseRouter.EventMonitorHandler?
|
|
var leftClicks = 0
|
|
let installedToken = try #require(StatusItemMouseRouter.installMonitor(
|
|
using: { mask, handler in
|
|
installedMask = mask
|
|
installedHandler = handler
|
|
return monitorToken
|
|
},
|
|
handler: { event in
|
|
StatusItemMouseRouter.route(
|
|
event,
|
|
hitsTarget: true,
|
|
onLeftClick: { leftClicks += 1 },
|
|
onRightClick: {})
|
|
}))
|
|
|
|
#expect(installedMask == [.leftMouseDown, .rightMouseDown])
|
|
#expect(installedToken as AnyObject === monitorToken)
|
|
let handler = try #require(installedHandler)
|
|
let left = try Self.mouseEvent(.leftMouseDown)
|
|
#expect(handler(left) == nil)
|
|
#expect(leftClicks == 1)
|
|
}
|
|
|
|
@Test func `routes left and right clicks without opening the native menu`() throws {
|
|
var leftClicks = 0
|
|
var rightClicks = 0
|
|
let left = try Self.mouseEvent(.leftMouseDown)
|
|
let right = try Self.mouseEvent(.rightMouseDown)
|
|
|
|
#expect(StatusItemMouseRouter.route(
|
|
left,
|
|
hitsTarget: true,
|
|
onLeftClick: { leftClicks += 1 },
|
|
onRightClick: { rightClicks += 1 }) == nil)
|
|
#expect(leftClicks == 1)
|
|
#expect(rightClicks == 0)
|
|
|
|
#expect(StatusItemMouseRouter.route(
|
|
right,
|
|
hitsTarget: true,
|
|
onLeftClick: { leftClicks += 1 },
|
|
onRightClick: { rightClicks += 1 }) == nil)
|
|
#expect(leftClicks == 1)
|
|
#expect(rightClicks == 1)
|
|
}
|
|
|
|
@Test func `retargets clicks without reinstalling the monitor`() throws {
|
|
let window = NSWindow(
|
|
contentRect: NSRect(x: 0, y: 0, width: 80, height: 24),
|
|
styleMask: .borderless,
|
|
backing: .buffered,
|
|
defer: false)
|
|
let firstButton = NSView(frame: NSRect(x: 0, y: 0, width: 24, height: 24))
|
|
let secondButton = NSView(frame: NSRect(x: 40, y: 0, width: 24, height: 24))
|
|
window.contentView?.addSubview(firstButton)
|
|
window.contentView?.addSubview(secondButton)
|
|
var monitorInstallCount = 0
|
|
var leftClicks = 0
|
|
let router = StatusItemMouseRouter(
|
|
eventMonitorInstaller: { _, _ in
|
|
monitorInstallCount += 1
|
|
return NSObject()
|
|
},
|
|
eventMonitorRemover: { _ in })
|
|
|
|
router.install(
|
|
on: firstButton,
|
|
onLeftClick: { leftClicks += 1 },
|
|
onRightClick: {})
|
|
let firstClick = try Self.mouseEvent(
|
|
.leftMouseDown,
|
|
location: NSPoint(x: 12, y: 12),
|
|
windowNumber: window.windowNumber)
|
|
#expect(router.route(firstClick) == nil)
|
|
#expect(leftClicks == 1)
|
|
|
|
router.install(
|
|
on: secondButton,
|
|
onLeftClick: { leftClicks += 1 },
|
|
onRightClick: {})
|
|
#expect(monitorInstallCount == 1)
|
|
#expect(router.route(firstClick) === firstClick)
|
|
let secondClick = try Self.mouseEvent(
|
|
.leftMouseDown,
|
|
location: NSPoint(x: 52, y: 12),
|
|
windowNumber: window.windowNumber)
|
|
#expect(router.route(secondClick) == nil)
|
|
#expect(leftClicks == 2)
|
|
}
|
|
|
|
@Test func `non-target and unrelated events continue to native dispatch`() throws {
|
|
var leftClicks = 0
|
|
var rightClicks = 0
|
|
let nonTarget = try Self.mouseEvent(.leftMouseDown)
|
|
let unrelated = try Self.mouseEvent(.mouseMoved)
|
|
|
|
let routedNonTarget = try #require(StatusItemMouseRouter.route(
|
|
nonTarget,
|
|
hitsTarget: false,
|
|
onLeftClick: { leftClicks += 1 },
|
|
onRightClick: { rightClicks += 1 }))
|
|
let routedUnrelated = try #require(StatusItemMouseRouter.route(
|
|
unrelated,
|
|
hitsTarget: true,
|
|
onLeftClick: { leftClicks += 1 },
|
|
onRightClick: { rightClicks += 1 }))
|
|
|
|
#expect(routedNonTarget === nonTarget)
|
|
#expect(routedUnrelated === unrelated)
|
|
#expect(leftClicks == 0)
|
|
#expect(rightClicks == 0)
|
|
}
|
|
|
|
private static func mouseEvent(
|
|
_ type: NSEvent.EventType,
|
|
location: NSPoint = .zero,
|
|
windowNumber: Int = 0) throws -> NSEvent
|
|
{
|
|
try #require(NSEvent.mouseEvent(
|
|
with: type,
|
|
location: location,
|
|
modifierFlags: [],
|
|
timestamp: 0,
|
|
windowNumber: windowNumber,
|
|
context: nil,
|
|
eventNumber: 1,
|
|
clickCount: 1,
|
|
pressure: 1))
|
|
}
|
|
}
|