cmux/Sources
Lawrence Chen dd54927cb9
Add React Grab inject button to browser toolbar (#2373)
* 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>
2026-03-30 18:00:45 -07:00
..
Find Fix command palette focus after terminal find (#2089) 2026-03-25 17:27:54 -07:00
Panels Add React Grab inject button to browser toolbar (#2373) 2026-03-30 18:00:45 -07:00
Update Fix update error details dialog overflow (#2359) 2026-03-30 03:05:48 -07:00
AppDelegate.swift Keep cmux browser Find shortcuts authoritative (#2356) 2026-03-30 03:16:10 -07:00
AppIconDockTilePlugin.swift Fix Dock persistence for manual app icons (#2360) 2026-03-30 03:34:35 -07:00
AppleScriptSupport.swift fix: read working directory from panelDirectories instead of TerminalPanel 2026-03-20 04:15:44 +08:00
Backport.swift Resync terminal portals after sidebar changes (#1253) 2026-03-12 02:46:30 -07:00
BrowserWindowPortal.swift Fix browser pane hangs from redundant portal refreshes (#2353) 2026-03-30 03:51:18 -07:00
cmuxApp.swift Add React Grab inject button to browser toolbar (#2373) 2026-03-30 18:00:45 -07:00
CmuxConfig.swift Validate workspace color during cmux.json decode (#2112) 2026-03-24 22:58:27 -07:00
CmuxConfigExecutor.swift Sanitize command before execution, not just display (#2122) 2026-03-25 00:28:30 -07:00
CmuxDirectoryTrust.swift feat: cmux.json for custom commands (#2011) 2026-03-24 22:28:46 -07:00
ContentView.swift Add React Grab inject button to browser toolbar (#2373) 2026-03-30 18:00:45 -07:00
GhosttyConfig.swift Add configurable sidebar tint color with light/dark mode support (#1465) 2026-03-15 15:48:57 -07:00
GhosttyTerminalView.swift Add cmd-click fallback for bare filenames (ls output) (#2294) 2026-03-30 04:49:17 -07:00
KeyboardLayout.swift fix: prevent Japanese IME confirmation Enter from executing command (#2075) 2026-03-24 22:24:13 -07:00
KeyboardShortcutSettings.swift Add React Grab inject button to browser toolbar (#2373) 2026-03-30 18:00:45 -07:00
NotificationsPage.swift Add Jump to Latest to the notifications popover (#1167) 2026-03-10 19:30:17 -07:00
PortScanner.swift Fix workspace creation crash after restore (#1985) 2026-03-25 00:04:03 -07:00
PostHogAnalytics.swift Flush PostHog hourly active events immediately (#934) 2026-03-05 04:00:04 -08:00
RemoteRelayZshBootstrap.swift Support image drag-and-drop into SSH terminals (#1838) 2026-03-20 18:31:19 -07:00
SentryHelper.swift Auto-heal missing CLI listener socket (#679) 2026-02-28 01:19:38 -08:00
SessionPersistence.swift fix: skip identical session autosave writes (#1732) 2026-03-18 03:24:40 -07:00
SidebarSelectionState.swift Implement session persistence pass 1 with multi-window restore 2026-02-22 15:39:59 -08:00
SocketControlSettings.swift Skip quit confirmation for tagged DEV builds (#2288) 2026-03-28 03:57:25 -07:00
TabManager.swift Add React Grab inject button to browser toolbar (#2373) 2026-03-30 18:00:45 -07:00
TerminalController.swift fix(socket): return not_found error when surface_id is provided but unresolvable (#2150) 2026-03-26 18:45:21 -07:00
TerminalImageTransfer.swift Fix SSH image transfer followups (#1904) 2026-03-20 21:32:21 -07:00
TerminalNotificationStore.swift Fix remote proxy notification spam with cooldown, backoff, and SSH keepalive (#2325) (#2330) 2026-03-29 18:07:39 -07:00
TerminalSSHSessionDetector.swift Fix SSH image transfer cleanup and IPv6 followups (#1907) 2026-03-20 22:04:52 -07:00
TerminalView.swift Add sidebar blur effect with withinWindow blending (#9) 2026-02-04 03:04:45 -08:00
TerminalWindowPortal.swift Fix #2210: coalesce portal sync to latest geometry (#2214) 2026-03-29 17:55:40 -07:00
UITestRecorder.swift Fix frozen terminals after split churn (#12) 2026-02-13 16:45:31 -08:00
WindowAccessor.swift Polish browser suggestions, focus, and tagged reload flow 2026-02-14 21:33:28 -08:00
WindowDecorationsController.swift Refine settings/about titlebar styling and controls alignment 2026-02-14 02:11:35 -08:00
WindowDragHandleView.swift Fix titlebar double-click zoom handling (#2130) 2026-03-25 02:15:15 -07:00
WindowToolbarController.swift Fix minimal mode toggle not updating titlebar state (#2218) 2026-03-27 20:25:51 -07:00
Workspace.swift Fix remote proxy notification spam with cooldown, backoff, and SSH keepalive (#2325) (#2330) 2026-03-29 18:07:39 -07:00
WorkspaceContentView.swift Fix minimal mode tab bar disappearing in fullscreen (#2375) 2026-03-30 16:41:05 -07:00