* Add React Grab inject button to browser toolbar
Adds a toolbar button (cursor click icon) that injects the react-grab
script (unpkg.com/react-grab/dist/index.global.js) into the current
page. Hover over React elements and Cmd+C to copy component context
(file, component name, line number) for AI agents.
Button highlights when active, resets on navigation.
* Auto-activate selection mode on React Grab inject
First click: injects the script and auto-activates selection mode via
the react-grab:init event. Subsequent clicks toggle selection mode
on/off via window.__REACT_GRAB__.toggle().
* Bridge React Grab state back to Swift via WKScriptMessageHandler
Register a cmux-bridge plugin after injecting react-grab that posts
state changes back to Swift via webkit.messageHandlers. The button
now highlights accent color only when selection mode is actually
active (not just when the script is loaded), and deactivates when
the user exits selection mode via Escape or the react-grab toolbar.
* Fetch react-grab script via URLSession to bypass CSP
Sites like vercel.com block loading external scripts via CSP headers.
Fetch the script with URLSession (not subject to page CSP), cache it,
and inject inline via evaluateJavaScript. Also guard against duplicate
injection on repeated clicks.
* Prefetch react-grab script on first browser panel init
Kick off a low-priority background fetch of the react-grab script
when the first BrowserPanel is created. The script is cached
statically so clicking the button is instant.
* Eliminate react-grab button and callback lag
Three changes:
1. Fire-and-forget: use evaluateJavaScript with completionHandler
instead of await, so button taps return immediately.
2. Single JS payload: combine bootstrap listener + script source
into one evaluateJavaScript call (one IPC round-trip, not two).
3. Dedupe state callbacks: only post webkit message when isActive
actually changes, not on every hover/drag state update.
* Fix duplicate state callback on react-grab toggle
toggleReactGrab was sending an explicit postMessage AND the plugin's
onStateChange hook was firing too, causing two @Published updates per
toggle. Remove the explicit postMessage since the plugin hook handles
it. Also add dlog instrumentation for debugging.
* Add Cmd+Shift+G shortcut for React Grab (configurable)
- Add toggleReactGrab to KeyboardShortcutSettings with Cmd+Shift+G default
- Add View menu item with customizable shortcut
- Add command palette entry (searchable as "react grab" or "inspect element")
- Simplify button to use toggleOrInjectReactGrab, remove local state tracking
* Fix Codex review findings: pin version, verify hash, fix retry and state
1. Pin react-grab to exact version (0.1.29) with SHA-256 integrity
check. Script is verified before evaluation to prevent supply-chain
attacks via compromised CDN responses.
2. Clear prefetchTask on failure so subsequent attempts retry the
download instead of reusing a permanently failed task.
3. Remove premature isReactGrabActive=true. State is now only set
by the onStateChange message handler callback after confirmed
initialization, or explicitly reset on evaluation error.
* Extract React Grab into own file, make version configurable
Move all react-grab logic (settings, script loader, message handler,
BrowserPanel extension) into Sources/Panels/ReactGrab.swift.
Add a "React Grab Version" text field in Settings > Browser that lets
the user pin which npm version is fetched. Only versions with a known
SHA-256 integrity hash in ReactGrabSettings.knownHashes are accepted.
The cache invalidates when the configured version changes.
---------
Co-authored-by: Lawrence Chen <lawrencecchen@users.noreply.github.com>
* Add "Match Terminal Background" sidebar setting
Adds a toggle in Settings > Sidebar Appearance that makes the sidebar
use the same background color and transparency as the terminal area.
Uses layer-level opacity on a fully opaque background color (the same
technique as TitlebarLayerBackground) with effective opacity formula
`1 - (1-alpha)^2` to account for the terminal's two stacked
semi-transparent layers (Bonsplit chrome + Ghostty Metal surface).
Also adds a 1px trailing border derived from the terminal chrome color,
matching the bonsplit tab bar separator logic.
* Fix sidebar border color not updating on theme change
Add @State + .onReceive(.ghosttyDefaultBackgroundDidChange) to
SidebarTrailingBorder so the separator color recomputes when the
Ghostty theme changes, matching the pattern used in SidebarBackdrop.
* Address review comments: localize debug toggle, fix separator refresh
- Localize the debug panel toggle label (Codex P1)
- Add .onAppear to SidebarTrailingBorder for initial color (Cubic P2)
- Fix stale doc comment on SidebarTerminalBackgroundView (Cubic P3)
---------
Co-authored-by: Lawrence Chen <lawrencecchen@users.noreply.github.com>
* feat: add Ukrainian (uk) website translation
Translate all 681 keys from en.json to uk.json for the cmux website.
Preserves all placeholders and HTML-like tags. Testimonials kept in original language.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* feat: register Ukrainian locale in web i18n routing config
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* feat: add Ukrainian translations to Localizable.xcstrings (916 keys)
Translated all 916 string keys from English to natural Ukrainian.
All format specifiers (%@, %lld, %1$@, etc.) preserved.
JSON syntax validated.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* feat: add Ukrainian README translation (README.uk.md)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* feat: add Ukrainian language link to all README language selectors
Added Українська link to the language selector paragraph in all 20 existing
README files (README.md and README.*.md), pointing to README.uk.md.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* Fix truncated Ukrainian translations for cli.claude-teams.usage and cli.omo.usage
---------
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-authored-by: Lawrence Chen <lawrencecchen@users.noreply.github.com>
Port links were reusing the PR-link preference
(openSidebarPullRequestLinksInCmuxBrowser), causing inconsistent
behavior when users toggled that setting. Adds a dedicated
openSidebarPortLinksInCmuxBrowser setting with its own toggle in
Settings so port and PR link behavior can be controlled independently.
Addresses review feedback from https://github.com/manaflow-ai/cmux/pull/1844
Co-authored-by: Lawrence Chen <lawrencecchen@users.noreply.github.com>
* Add `cmux omo` command for OpenCode + oh-my-openagent integration
Same pattern as `cmux claude-teams`: creates a tmux shim so
oh-my-openagent's TmuxSessionManager spawns agents as native cmux
splits instead of tmux panes. Sets TMUX/TMUX_PANE env vars, prepends
shim to PATH, and execs into opencode.
Closes https://github.com/manaflow-ai/cmux/issues/2085
* Auto-install oh-my-opencode plugin when running cmux omo
Before launching opencode, cmux omo now:
- Checks if oh-my-opencode is registered in ~/.config/opencode/opencode.json
- If not, creates/updates the config with the plugin entry
- Checks if the npm package is installed in node_modules
- If not, runs bun add (or npm install) to install it
- Then proceeds with tmux shim setup and exec
* Use shadow config dir to avoid modifying user's opencode setup
Instead of writing directly to ~/.config/opencode/opencode.json,
cmux omo now creates a shadow config at ~/.cmuxterm/omo-config/ that
layers oh-my-opencode on top of the user's existing config. Symlinks
node_modules, package.json, bun.lock, and plugin config from the
original dir. Sets OPENCODE_CONFIG_DIR to the shadow directory.
Running plain `opencode` remains unaffected.
* Add Agent Integrations docs section with Claude Code Teams and oh-my-opencode pages
Adds sectioned sidebar navigation to the docs site. The new Agent
Integrations section contains separate pages for cmux claude-teams and
cmux omo, documenting usage, tmux shim mechanics, directory layout,
environment variables, and the shadow config approach. Both pages
include a nightly-only warning. Full English and Japanese translations,
nav item keys added to all 19 locales.
* Remove uppercase from sidebar section headers
* Add more spacing above and below sidebar section headers
* Enable tmux mode in oh-my-opencode config, improve docs
- cmux omo now writes tmux.enabled=true to the shadow oh-my-opencode.json
config. Without this, oh-my-openagent's TmuxSessionManager won't spawn
visual panes even though $TMUX is set (the config defaults to false).
- Nightly warnings now link to /nightly instead of generic text.
- Added "What you get" section to oh-my-opencode docs explaining the
visual pane behavior (auto-layout, idle cleanup, queueing).
- Added tmux.enabled step to first-run and how-it-works sections.
* Add terminal-notifier shim to route oh-my-openagent notifications to cmux
oh-my-openagent sends macOS notifications via terminal-notifier
(args: -title <t> -message <m> [-activate <id>]). The shim in
~/.cmuxterm/omo-bin/terminal-notifier intercepts these calls and
routes them through cmux notify, so notifications appear in cmux's
sidebar panel instead of as raw macOS notifications.
* Add pane geometry to tmux-compat for oh-my-openagent grid planning
oh-my-openagent's TmuxSessionManager needs pane geometry (columns,
rows, position, window dimensions) to decide where to spawn agent
panes. Without this data, agents run headlessly.
Server side:
- pane.list v2 response now includes pixel_frame, cell_size, columns,
rows per pane, plus container_frame at the top level
- Uses BonsplitController.layoutSnapshot() for pixel geometry and
ghostty_surface_size() for terminal grid dimensions
CLI side:
- tmuxEnrichContextWithGeometry() computes character-cell positions
from pixel frames and cell dimensions for tmux format variables
(pane_width, pane_height, pane_left, pane_top, pane_active,
window_width, window_height)
- list-panes now resolves pane targets (%uuid) via tmuxResolvePaneTarget
instead of failing with "Workspace not found"
- display-message enriched with geometry for format strings like
#{pane_width},#{window_width}
- tmux -V now returns "tmux 3.4" (needed by oh-my-openagent's
tmux-path-resolver verification)
* Add socket tests for tmux-compat pane geometry
6 tests verifying the geometry enrichment works end-to-end:
- pane.list returns pixel_frame, columns, rows, cell_size, container_frame
- tmux -V returns version string
- list-panes -F renders geometry format variables as integers
- list-panes -t %<uuid> resolves pane targets
- display -p renders pane_width and window_width
- After split, two panes have different positions and halved widths
All 6 pass on macmini (cmux-macmini).
* Handle tmux -V in shim script directly (no socket needed)
oh-my-openagent's tmux-path-resolver runs tmux -V to verify the binary
works. The __tmux-compat handler requires a socket connection, which
may not be established at verification time. Handle -V in the bash
shim directly to avoid the socket dependency.
* Lower default tmux pane min widths for cmux omo
oh-my-openagent defaults: main_pane_min_width=120, agent_pane_min_width=40,
requiring 161+ columns. Most terminal windows are narrower, causing
decideSpawnActions to return canSpawn=false and defer agents forever.
cmux omo now sets: main_pane_min_width=60, agent_pane_min_width=30,
main_pane_size=50, requiring only 91 columns. Also moved tmux -V
handling into the bash shim to avoid needing a socket connection for
the version check.
* Resolve merge conflicts with main (main-vertical layout, focus param)
- Keep upstream main-vertical layout anchoring from #2119
- Keep upstream focus param (v2Bool) instead of no_focus
- Combine with our -d flag handling: -d sets focus=false
- Include customCommands nav item from main
* Implement select-layout equalize and resize-pane absolute width
When oh-my-openagent spawns agent panes, it calls select-layout
main-vertical after each split to redistribute panes evenly, then
resize-pane -x <columns> to set the main pane width. Both were
previously no-ops, causing cascading uneven splits.
Server side:
- Add workspace.equalize_splits v2 API that calls the existing
TabManager.equalizeSplits (sets all dividers to 0.5)
CLI side:
- select-layout now calls workspace.equalize_splits before tracking
main-vertical state
- resize-pane -x <columns> without directional flags now computes
the pixel delta from current to desired width and resizes accordingly
* Fix equalize to use proportional divider positions
The previous equalize set all dividers to 0.5, which in a right-
recursive binary tree (from successive splits) gives 50/25/12.5/6.25%
instead of equal sizes.
New algorithm counts leaf panes on each side of each split and sets
the divider to N_left / (N_left + N_right). For 5 panes in a chain:
1/5, 1/4, 1/3, 1/2, giving each pane exactly 20%.
* Fix select-layout main-vertical to only equalize vertical splits
The proportional equalize was treating the top-level horizontal split
(main vs agent column) the same as vertical splits, setting the main
pane to 1/6 of the window with 5 agents.
For main-vertical layout, only equalize vertical splits (the agent
column), leaving the horizontal main/agent divider untouched. The
subsequent resize-pane -x handles the main pane width.
workspace.equalize_splits now accepts an optional orientation filter
("vertical" or "horizontal") to scope which splits get equalized.
* Re-equalize agent column after kill-pane
* Address PR review comments
- Fix cmux omo --help: remove omo from the help-bypass guard so
--help shows usage text instead of trying to launch opencode
- Don't overwrite unreadable opencode.json: fail with an error
instead of silently resetting to empty config
- Drain installer pipes concurrently before waitUntilExit to
prevent deadlock from full pipe buffers during bun/npm install
---------
Co-authored-by: Lawrence Chen <lawrencecchen@users.noreply.github.com>
* feat(sidebar): make listening ports clickable to open in browser
Wrap each sidebar port in a Button that opens http://localhost:{port}
in the cmux built-in browser (or system browser as fallback), matching
the existing PR link click behavior.
Fixes#1602
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* fix: address bot review feedback on port clickability
- Localize port label text with String(localized:) instead of bare literal
- Add sidebar.port.label and sidebar.port.openTooltip keys to
Localizable.xcstrings with English and Japanese translations
- Respect openSidebarPullRequestLinksInCmuxBrowser user preference in
openPortLink, matching the openPullRequestLink pattern exactly
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
---------
Co-authored-by: Matt Van Horn <455140+mvanhorn@users.noreply.github.com>
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
* Pre-launch app for browser UI test on headless CI runners
XCUIApplication.launch() blocks ~60s then fails on headless WarpBuild
runners because foreground activation requires a GUI login session.
Apply the same pre-launch strategy used for the display resolution test:
- CI shell launches the app with env vars before running xcodebuild
- Test detects pre-launched app via manifest, uses activate() instead of
launch() to avoid killing and relaunching the app
- Falls back to clicking the window for focus via accessibility framework
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* Revert "Pre-launch app for browser UI test on headless CI runners"
This reverts commit a540e2fd99aaa1395b91a8d50caa797cdd7551b8.
* feat: cmux.json for custom commands
* tests: add cmux json tests
* fix: pr review feedback: validation, translations, input handling, and palette improvements
- Fix Danish ("Overfladedef inition") and Norwegian ("rotmapp") translation typos
- Add empty-string check for baseCwd fallback in command palette handlers
- Coalesce \r\n into single Return keypress in sendInput
- Redact command text from timeout log to prevent secret leakage
- Add decode-time validation: reject hybrid/empty commands, ambiguous layout
nodes, wrong split children count, and empty pane surfaces
- Namespace custom command IDs with "cmux.config.command." prefix
- Forward command description to palette subtitle when available
- Update tests for new validation rules and ID prefix
* fix: address PR review feedback — per-window config isolation, blank validation, ancestor walk,
palette sanitization
* fix: fallback to current dir cmux.json watching if no any cmux.json found in full acesor walk
* ci: trigger CI for fork PR
* Add directory trust for cmux.json command confirmation
The confirm dialog now shows the actual command text and has an "Always
trust commands from this folder" checkbox. When checked, future confirm
commands from that directory skip the dialog.
Trust is scoped to the git repo root if the cmux.json is inside a repo,
so trusting once covers all subdirectories. Non-git directories are
trusted by exact path. Global config is always trusted.
Trusted directories are persisted in ~/Library/Application Support/cmux/
trusted-directories.json.
* Add trusted directories section to Settings
Shows all trusted directories with per-directory revoke buttons and a
Clear All option. Placed in a "Custom Commands" section between
Automation and Browser in Settings.
* Replace trusted directories list with editable textarea
One path per line, with a Save button that activates on changes.
Users can add, remove, or edit paths directly.
* Auto-save trusted directories on edit, remove Save button
Matches the behavior of other textarea settings (browser host
whitelist, external URL patterns) which auto-save via @AppStorage.
* Sanitize command text in confirm dialog against BiDi attacks
Strip zero-width and BiDi override characters from the command preview
so the dialog shows exactly what will be executed.
---------
Co-authored-by: austinpower1258 <austinwang115@gmail.com>
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Co-authored-by: Lawrence Chen <lawrencecchen@users.noreply.github.com>
* Allow customizing numbered workspace and surface shortcuts
* Update bonsplit submodule to squashed main commit
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Add English and Japanese translations for the named tab close
confirmation message. Uses %@ format specifier for the tab name
to support proper localization word order.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* 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>
- 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>
* 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>