TabManager.updateWindowTitle() used NSApp.keyWindow to find the target
window, meaning any terminal title change (e.g. a spinner) would update
whichever window happened to be focused, not the window that owns that
TabManager. This corrupted the macOS Accessibility title attribute and
caused visible title flapping in multi-window setups.
Add a weak back-reference from TabManager to its owning NSWindow, set
by AppDelegate.registerMainWindow(), and use it instead of keyWindow.
* Upgrade Sentry: tracing, breadcrumbs, dSYM upload
- Enhanced Sentry SDK init with performance tracing (10% sample),
explicit app hang timeout, stack trace attachment, and HTTP
failure capture
- Added breadcrumbs for key user actions: workspace switch/create/close,
split creation, command palette open/close, app focus — these give
context to hang/crash reports
- Added dSYM upload step to nightly and release CI workflows so hang
stacks are fully symbolicated (requires SENTRY_AUTH_TOKEN secret)
- Created SentryHelper.swift with lightweight breadcrumb helper
Closes https://github.com/manaflow-ai/cmux/issues/365
* Remove command palette breadcrumbs
Not useful for hang diagnosis — keep only workspace/tab/split/focus
breadcrumbs that correlate with heavy operations.
* Fix terminal Cmd zoom routing for Ghostty focus descendants (#383)
* Inherit new terminal zoom from last terminal context
Prefer pane-selected terminal as Ghostty config inheritance source when creating splits/new terminals, then focused/fallback terminals. This preserves runtime zoom/font size when opening the next terminal.
* Fix terminal zoom inheritance across split/tab/workspace creation
* Add tab color feature to sidebar workspaces
Lets users assign a custom background color to any sidebar workspace tab
via a right-click "Tab Color" submenu. The primary motivation is working
across multiple projects simultaneously — coloring tabs by project makes
it instant to visually locate the right workspace without reading the title.
- Workspace: adds `customColor: String?` (@Published hex string) and
`setCustomColor()` setter
- TabManager: adds `setTabColor(tabId:color:)` convenience method
- ContentView: 16-color dark palette (all luminance < 0.30, white text
always readable), `Color(hex:)` extension, `coloredCircleImage(hex:)`
helper to render bitmapped NSImage circles (needed because macOS menus
strip SwiftUI foregroundColor from SF Symbols), updated `backgroundColor`
to use custom color at full/70%/35% opacity for active/inactive/
multi-selected states, "Tab Color" submenu in context menu with
"Clear Color" option, and a 1.5pt `Color.primary` border overlay on
the active tab for clear selection indication when custom colors are set
* Add workspace tab color schemes with settings and debug toggles
* Remove Kelly scheme and keep only original tab color palette
* Preserve neutral grayscale when brightening tab colors
* Harden UpdatePill UI test polling timeouts
---------
Co-authored-by: Andreas Fruth <andreas.fruth@gmail.com>
* Fix CJK IME input not working (#118)
CJK (Korean, Japanese, Chinese) IME input was completely broken because
cmux never forwarded preedit/composition state to Ghostty's libghostty.
Root causes and fixes:
1. Missing preedit sync: Added syncPreedit() that calls
ghostty_surface_preedit() to notify Ghostty about IME composition
text. Called from setMarkedText, unmarkText, and after
interpretKeyEvents in keyDown.
2. Wrong composing flag: The composing flag on key events now correctly
accounts for when composition just ended (markedTextBefore was true
but markedText is now empty), preventing spurious deletions when
canceling composition.
3. Event interception during IME: Added early exits in
performKeyEquivalent, the NSWindow swizzle, and the local event
monitor (handleCustomShortcut) to avoid stealing key events while
IME has active marked text.
4. IME popup positioning: firstRect(forCharacterRange:) now uses
ghostty_surface_ime_point() for accurate cursor-relative positioning
of the IME candidate window.
* Add regression tests for CJK IME composition (#118)
31 tests covering Korean, Japanese, and Chinese IME input scenarios:
- Korean jamo combining: ㅎ -> 하 -> 한 composition lifecycle
- Chinese pinyin: multi-letter marked text and candidate selection
- Japanese hiragana-to-kanji: romaji -> hiragana -> kanji conversion
- insertText correctly commits composed text and clears marked state
- unmarkText properly clears composition state (idempotent)
- performKeyEquivalent returns false during active composition for
all key types (plain, shift, space, return, escape)
- Shortcut bypass: hasMarkedText gates the handleCustomShortcut bypass
- Multi-syllable sequences, backspace correction, and rapid transitions
- keyTextAccumulator lifecycle tests
Also adds #if DEBUG test accessors for keyTextAccumulator on
GhosttyNSView to enable unit testing the accumulator path.