Commit graph

1047 commits

Author SHA1 Message Date
Lawrence Chen
6993c8ceef Allow notify_target across windows 2026-03-05 22:53:37 -08:00
Lawrence Chen
ae4781ef66 Prepare notify regression source terminal in app setup 2026-03-05 22:39:26 -08:00
Lawrence Chen
5f82a1ae2a Run notify regression through a real terminal 2026-03-05 22:18:14 -08:00
Lawrence Chen
21bb31dcfb Avoid blocking notify regression socket replies 2026-03-05 21:58:38 -08:00
Lawrence Chen
7bee9ddc13 Increase notify regression socket timeout 2026-03-05 21:43:20 -08:00
Lawrence Chen
335aaaecec Fix send_workspace routing in inactive windows 2026-03-05 21:24:25 -08:00
Lawrence Chen
2023d8eb57 Fallback panel lookup for notify UI setup 2026-03-05 20:34:51 -08:00
Lawrence Chen
acd8dbff69 Keep send_workspace test-only 2026-03-05 20:24:49 -08:00
Lawrence Chen
e6e5a57dd6 Send notify regression input via workspace socket 2026-03-05 20:18:45 -08:00
Lawrence Chen
6c163b1eb0 Run notify regression from in-app shell 2026-03-05 20:04:52 -08:00
Lawrence Chen
f9dbbd3562 Reset surface wait timeout in notify UI setup 2026-03-05 19:26:08 -08:00
Lawrence Chen
2b99af19bc Harden multi-window notify regression test 2026-03-05 19:18:01 -08:00
Lawrence Chen
3e10c3f790 Use raw socket fallback in notify UI test 2026-03-05 18:55:05 -08:00
Lawrence Chen
827df22fee Prefer standalone CLI in notify UI test 2026-03-05 18:41:26 -08:00
Lawrence Chen
9853235cb7 Resolve notify UI test CLI path on CI 2026-03-05 18:22:05 -08:00
Lawrence Chen
5fa3570632 Harden notify focus regression socket checks 2026-03-05 18:10:00 -08:00
Lawrence Chen
9c9670ea71 Use socket ping before notify UI test fallback 2026-03-05 17:44:48 -08:00
Lawrence Chen
ee899042ca Publish socket health into notify focus UI test 2026-03-05 17:35:57 -08:00
Lawrence Chen
69cfce9596 Stabilize notify focus UI test socket and surface waits 2026-03-05 03:14:52 -08:00
Lawrence Chen
c8487e1457 Stabilize notify focus regression socket detection 2026-03-05 02:47:36 -08:00
Lawrence Chen
efdfd76484 Harden notify UI test socket resolution 2026-03-05 02:39:46 -08:00
Lawrence Chen
4351c3cf18 Fix notify CLI focus-steal regression and add UI test 2026-03-05 02:20:27 -08:00
Lawrence Chen
017f4416d0
Prompt bug reporters to test NIGHTLY (#930) 2026-03-05 01:48:09 -08:00
sminamot
bf28f5df4b
Skip Ctrl fast path during IME composition (#790)
During IME composition (e.g. Japanese input), Ctrl+H should delete
composing characters via the IME, not bypass it and send a backspace
directly to the terminal. Add a hasMarkedText() check so the fast path
is only taken when no IME composition is active, letting
interpretKeyEvents() handle the key instead.

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-05 00:24:40 -08:00
Eray Bozoglu
2712cabac9
Fix orphaned child processes when closing workspace tabs (#889)
* Fix orphaned child processes when closing workspace tabs

When closing a workspace tab via the sidebar X button, child processes
(login → zsh → claude) survived as orphans because TabManager.closeWorkspace()
only removed the workspace from the tabs array without explicitly freeing
Ghostty surfaces. It relied on ARC to cascade deallocation, but SwiftUI views
and Combine publishers held references, delaying or preventing
ghostty_surface_free() (which sends SIGHUP) from ever running.

This adds explicit teardown on the workspace close path:
- TerminalSurface.teardownSurface(): idempotent method to free the Ghostty
  runtime surface eagerly, matching the existing deinit logic
- TerminalPanel.close() now calls teardownSurface() to ensure SIGHUP is sent
- Workspace.teardownAllPanels() iterates all panels and closes them
- TabManager.closeWorkspace() calls teardownAllPanels() before removing
  the workspace from the tabs array

* Harden workspace teardown and ownership checks

* Address follow-up teardown review feedback

---------

Co-authored-by: Lawrence Chen <54008264+lawrencecchen@users.noreply.github.com>
2026-03-04 20:00:35 -08:00
Lawrence Chen
604ba6fcab
Fix Cmd+F Escape passthrough into terminal (#918)
* Fix find-bar Escape passthrough to terminal

* Keep find Escape suppression armed until key-up
2026-03-04 19:26:05 -08:00
Austin Wang
6fe1410918
Fix terminal link opens to stay in source workspace (#912) 2026-03-04 19:21:45 -08:00
Austin Wang
8193e55dae
Fix background terminal startup for unfocused workspaces (#920) 2026-03-04 19:21:32 -08:00
Lawrence Chen
26bef7316e
Fix custom notification sound staging reliability (#919) 2026-03-04 19:19:07 -08:00
Austin Wang
39a0da2b7e
Fix sidebar branch refresh during agent-driven git checkout (#671) 2026-03-04 19:13:19 -08:00
Austin Wang
80baae355a
Add browser camera permission support and metadata (#760) (#913) 2026-03-04 18:56:22 -08:00
Lawrence Chen
0a490b0e03
Fix Claude wrapper hook errors when cmux socket is stale (#868)
* Fix Claude wrapper hook injection when cmux socket is stale

* Harden socket listener lifecycle and rearm policy

* Unset CLAUDECODE in stale-socket passthrough

* Harden listener cleanup and bound claude ping probe

* Guard socket unlink during listener startup window
2026-03-04 18:52:57 -08:00
Austin Wang
34989e8ad0
Fix portal browser click focus after workspace switch (#908) 2026-03-04 18:36:26 -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
Austin Wang
76835662f5
Fix CLI socket autodiscovery for tagged cmux sockets (#832) 2026-03-04 18:03:25 -08:00
Ismail Pelaseyed
d72b014d6d
feat: add markdown viewer panel with live file watching (#883)
* Add markdown viewer panel with live file watching

Introduce a new PanelType.markdown that renders .md files in a dedicated
panel using MarkdownUI (SwiftUI), with live file watching via DispatchSource
so content auto-updates when the file changes on disk.

- New MarkdownPanel class with file system watcher (write/delete/rename/extend)
- New MarkdownPanelView with custom cmux theme (headings, code blocks, tables,
  blockquotes, inline code, lists, horizontal rules, light/dark mode)
- Full workspace integration: SurfaceKind, creation methods, tab subscription
- Session persistence: snapshot/restore across app restarts
- V2 socket command: markdown.open (validates path, resolves workspace, splits)
- CLI command: cmux markdown open <path> with routing flags and help text
- Agent skill: skills/cmux-markdown/ with SKILL.md, openai.yaml, and references
- Cross-link from skills/cmux/SKILL.md to the new markdown skill
- SPM dependency: gonzalezreal/swift-markdown-ui 2.4.1

* Fix unreachable guard in markdown subcommand dispatch

Use looksLikePath() to distinguish subcommands from path arguments
so the guard can catch unknown subcommands and future subcommands
are parsed correctly.

* Use .isoLatin1 fallback instead of .ascii for encoding recovery

ASCII is a strict subset of UTF-8, so falling back to .ascii after
UTF-8 fails is dead code. Use .isoLatin1 which accepts all 256 byte
values and covers legacy encodings like Windows-1252.

* Mark fileWatchSource as nonisolated(unsafe) for deinit safety

deinit is not guaranteed to run on the main actor, so accessing
@MainActor-isolated storage is a data race under strict concurrency.
DispatchSource.cancel() is thread-safe, so nonisolated(unsafe) is
sufficient with a documented invariant that writes only occur on main.

* Fix file watcher reattach: retry loop with cancellation guard

- Replace one-shot 500ms retry with up to 6 attempts (3s total window)
  so files that reappear after a slow atomic replace are picked up
- Add isClosed flag checked before each retry to prevent restarting
  the watcher after close()/deinit

* Harden path validation in markdown.open command

Reject directories and non-absolute paths before panel creation
to prevent ambiguous behavior and generic downstream failures.

* Always reattach file watcher on delete/rename events

After an atomic save (delete old + create new), the DispatchSource still
points to the old inode. Previously we only reattached when the file was
unreadable, so successful atomic saves left the watcher on a stale inode
and live updates silently stopped. Now we always stop and reattach:
immediately if the new file is readable, via retry loop if not.

* Restore markdown panels even when file is missing at launch

MarkdownPanel already handles unavailable files gracefully (shows
'file unavailable' UI and retries via the reattach loop). Dropping
the panel on restore lost the user's layout for files that may
reappear shortly after (network drives, build artifacts, etc.).

* Harden markdown CLI parsing and startup reconnect behavior

---------

Co-authored-by: Lawrence Chen <54008264+lawrencecchen@users.noreply.github.com>
2026-03-04 17:48:28 -08:00
Qian Wan
6f210dd2c7
Fix voice dictation text insertion path in GhosttyNSView. (#857)
* Fix voice dictation text insertion path in GhosttyNSView.

* Fix AX selected text decoding to use explicit length
2026-03-04 17:25:39 -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
28977c8e3b
Fix browser panel lifecycle after WebContent process termination (#892)
* Fix browser panel webview lifecycle after web content crashes

* Fix BrowserPanel observer lifecycle during webview replacement

* Fix WebKit termination delegate and harden lifecycle regression check
2026-03-04 16:23:22 -08:00
Yoshiki Agatsuma
76bdf7631a
Add find-in-page (Cmd+F) for browser panels (#837) (#875)
JavaScript-based find using TreeWalker + <mark> highlights with
match counter, next/previous navigation, and drag-to-corner overlay
matching the existing terminal find bar.

- BrowserFindJavaScript: JS generation for search/next/prev/clear
- BrowserSearchOverlay: SwiftUI overlay with IME-safe onSubmit
- BrowserSearchState: Observable state (needle/selected/total)
- TabManager routing: Cmd+F/G dispatches to browser when focused
- Visibility filter: skips script/style/hidden/aria-hidden elements
- Stale DOM guard: isConnected check in next/previous scripts
- Navigation cleanup: clears find on didFinish and didFailNavigation

Co-authored-by: Lawrence Chen <54008264+lawrencecchen@users.noreply.github.com>
2026-03-04 16:15:15 -08:00
Orkhan Rzazade
5baf0d1a3b
fix: prevent crash in parseNotificationPayload when fields are empty (#881)
Swift's split(separator:) omits empty subsequences by default, so a
payload like "||" or "||body" produces an empty or misaligned array.
Accessing parts[0] unconditionally then triggers an out-of-bounds trap
(EXC_BREAKPOINT / SIGTRAP).

Two changes:
1. Pass omittingEmptySubsequences: false to preserve field positions
   across the pipe delimiters, so "title||body" correctly yields
   ["title", "", "body"] instead of ["title", "body"].
2. Guard parts[0] with a bounds check, consistent with how parts[1]
   and parts[2] are already accessed.

Reproduces when cmux notify is called with empty --title or via
Claude Code's Notification hook where env vars may be empty.
2026-03-04 16:05:43 -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
Connor Callison
80eca0de48
Handle TLS authentication challenges to fix Microsoft device compliance (#806)
WKWebView rejects all authentication challenges by default when
webView(_:didReceive:completionHandler:) is not implemented, using
.rejectProtectionSpace. This silently breaks TLS client-certificate
flows like Microsoft Entra ID Conditional Access, which verifies
device compliance via a certificate stored in the system keychain
by MDM enrollment.

By implementing the delegate method and returning
.performDefaultHandling, the system's standard URL-loading behaviour
takes over: the keychain is searched for matching client identities,
MDM-installed root CAs are trusted, and any configured SSO extensions
(e.g. Microsoft Enterprise SSO) can intercept the challenge.

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-04 15:48:18 -08:00
Lawrence Chen
102931d975
Reduce typing lag by hiding invisible views from accessibility tree (#862)
Profiling shows the main thread spends ~24% of time in
AccessibilityViewGraph.needsUpdate walking invisible SwiftUI views
during every layout pass, blocking key event dequeue.

- Add .accessibilityHidden on inactive workspaces in the ZStack
  (opacity-0 but still walked by the accessibility subsystem)
- Add .accessibilityHidden on NotificationsPage and the tabs
  container when their respective selection is not active
- Mark GhosttyTerminalView's HostContainerView (empty portal
  placeholder) as non-accessible since the terminal surface
  lives in the AppKit portal layer above SwiftUI
2026-03-04 15:40:57 -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
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