Summary
With --system-tray, the status item renders and its menu opens on right click, but neither craft:tray:click nor craft:tray:menuAction ever fires in the page. Left-clicking the item does nothing at all, and clicking an item in the right-click menu does nothing.
Every precondition on the JavaScript side checks out, so the break appears to be in native delivery.
What was verified from the page
Instrumented the real app and reported out over HTTP:
tray.js evaluated; craft=true tray=true
render -> setTitle/setIcon/setMenu: shield
POLLING FLAG = true | deliverTrayClick=function | deliverAction=functionwindow.__craftEnableTrayPolling === true, so the bridge'ssetInterval(() => _post('tray','pollActions',''), 100)is runningwindow.__craftDeliverTrayClickandwindow.__craftDeliverActionare both functionssetIcon/setTitle/setTooltip/setMenuall succeed — the icon visibly changes to the symbol we set, so the tray handle is valid- A listener is attached via the injected bridge's
tray.onClick(window.addEventListener('craft:tray:click', ...)) and viaonMenuAction(_evt('craft:tray:menuAction')) - No page errors (
window.onerrorreported nothing)
Left-clicking the status item produces no log line from the click handler. Right-clicking opens the menu correctly, and choosing an item produces no log line either.
Native path appears correct
Reading v0.0.90:
tray.zig:387—tray_click.install(statusItem)is called, withsendActionOn:set to left|right mouse uptray.zig:505 macosSetMenu— correctly holds the page's menu for right click rather than attaching it, so the button action stays livetray_click.zig trayClickCallback— setsclick_pending = truefor a left clickbridge_tray.zig pollActions— callstakePendingClick()and then:
bridge.evalJS("if(window.__craftDeliverTrayClick)window.__craftDeliverTrayClick('left');") catch |err| {
log.debug("Failed to deliver tray click: {}", .{err});
};Each step reads correctly, which is why I suspect the evalJS target: if the bridge's webview reference is not the tray app's window, the eval fails and the catch logs at debug, which is compiled out of release builds — so the failure is completely silent in a shipped binary.
The same evalJS path carries menu actions, which matches both symptoms failing together.
Reproduce
craft http://localhost:3000 --system-traywith a page that does:
craft.tray.setMenu([{ label: 'Hello', action: 'hello' }])
craft.tray.onClick(() => console.log('left click'))
craft.tray.onMenuAction(d => console.log('action', d))The icon and menu appear. Neither callback ever runs.
Note
Possibly related to #419, in that both are --system-tray paths that leave part of the app unwired — but the mechanisms look independent, and #419 has a known one-line cause while this one does not.
Environment
- craft 0.0.90
- macOS 26 (Darwin 25.3.0), arm64