* 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
- Delete docs-site/ (superseded by web/app/docs)
- Add posthog-js with Vercel reverse proxy at /cdata to bypass adblockers
- Track pageviews (SPA-aware), download clicks (hero/navbar/mobile_drawer),
and GitHub link clicks (hero/navbar/mobile_drawer/footer)
Three bonsplit commits (429af82, 2ff740d, b1948ab) adding the tab bar
+ button were lost when ad159da moved the submodule pointer to a
branch that forked before those commits. Merge origin/term-browser-icons
back into bonsplit main to restore them.
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.
Replace the hero screenshot and add a visual features table showing
notification rings, notification panel, in-app browser, and vertical
tabs & splits with per-feature screenshots.
* Add features table with images to README
Replace the hero screenshot and add a visual features table showing
notification rings, notification panel, in-app browser, and vertical
tabs & splits with per-feature screenshots.
* Rename feature heading to "Vertical + horizontal tabs"
* Remove parenthetical from notification rings description
* 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
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.
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
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.
Add a local release skill that builds, signs, notarizes, and uploads
releases without GitHub Actions. Set up direnv to load Apple signing
secrets from ~/.secrets/cmuxterm.env.
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).
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
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.
* 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.
* 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>
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>
* 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
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.