Commit graph

592 commits

Author SHA1 Message Date
Lawrence Chen
bd6fa9e8bc
Extract SettingsPickerRow to enforce correct picker style (#887)
Every settings picker needs .pickerStyle(.menu), controlWidth, and
.labelsHidden(). Missing any of these causes layout bugs (like the
notification sound picker rendering as an expanded control). This
new SettingsPickerRow component bakes in the correct modifiers so
future pickers get them by construction. Migrates all 8 existing
settings pickers.
2026-03-04 15:04:59 -08:00
atani
2c330efb8a
feat: add Japanese localization with String Catalog (#819)
* Add i18n infrastructure with String Catalog and Japanese translations

Introduce String Catalog (.xcstrings) for localization support:
- Localizable.xcstrings: 195 UI string entries with en and ja translations
- InfoPlist.xcstrings: Info.plist strings (microphone usage, Finder menu items)
- project.pbxproj: add xcstrings to build phase and ja to knownRegions

* Replace hardcoded UI strings with String(localized:defaultValue:)

Migrate all user-facing strings across 11 source files to use
String(localized:defaultValue:) API (macOS 13+). Each string references
a key in Localizable.xcstrings, with the English text preserved as
defaultValue for fallback.

Files modified:
- KeyboardShortcutSettings: 28 shortcut labels
- SocketControlSettings: mode names and descriptions
- TabManager: placement labels, color names, close dialogs
- BrowserPanel/BrowserPanelView: error pages, context menus, tooltips
- UpdateViewModel/UpdatePopoverView/UpdatePill: update UI states
- NotificationsPage: notification panel labels
- SurfaceSearchOverlay: search bar placeholder and tooltips
- AppDelegate: menus, dialogs, command palette items

* Fix localization gaps from review feedback

Address review comments from CodeRabbit, Greptile, and Cubic Dev AI:
- Use interpolated String(localized:) instead of concatenation for
  version/progress strings in UpdateViewModel
- Localize remaining hardcoded strings in AppDelegate: window labels,
  rename dialog, status menu items, unread notification count
- Localize insecure HTTP alert body in BrowserPanel
- Add 12 new entries to Localizable.xcstrings with Japanese translations

* Fix String(localized:defaultValue:) keys to use StaticString

The localized: parameter requires StaticString when defaultValue: is
used. Move string interpolation from the key to defaultValue only,
and revert maxWidthText to plain strings since they are only used for
layout width calculation.

* Localize remaining UI strings across all source files

Add String(localized:defaultValue:) to all user-facing strings in:
- cmuxApp.swift: settings screen, menus, about panel, dialogs (~180 strings)
- ContentView.swift: command palette, sidebar context menu, dialogs (~200 strings)
- Workspace.swift: rename/move/close tab dialogs, tooltips (~20 strings)
- UpdateTitlebarAccessory.swift: titlebar tooltips, notifications popover (~10 strings)
- TerminalNotificationStore.swift: notification permission dialog (4 strings)
- CmuxWebView.swift: browser context menu items (2 strings)
- AppDelegate.swift: CLI install/uninstall alerts (6 strings)

Add 418 new entries to Localizable.xcstrings with Japanese translations.
Extract sidebar context menu into separate @ViewBuilder to fix Swift
type-checker timeout in large body.
Fix xcstrings format specifiers for interpolated strings (%lld, %@).

Total: 624 localization entries covering the full UI.

* Address review feedback: fix missing localizations and terminology

- Localize javaScriptDialogTitle URL branch in BrowserPanel
- Localize cantReach error message in BrowserPanel
- Localize close other tabs dialog message in TabManager
- Localize workspace accessibility label in ContentView
- Fix unread notification singular/plural (split into two keys)
- Fix insecure connection apostrophe inconsistency (unify to U+2019)
- Rename socketControl.fullOpen.description to socketControl.allowAll.description
- Remove dead code: renameTargetNoun function
- Fix terminology inconsistencies in xcstrings:
  - Unify "Developer Tools" to デベロッパツール
  - Unify "Jump to Latest Unread" phrasing
  - Unify "Flash Focused Panel" terminology
  - Fix dialog.enableNotifications.notNow translation

* fix: address remaining PR 819 review feedback

* fix: use a single localized key for close-other-tabs

* fix: avoid inflection markup in close-other-tabs message

* Address review feedback: localize tooltip, fix subtitle concat, unify keys

- Localize menubar tooltip unread count (hardcoded English -> localized)
- Replace subtitle string concatenation anti-pattern with single localized
  keys containing interpolation placeholders
- Unify workspace fallback key to workspace.displayName.fallback
- Remove unused workspace.defaultName key from xcstrings
- Add Japanese translations for new tooltip and subtitle keys
2026-03-04 14:58:28 -08:00
Lawrence Chen
422c86e822
Fix notification sound picker using wrong style in Settings (#885)
The notification sound picker was missing .pickerStyle(.menu) and
controlWidth, causing it to render as an expanded picker with a large
empty area instead of a compact dropdown menu. Apply the same pattern
used by all other settings pickers.
2026-03-04 14:41:04 -08:00
Yoshiki Agatsuma
a4bf2214fe
Fix browser address bar Japanese IME input (#789) (#867)
* Fix browser address bar Japanese IME input (#789)

* Remove redundant comments
2026-03-04 14:32:52 -08:00
Lawrence Chen
e0ec448701
Fix Escape propagation when command palette is visible (#847)
* Fix command palette Escape propagation and add regressions

* Respect IME marked text for command palette Escape

* Harden command palette escape pending-open routing
2026-03-04 03:27:54 -08:00
Lawrence Chen
79cfe2d168
Fix cross-window theme background gating after jump-to-unread (#861)
* Fix cross-window theme background gating

* Handle owning-manager nil-selection theme edge case

* Simplify window background gating helper
2026-03-04 02:48:06 -08:00
Lawrence Chen
ace539326f
Add debug logs for Cmd+F find bar refocus (#840)
* Add debug logs for Cmd+F find bar focus/refocus state machine

Traces the full lifecycle: menu action, startSearch, overlay mount/unmount,
focus changes, window key/resign, applyFirstResponderIfNeeded guards, and
moveFocus calls. Helps reproduce the bug where Cmd+F fails to reopen after
switching away and back to the terminal window.

* Fix Cmd+F find bar focus loss after window switch

When the find bar is open and the user switches away and back, the
window's first responder was left as the NSWindow itself because
applyFirstResponderIfNeeded bailed on the searchState guard and nothing
refocused the find bar. This caused a dead state where neither the
search field nor the terminal accepted keyboard input.

Add a SearchFocusTarget state machine (.searchField / .terminal) to
GhosttySurfaceScrollView that tracks user intent. On window-become-key,
restoreSearchFocus() makes the correct view first responder based on
the target. Pressing Escape with a non-empty needle sets target to
.terminal so window reactivation preserves that intent. Cmd+F and
.ghosttySearchFocus notifications reset target to .searchField.

* Fix multi-surface focus stealing and NSHostingView responder issue

Two bugs found from debug logs:

1. Other surfaces in the same window (without search active) were calling
   applyFirstResponderIfNeeded and stealing focus from the find bar's
   surface. Added a check: if current first responder is inside a search
   overlay NSHostingView, don't steal it.

2. window.makeFirstResponder(overlay) on the NSHostingView was wrong.
   It made the hosting view itself the responder, which ate keystrokes
   as performKeyEquivalent instead of routing them to the SwiftUI
   TextField inside. Removed that call, now only posting the
   .ghosttySearchFocus notification to let SwiftUI handle internal
   focus via @FocusState.

* Use AppKit NSTextField focus instead of SwiftUI @FocusState for search restore

The notification-only approach fails because SwiftUI @FocusState can't
propagate to AppKit when the first responder is the NSWindow itself
(no view in the responder chain to anchor the change). And making the
NSHostingView first responder eats keys as performKeyEquivalent.

Now walks the hosting view's subview tree to find the actual editable
NSTextField backing the SwiftUI TextField, and calls
window.makeFirstResponder directly on it. Falls back to notification
if the text field isn't found.

* Two-phase focus restore: AppKit + SwiftUI sync, click-to-terminal fix

restoreSearchFocus now does both:
1. AppKit: makeFirstResponder(nsTextField) so typing works immediately
2. SwiftUI: post .ghosttySearchFocus so @FocusState syncs and
   .onExitCommand (Escape) and .onKeyPress (Return) still work

Also: clicking the terminal while find bar is open now sets
searchFocusTarget to .terminal, so window reactivation correctly
restores terminal focus instead of jumping back to the search field.

* Replace SwiftUI TextField with NSViewRepresentable for find bar

The core issue: SwiftUI @FocusState does not sync with AppKit's
first responder after window resign/become-key cycles. This caused
the find bar to lose all keyboard input after switching windows.

Previous attempts to bridge SwiftUI and AppKit focus (notifications,
makeFirstResponder on the backing NSTextField, belt-and-suspenders
approaches) all failed because SwiftUI event handlers (.onExitCommand
for Escape, .onKeyPress for Return) require @FocusState to be set.

Fix: replace the SwiftUI TextField with an NSViewRepresentable-wrapped
NSTextField (SearchTextFieldRepresentable), following the proven
OmnibarNativeTextField pattern already in BrowserPanelView.swift.

- Escape and Return handled via control(_:textView:doCommandBy:)
  at the AppKit delegate level, no @FocusState needed
- Focus restored via .ghosttySearchFocus notification observed
  directly by the Coordinator, calling makeFirstResponder immediately
- hasMarkedText() guard preserves CJK IME composition (issue #118)
- isProgrammaticMutation guard prevents text binding cursor reset
- Removes findTextField(in:) subview walk hack

* Explicitly unfocus terminal surface when find bar takes focus

The Ghostty cursor kept blinking even when the search field was focused
because ghostty_surface_set_focus(false) was only called via
surfaceView.resignFirstResponder. After window switching, the surface
view may not have been the first responder, so resign was never called.

Fix: call surface.setFocus(false) in both the .ghosttySearchFocus
notification observer and directly in restoreSearchFocus. This ensures
the cursor stops blinking regardless of previous first-responder state.

* Address review findings: field-editor guard, NSLog→dlog, stale focus

1. isSearchOverlayOrDescendant now accepts NSResponder and follows
   the field-editor delegate chain back to the owning NSTextField.
   Previously, when the search field was being edited, the shared
   NSTextView field editor was the first responder (outside the
   overlay hierarchy), so the guard missed it and other surfaces
   could steal focus.

2. Converted all NSLog calls in TabManager (startSearch, hideFind,
   searchSelection), cmuxApp (Find menu), and GhosttyTerminalView
   (searchState didSet) to dlog() wrapped in #if DEBUG. Avoids
   leaking search needle text to system logs in release builds.

3. Added isFocused re-check inside the deferred focus block in
   SearchTextFieldRepresentable to prevent stale focus requests
   from stealing focus back after intent has changed.

* Guard against re-focusing already-focused search field

Every keystroke updated searchState.needle (@Published), which triggered
a SwiftUI re-render → ensureFocus → restoreSearchFocus → posted
.ghosttySearchFocus notification → Coordinator called makeFirstResponder
unconditionally. makeFirstResponder on an already-editing NSTextField
ends the editing session and restarts with all text selected, so the
next typed character replaced the previous one ("hi" → "i").

Fix: check if the field is already first responder before calling
makeFirstResponder in the notification handler.

* Address review findings: stale focus target, IME guard, tab/pane gating

- Add onFieldDidFocus callback so clicking back into the search field
  after Escape updates searchFocusTarget = .searchField, fixing stale
  focus restoration after window switches.
- Guard updateNSView text sync with !editor.hasMarkedText() to prevent
  stomping active CJK IME composition.
- Move ensureFocus search state check after tab/pane selection guards
  so search focus isn't restored on non-active tabs/panes.
- Clear surfaceView.onFocus when setFocusHandler(nil) is called.
2026-03-04 02:30:49 -08:00
Lawrence Chen
e65bc65ac7
Fix Cmd+V clipboard image paste not working (#853)
Ghostty's keybinding system intercepts Cmd+V and routes it through
read_clipboard_cb, which only reads text. The paste(_:) NSResponder
method with image handling was never reached.

Move clipboard image save logic into GhosttyPasteboardHelper and call
it from read_clipboard_cb when the clipboard has no text but has image
data. This makes image paste work regardless of whether paste is
triggered via Ghostty keybinding (Cmd+V) or menu action (Edit > Paste).

Fixes regression from https://github.com/manaflow-ai/cmux/pull/562
2026-03-04 00:33:52 -08:00
Lawrence Chen
dad52b09d9
Cmd+P: show workspaces only (#844)
* Limit Cmd+P switcher to workspaces

* Fix command palette Enter/Escape handling and add regression test

* Scope command palette key handling to event window
2026-03-04 00:19:01 -08:00
Lawrence Chen
1f62d770c7
Skip keychain migration for DEV/staging builds (#845)
* Skip keychain migration for DEV/staging builds

Each tagged DEV build gets a unique bundle ID (com.cmuxterm.app.debug.<tag>)
with its own UserDefaults domain. This means the migration version key is
never set, so migrateLegacyKeychainPasswordIfNeeded runs on every launch.
The SecItemCopyMatching call then triggers a macOS keychain access prompt
because the ad-hoc re-signed binary doesn't match the ACL on the legacy
keychain item.

Guard the migration call so it only runs for production bundle IDs.

* Suppress keychain UI prompt in legacy password lookup

Add kSecUseAuthenticationUIFail to the SecItemCopyMatching query so macOS
fails silently instead of showing a keychain access dialog when the app's
code signing identity doesn't match the item's ACL. This closes the
remaining code path (lazy keychain fallback in configuredPassword) that
could trigger a prompt in DEV builds at runtime.

* Revert "Suppress keychain UI prompt in legacy password lookup"

This reverts commit 6453b7b5d70e713b324b0ffff7ecc4b22cc9fb5f.
2026-03-04 00:15:04 -08:00
Lawrence Chen
a5de92e9d6
Add customizable notification sound (#839)
* Add customizable notification sound setting

Adds a "Notification Sound" picker in Settings > App that lets users
choose from macOS system sounds (Default, Basso, Blow, Glass, etc.)
or silence notifications entirely with "None".

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

* Add custom notification command with env vars and sound preview

Users can set a shell command in Settings > App > Notification Command
that runs on every notification. CMUX_NOTIFICATION_TITLE,
CMUX_NOTIFICATION_SUBTITLE, and CMUX_NOTIFICATION_BODY env vars are
set. Also adds a play button to preview system sounds and docs.
2026-03-04 00:12:05 -08:00
Lawrence Chen
044a3dbb64
Vi mode: half-page scroll, visible cursor, gg fix (#851)
* 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.
2026-03-03 23:56:01 -08:00
Austin Wang
c7bdd92df9
Fix Ghostty theme loading in debug builds (#830)
* Revert "Fix Cmd+Tab activation ordering for cmux windows (#744) (#766)"

This reverts commit a6f6485e3c.

* Fix debug Ghostty theme loading fallback
2026-03-03 19:14:55 -08:00
Lawrence Chen
2f6cb6ff38
Add keyboard copy mode for terminal scrollback (#792)
* 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
2026-03-03 19:01:21 -08:00
Lawrence Chen
bfe843f0bd
Clear sidebar notification when user submits prompt (#821)
* Clear sidebar notification when user submits prompt in Claude Code

Add UserPromptSubmit hook to the Claude Code wrapper that calls
`cmux claude-hook prompt-submit`. This clears the workspace notification
and sets status back to "Running" when the user addresses Claude's question,
so the "waiting for input" preview in the sidebar goes away.

Also adds --tab support to clear_notifications socket command and
--workspace support to the clear-notifications CLI command for
per-workspace notification clearing.

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

* Address review feedback: stricter error handling

- clear-notifications CLI: error on explicit --workspace failure instead of
  falling back to global clear. Env var still gracefully degrades.
- prompt-submit hook: propagate sendV1Command errors instead of swallowing
  with try?.
- clear_notifications socket: validate --tab flag is present before resolving,
  reject malformed args instead of falling back to selected tab.

* Gate env workspace fallback on windowId == nil in clear-notifications

Matches the pattern used by other CLI commands to avoid using
CMUX_WORKSPACE_ID from the caller shell when --window targets
a different window.
2026-03-03 18:48:32 -08:00
Austin Wang
355012b252
Revert "Fix Cmd+Tab activation ordering for cmux windows (#744) (#766)" (#827)
This reverts commit a6f6485e3c.
2026-03-03 18:38:24 -08:00
Lawrence Chen
a139c346f2
Fix Cmd+Shift+Enter pane zoom regression in browser focus (#826) 2026-03-03 18:37:46 -08:00
Lawrence Chen
fd31210ea4
Fix settings dropdown for Sidebar Branch Layout taking full width (#825)
The Picker was missing the controlWidth parameter that all other picker
rows in settings use, causing it to expand and hide its label.
2026-03-03 18:21:02 -08:00
Lawrence Chen
d1f4c66378
Move UNUserNotificationCenter remove calls off main thread (#820)
* Move UNUserNotificationCenter remove calls off main thread

removeDeliveredNotifications(withIdentifiers:) and
removePendingNotificationRequests(withIdentifiers:) perform synchronous XPC
to usernoted. When usernoted is slow or overwhelmed, this blocks the main
thread indefinitely, freezing the entire UI (confirmed via sample showing
__NSXPCCONNECTION_IS_WAITING_FOR_A_SYNCHRONOUS_REPLY__ for the full sample
duration on both cmux and cmux NIGHTLY).

Add extension methods on UNUserNotificationCenter that dispatch these calls
to a background queue. All 13 call sites in TerminalNotificationStore are
fire-and-forget (void, no data flows back), so moving them off-main is safe.
The @Published property mutations and dock badge updates remain on @MainActor.

* Use dedicated serial queue for notification removal dispatch

Replaces DispatchQueue.global(qos: .utility) with a private static serial
queue. If usernoted stalls, concurrent dispatches to the global pool could
exhaust threads. A dedicated queue caps concurrency at 1.
2026-03-03 17:42:10 -08:00
Austin Wang
a12fb563ff
Fix Ghostty config reload theme refresh (#812) (#818) 2026-03-03 16:28:51 -08:00
Austin Wang
bdfcc74df3
Fix Shift+backquote input regression in Ghostty key path (#815) 2026-03-03 15:57:36 -08:00
Austin Wang
a3681ede5b
ok (#717) 2026-03-03 15:53:39 -08:00
Lawrence Chen
5d463af122
Fix ghost terminal surface rebind after close (#808)
* Fix ghost terminal lifecycle rebind race

* Address review feedback on portal regression checks

* Address follow-up review feedback
2026-03-03 15:20:42 -08:00
Lawrence Chen
56f184d02e
Add debug logging for cmd+click link handling (#777)
* Add debug logging for cmd+click link handling

Logs every decision point in the link opening pipeline:
- mouseDown/mouseUp: modifier keys, click count, position
- resolveTerminalOpenURLTarget: input URL, classification (external/embedded/fallback)
- OPEN_URL action handler: routing decision (cmuxBrowser disabled, external, whitelist miss, embedded browser pane)

All logs are #if DEBUG only via dlog().

* Update tagged build link format for Claude Code cmd+clickability

Claude Code gets a markdown link with the real derived-data path
(file:///tmp/cmux-<tag>/Build/Products/Debug/cmux%20DEV%20<tag>.app)
which is cmd+clickable in cmux. Codex keeps the original format.

* Add equal sign separators to Claude Code build link format

* Fix cmd+click URL buffer overread in OPEN_URL handler

cmux was using String(cString:) to decode the URL from
GHOSTTY_ACTION_OPEN_URL, which reads until a null terminator.
The C struct provides both a pointer and a length field (url + len),
and the pointer is not guaranteed to be null-terminated. This caused
cmux to read past the URL bytes into adjacent memory, producing
corrupted URLs like "https://example.comcom" and multi-URL
concatenations.

Fix: use Data(bytes:count:) with the length field, matching how
vanilla Ghostty decodes the same struct in Ghostty.Action.swift.

* Address review feedback: extract debugModifierString helper
2026-03-02 21:47:24 -08:00
Lawrence Chen
919f77b6dc
Add setting to hide Cmd-hold shortcut hints (#765)
* 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
2026-03-02 19:56:27 -08:00
Lawrence Chen
5bbdd87c29
Fix re-entrant exclusive-access crash in drag handle hit test (#771)
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.
2026-03-02 19:20:14 -08:00
Lawrence Chen
682a57d7db
Add workspace-churn typing lag regression and fix (#767)
* Add workspace-churn typing lag regression and fix

* Fix CI build for debug stress split calls

* Stabilize lag regression gate for low baseline latency
2026-03-02 19:06:50 -08:00
Jose Masri
b6163ccfad
Support pasting clipboard images in terminal (#562)
* Support pasting clipboard images as file paths in terminal

When the macOS clipboard contains only image data (e.g. from
Cmd+Ctrl+Shift+4 screenshot) and no text, Cmd+V now saves the image
as a temporary PNG file and pastes the file path into the terminal.
This allows CLI tools like Claude Code to receive pasted images.

The pasteboard heuristic only intercepts when there is image data
(TIFF/PNG) and no text/HTML/RTF, so normal text paste is unaffected.
Images over 10 MB are skipped and fall through to default behavior.

Closes #457

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

* Fix clipboard image paste: collision-free filenames and pasteAsPlainText validation

- Add UUID suffix to temp filenames to prevent overwrites when pasting
  images multiple times in the same second
- Only enable Paste menu (not Paste as Plain Text) for image-only clipboard,
  since pasteAsPlainText has no image-path handling

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

* Shell-escape pasted image path before sending to terminal

Use escapeDropForShell on the clipboard image temp path, consistent
with how drag/drop paths are escaped, to avoid issues with
shell-special characters in the path.

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

* Add docstrings to paste and validation functions

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

---------

Co-authored-by: Jose Masri <ae_jmsalame@contractor.indeed.com>
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Co-authored-by: Lawrence Chen <54008264+lawrencecchen@users.noreply.github.com>
2026-03-02 18:00:00 -08:00
Lawrence Chen
fdc38a3326
Add external URL bypass rules for embedded browser opens (#768)
* Add external URL bypass rules for embedded browser opens

* Align open-wrapper external regex handling with app-side matcher
2026-03-02 17:50:34 -08:00
Austin Wang
a6f6485e3c
Fix Cmd+Tab activation ordering for cmux windows (#744) (#766) 2026-03-02 16:46:00 -08:00
Lawrence Chen
4d9587c3b0 Fix window.open() and target=_blank not opening in new tab (#693)
Always open programmatic navigation (window.open, target=_blank) in a new tab.
Fix insecure HTTP path to treat nil targetFrame as new-tab intent.

Closes #606

Co-authored-by: lark <lark1115caster@gmail.com>
2026-03-01 21:40:58 -08:00
Morten Trydal
bd3ee68e0c
Fix display-pass crash from isHidden toggle in debug hit test (#698)
Toggling isHidden during a display cycle calls _setHidden:setNeedsDisplay:,
which posts another window-needs-display and pushes the pass count past
AppKit's per-cycle limit, causing an NSException crash near resize handles.

Remove the isHidden toggle from debugTopHitViewForCurrentEvent(); the hit
test now returns the overlay itself when it is the topmost view, which is
acceptable for debug logging purposes.
2026-03-01 21:38:51 -08:00
Haruki Tosa
c6e8d8484b
Fix exclusive-access crash in WindowDragHandleView hitTest (#736)
Add early return for non-leftMouseDown events in DraggableView.hitTest
to prevent re-entering SwiftUI view state during mouseMoved layout
passes, which caused fatal exclusive-access violations.
2026-03-01 21:38:23 -08:00
Lawrence Chen
33242f8780
Fix menubar icon invisible in light mode (#741)
Set isTemplate = true on the menu bar icon image so macOS automatically
renders it black in light mode and white in dark mode. Changed the glyph
fill from white to black per template image convention.

Closes https://github.com/manaflow-ai/cmux/issues/737
2026-03-01 21:35:50 -08:00
Lawrence Chen
ae79d32494
Add inline VS Code open-directory command palette action (#685)
* Add inline VS Code directory command-palette action

* Harden inline VS Code serve-web lifecycle handling

* Add command-palette actions to stop/restart inline VS Code server

* Fix inline VS Code serve-web review comments

* Fix serve-web stop/start race and add regression test
2026-03-01 18:53:08 -08:00
Lawrence Chen
8378bbeaa2
Add dark mode app icon for macOS Sequoia (#702)
* Add dark mode app icon variant for macOS Sequoia

Adds dark appearance entries to the AppIcon asset catalog so macOS 15+
automatically shows a dark-background icon when the system is in dark
mode. The chevron gradient and glow are preserved by recompositing the
foreground over a dark background (#1C1C1E).

Includes a generation script (scripts/generate_dark_icon.py) that
derives the dark PNGs from the light originals.

* Add icon picker in Settings and fix dark icon quality

Use the Figma chevron layer (design/cmux-icon-chevron.png) composited
over a dark background for pixel-perfect results, no white halo or
darkened gradient. Falls back to mathematical recomposition if the Figma
layer is missing.

Add an "App Icon" picker to Settings (under Theme) with three visual
options: Automatic (follows system appearance via asset catalog dark
variants on macOS 15+), Light, and Dark. The selection persists via
UserDefaults and is applied on launch in AppDelegate.ensureApplicationIcon.

* Fix dark icon chevron scale to match light icon

The Figma export was ~25% larger than the repo icon. Scale the Figma
chevron layer by 0.80x before compositing so the chevron size matches
exactly between light and dark variants.

* Use enhanced glow for dark icon

Add a soft blue bloom around the chevron on the dark background using
two Gaussian blur passes (wide at r=25 and tight at r=12) composited
at reduced opacity beneath the sharp chevron. Makes the icon pop more
against the dark squircle.
2026-03-01 03:57:09 -08:00
Lawrence Chen
bc1b6fd9eb
Honor Ghostty background-opacity across all cmux chrome (#667)
* 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.
2026-03-01 03:48:46 -08:00
doug
e01236115e
Fix: use policy constants for minimum window size instead of hardcoded 800×600 (#715)
The root ContentView body had `.frame(minWidth: 800, minHeight: 600)`
hardcoded since the initial commit, preventing users from resizing the
window narrower than 800px even when the sidebar is hidden and a narrow
terminal layout is perfectly usable.

Replace the magic numbers with the existing SessionPersistencePolicy
constants (minimumWindowWidth = 300, minimumWindowHeight = 200), which
were already defined and used for session-restore frame validation.
This gives those constants a second job as the canonical size floor and
makes it easy to tune the minimum in one place.

Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-01 02:42:41 -08:00
Austin Wang
aa8fc7232a
Fix Shift+Space IME toggle inserting space (#641) (#670) 2026-02-28 21:12:17 -08:00
Lawrence Chen
f451766d12
Add cmux <path> to open directories and Homebrew binary stanza (#705)
* Add `cmux <path>` to open directories and Homebrew binary stanza

CLI: `cmux .` or `cmux /path/to/dir` opens a new workspace at the
given directory. If the app isn't running, it launches first and waits
for the socket. Also adds `--cwd` flag to `new-workspace`.

Server: `workspace.create` now accepts an optional `cwd` parameter,
passed through to `TabManager.addWorkspace(workingDirectory:)`.

Homebrew: adds `binary` stanza to the cask so `cmux` CLI is globally
available after `brew install --cask cmux`. Updated both the cask file,
the CI workflow template, and the manual release script so automated
version bumps preserve the stanza.

* Address review: validate cwd type, fix socket detection, propagate errors

- looksLikePath now also matches paths containing `/` (e.g. `foo/bar`)
- openPath uses socket connection attempt instead of fileExists to detect
  whether the app is running (Unix sockets may not appear on filesystem)
- launchApp/activateApp now throw instead of swallowing errors with try?
- Server validates that cwd param is a string, returns invalid_params error
  if wrong type is passed
2026-02-28 20:25:41 -08:00
Lawrence Chen
838d1b07b1
Fix local HTML file routing in open wrapper (#684)
* 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
2026-02-28 19:08:39 -08:00
Lawrence Chen
7143359c04
Stabilize UI keyboard/focus regressions and flaky omnibar/sidebar tests (#689) 2026-02-28 07:09:37 -08:00
Lawrence Chen
7916b2d418
Fix Xcode Cloud UI tests by running TestAction in Debug (#672)
* 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
2026-02-28 01:48:49 -08:00
Lawrence Chen
168e6b9b25
Auto-heal missing CLI listener socket (#679)
* Auto-heal missing CLI socket listener

* Add Sentry socket listener breadcrumbs and failure capture
2026-02-28 01:19:38 -08:00
Lawrence Chen
c3b55e2a9f
Fix Cmd+plus zoom handling on non-US layouts (#680) 2026-02-28 00:16:03 -08:00
Lawrence Chen
f73887154d
Precompute panel ordering to reduce sidebar scroll lag (#661)
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
2026-02-27 18:44:07 -08:00
Lawrence Chen
1392bd16d7 Revert "Use workspace color for notification ring and selection bar (#664)"
This reverts commit 4bfe95d125.
2026-02-27 18:24:51 -08:00
Lawrence Chen
4bfe95d125
Use workspace color for notification ring and selection bar (#664)
- 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
2026-02-27 18:14:19 -08:00
Austin Wang
c5f749640a
Fix terminal wrap width for overlay scrollbar (#522) 2026-02-27 17:15:32 -08:00
Austin Wang
5c065bcf05
Merge pull request #646 from manaflow-ai/cmux/cmd-w-fails
Fix browser split behavior and prevent tagged app auto-close
2026-02-27 17:03:51 -08:00