The equalize splits command was a no-op that always returned false.
Implement it by recursively walking the bonsplit tree and setting
every split divider position to 0.5. Also register the command in
the command palette with a "workspace has splits" precondition so
it only appears when there are multiple panes.
Adds a regression test that creates a nested split layout, skews
divider positions, equalizes, and verifies all dividers are at 0.5.
Fixes https://github.com/manaflow-ai/cmux/issues/571
Only enable .onHover tracking on TitlebarControlButton when the style
uses hoverBackground (e.g. pillGroup). Styles without a visible hover
background no longer install the tracking area, preventing the crash
on notification-bell hover.
Also switches the notifications anchor from .overlay to .background so
AppKit hit-testing no longer conflicts with the popover anchor view.
Includes regression test for the hover-tracking policy.
Fixes https://github.com/manaflow-ai/cmux/issues/537
During app launch, mouseMoved events can trigger hitTest on the drag
handle while SwiftUI is still modifying view state in a layout pass.
The previous blacklist approach (only deferring mouseMoved, cursorUpdate,
nil) let unexpected event types slip through — e.g. activation events
where NSApp.currentEvent is not the mouseMoved being routed — causing
contentView.hitTest() to re-enter SwiftUI views and trigger an exclusive
access violation.
Switch to a whitelist: only leftMouseDown (the sole event the drag
handle actually handles) proceeds with the full view-hierarchy walk.
All other event types bail out immediately. The deferred-event check
runs after suppression recovery (which uses only ObjC associated-object
calls, safe from Swift exclusivity) so stale suppression is still
cleared on passive events, but before the view-hierarchy walk that
triggered the crash.
Fixes#490
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Prevent crash (KERN_INVALID_ADDRESS at ghostty_surface_refresh) during
geometry reconcile after wake-from-sleep by adding proper lifetime
guards for freed surfaces:
- Re-read self.surface before each ghostty C call in forceRefresh()
instead of using a stale captured local that can outlive the surface
- Nil out self.surface in deinit before scheduling the async free Task,
so in-flight closures see nil and bail out
- Re-check surface validity in reconcileTerminalGeometryPass() after
reconcileGeometryNow() which can trigger AppKit layout that frees
surfaces
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
cmux_daily_active deduplicates by UTC date, so PostHog hourly retention
cohorts show 0s. Add a companion cmux_hourly_active event that fires at
most once per UTC hour, deduped via UserDefaults. No flush() after
hourly events (let them batch). The existing 30-minute timer provides
adequate hour-boundary coverage without changes.
Cmd-based keyboard shortcuts (Cmd+T, Cmd+Shift+L, etc.) were blocked
while an IME had active composition (marked text), affecting Chinese
Pinyin, Japanese, Korean, and all other input methods. Since Cmd is a
system modifier never consumed by IME input sequences, exempt
Cmd-modified events from the three IME bypass points in the key event
chain.
Fixes#440
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add otherMouseDown/otherMouseUp handlers to GhosttyNSView that forward
middle-click (button 2) to Ghostty as GHOSTTY_MOUSE_MIDDLE. Ghostty
handles this as paste_from_selection, reading from the selection
clipboard. Non-middle buttons are passed to super for default handling.
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
XCUITest launches the app as a separate process that doesn't inherit
XCTest env vars (XCTestConfigurationFilePath, etc.), so
isRunningUnderXCTest() returns false. The app then hits
shouldBlockUntaggedDebugLaunch() and exits with code 64, causing the
test runner to hang waiting for the app to launch.
Fix: detect CMUX_UI_TEST_* env vars set via XCUIApplication.launchEnvironment
and skip the launch guard. Also revert the failed CMUX_TAG ci.yml workaround.
The ctrl fast path unconditionally returned after calling ghostty_surface_key,
even when it returned false (e.g. ignore keybindings), preventing IMEs from
receiving Ctrl-modified key events. Now falls through to interpretKeyEvents
when the key is not handled.
Also adds keyboard layout change detection around interpretKeyEvents (matching
Ghostty upstream) so that IME-triggered layout switches cause an early return
instead of sending the key to Ghostty.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>