ReviewOS

craft-native/craft

--system-tray skips activateWindowedApp(), leaving the window unable to take left clicks

#419
Closed glennmichael123 opened this 7 days ago ·
7 days ago

Summary

A window launched with --system-tray never becomes key, so every left click inside it is swallowed as first-mouse activation. Right clicks work, because a context menu does not need a key window — which makes this present as "the page renders perfectly and the buttons are dead".

Cause

main.zig:

if (self.system_tray == null and !self.headless) {
    macos.showAllWindows();
    // Windowed app: claim regular activation (Dock icon, key
    // window, foreground) — launching from a bare binary would
    // otherwise leave the window buried with no Dock presence.
    macos.activateWindowedApp();
} else if (self.headless) {
    macos.setAccessoryActivationPolicy();
}

With a system tray configured, system_tray != null, so neither branch runs. activateWindowedApp() is the function that does both halves of the job:

pub fn activateWindowedApp() void {
    ...
    _ = msgSend1(app, "setActivationPolicy:", @as(c_long, 0)); // Regular
    ...makeKeyAndOrderFront / activate...
}

So a tray app never claims NSApplicationActivationPolicyRegular and is never activated. The window is ordered on screen but cannot become key.

acceptsFirstMouse: is not overridden anywhere in the tree, so the webview does not opt out of first-mouse either.

Reproduce

craft http://localhost:3000 --system-tray --width 380 --height 620

with any page containing a button. Left-click it: nothing. Right-click anywhere: the context menu appears.

Dropping --system-tray alone fixes it.

Expected

A tray app that also has a window should still be able to take clicks in that window. The two are not mutually exclusive — a VPN or a cleaner has both a menu bar item and a main window.

Workaround

Reachable from the page, which is how we are shipping around it today:

craft.app.showDockIcon()  // sets NSApplicationActivationPolicyRegular
craft.window.focus()      // makeKeyAndOrderFront

Suggested fix

Run the activation branch whenever a window exists, independently of whether a tray does — menubarOnly / headless are the cases that genuinely want no activation, not systemTray.

Environment

  • craft 0.0.90 (also present in main at 0.0.359 — the branch is unchanged)
  • macOS 26 (Darwin 25.3.0), arm64
7 days ago

Implemented on main in 8694a21 (five files). Not published yet; leaving this open until the shipping version is confirmed.

Visible tray windows now get activation independently of the status item. Explicit --hide-dock-icon uses Accessory policy while still allowing window interaction. Headless and windowless menubar apps remain unshown. The tray CLI now propagates its headless setting into App as well.

The native regression also exposed that the first NSApplication window may be a status-item utility window. Activation now searches for a visible window that can become key instead of blindly choosing index zero.

Validation: App.run with a real status item and real AppKit mouse-down/up delivery into a WebKit button; framed and frameless windows with and without a Dock icon; headless and menubar-only cases; status item remains visible. All six cases passed in both public-API mode and ReleaseSafe with the default JavaScript backend. This is in the existing test:webkit-public CI target. No page-side showDockIcon/focus workaround and no Accessibility grant are used by the test.

Also passed locally: full Zig suite with js-runtime=false, ReleaseSafe CLI build with the default backend, SDK suite (796 passed, 8 skipped), typecheck, package verification (43 checks), release suite (397 passed), formatting and lint (0 errors, 27 existing warnings). Remote CI for the new commit is still pending. This does not claim to fix the separate tray callback issue #420.

6 days ago

Shipped in v0.0.369, containing fix 8694a21.

Exact-tag macOS CI passed all six real tray-window input cases, twice (public-API-only and ordinary builds): visible framed/frameless windows with and without the Dock icon, plus headless and windowless menubar modes. The retained job log explicitly reports every case verified: https://github.com/craft-native/craft/actions/runs/40491396124/job/122162890120 . The tests use App.run, a real status item and AppKit mouse delivery into WebKit, without a page-side showDockIcon/focus workaround.

Release identity, exact native/source acceptance, downloaded Darwin assets and final publication all succeeded: https://github.com/craft-native/craft/actions/runs/40491396336 . Closing this activation issue; the independent callback reproduction tracked by #420 is not being closed by this result.

Sign in to comment on this issue.