Commit graph

138 commits

Author SHA1 Message Date
Lawrence Chen
d369778f7f
Scroll settings hint to import controls 2026-03-17 04:48:21 -07:00
Lawrence Chen
c5ae8dc9eb
Defer browser import dialog presentation 2026-03-17 03:23:18 -07:00
Lawrence Chen
b9de0f0446
Add browser import hint debug variants 2026-03-17 03:01:50 -07:00
Lawrence Chen
7bb7564726
Stabilize browser import menu test flow 2026-03-17 02:18:58 -07:00
Lawrence Chen
746fb8e5ec
Merge pull request #318 from manaflow-ai/issue-122-import-browser-cookies-history-settings
Browser: import data from detected installed browsers
2026-03-16 22:42:40 -07:00
Manfred Neustifter
6b138f7d9d
feat: support window.open() popup windows (#1150)
* feat: support window.open() popup windows (#742)

Return a live WKWebView from createWebViewWith using WebKit's supplied
configuration, preserving popup browsing-context semantics (window.opener,
postMessage). This fixes OAuth/OIDC flows and any site relying on standard
popup patterns.

- Add BrowserPopupWindowController: NSPanel-based popup with self-retention,
  KVO title/URL, read-only URL label, nested popup depth limit (3),
  insecure-HTTP prompt parity, auth challenge parity, download delegate
- Classifier: scripted requests (window.open) create popups; user-initiated
  actions (Cmd+click, middle-click, context menu) open tabs
- Retarget context menu "Open Link in New Tab" to bypass createWebViewWith,
  wired in both main browser and popup web views
- Cmd+W fast path in AppDelegate for popup windows
- Opener panel owns popup lifecycle; close() tears down all child popups

* fix: Cmd+W closes only the popup, not the parent tab

Add BrowserPopupPanel (NSPanel subclass) that intercepts Cmd+W in
performKeyEquivalent before the swizzled cmux_performKeyEquivalent
can dispatch it to the main menu's "Close Tab" action.

Also refine the popup classifier to reuse browserNavigationShouldOpenInNewTab
for Cmd+click/middle-click detection, add download delegate wiring, and
wire onContextMenuOpenLinkInNewTab for popup web views.

* fix: tighten popup routing and window behavior

* test: cover oversized popup frame clamping

* test: cover plain link-activated popup routing

---------

Co-authored-by: Lawrence Chen <lawrencecchen@users.noreply.github.com>
2026-03-15 17:48:05 -07:00
Gale
07ec05b0f1
fix: make Cmd+W close About/Licenses windows instead of terminal tabs (#1473)
* fix: make Cmd+W close auxiliary windows

* test: harden auxiliary window close coverage

* style: normalize auxiliary window helper layout
2026-03-15 16:09:22 -07:00
Ragnar Rova
a7cb968a55
Add configurable sidebar tint color with light/dark mode support (#1465)
- Config: sidebar-background supports plain hex (#336699) or
  light/dark syntax (light:#fbf3db,dark:#103c48)
- Config: sidebar-tint-opacity overrides tint opacity
- Settings UI: per-scheme color pickers, opacity slider (0-70%), reset
- SidebarBackdrop resolves light/dark hex based on @Environment colorScheme
- applySidebarAppearanceToUserDefaults guards on rawSidebarBackground presence
  so UI picks survive appearance toggles when no config is set
- Stale light/dark UserDefaults keys cleared when config switches from
  dual-mode to single or sidebar-background is removed
- applyPreset() and Reset Tint clear per-scheme overrides
- Debug snapshot (combinedPayload + copySidebarConfig) includes new keys
- ColorPicker labels use String(localized:) per localization policy
- Opacity slider capped at 0.7 to match debug view vibrancy constraint

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-15 15:48:57 -07:00
Lawrence Chen
f90bcbc862
Merge pull request #1382 from manaflow-ai/task-cmd-p-search-all-surfaces
Add Cmd+P all-surface search option
2026-03-13 17:55:10 -07:00
austinpower1258
9bfa3b9143 Make Cmd+W close the window when it closes the last terminal
Previously, Cmd+W on the last surface kept the workspace alive with a
replacement shell, unless the user toggled a hidden setting. This was
confusing—users expect Cmd+W to close the window when there's nothing
left.

Now Cmd+W (and the tab-strip X button) always close the workspace when
they close its last surface, and close the window when that was the last
workspace. Internal/programmatic closes (e.g. process exit, panel moves)
still spawn a replacement shell so the workspace stays alive.

Key changes:
- Track explicit user close gestures via markExplicitClose / onTabCloseRequest
- Remove the LastSurfaceCloseShortcutSettings toggle (now always-on)
- Use window.performClose for last-workspace window close
- Update tests to match the new behavior

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-13 12:43:24 -07:00
Lawrence Chen
3135b4b13e
Add Cmd+P all-surfaces search option 2026-03-13 07:43:57 -07:00
Lawrence Chen
0ff9d7aa27
Add VoiceOver selected traits to pickers and reorder settings (#1372)
Add accessibilityAddTraits(.isSelected) to Theme and App Icon picker
buttons so VoiceOver announces the active option. Reorder the top of
the App section to: Language, Theme, App Icon.

Addresses review feedback from https://github.com/manaflow-ai/cmux/pull/1367

Co-authored-by: Lawrence Chen <lawrencecchen@users.noreply.github.com>
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-13 06:46:38 -07:00
Lawrence Chen
c7b54b78d6
Add subtitle to App Icon setting to clarify it's not a theme toggle (#1367)
* Add subtitle to App Icon setting to reduce confusion with theme

Users were confusing the App Icon picker (Automatic/Light/Dark) with
the Theme toggle. Add "Dock and app switcher" subtitle to clarify
this setting only affects the icon appearance, not the app theme.

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

* Replace Theme dropdown with visual appearance picker

Draw window thumbnails showing light/dark previews with traffic light
dots and content bars, matching the macOS System Settings appearance
picker pattern. System mode shows overlapping light+dark thumbnails.

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

* Swap theme picker to Gemini's wallpaper + layered window design

Replace flat single-window thumbnails with richer previews: wallpaper
gradient backgrounds, menu bar with Apple logo, two layered windows
with shadows, and a split-mask for the System option (light on left,
dark on right with center divider), matching the macOS System Settings
appearance picker more closely.

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

* Move Theme and App Icon pickers to right-aligned row layout

Both pickers now use an HStack with the label on the left and
thumbnails on the right, matching the SettingsCardRow pattern.
Thumbnails get layoutPriority(1) so the label text compresses
first on narrow windows. Slightly smaller thumbnails (76x50 for
theme, 48px icons) to fit comfortably at minimum settings width.

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

* Top-align labels in Theme and App Icon picker rows

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

* Increase hitbox on picker buttons and remove focus ring

Add contentShape(Rectangle()) for full-area tap targets, increase
padding (8v/10h), and add focusable(false) to prevent the macOS
keyboard focus outline from showing on the buttons.

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

* Use rounder squircle corners on theme thumbnails

Bump cornerRadius from 10 to 14 on the thumbnail clip shape and
border for a more pronounced squircle look.

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

* Use true superellipse squircle shape for theme thumbnails

Add a Squircle shape that draws a superellipse (n=4) path blended
with an ellipse based on corner radius. Apply it to the theme
thumbnail clip and border instead of RoundedRectangle.

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

* Remove custom Squircle struct, use built-in continuous rounded rect

RoundedRectangle(style: .continuous) is Apple's squircle. The custom
superellipse shape was unnecessary.

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

---------

Co-authored-by: Lawrence Chen <lawrencecchen@users.noreply.github.com>
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-13 06:42:08 -07:00
Lawrence Chen
9eefc80e32 Share last-surface close handling across close actions 2026-03-13 04:16:59 -07:00
Lawrence Chen
fe7ef33fea
Keep workspaces open when closing the last surface (#1315)
* Add last-surface close regression tests

* Keep workspaces open when closing last surface

* Add Cmd+W last-surface close setting

* Share Cmd+W surface-close path
2026-03-13 03:58:07 -07:00
Lawrence Chen
2596f78380
Add settings to disable pane ring and flash (#1217)
* Add setting to disable unread pane ring

* Add setting to disable pane flash

* Label notification toggles for accessibility

* Clean up notification settings review follow-ups
2026-03-13 03:52:56 -07:00
Lawrence Chen
da547bad3c
Add menu bar visibility setting (#1330)
Co-authored-by: Lawrence Chen <lawrencecchen@users.noreply.github.com>
2026-03-13 03:28:33 -07:00
Lawrence Chen
4b0816f91b
Summarize multi-workspace close confirmation (#1329)
* Add multi-workspace close UI regression test

* Summarize multi-workspace close confirmation

* Add Cmd+Shift+W multi-close UI regression test

* Honor sidebar multi-select for close workspace

---------

Co-authored-by: Lawrence Chen <lawrencecchen@users.noreply.github.com>
2026-03-13 03:26:37 -07:00
Lawrence Chen
9b0bf2f66d
Add minimal sidebar detail toggles (#1312)
* Add minimal sidebar detail toggles

* Address sidebar review comments
2026-03-12 21:22:51 -07:00
Lawrence Chen
1d540d0806 Merge origin/main into issue-122-import-browser-cookies-history-settings 2026-03-12 18:40:20 -07:00
Lawrence Chen
f7cbbad434 Revert "Merge pull request #239 from manaflow-ai/issue-151-ssh-remote-port-proxying"
This reverts commit 78e4bd32ba, reversing
changes made to cf75da8f8a.
2026-03-12 14:45:58 -07:00
Lawrence Chen
de47345538 Address ssh review feedback and CI blockers 2026-03-11 23:03:53 -07:00
Lawrence Chen
6849b83f8d
Reduce typing lag from sidebar re-evaluation and hitTest overhead (#1204)
* Add typing hot path timing diagnostics

* Add stress workspace debug menu item

* Restore stress workspace preload debug path

* Reduce typing lag from sidebar re-evaluation and hitTest overhead

hitTest: gate divider/sidebar/drag routing to pointer events only,
avoiding two full view-tree walks per non-pointer event.

forceRefresh: replace per-keystroke ISO8601DateFormatter + FileHandle
I/O with dlog() in DEBUG builds.

TabItemView: replace @EnvironmentObject subscriptions with plain refs
and precomputed parameters, add Equatable conformance to skip body
re-evaluation when parent rebuilds with unchanged values. @self changed
re-evaluations dropped from 668 to 1 during rapid typing.

* Add typing-latency guardrail comments and CLAUDE.md pitfalls

Strategic comments on hitTest, TabItemView, and forceRefresh to prevent
future regressions. Adds typing-latency-sensitive paths to CLAUDE.md
pitfalls section so agents know the constraints before editing.

* Add workspace palette actions and fix release autosave typing guard

Add Move Up/Down/Top, Close Other/Above/Below, Mark Read/Unread to
Cmd+Shift+P command palette and a Workspace submenu in the menu bar.

Fix recordTypingActivity() being gated behind #if DEBUG, which prevented
release builds from honoring the typing quiet period in autosave.
2026-03-11 17:54:02 -07:00
Lawrence Chen
52783bddf0
Polish welcome, feedback, and shortcuts flows (#1169)
* Add cmux welcome command with ASCII logo and shortcuts

Adds `cmux welcome` CLI command that prints a blue-to-purple gradient
chevron logo, version info, and key shortcuts. Auto-runs once on first
workspace creation via UserDefaults. Adds "Welcome" to the ? help menu
in the sidebar footer, which opens a new workspace running the command.

* Polish welcome, feedback, and shortcuts flows
2026-03-10 20:59:34 -07:00
Lawrence Chen
6b6d551f62
Constrain notification sound picker width (#1168) 2026-03-10 18:45:02 -07:00
Lawrence Chen
e7c3961489
Revert "Add workspace pages in the titlebar (#1030)" (#1040)
This reverts commit 4de975e6a4.
2026-03-07 00:05:58 -08:00
Lawrence Chen
4de975e6a4
Add workspace pages in the titlebar (#1030)
* Add workspace pages in the titlebar

* Add workspace pages UI test target entry

* Relax workspace pages UI test titlebar checks

* Use page close button in workspace pages UI test

* Stabilize workspace pages UI test interruptions

* Skip page close confirms in UI tests

* Clean up superseded workspace handoffs

* Tighten page hint UI assertions

---------

Co-authored-by: cmux <cmux@cmuxs-Mac-mini.local>
2026-03-06 21:23:11 -08:00
Lawrence Chen
6a319bec8c
Move update logs into Debug menu (#1008)
Move "Copy Update Logs" and "Copy Focus Logs" from a standalone
top-level "Update Logs" menu into the existing Debug menu. Remove
the now-unused menu.updateLogs.title localization key.

Co-authored-by: cmux <cmux@cmuxs-Mac-mini.local>
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-06 01:03:38 -08:00
Lawrence Chen
29054dc709
Add sidebar help menu to footer (#958)
* Add sidebar help menu

* Fix help menu test wiring

* Fix help menu accessibility

* Use native popup for help menu

* Use icon button for sidebar help

* Add feedback composer and feedback API

* Allow preview builds without feedback env

* Tighten feedback upload limits

* Adjust sidebar footer padding

* Tighten sidebar footer spacing

* Add link affordances to help menu

* Polish sidebar feedback composer

* Move feedback icon to trailing edge

* Normalize help menu trailing icon sizes

* Enlarge help menu trailing icons

* Reduce help menu link icon size

* Shrink help menu link arrow

* Reduce help menu link arrow again

* Fix feedback message editor focus

* Add send feedback keyboard shortcut

* Polish feedback launch and delivery
2026-03-05 21:00:42 -08:00
Lawrence Chen
6347571b7c
Fix Dvorak Cmd+C colliding with notifications shortcut (#762)
* Fix layout-safe command shortcut matching

* Fix unshifted symbol shortcut coercion

* Fix layout handling for hardcoded Cmd shortcuts

* Support Cmd/Ctrl modifier hold shortcut hints

* Address PR shortcut review feedback

* Handle Caps Lock in command shortcut matching

* Address remaining PR shortcut review comments

* Handle Cmd+Ctrl+F control-character fallback

* Tighten shortcut hint hold-delay test

* Expose shortcut hint visibility in settings

* Restore Cmd+digit fallback on symbol-first layouts

* Stabilize shortcut regression coverage

* Align shortcut hint toggle with Cmd/Ctrl behavior

* Restore Claude workflow file

* Match Claude workflow file to main

* Keep shortcut hint hold behavior command-only

* Restore command shortcut fallback when layout data is unavailable

* Preserve command-aware shortcut translation
2026-03-05 18:32:42 -08:00
Lawrence Chen
26bef7316e
Fix custom notification sound staging reliability (#919) 2026-03-04 19:19:07 -08:00
Lawrence Chen
895fb802c8
Add 16 new languages to localization (#895)
* Add localization for 16 new languages

Add translations for all 637 UI string keys and 3 InfoPlist keys in:
ar, bs, da, de, es, fr, it, ko, nb, pl, pt-BR, ru, th, tr, zh-Hans, zh-Hant

Update AppLanguage enum and knownRegions to include all 18 languages.
Total supported languages: en, ja + 16 new = 18.

* Reorder languages: English first, rest alphabetical, explicit display names

Use "Chinese Simplified" / "Chinese Traditional" naming. Show native script
with English name in parentheses for non-Latin languages.

* Add Chinese native characters to language display names

* Delay language restart dialog until picker dropdown closes

* Fix Arabic bidi rendering in language picker with LTR mark

* Defer AppleLanguages write to app launch, fix picker animation lag

Writing AppleLanguages to UserDefaults triggers synchronous locale
recalculation on the main thread, causing the picker dropdown dismiss
animation to stutter. Since a restart is already required, move the
AppleLanguages write to init() on next launch instead of onChange.

* Fix reset path and add apply() back to delayed onChange

- resetAllSettings() now calls LanguageSettings.apply(.system) and
  shows restart alert if language was changed from launch value
- onChange also calls apply() inside the 0.3s delay block, so
  AppleLanguages is set before restart (avoids two-restart issue)
- init() still calls apply() as belt-and-suspenders for launch

* Fix deferred alert race: re-check current language in closure

If user changes language then changes back within 0.3s, the stale
closure would fire with the old value. Now reads current appLanguage
inside the closure instead of capturing newValue.

* Fix Spanish and Danish translations: restore missing diacritics

Spanish was missing all áéíóúñ characters (now 315 diacritics).
Danish was missing all æøå characters (now 401 diacritics).
Both languages fully retranslated with correct orthography.

* Fix reset restart alert: compare new value against launch language

Was comparing previousLanguage against languageAtLaunch, which would
miss the case where user launched in Spanish and reset to System
(Spanish != Spanish = false, so no alert). Now compares the new
appLanguage (system) against languageAtLaunch.
2026-03-04 18:04:33 -08:00
Lawrence Chen
80bbfdf206
Add custom file support for notification sounds (#869)
* Add custom-file notification sound option

* Add notification permission controls and test action

* Allow notification enable retries from settings

* Add notification permission flow debug logging
2026-03-04 17:18:07 -08:00
Lawrence Chen
b07532c522
Add Language setting for per-app locale override (#886)
* Add Language setting to Settings for per-app locale override

Uses UserDefaults AppleLanguages to override locale without changing
macOS system language. Picker shows System/English/Japanese with a
restart prompt when the selection changes.

* Address review feedback: guard relaunch and reset behavior

- Guard relaunchApp() against Process launch failure (don't terminate
  if the new instance didn't start)
- Prevent restart dialog from firing during Reset All Settings

* Add localization requirement to CLAUDE.md

All user-facing strings must use String(localized:) with keys in
Localizable.xcstrings and translations for all supported languages.

* Fix relaunch: use detached shell so open survives app exit

The previous approach spawned open as a child process that could get
killed when the parent terminated. Now spawns a shell with sleep+open
that outlives the current process.

* Fix shell injection risk and SwiftUI state batching in language setting

- Pass bundle path via environment variable instead of interpolating
  into shell command string
- Defer isResettingSettings=false to next run loop tick so onChange
  handler reliably sees the guard during Reset All Settings
2026-03-04 16:03:33 -08:00
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
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
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
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
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
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
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
Lawrence Chen
1fa0f2bcb6
Add Open Folder command (Cmd+O) (#656)
* Add "Open Folder…" command to open a workspace at a chosen directory

Adds a native folder picker (NSOpenPanel) accessible from:
- Command Palette (⌘⇧P → "Open Folder…")
- File menu with ⌘O shortcut

Selecting a folder opens a new workspace at that path.

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

* Rename openRepository → openFolder, add customizable shortcut

- Rename command ID from palette.openRepository to palette.openFolder
- Register openFolder in KeyboardShortcutSettings (default: Cmd+O)
- Wire menu bar shortcut through settings instead of hardcoding
- Add commandPaletteShortcutAction mapping for shortcut hint display

* Dismiss command palette before showing Open Folder panel

The NSOpenPanel.runModal() call blocked the main thread, keeping the
command palette visible behind the file picker. Wrapping in
DispatchQueue.main.async lets the palette dismiss first.

* Trigger GitHub PR refresh

---------

Co-authored-by: michalstrnadel <michal.strnadel@gmail.com>
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-27 16:28:18 -08:00
Lawrence Chen
5a763a8b5e
Rename 'tab color' to 'workspace color' in user-facing strings (#637)
Closes https://github.com/manaflow-ai/cmux/issues/635
2026-02-27 02:43:08 -08:00
Lawrence Chen
e20d692094
Remove duplicate telemetry assignment in resetAllSettings (#611) 2026-02-26 22:14:17 -08:00
Lawrence Chen
fa6a18c753
Add telemetry opt-out setting (#610)
Adds a "Send anonymous telemetry" toggle in Settings that lets users
disable Sentry crash reporting and PostHog analytics. The setting is
frozen at launch so toggling mid-session shows a restart hint. The hint
correctly clears if the user toggles back to the launch-time value.
2026-02-26 22:02:29 -08:00