* Fix orphaned child processes when closing workspace tabs
When closing a workspace tab via the sidebar X button, child processes
(login → zsh → claude) survived as orphans because TabManager.closeWorkspace()
only removed the workspace from the tabs array without explicitly freeing
Ghostty surfaces. It relied on ARC to cascade deallocation, but SwiftUI views
and Combine publishers held references, delaying or preventing
ghostty_surface_free() (which sends SIGHUP) from ever running.
This adds explicit teardown on the workspace close path:
- TerminalSurface.teardownSurface(): idempotent method to free the Ghostty
runtime surface eagerly, matching the existing deinit logic
- TerminalPanel.close() now calls teardownSurface() to ensure SIGHUP is sent
- Workspace.teardownAllPanels() iterates all panels and closes them
- TabManager.closeWorkspace() calls teardownAllPanels() before removing
the workspace from the tabs array
* Harden workspace teardown and ownership checks
* Address follow-up teardown review feedback
---------
Co-authored-by: Lawrence Chen <54008264+lawrencecchen@users.noreply.github.com>
* Fix Claude wrapper hook injection when cmux socket is stale
* Harden socket listener lifecycle and rearm policy
* Unset CLAUDECODE in stale-socket passthrough
* Harden listener cleanup and bound claude ping probe
* Guard socket unlink during listener startup window
JavaScript-based find using TreeWalker + <mark> highlights with
match counter, next/previous navigation, and drag-to-corner overlay
matching the existing terminal find bar.
- BrowserFindJavaScript: JS generation for search/next/prev/clear
- BrowserSearchOverlay: SwiftUI overlay with IME-safe onSubmit
- BrowserSearchState: Observable state (needle/selected/total)
- TabManager routing: Cmd+F/G dispatches to browser when focused
- Visibility filter: skips script/style/hidden/aria-hidden elements
- Stale DOM guard: isConnected check in next/previous scripts
- Navigation cleanup: clears find on didFinish and didFailNavigation
Co-authored-by: Lawrence Chen <54008264+lawrencecchen@users.noreply.github.com>
* Vi mode P0 improvements: half-page scroll, visible cursor, gg fix
Three changes to keyboard copy mode (vi mode):
1. Ctrl+U/D now scroll half-page (was full-page). Ctrl+B/F remain
full-page. Uses Ghostty's scroll_page_fractional binding.
2. Entering copy mode now creates a 1-cell selection at the terminal
cursor via select_cursor_cell, giving the user a visible cursor
indicator. Visual mode (v) is tracked separately from Ghostty's
has_selection so the cursor selection doesn't make every motion
behave as visual. Exiting visual mode (v again) collapses back
to the cursor cell.
3. Single 'g' is now a prefix key requiring 'gg' to scroll to top,
matching standard vim behavior. Uses the same pendingG state
machine pattern as pendingYankLine for 'yy'.
Part of https://github.com/manaflow-ai/cmux/issues/846
* Fix viewport row refresh with persistent cursor selection
The 1-cell cursor selection made refreshKeyboardCopyModeViewportRowFromVisibleAnchor
always bail (has_selection was always true). Guard on keyboardCopyModeVisualActive
instead so viewport row updates work after scrolling in non-visual mode. Also
re-creates the cursor cell after refresh to preserve visibility.
Additionally: use performBindingAction(_:repeatCount:) for scrollHalfPage,
add state-reset assertion to testGGWithSelectionAdjustsToHome.
Addresses review feedback from CodeRabbit, Cubic, Codex, and Greptile.
* Add keyboard copy mode for terminal scrollback
* Show vim copy mode indicator in terminal
* Fix vi copy-mode symbol keys and pending yank handling
* Refine copy-mode badge wording and font
* Rename keyboard copy-mode badge to VI MODE
* Address PR feedback for copy-mode routing and keyup handling
* Refresh copy-mode viewport row after scrolling
* Add setting to hide Cmd-hold shortcut hints
* Bump bonsplit for Cmd-hold pane hint toggle
* Document tagged app link format in agent notes
* Disable Ctrl pane hints when hold-hints toggle is off
When sibling.hitTest() triggers a SwiftUI layout pass during the
drag handle's sibling walk, AppKit can call back into
windowDragHandleShouldCaptureHit before the outer invocation
finishes. This re-entry accesses SwiftUI view state that is already
held exclusively, causing a Swift runtime SIGABRT.
Add a module-level re-entrancy guard that bails out (returns false)
on nested calls to the sibling walk. Since hitTest is always called
on the main thread, a simple Bool flag is sufficient.
Crash was reproduced on macOS Sequoia 15.1.1 (24B91) in a UTM VM.
The crash stack: DraggableView.hitTest -> windowDragHandleShouldCaptureHit
-> sibling.hitTest -> SwiftUI body evaluation -> hitTest (re-entry)
-> exclusive-access violation -> SIGABRT.
* Honor Ghostty background-opacity across all cmux chrome
Parse background-opacity from Ghostty config and propagate it through
the entire chrome pipeline: bonsplit tab bar (via RRGGBBAA hex),
browser panel/omnibar, titlebar, empty panel, and window background.
Decouple glass effect from sidebar blend mode — bgGlassEnabled now
defaults to false so opacity works independently. Add
GhosttyBackgroundTheme helper for consistent color+opacity resolution
across all UI surfaces.
Fixes https://github.com/manaflow-ai/cmux/issues/263
* Titlebar and chrome opacity matches terminal background-opacity
Use CALayer-level opacity for the titlebar background instead of SwiftUI
Color alpha, matching the terminal's Metal compositing path. Account for
the double alpha stacking in the terminal area (Bonsplit container bg +
Ghostty renderer) so the titlebar visually matches.
Also fix opacity-only config changes not triggering titlebar refresh on
Cmd+Shift+, reload.
* Route local HTML open targets to cmux browser
* Keep file:// omnibar navigation inside cmux browser
* Load local file URLs via WKWebView file API
* Add browser regression test for local file URL loads
* Address PR feedback on local HTML and file URL handling
* Set cmux TestAction to Debug for UI tests
* Broaden XCTest detection for debug launch gate
* Fix AutomationSocketUITests launch hang in CI
* Stabilize CI Swift package resolution for test jobs
* Stabilize Xcode Cloud UI test focus and socket handling
* Add Xcode Cloud pre-xcodebuild submodule bootstrap
* Harden Xcode Cloud bonsplit bootstrap fallback
Sidebar body was calling sidebarOrderedPanelIds() multiple times per
render for branches, directories, and pull requests. Now computes it
once and passes through. Reduces redundant work during scroll frames.
Closes https://github.com/manaflow-ai/cmux/issues/655
- Notification/focus flash uses workspace customColor (fallback: accent)
- Selection bar/indicator uses workspace customColor when set
- Flash color propagated through Panel.triggerFlash(color:) API
- Browser panel flash overlay uses workspace color
- Regression tests for flash color resolution
Fixes https://github.com/manaflow-ai/cmux/issues/557
Adds CmuxCLIPathInstaller with symlink management at /usr/local/bin/cmux,
exposed via Cmd+Shift+P command palette (VS Code style). Install entry
shown when CLI is not in PATH, uninstall entry shown when it is.
Falls back to osascript admin privilege escalation when /usr/local/bin
is not user-writable. Uses attributesOfItem instead of fileExists to
correctly handle dangling symlinks from relocated app bundles.
Closes https://github.com/manaflow-ai/cmux/issues/618
Add nil guard in forceRefresh() to prevent dereferencing freed surface
pointer. Split else-if chains in Workspace.swift so
requestBackgroundSurfaceStartIfNeeded() runs if surface is freed during
the refresh call. Add regression test exercising the crash path.
Add window-identity check to windowDragHandleShouldCaptureHit so stale
leftMouseDown events from other apps (Finder, Dock) during launch don't
trigger the SwiftUI hierarchy walk while initial layout is mutating.
Add NSLock to breadcrumb limiter for thread safety. Update existing
tests to pass eventWindow for window-attached drag handles.
Adds a "Send anonymous telemetry" toggle in Settings that lets users
disable Sentry crash reporting and PostHog analytics. The setting is
frozen at launch so toggling mid-session shows a restart hint. The hint
correctly clears if the user toggles back to the launch-time value.
Add a cached lazy keychain fallback to SocketControlPasswordStore so
that authentication paths in TerminalController can transparently read
a legacy keychain password without blocking on every request. The
keychain is read at most once and the result is cached behind an
NSLock. File-based and environment passwords still take priority.
Closes https://github.com/manaflow-ai/cmux/issues/579
Moves socket control password from the macOS login keychain to a
plain file at ~/Library/Application Support/cmux/socket-control-password.
This eliminates the system keychain prompt that interrupts users on
first launch or after keychain changes.
- Directory created with 0700, file written with 0600 permissions
- One-time migration copies existing keychain password to the file,
deletes the keychain entry, and records a migration version in
UserDefaults so it runs only once
- CLI SocketPasswordResolver also reads from the file path
- Security framework import is now conditional (#if canImport)
- Adds SocketControlPasswordStoreTests covering round-trip, env
priority, path resolution, and migration behavior
Fixes https://github.com/manaflow-ai/cmux/issues/541
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