* fix(window): restore the macOS 26 reflow without touching the native frame
#10253 stopped the main-thread deadlock by skipping the repaint size nudge on
macOS 26, but invalidate() repaints without reflowing, so the h-dvh root kept a
stale viewport height and the status bar stayed clipped off-screen (STA-2383) on
every Tahoe reveal, restore and wake.
Drive the reflow through device emulation instead: a +1px emulated viewport,
reverted a frame later, makes the renderer recompute layout without any NSWindow
mutation, so the FrontBoardServices re-entrancy that wedged the main thread for
109 minutes is still never triggered.
Verified against real Electron 43.1.0 on macOS 26.3.1 (Darwin 25.3.0): the
renderer sees the resize and relayouts, the native frame is untouched, the
viewport and devicePixelRatio restore exactly across zoom levels, overlapping
calls collapse to one cycle, and a 1px delta never crosses a terminal cell
boundary so no pane reports new geometry (no SIGWINCH to running shells).
Also close two gaps in the surrounding code:
- the pre-Tahoe size jiggle now clears its WeakSet latch in a finally block, so a
throwing setSize can no longer suppress every later repaint for that window
- cover powerMonitor 'resume' under the Tahoe guard, the other AppKit dispatch
context implicated in the freeze
* fix(tray): keep NSStatusItem scene updates off the AppKit callout stack
The main-window repaint was only one of the two doors into the macOS 26
FrontBoardServices deadlock. Showing or restoring the window calls
setTrayAttention(false) straight from the window event handler, and
tray.setImage/setToolTip drive an NSStatusItem scene update — the same
re-entrant scene mutation from inside AppKit's own dispatch, matching the
stackshot in openai/codex#23695.
Defer the native mutation to a fresh event-loop turn so the callout frame is
vacated first. The attention flag itself still flips synchronously: rapid
show/hide would otherwise mis-dedupe against a value that had not landed yet.
Bursts collapse to a single repaint, and because the deferred pass reads current
module state rather than a captured value, a coalesced schedule can never apply
a stale icon. applyTrayImage already no-ops on a destroyed tray, so a repaint
still queued when the tray goes away is harmless.
* fix(window): reflow maximized and fullscreen windows on macOS 26 too
The maximized/fullscreen bail-out predates the Tahoe path and exists only to
keep the size nudge from resizing a window out of those states. Emulation never
touches the frame, so that guard was suppressing the reflow for no reason — and
a maximized window strands its dvh layout exactly like a normal one.
Run the Tahoe branch before the guard. Verified on macOS 26.3.1 that the
emulated viewport reflows a maximized and a fullscreen window while leaving both
states intact.
* fix(window): retry the viewport restore instead of stranding the renderer
If disableDeviceEmulation threw while the webContents was still alive, the
previous code swallowed the error and cleared the latch anyway, leaving the
renderer pinned at the emulated 1px-taller viewport for the rest of the window's
life — and letting the next reveal stack a fresh cycle on top of it.
Retry the restore on a bounded schedule and hold the latch while a retry is
pending. A destroyed webContents still short-circuits, since the emulated
viewport dies with it, and the attempt budget keeps a permanently failing
restore from pinning the latch forever.
* Add native macOS menu bar status item with settings toggle
Extend the Windows-only system tray into a shared status-item module
so macOS gets a template menu bar icon (Open/Settings/Check for
Updates/Quit), a theme-aware attention dot, and a "Show Menu Bar
Icon" appearance setting. Also fixes a startup race where a tray
"Settings…" click could fire before the renderer's ui:openSettings
listener attached, by queuing a one-shot pending-open-settings intent
the renderer consumes on mount.
* Fix Retina blur, race conditions, and menu-label duplication in tray Set
- Rebuild the tray attention icon's @2x representation since toBitmap only
read 1x pixels, blurring the glyph on Retina displays
- Fix premultiplied-alpha math so light-glyph tinting uses per-pixel alpha
instead of a flat 0xff, keeping antialiased edges valid
- Always push ui:openSettings and leave a longer-lived pending flag, since
there was no reliable signal that a renderer's listener was attached
- Preserve tray attention state across macOS menu-bar hide/show toggles
instead of resetting it on tray destroy
- Route macOS tray creation through syncMacMenuBarIcon so startup and the
live toggle share one visibility policy
- Reuse app-menu translation keys for tray Settings/Check for Updates and
drop the now-duplicate tray-scoped locale strings
* Make menu bar icon default on and platform-independent in settings
Previously showMenuBarIcon was hardcoded to darwin-only in both the
default settings and the sanitize/load paths, so a profile written on
macOS lost its opt-out when touched from another OS. Store the raw
boolean everywhere and let darwin-specific consumers decide whether
to act on it, so the value round-trips unchanged across platforms.
* Fix Settings menu click being silently dropped after a slow cold rendere
Replace the 60s pendingOpenSettings TTL with an untimed intent, since a
cold renderer start can outrun any fixed timeout and cause the flag to
expire before the click is consumed. webContents-id scoping plus
consume-on-read still prevent the intent from leaking to an unrelated
renderer. Adds a test covering the queued-before-mount pull path.
Show a small amber dot on the floating-workspace launcher (both the
floating-button and status-bar triggers) whenever any floating-workspace tab
still has an unacknowledged terminal bell or agent completion, and a
composited amber dot on the Windows tray icon when the window is
minimized/hidden. Both clear through the existing show-until-interact paths —
engaging with or closing the offending tab drops the dot with no stale unread
state left behind.
The launcher dot derives from the existing per-tab/per-pane unread maps via a
new selectFloatingWorkspaceHasUnread selector (primitive boolean, empty-
workspace early return, no bespoke state). The tray dot rides the notification
dispatch and clears on window show/restore.