Commit graph

1162 commits

Author SHA1 Message Date
Lawrence Chen
02e5ef9455
Localize tab context menu and alert strings (#1998)
* Pre-launch app for browser UI test on headless CI runners

XCUIApplication.launch() blocks ~60s then fails on headless WarpBuild
runners because foreground activation requires a GUI login session.

Apply the same pre-launch strategy used for the display resolution test:
- CI shell launches the app with env vars before running xcodebuild
- Test detects pre-launched app via manifest, uses activate() instead of
  launch() to avoid killing and relaunching the app
- Falls back to clicking the window for focus via accessibility framework

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

* Revert "Pre-launch app for browser UI test on headless CI runners"

This reverts commit a540e2fd99aaa1395b91a8d50caa797cdd7551b8.

* Localize tab context menu and alert strings

Replace hardcoded English strings in tab rename/move alert dialogs
with String(localized:defaultValue:) calls. Add 10 new localization
keys to Localizable.xcstrings with translations for all 18 supported
languages.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* Localize move-failure alert strings

Address CodeRabbit review: localize remaining hardcoded strings
in the move-failure alert dialog (Move Failed, error message, OK button).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: austinpower1258 <austinwang115@gmail.com>
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Co-authored-by: anthhub <anthhub@163.com>
Co-authored-by: Lawrence Chen <lawrencecchen@users.noreply.github.com>
2026-03-31 04:15:40 -07:00
Lawrence Chen
acd45bbd9e
Remove copy-on-select setting 2026-03-31 04:05:13 -07:00
Austin Wang
922c45400f
Merge pull request #2409 from manaflow-ai/issue-2401-vscode-web-local-folders
Add Open Folder in VS Code (Inline) menu item and command palette entry
2026-03-31 01:15:46 -07:00
Austin Wang
6e6a2c95b5
Merge pull request #2405 from manaflow-ai/issue-2388-sidebar-layout-regression
Fix duplicate sidebar git metadata publishes
2026-03-31 01:01:25 -07:00
austinpower1258
3666f48a9a Scope sidebar row observation to visible workspace state 2026-03-31 00:41:18 -07:00
austinpower1258
a1352e0bfb Merge branch 'main' of https://github.com/manaflow-ai/cmux into issue-2401-vscode-web-local-folders 2026-03-31 00:14:46 -07:00
austinpower1258
514f6b4358 Revert cmd-p title churn lifecycle changes 2026-03-30 23:34:46 -07:00
austinpower1258
57e4f823cb Avoid retaining workspaces in cmd-p switcher 2026-03-30 23:15:17 -07:00
austinpower1258
1918264ae0 Add "Open Folder in VS Code (Inline)" via menu and command palette
Adds a File menu item and command palette entry to pick a local folder
and open it in an inline VS Code browser panel. Extracts the inline
VS Code open logic from ContentView into a reusable AppDelegate method
so both the right-click context action and the new open-panel flow
share the same code path.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-30 22:27:36 -07:00
Austin Wang
a5120be321
Revert "Map Shift+Enter to raw newline in Ghostty" 2026-03-30 21:56:59 -07:00
austinpower1258
83789437ac Avoid duplicate sidebar git metadata publishes 2026-03-30 21:48:31 -07:00
Lawrence Chen
112358ed01
Merge pull request #2393 from manaflow-ai/issue-2391-cmd-p-animated-workspace-rerender
Keep cmd+p stable during animated workspace title updates
2026-03-30 21:21:09 -07:00
Lawrence Chen
4c1bdb554a
Address cmd-p review feedback 2026-03-30 21:15:51 -07:00
Lawrence Chen
d3a3dab74a
Live-update cmd-p workspace titles 2026-03-30 21:08:34 -07:00
Lawrence Chen
56deaf66e7
Merge pull request #2394 from manaflow-ai/task-ctrl-k-command-palette
Fix Ctrl+K in the command palette
2026-03-30 21:00:21 -07:00
Lawrence Chen
543481ce12
Fix transparent background flash during sidebar toggle (#2378)
* Fix transparent background flash during sidebar toggle

Move terminal background rendering from the Metal GPU pass to a
CALayer (backgroundView). The GPU bg_color pass is disabled via a
new Ghostty config flag (macos-background-from-layer). The CALayer
resizes instantly with its parent NSView, eliminating the 3-5 frame
gap where the desktop was visible through the transparent window
during sidebar toggles and layout transitions.

Also simplifies the titlebar and sidebar opacity formulas since
there is now a single background layer instead of two stacked
semi-transparent layers.

* Document macos-background-from-layer fork change

* Pin GhosttyKit checksum for macos-background-from-layer

* Address review feedback: fix fallback config path, inline identity wrapper

- Inject macos-background-from-layer in the fallback config path too,
  preventing alpha double-stacking when user config is invalid
- Inline panelBackgroundFillColor (now an identity function) at its
  two call sites and remove the wrapper

* Address adversarial review: skip fullscreen bg draw call explicitly

The bg_color uniform alpha is still zeroed for cell compositing (so
transparent cells pass through to the CALayer), but the fullscreen
background fill draw step is now explicitly skipped instead of relying
on alpha=0 as a no-op.

* Pin GhosttyKit checksum for bg draw-call skip

---------

Co-authored-by: Lawrence Chen <lawrencecchen@users.noreply.github.com>
2026-03-30 19:08:32 -07:00
Lawrence Chen
0322e78e8a
Stop stale command palette empty-state flashes 2026-03-30 18:50:10 -07:00
Lawrence Chen
8979c8cabe
Let ctrl-k reach command palette text editing 2026-03-30 18:41:04 -07:00
Lawrence Chen
c7bb691285
Keep cmd-p stable during workspace title churn 2026-03-30 18:39:55 -07:00
Austin Wang
2d2d8da1c7
Merge pull request #2355 from manaflow-ai/issue-2352-shift-enter-tmux
Map Shift+Enter to raw newline in Ghostty
2026-03-30 18:10:57 -07:00
Lawrence Chen
dd54927cb9
Add React Grab inject button to browser toolbar (#2373)
* Add React Grab inject button to browser toolbar

Adds a toolbar button (cursor click icon) that injects the react-grab
script (unpkg.com/react-grab/dist/index.global.js) into the current
page. Hover over React elements and Cmd+C to copy component context
(file, component name, line number) for AI agents.

Button highlights when active, resets on navigation.

* Auto-activate selection mode on React Grab inject

First click: injects the script and auto-activates selection mode via
the react-grab:init event. Subsequent clicks toggle selection mode
on/off via window.__REACT_GRAB__.toggle().

* Bridge React Grab state back to Swift via WKScriptMessageHandler

Register a cmux-bridge plugin after injecting react-grab that posts
state changes back to Swift via webkit.messageHandlers. The button
now highlights accent color only when selection mode is actually
active (not just when the script is loaded), and deactivates when
the user exits selection mode via Escape or the react-grab toolbar.

* Fetch react-grab script via URLSession to bypass CSP

Sites like vercel.com block loading external scripts via CSP headers.
Fetch the script with URLSession (not subject to page CSP), cache it,
and inject inline via evaluateJavaScript. Also guard against duplicate
injection on repeated clicks.

* Prefetch react-grab script on first browser panel init

Kick off a low-priority background fetch of the react-grab script
when the first BrowserPanel is created. The script is cached
statically so clicking the button is instant.

* Eliminate react-grab button and callback lag

Three changes:
1. Fire-and-forget: use evaluateJavaScript with completionHandler
   instead of await, so button taps return immediately.
2. Single JS payload: combine bootstrap listener + script source
   into one evaluateJavaScript call (one IPC round-trip, not two).
3. Dedupe state callbacks: only post webkit message when isActive
   actually changes, not on every hover/drag state update.

* Fix duplicate state callback on react-grab toggle

toggleReactGrab was sending an explicit postMessage AND the plugin's
onStateChange hook was firing too, causing two @Published updates per
toggle. Remove the explicit postMessage since the plugin hook handles
it. Also add dlog instrumentation for debugging.

* Add Cmd+Shift+G shortcut for React Grab (configurable)

- Add toggleReactGrab to KeyboardShortcutSettings with Cmd+Shift+G default
- Add View menu item with customizable shortcut
- Add command palette entry (searchable as "react grab" or "inspect element")
- Simplify button to use toggleOrInjectReactGrab, remove local state tracking

* Fix Codex review findings: pin version, verify hash, fix retry and state

1. Pin react-grab to exact version (0.1.29) with SHA-256 integrity
   check. Script is verified before evaluation to prevent supply-chain
   attacks via compromised CDN responses.
2. Clear prefetchTask on failure so subsequent attempts retry the
   download instead of reusing a permanently failed task.
3. Remove premature isReactGrabActive=true. State is now only set
   by the onStateChange message handler callback after confirmed
   initialization, or explicitly reset on evaluation error.

* Extract React Grab into own file, make version configurable

Move all react-grab logic (settings, script loader, message handler,
BrowserPanel extension) into Sources/Panels/ReactGrab.swift.

Add a "React Grab Version" text field in Settings > Browser that lets
the user pin which npm version is fetched. Only versions with a known
SHA-256 integrity hash in ReactGrabSettings.knownHashes are accepted.
The cache invalidates when the configured version changes.

---------

Co-authored-by: Lawrence Chen <lawrencecchen@users.noreply.github.com>
2026-03-30 18:00:45 -07:00
Lawrence Chen
978dd2c023
Add hover background to split action buttons (#2271)
* Add hover background to split action buttons

Split buttons (terminal, browser, split right/down) now show a subtle
rounded-rect background highlight on hover. Matches standard macOS
toolbar button behavior.

* Prevent fade overlays from bleeding into bottom separator

* Render bottom separator above fade overlays to prevent bleed

* Exclude drop zone from scroll fade threshold

* Revert button hover, fix fade threshold with 32pt buffer

* Fix right fade threshold: subtract drop zone, 4pt tolerance

* Add leading padding to split buttons, fix fade threshold

* Rework tab bar: full-width scroll with floating split buttons

* Use ultraThinMaterial blur for floating split buttons

* Make split buttons group full height

* Full-height split button blur background

* Inset split buttons from bottom separator

* Fix blur overlapping separator

* Use matching tab bar bg for split buttons, clear separator

* Use regularMaterial blur for split buttons

* Try thickMaterial for split buttons

* Fade gradient + solid barFill for floating split buttons

* Use extended right fade as split buttons backdrop

* Add 5 debug styles for split button background

* Add Split Button Style debug window to Debug menu

* Clean up: no-bg floating buttons, alphabetical debug menu

- Split buttons float with no background (tabBarBackground covers the
  area, scroll padding prevents tabs from appearing behind buttons)
- Default splitButtonsWidth to 120 so first render has correct padding
- Remove split button style debug window and debug styles
- Alphabetize Debug Windows menu entries, remove dividers

* Revert to HStack sibling layout, add debug menu docs to CLAUDE.md

- Split buttons are HStack siblings of the ScrollView, not overlays.
  Single .background() on parent, no compositing mismatch.
- Alphabetize Debug Windows menu, remove dividers.
- Document Debug menu in CLAUDE.md.

* Add Split Button Layout debug window with 5 switchable approaches

* Fix fade gradient color to match tab bar background

* Add fade color debug window with 6 color options

* Use mask for scroll fades, fixes color mismatch

* Hide scroll fades in minimal mode unless hovering

* Remove split buttons from layout when hidden in minimal mode

* Always show fades, overlay buttons to prevent scroll jump

* Add hover-only mask fade behind split buttons

* Reduce button mask area to 90pt

* Animate button mask smoothly

* Fade entire button group together via opacity

* Add blur behind buttons with fade mask

* Use theme barBackground for button backdrop

* Blur + theme tint for button backdrop

* More tint (0.85), less blur

* Tint 0.2, clear bottom border

* Use terminal bg color, add scroll trailing padding for buttons

* Less blur, paneBackground at 0.75 opacity

* paneBackground at 0.9 opacity

* 0.97 opacity for button backdrop

* Test: fully opaque paneBackground

* Test: solid red backdrop

* Gradient + solid paneBackground backdrop, no mask

* Force opaque paneBackground for button backdrop

* Use barBackground for button backdrop

* Use terminal bg (paneBackground forced opaque)

* Pre-composite backdrop color for exact match

* Add 6 switchable backdrop styles in debug window

* Mask-based button area hiding, no backdrop color needed

---------

Co-authored-by: Lawrence Chen <lawrencecchen@users.noreply.github.com>
2026-03-30 17:02:53 -07:00
Lawrence Chen
d015ace094
Fix minimal mode tab bar disappearing in fullscreen (#2375)
Three issues caused the Bonsplit horizontal tab bar to be hidden
when entering fullscreen with minimal mode enabled:

1. ignoresSafeArea(.container, edges: .top) was applied unconditionally
   in minimal mode, pushing content behind the fullscreen menu bar area.
   Now gated on !isFullScreen.

2. effectiveTitlebarPadding returned -titlebarPadding in minimal mode
   regardless of fullscreen state. In fullscreen there is no native
   titlebar to compensate for, so the negative offset pushed content
   off the top of the screen. Now returns 0 in fullscreen.

3. Traffic light leading inset (80px) was applied in fullscreen minimal
   mode even though there are no traffic light buttons. Now gated on
   !isFullScreen, and syncTrafficLightInset is called on fullscreen
   enter/exit.

Closes https://github.com/manaflow-ai/cmux/issues/2317
Based on https://github.com/manaflow-ai/cmux/pull/2341

Co-authored-by: Lawrence Chen <lawrencecchen@users.noreply.github.com>
2026-03-30 16:41:05 -07:00
Lawrence Chen
f6c949add7
Add cmd-click fallback for bare filenames (ls output) (#2294)
* Add cmd-click fallback for bare filenames in terminal output

When cmd-clicking text that ghostty's built-in URL/path regex doesn't
match (e.g. bare filenames from `ls` like README.md, src, config.json),
fall back to checking if the word under cursor is a valid file or
directory in the terminal panel's CWD. Uses the existing
ghostty_surface_quicklook_word API to extract the word, then resolves
it against the panel's working directory and opens it if it exists.

* Add pointing-hand cursor on Cmd-hover over bare filenames

When holding Cmd and hovering over a word that resolves to an existing
file/directory in the terminal's CWD, show the pointing-hand cursor.
Hooks into mouseMoved and flagsChanged so the cursor updates both when
moving the mouse with Cmd held and when pressing/releasing Cmd while
the mouse is stationary.

* Address PR review comments

- Refresh ghostty mouse position before quicklook_word in mouseUp and
  flagsChanged so stale coordinates don't resolve the wrong word
- Use failable String(bytes:encoding:.utf8) instead of lossy decoding
- Skip absolute-path words (already handled by ghostty's regex)
- Guard against remote terminal sessions (local fileExists would be wrong)
- Use invalidateCursorRects instead of forcing iBeam on hover deactivation
  to avoid overwriting ghostty/AppKit's cursor state

* Add preferred editor setting for cmd-click file opens

New "Open Files With" picker in Settings > App lets users choose which
editor opens when cmd-clicking bare filenames. Options: System Default,
Cursor, VS Code, Windsurf, Zed, Sublime Text, Xcode. Reuses the
existing TerminalDirectoryOpenTarget app detection infrastructure.
Defaults to system default (NSWorkspace default handler).

* Replace editor picker with free-form command field, respect $VISUAL/$EDITOR

The "Open Files With" setting is now a text field where users can type
any command (code, zed, subl, open -a Xcode, etc.). Resolution order:
1. User-configured command from settings
2. $VISUAL environment variable
3. $EDITOR environment variable
4. System default (NSWorkspace)

Removes the fixed PreferredEditor enum in favor of flexibility.

* Fix stuck pointing-hand cursor using NSCursor push/pop

invalidateCursorRects did nothing since the view has no cursor rects.
Use NSCursor push/pop stack instead so the previous cursor is properly
restored when the hover deactivates.

* Remove $VISUAL/$EDITOR fallback, use system default when empty

$EDITOR/$VISUAL are typically terminal editors (vim, nano) that can't
launch as GUI subprocesses. Empty field now falls back to system default
(opens in Finder/default app) which is the expected behavior.

* Address PR review comments (round 2)

- Use broader CWD fallback chain (panelDirectories → requestedWorkingDirectory
  → workspace currentDirectory) matching Workspace split creation logic
- Pop cursor stack in viewDidMoveToWindow to balance push if view is removed
  while hover is active
- Reset preferredEditorCommand in resetAllSettings()
- Fall back to NSWorkspace.open when the custom editor command exits non-zero
  (e.g. command not found exits 127 but /bin/sh itself succeeds)

* Clear cursor on mouse exit to prevent stuck pointing-hand

---------

Co-authored-by: Lawrence Chen <lawrencecchen@users.noreply.github.com>
2026-03-30 04:49:17 -07:00
austinpower1258
8336aae865 Use pane TTY fallback for tmux Shift+Enter 2026-03-30 04:15:23 -07:00
Austin Wang
90a9edb761
Fix browser pane hangs from redundant portal refreshes (#2353)
* Fix browser pane hangs from redundant portal refreshes (#2326)

* Preserve force-refresh recovery for browser panes

* Keep browser refresh generations monotonic

* Preserve inspector reattach refreshes during churn

* Fix hosted inspector restore after portal reattach
2026-03-30 03:51:18 -07:00
austinpower1258
f4c99d34f3 Fix tmux Shift+Enter state reporting 2026-03-30 03:49:00 -07:00
Austin Wang
0666a98ae9
Fix Dock persistence for manual app icons (#2360) 2026-03-30 03:34:35 -07:00
Austin Wang
867c93e4fa
Keep cmux browser Find shortcuts authoritative (#2356)
* Route browser Find shortcuts through web content first

* Keep cmux browser Find shortcuts authoritative

* Add browser Find inspector regression test

* Fix browser Find routing follow-ups
2026-03-30 03:16:10 -07:00
austinpower1258
9ce4997ced Fix Shift+Enter tmux follow-up regressions 2026-03-30 03:10:20 -07:00
Austin Wang
6a39bac0e1
Fix update error details dialog overflow (#2359) 2026-03-30 03:05:48 -07:00
Austin Wang
ae59e571a8
Fix #2347 terminal focus and surface recovery (#2354)
* Add regressions for issue #2347 terminal focus loss

* Fix issue #2347 terminal focus and surface recovery

* Add regressions for missing-surface recovery review cases

* Fix missing-surface recovery lifecycle and focus replay
2026-03-30 03:01:39 -07:00
austinpower1258
9080248393 Gate Shift+Enter newline remap to tmux 2026-03-30 02:55:19 -07:00
Austin Wang
29c0f525db
Add New Window to Dock menu (#2340)
* Add Dock menu new-window regression test

* Add New Window to Dock menu
2026-03-30 02:19:17 -07:00
austinpower1258
540015537d Map Shift+Enter to raw newline in Ghostty 2026-03-30 02:18:15 -07:00
Austin Wang
27db2eb8dd
Add reset-terminal terminal menu workaround (#2349) 2026-03-30 02:05:13 -07:00
Austin Wang
63dd7281f5
Fix fullscreen new windows opening in current Space (#2345)
* Fix fullscreen new windows opening in current Space

* works

* Stabilize fullscreen tiling regression test
2026-03-30 01:43:41 -07:00
Austin Wang
79bcf1d370
Fix browser pane dark-mode leak on light pages (#2346)
* Fix browser pane dark-mode leak

* Restore browser theme mode without CSS injection
2026-03-30 00:57:25 -07:00
Austin Wang
35cb42fbc8
Add copy-on-select preference (#2282) 2026-03-29 18:16:05 -07:00
Austin Wang
94cc865e83
Fix sidebar live refresh for branch and PR state (#2331)
* Add regression coverage for sidebar live refresh

* Refresh sidebar git metadata on active workspaces
2026-03-29 18:15:57 -07:00
Austin Wang
e419fd9164
Fix remote proxy notification spam with cooldown, backoff, and SSH keepalive (#2325) (#2330)
- Add 5-minute per-host cooldown for remote error notifications
- Add exponential backoff (capped at 60s) to proxy broker and session controller retries
- Add default SSH ConnectTimeout/ServerAliveInterval/ServerAliveCountMax to detect dead connections faster
- Fix error status clearing to only reset on actual .connected state

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-29 18:07:39 -07:00
Austin Wang
9e75355525
Fix sidebar layout loop in workspace list (#2328) 2026-03-29 17:57:17 -07:00
Austin Wang
d95158e69d
Fix #2210: coalesce portal sync to latest geometry (#2214) 2026-03-29 17:55:40 -07:00
Austin Wang
f1be3978ab
Fix stale session geometry crash after 0.63.0 upgrade (#2306)
* Add regression coverage for stale window geometry migration

* Discard stale persisted window geometry on launch
2026-03-28 13:52:48 -07:00
Kyle James Walker (he/him)
97c2bc92d4
Defer layout follow-up flush to avoid re-entrant displayIfNeeded crash (#2305)
* fix(workspace): defer layout follow-up flush to avoid re-entrant displayIfNeeded crash

beginEventDrivenLayoutFollowUp() ended with a synchronous call to
attemptEventDrivenLayoutFollowUp(), which calls flushWorkspaceWindowLayouts()
→ window.contentView?.displayIfNeeded(). This is fine when invoked from
user-event handlers, but splitTabBar(_:didChangeGeometry:) fires from inside
SwiftUI's .onChange(of: geometry) during an active AppKit display/layout pass.

Calling displayIfNeeded() re-entrantly during that pass caused AppKit to
increment the per-window Update Constraints pass counter on every display
cycle. Once the counter exceeded the view-count limit AppKit threw an
NSGenericException and crashed:

 'The window has been marked as needing another Update Constraints in Window
 pass, but it has already had more Update Constraints in Window passes than
 there are views in the window.'

Fix: replace the direct attemptEventDrivenLayoutFollowUp() call with
scheduleLayoutFollowUpAttempt(), which defers via asyncAfter(.now() + 0).
When layoutFollowUpStalledAttemptCount == 0 the backoff delay is zero, so
there is no meaningful latency increase — the flush simply runs at the start
of the next run loop iteration, after the current layout pass has fully
unwound. The NSWindow.didUpdateNotification observer and the existing timeout
still drive retries, so convergence is unaffected.

Made-with: Bunny

* fix(workspace): supersede stale layout follow-up retry on reset

scheduleLayoutFollowUpAttempt() is a no-op when
layoutFollowUpAttemptScheduled is true, so a pending retry with a
long backoff delay would survive a beginEventDrivenLayoutFollowUp()
call even though that call resets layoutFollowUpStalledAttemptCount
to 0. The stale closure would then fire after its original delay
rather than immediately.

Adds a layoutFollowUpAttemptVersion counter. beginEventDrivenLayoutFollowUp()
increments the version and clears layoutFollowUpAttemptScheduled,
allowing a fresh asyncAfter(0) attempt to be enqueued. Pending
closures capture the version at scheduling time and exit early if it
no longer matches. clearLayoutFollowUp() also increments the version
to cancel any in-flight closure during teardown.

Made-with: Bunny
2026-03-28 13:21:02 -07:00
Lawrence Chen
550d98ca4f
Add Match Terminal Background sidebar setting (#2293)
* Add "Match Terminal Background" sidebar setting

Adds a toggle in Settings > Sidebar Appearance that makes the sidebar
use the same background color and transparency as the terminal area.

Uses layer-level opacity on a fully opaque background color (the same
technique as TitlebarLayerBackground) with effective opacity formula
`1 - (1-alpha)^2` to account for the terminal's two stacked
semi-transparent layers (Bonsplit chrome + Ghostty Metal surface).

Also adds a 1px trailing border derived from the terminal chrome color,
matching the bonsplit tab bar separator logic.

* Fix sidebar border color not updating on theme change

Add @State + .onReceive(.ghosttyDefaultBackgroundDidChange) to
SidebarTrailingBorder so the separator color recomputes when the
Ghostty theme changes, matching the pattern used in SidebarBackdrop.

* Address review comments: localize debug toggle, fix separator refresh

- Localize the debug panel toggle label (Codex P1)
- Add .onAppear to SidebarTrailingBorder for initial color (Cubic P2)
- Fix stale doc comment on SidebarTerminalBackgroundView (Cubic P3)

---------

Co-authored-by: Lawrence Chen <lawrencecchen@users.noreply.github.com>
2026-03-28 04:36:20 -07:00
Lawrence Chen
e9afc22353
Skip quit confirmation for tagged DEV builds (#2288)
* Skip quit confirmation for tagged DEV builds

Tagged DEV builds are ephemeral dev iterations, so the "Quit cmux?"
dialog just adds friction. Check SocketControlSettings.launchTag() in
both applicationShouldTerminate and handleQuitShortcutWarning to bypass
the confirmation when a tag is present.

Closes https://github.com/manaflow-ai/cmux/issues/2286

* Use bundle ID instead of env var for tagged DEV detection

CMUX_TAG env var is only set when reload.sh --launch opens the app.
When the user cmd-clicks the app path, it launches via Finder without
the env var, so launchTag() returns nil and the quit dialog still shows.

Switch to checking the bundle identifier (com.cmuxterm.app.debug.<tag>)
which is baked into the built app and available regardless of how it was
launched. Add SocketControlSettings.isTaggedDevBuild() helper.

---------

Co-authored-by: Lawrence Chen <lawrencecchen@users.noreply.github.com>
2026-03-28 03:57:25 -07:00
Austin Wang
c4bc18d906
Fix ARC workspace inheritance crash and native Zig helper builds (#2283)
* Fix ARC workspace inheritance crash and native Zig helper builds

* Fix Nightly Cmd+N workspace creation crash

* Restore safe terminal config snapshots for Intel Nightly
2026-03-28 03:05:00 -07:00
Austin Wang
97fee253b5
fix: honor CJK-capable font-family before fallback injection (#2241) 2026-03-27 23:45:30 -07:00
Jun
2d51c14ba1
perf: coalesce scrollbar updates during bulk output (#2116)
* perf: coalesce high-frequency scrollbar updates to reduce main-thread pressure

During bulk terminal output (e.g. `seq 1 100000`), GHOSTTY_ACTION_SCROLLBAR
fires thousands of times per second.  Previously each callback enqueued a
separate DispatchQueue.main.async block that updated the scrollbar property
and posted a NotificationCenter notification, causing the main thread to
process thousands of redundant scroll-geometry recalculations.

This change adds a lightweight coalescing layer: the action callback stores
the latest scrollbar value behind an NSLock and schedules at most one async
flush.  The flush picks up whichever value is current at execution time,
collapsing N callbacks into a single synchronizeScrollView() pass.

Measured improvement on `time seq 1 10000`:
- Before: ~0.052s (26% CPU — seq blocked on PTY backpressure)
- After:  expected ~0.025-0.030s (reduced main-thread contention)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* refactor: use defer for NSLock release in scrollbar coalescing

Address review feedback: wrap unlock() in defer blocks in both
enqueueScrollbarUpdate and flushPendingScrollbar to guarantee
lock release on any future early-return or exception path.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* perf: coalesce wakeup→tick dispatches to eliminate main-thread queue flooding

During bulk terminal output, Ghostty's I/O thread fires wakeup_cb thousands
of times per second.  Previously each wakeup enqueued a separate
DispatchQueue.main.async { tick() } block, flooding the main queue and
starving the run loop.  The main thread spent all its time draining tick
blocks, creating PTY backpressure that blocked the writing process.

Add a lightweight coalescing gate: scheduleTick() only enqueues a single
async block; subsequent wakeups while the block is pending are no-ops.
The pending tick picks up all accumulated state in one ghostty_app_tick()
call, collapsing N wakeups into 1 main-thread dispatch.

Combined with the earlier scrollbar coalescing, measured improvement:

  time seq 1 10000:
  - Ghostty standalone: 0.019s (82% CPU)
  - cmux before:        0.052s (26% CPU)  ← main-thread saturated
  - cmux after:         0.016s (62% CPU)  ← faster than standalone

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix: release scrollbar lock before posting notification

Move NotificationCenter.post outside the _scrollbarLock critical section
in flushPendingScrollbar(). Holding the lock through observer dispatch
would block the I/O thread's enqueueScrollbarUpdate() calls behind
main-thread observer work, recreating the backpressure this change
aims to eliminate.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-27 20:55:27 -07:00