Commit graph

145 commits

Author SHA1 Message Date
Lawrence Chen
cd0b8da82b Fix split-close stale-frame stretch and add regression coverage 2026-02-19 04:15:50 -08:00
Lawrence Chen
1d246b2bbd Fix omnibar focus intent races for Cmd+L 2026-02-19 00:35:25 -08:00
Lawrence Chen
5fc4df467a
Fix omnibar Cmd+L infinite focus loop causing 100% CPU (#97)
The browser omnibar's updateNSView and controlTextDidEndEditing
were both dispatching makeFirstResponder calls without any guard
against re-dispatch. Each makeFirstResponder triggers SwiftUI's
FirstResponderObserver, which re-evaluates the view graph, which
calls updateNSView again, creating an infinite loop via the main
dispatch queue.

Fix: Add a pendingFocusRequest flag on the coordinator to prevent
re-dispatching while a focus/blur request is already in flight.
Also add nsView.currentEditor() != nil to the isFirstResponder
check so the field is recognized as focused during the transition
when the field editor (not the field itself) is first responder.
2026-02-18 23:18:08 -08:00
Lawrence Chen
d08f28d770
Merge pull request #83 from manaflow-ai/perf/portal-hosting-selected-mount
Reduce terminal input latency via portal hosting + selected-only workspace mounting
2026-02-18 22:31:45 -08:00
Lawrence Chen
ee4848c008 Speed up workspace switching: reduce portal churn and enforce selected z-order 2026-02-18 22:13:40 -08:00
Austin Wang
ab89fab897
Fix browser opening new tabs on link click (#92)
* Fix browser panel opening new tabs on every link click

Navigate target=_blank and window.open() links in the current webview
instead of spawning new browser tabs.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* Preserve cmd+click new tab behavior in createWebViewWith

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-18 21:53:36 -08:00
Lawrence Chen
7aa80b9cdc Stabilize rapid workspace switching handoff 2026-02-18 21:17:53 -08:00
Austin Wang
2d64ecfc44
fixes crashes and filedrop fatal issue 2026-02-18 20:46:31 -08:00
Lawrence Chen
a723bbaa6a Fix Finder file drop routing for portal-hosted terminals 2026-02-18 20:33:35 -08:00
Lawrence Chen
27df9cd171 Remove SwiftUI dimming fallback for portal terminals 2026-02-18 20:28:00 -08:00
Lawrence Chen
b05347884d Restore unfocused pane dimming for portal-hosted terminals 2026-02-18 20:24:54 -08:00
Lawrence Chen
22aa4d48da Fix portal lifecycle retention and add regression tests 2026-02-18 20:17:44 -08:00
Lawrence Chen
edadda6d8c Fix portal hit-testing and teardown visibility 2026-02-18 20:05:49 -08:00
Lawrence Chen
ed7f6301d0 Improve terminal hosting depth and workspace mount policy 2026-02-18 19:55:41 -08:00
Lawrence Chen
03a1cc13e0
Fix stack overflow in FileDropOverlayView mouse forwarding (#82)
The hide-send-unhide pattern in forwardEvent() can recurse infinitely
when gesture recognizer routing re-delivers the event despite isHidden.
Add a re-entrancy guard to break the cycle.

Fixes EXC_BAD_ACCESS (stack overflow) crash in production.
2026-02-18 19:51:25 -08:00
Lawrence Chen
88ee368f78
Fix titlebar folder icon briefly enlarging on workspace switch (#81) 2026-02-18 19:05:57 -08:00
Lawrence Chen
6748c202f2
Fix sidebar drag-and-drop broken by FileDropOverlayView (#76)
* Fix sidebar drag-and-drop broken by FileDropOverlayView

The FileDropOverlayView (added in 9fd3cc2) sits on the window's theme
frame above the content view. Its hitTest returned self for all events,
causing AppKit to route drag sessions to the overlay instead of the
content view where SwiftUI lives. AppKit walks UP the superview chain
from the hit-tested view, never checking siblings — so SwiftUI's
.onDrop handlers for sidebar tab reordering were never reached.

Three changes fix this:

1. Smart hitTest: check NSPasteboard(name: .drag) for .fileURL and only
   return self during Finder file drags. Return nil otherwise so mouse
   events and internal drags pass through to the content view.

2. Custom UTType for sidebar drags: replace the fragile UTType.plainText
   hack with a proper com.cmux.sidebar-tab-reorder type registered in
   Info.plist. Uses visibility: .ownProcess since it's internal-only.

3. Narrow overlay registration: only register for .fileURL instead of
   .fileURL + .URL + .string. The broad .string type collided with
   text-based drag payloads.

* Add custom UTType Info.plist pitfall to CLAUDE.md
2026-02-18 17:20:22 -08:00
Austin Wang
5647aacacd
Revert "ok (#68)" (#74)
This reverts commit 5a10ec8d17.
2026-02-18 16:42:41 -08:00
Austin Wang
5a10ec8d17
ok (#68) 2026-02-18 14:38:07 -08:00
Lawrence Chen
4c42bd8078 Fix migrateMode dropping allowAll to default cmuxOnly
migrateMode() had no case for "allowAll" rawValue, so it fell
through to the default branch which returned .cmuxOnly. This
silently downgraded any persisted allowAll setting.
2026-02-18 02:12:19 -08:00
Lawrence Chen
51a67e31fd
Socket access control: process ancestry check (#58)
* Socket access control: process ancestry check + file permissions

Redesign socket control modes from (off, notifications, full) to
(off, cmuxOnly, allowAll):

- cmuxOnly (default): uses LOCAL_PEERPID + sysctl process tree walk to
  verify the connecting process is a descendant of cmux. External
  processes (SSH, other terminals) are rejected.
- allowAll: hidden mode accessible only via CMUX_SOCKET_MODE=allowAll
  env var, skips ancestry check. Legacy "full"/"notifications" env
  values map here for backward compat.
- off: disables socket entirely.

Security hardening:
- Server: chmod 0600 on socket after bind (owner-only access)
- CLI: stat() ownership check before connect (reject fake sockets)

Removes per-command allow-list (isCommandAllowed) — once a process
passes the ancestry check, all commands are available.

Includes migration for persisted UserDefaults values and env var
aliases (cmux_only, cmux-only, allow_all, allow-all).

* Add /sync-branch skill for submodule + main sync
2026-02-18 01:09:24 -08:00
Lawrence Chen
d8b39aeaa4 Merge remote-tracking branch 'origin/main' into fix-file-drop-targeting 2026-02-17 22:12:26 -08:00
Lawrence Chen
8104c1675b Use paste path for file drops and don't steal focus
Switch from ghostty_surface_key (key event path) to ghostty_surface_text
(paste path) for file drops, matching upstream Ghostty. This triggers
bracketed paste mode and eliminates the lag on drop.

Remove makeFirstResponder calls from insertDroppedPasteboard and
handleDroppedURLs so dropping a file doesn't steal keyboard focus from
the currently focused terminal.
2026-02-17 22:11:47 -08:00
Lawrence Chen
9af7df0dac Fix socket accept loop not restarted after Sparkle update relaunch
After a Sparkle auto-update relaunches cmux, the control socket stops
accepting connections because start() early-returns when isRunning is
true, without checking if the accept loop thread is actually alive.

- Add acceptLoopAlive flag to track accept loop thread liveness
- Fix start() early-return to also check acceptLoopAlive, so a dead
  thread triggers full socket re-creation
- Break acceptLoop() after 50 consecutive accept() failures with 10ms
  backoff instead of tight-spinning forever
- Clean up socket in applicationWillTerminate and
  updaterWillRelaunchApplication for clean teardown before relaunch
2026-02-17 22:00:47 -08:00
Lawrence Chen
9fd3cc2a6c Add file drop support from Finder into terminal splits
Nested NSHostingController layers (from bonsplit's SinglePaneWrapper)
prevent AppKit's NSDraggingDestination routing from reaching terminal
views. Install a transparent FileDropOverlayView on the window's theme
frame that intercepts file drags and forwards drops to the GhosttyNSView
under the cursor. Mouse events pass through via a hide-send-unhide
pattern.

Fix y-axis inversion in split targeting: hitTest expects coordinates in
the receiver's superview's coordinate system, not the receiver's own.
Converting to contentView's coords flipped y because NSHostingView is
flipped, causing top/bottom split drops to land in the wrong terminal.

Also adds bonsplit onFileDrop API, PaneDragContainerView, and
drop_hit_test socket command for testing coordinate-to-terminal mapping.
2026-02-17 21:55:31 -08:00
Lawrence Chen
bf3d22fa8a
Fix flaky WebKit escape focus tests on slow environments (#57)
Two fixes for escape-focus flakiness on VMs:

1. App-side: recordGotoSplitUITestWebViewFocus now retries with increasing
   delays (0.05, 0.1, 0.25, 0.5s) for the exit-address-bar case, giving
   WebKit more time to accept first responder.

2. Test-side: both testEscapeLeavesOmnibarAndFocusesWebView and
   refocusWebView helper send a second Escape if the first only clears
   suggestions/editing state (Chrome-like two-stage escape behavior).
2026-02-17 21:12:28 -08:00
Lawrence Chen
f0e4ccdc1d
Show sidebar/notification/new-tab controls in fullscreen without hovering titlebar (#55)
In fullscreen mode, the NSTitlebarAccessoryViewController buttons are hidden
with the system titlebar. This adds SwiftUI-based fullscreen controls that
appear in the sidebar area (when visible) or inline in the custom titlebar
(when sidebar is hidden), reusing the existing TitlebarControlsView component.

- Track fullscreen state via window notifications and toggle controls visibility
- Hide original titlebar accessory (isHidden + alphaValue=0) in fullscreen
- Route notification popover anchoring through fullscreen controls view model
  so both button clicks and keyboard shortcuts (Cmd+Shift+I) position correctly
- Add debug titlebar spacing slider for fine-tuning leading inset
2026-02-17 20:24:01 -08:00
Lawrence Chen
4220c3808f
Fix sidebar metadata commands failing in multi-window setups (#54)
resolveTabForReport only searched the active window's tab manager, so
report_ports, report_git_branch, report_pwd, set_status etc. returned
"Tab not found" when the shell's --tab UUID belonged to a different
window. Fall back to AppDelegate.tabManagerFor(tabId:) to search all
window contexts.
2026-02-17 19:33:20 -08:00
Lawrence Chen
6d6efb6b60 Reversion all 1.x.x to 0.x.x for pre-launch versioning 2026-02-17 18:31:05 -08:00
Lawrence Chen
7f8b639ae3
Open cmd+clicked terminal links in cmux browser panel (#53)
* Open cmd+clicked terminal links in cmux browser panel instead of system browser

Handle GHOSTTY_ACTION_OPEN_URL in the Ghostty action callback to intercept
link opens. Uses preferredBrowserTargetPane to reuse an existing right-side
pane, falling back to a new horizontal split.

* Use tab-specific manager for OPEN_URL action

Use tabManagerFor(tabId:) instead of the active-window tabManager so the
lookup succeeds when the surface belongs to a different window context.
2026-02-17 18:24:36 -08:00
Austin Wang
0cca513eac
Browser searchbar bugs (#51)
* Add "+" menu button to horizontal tab bar for new terminal/browser tabs

Adds a "+" button to the tab bar (next to split buttons) that shows a
dropdown menu with "New Terminal ⌘T" and "New Browser ⌘⇧L" options.

- Uses native NSButton + NSMenu so the icon matches the split buttons
- Menu appears below the button
- Routes tab creation through new didRequestNewTab delegate method

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* works

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-17 16:26:42 -08:00
Austin Wang
23db0a3fa2
maximize-sidebar (#50) 2026-02-17 16:11:00 -08:00
Austin Wang
addb99ed50
Add "+" menu button to horizontal tab bar for new terminal/browser tabs (#48)
Adds a "+" button to the tab bar (next to split buttons) that shows a
dropdown menu with "New Terminal ⌘T" and "New Browser ⌘⇧L" options.

- Uses native NSButton + NSMenu so the icon matches the split buttons
- Menu appears below the button
- Routes tab creation through new didRequestNewTab delegate method

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-17 14:59:24 -08:00
Lawrence Chen
c2fdd48290
Release v1.36.0 (#47)
* Fix multi-workspace drag/drop, WebView click focus, and add regression tests

- Wire bonsplit isInteractive to workspace active state so inactive
  workspace NSViews are hidden from AppKit event routing
- Add CmuxWebView.mouseDown notification for browser panel focus
  tracking (AppKit delivers clicks to WKWebView, not SwiftUI overlays)
- Add multi-workspace focus regression test covering isHidden fix,
  rapid workspace switching, and browser panel focus routing

* Bump version to 1.36.0
2026-02-17 04:04:29 -08:00
Lawrence Chen
2678606a20
Fix omnibar tracking timeout using background queue (#46)
The 3-second safety net that posts a synthetic mouseUp to break out of
NSTextView's stuck tracking loop was dispatched on the main queue. Since
super.mouseDown blocks the main thread in the tracking loop, the timeout
could never fire. Use a background queue instead (NSApp.postEvent is
thread-safe). Use DispatchWorkItem.isCancelled for atomic cancellation.
2026-02-17 03:35:49 -08:00
Lawrence Chen
484b66c8ac
Fix terminal keys swallowed after opening browser (#45)
* Fix terminal keys (arrows, Ctrl+N/P) swallowed after opening browser

After a browser panel is shown, SwiftUI's internal focus system activates
and its _NSHostingView starts consuming arrow keys and other non-Command
key events via performKeyEquivalent, preventing them from reaching the
terminal's keyDown handler.

Fix: In the NSWindow performKeyEquivalent swizzle, when GhosttyNSView is
the first responder and the event has no Command modifier, route directly
to the terminal's performKeyEquivalent — bypassing SwiftUI's view hierarchy
walk entirely.

Also clear stale browserAddressBarFocusedPanelId when a terminal surface
has focus, preventing Cmd+N from being eaten by omnibar selection logic
after focus transitions away from a browser.

Adds DEBUG-only keyboard event ring buffer (KeyDebugLog) that dumps to
/tmp/cmux-key-debug.log for diagnosing future key routing issues.

* Fix split focus and Cmd+Shift+N swallowed after opening browser

Split focus: capture the source terminal's hostedView before bonsplit
mutates focusedPaneId, so focusPanel moves focus FROM the old pane
instead of from the new pane to itself. Also retry ensureFocus when the
new terminal's view has no window yet (matching the existing retry
pattern for isVisibleInUI).

Cmd+Shift+N: after WKWebView has been in the responder chain, SwiftUI's
internal focus system can intercept Command-key events in the content
view hierarchy (returning true) without firing the CommandGroup action
closure. Fix by dispatching Command-key events directly to NSApp.mainMenu
when the terminal is first responder, bypassing the broken SwiftUI path.
Also add Cmd+Shift+N to handleCustomShortcut so it's customizable and
doesn't depend on SwiftUI menu dispatch at all.

* Unified debug event log: merge key/mouse/focus into /tmp/cmux-debug.log

- Delete KeyDebugLog, MouseDebugLog, klog(), mlog() from AppDelegate
- Replace all klog/mlog calls with dlog() (provided by bonsplit)
- Remove debugLogCallback wiring from Workspace
- Add focus change logging: focus.panel, focus.firstResponder,
  split.created, focus.moveFocus
- Add import Bonsplit where needed for dlog access
- Fix stale drag state on cancelled tab drags (bonsplit submodule)

* Fix split focus stolen by re-entrant becomeFirstResponder during reparenting

During programmatic splits (Cmd+D / Cmd+Shift+D), SwiftUI reparents the old
terminal view, which fires becomeFirstResponder → onFocus → focusPanel for the
OLD panel, stealing focus from the newly created pane.

Add programmaticFocusTargetPanelId guard to suppress re-entrant focusPanel
calls for non-target panels during split creation.

Also document the unified debug event log in CLAUDE.md.

* Clear stale title/favicon when browser navigation fails

When a page fails to load (e.g. connection refused), the tab was still
showing the previous page's title and favicon. Now didFailProvisionalNavigation
resets pageTitle to the failed URL and clears faviconPNGData.

* Fix Cmd+N swallowed by browser omnibar and improve split focus suppression

- Only Ctrl+N/P trigger omnibar navigation, not Cmd+N/P (Cmd+N should
  always create new workspace regardless of address bar focus)
- Move split focus suppression from workspace-level guard to source:
  suppress becomeFirstResponder side-effects (onFocus + ghostty_surface_set_focus)
  directly on the old GhosttyNSView during reparenting, preventing both
  model-level and libghostty-level focus divergence
- Remove programmaticFocusTargetPanelId from Workspace.focusPanel

* Fix omnibar hang, WebView white flash, drag-over-browser, and idle CPU spin

- Omnibar: first click selects all without entering NSTextView tracking loop;
  subsequent clicks have 3s synthetic mouseUp safety net to prevent hang
- WebView: set underPageBackgroundColor to match window so new browsers don't
  flash white before content loads
- Drag/drop: register custom UTType (com.splittabbar.tabtransfer) in Info.plist
  so WKWebView doesn't intercept tab drags; override registerForDraggedTypes
  on CmuxWebView as belt-and-suspenders
- CPU: fix infinite makeFirstResponder loop in controlTextDidEndEditing by
  checking both the text field and its field editor (the actual first responder)
2026-02-17 03:21:08 -08:00
austinpower1258
bd54d06213 fixes browser issue with localhost 2026-02-16 16:21:31 -08:00
Lawrence Chen
c0f7a07a7b Fix sidebar tabs getting extra left padding when update pill is visible
Move GeometryReader from wrapping the entire VStack to wrapping only the
ScrollView so proxy.size.height reflects available height (minus pill),
preventing unnecessary scrollability that triggered macOS horizontal insets.

Also clamp update pill text width with maxWidth instead of fixed width so
it truncates gracefully at narrow sidebar widths and grows when wider, add
horizontal padding, left-align truncated text, and add debug menu item for
testing with long nightly version strings.
2026-02-16 03:20:51 -08:00
Lawrence Chen
3fa72a0b0b Add sidebar metadata (git branch, ports, logs, progress), fix localhost URL resolution, simplify analytics
- Sidebar now shows git branch, listening ports, log entries, progress bars, and status pills with expand/collapse
- Fix localhost/127.0.0.1 URL parsing by checking before generic URL(string:) which misinterprets the scheme
- Remove custom Keychain distinct ID in favor of PostHog SDK's built-in anonymous ID
- browser open now defaults to caller's workspace via CMUX_WORKSPACE_ID env var
- Improve CLI help text for environment variables
2026-02-16 02:46:39 -08:00
Lawrence Chen
957178d03e tag PostHog events with platform: cmuxterm 2026-02-15 21:49:44 -08:00
Lawrence Chen
b351277063 add arrow key omnibar navigation, browser zoom shortcuts, and history flush on quit 2026-02-15 21:49:38 -08:00
Lawrence Chen
4af05b63d6 remap browser shortcut to Cmd+Shift+L, flash to Cmd+Shift+H 2026-02-15 21:49:33 -08:00
Lawrence Chen
ac4b49d7a4 move update pill to sidebar only, add Install Update menu item 2026-02-15 21:17:33 -08:00
Lawrence Chen
de6cfededa Fix inline completion showing "news." instead of "news.ycombinator.com"
Reject URLs whose host lacks a TLD (no internal dot) from autocompletion
candidates, history recording, and clean up existing bogus entries on load.
2026-02-15 21:13:31 -08:00
Lawrence Chen
430e86909f show update pill in sidebar for Release builds 2026-02-15 19:59:55 -08:00
Lawrence Chen
2080a8ea5c
Release v1.29.0 (#37)
* Bump version to 1.29.0

* Include uncommitted TabManager changes needed for build
2026-02-15 18:41:13 -08:00
Lawrence Chen
ad4409c55e Cmd+[/] browser back/forward, cmd+click open in new tab, right-click open in new tab
- Change Cmd+[/] from workspace history navigation to browser back/forward
  (only when focused surface is a browser, no-op on terminal)
- Add WKUIDelegate with createWebViewWith to handle target=_blank links
- Add decidePolicyFor check for targetFrame==nil and cmd+click modifier
  to open links in new browser surface in the same pane
- Override willOpenMenu in CmuxWebView to rename "Open Link in New Window"
  to "Open Link in New Tab"
- Add E2E test for browser back/forward via socket and Cmd+[/] shortcuts
2026-02-15 18:35:58 -08:00
Lawrence Chen
777d6b048e Replace CLAUDE_CONFIG_DIR with claude wrapper + richer notifications
Instead of creating a merged config directory and injecting
CLAUDE_CONFIG_DIR on every terminal spawn, place a thin wrapper
script at Resources/bin/claude that intercepts claude invocations
to inject --session-id and --settings flags. This eliminates
blocking I/O on terminal creation and removes config management
complexity.

- Add Resources/bin/claude wrapper script with hook injection
- Add shell integration PATH fix (re-prepend after .zshrc/.bashrc)
- Add transcript reading for richer stop notifications
- Add set_status/clear_status to notifications socket allowlist
- Add Settings toggle to disable Claude Code integration
- Update docs to reflect automatic integration approach
- Unset CLAUDECODE env var to avoid nested session detection
2026-02-15 18:33:36 -08:00
Lawrence Chen
f789306a97 enable update pill in Release builds 2026-02-15 18:08:32 -08:00
Lawrence Chen
2c205893fb Change reload configuration shortcut to Cmd+Shift+, 2026-02-15 17:04:45 -08:00