Repository navigation
Add unified surface selection context API - #10022
Conversation
|
Note Reviews pausedIt looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the Use the following commands to manage reviews:
Use the checkboxes below for quick actions:
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Path: .coderabbit.yaml Review profile: ASSERTIVE Plan: Pro Plus Run ID: 📒 Files selected for processing (1)
📝 WalkthroughWalkthroughThe PR implements ChangesSurface selection implementation
Estimated code review effort: 4 (Complex) | ~60 minutes Sequence Diagram(s)sequenceDiagram
participant CLI
participant SocketWorker
participant TerminalController
participant Panel
CLI->>SocketWorker: read-selection selectors
SocketWorker->>TerminalController: surface.read_selection
TerminalController->>Panel: readSurfaceSelection()
Panel-->>TerminalController: SurfaceSelectionReadResult
TerminalController-->>SocketWorker: selection payload
SocketWorker-->>CLI: plain or JSON output
Possibly related issues
Possibly related PRs
Suggested reviewers: Important Pre-merge checks failedPlease resolve all errors before merging. Addressing warnings is optional. ❌ Failed checks (3 errors, 2 warnings)
✅ Passed checks (20 passed)
✨ Finishing Touches 💡 1📝 Generate docstrings 💡
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 7
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@CLI/CMUXCLI`+SurfaceSelection.swift:
- Around line 74-82: Update the selection handling in the relevant CLI
read-selection flow so a valid has_selection: false response returns success
instead of throwing CLIError. Define a plain-output representation for the
no-selection case that clearly distinguishes it from selection text, while
preserving existing behavior when a selection is present.
In `@cmuxTests/CLIExplicitSurfaceRoutingTests.swift`:
- Around line 136-144: Expand the assertions in the CLI JSON response test
around jsonObject so they validate every field in the no-selection payload,
including text, base64, workspace_id, workspace_ref, and surface_id alongside
the existing fields. Ensure the expected values match the complete socket
response returned by the explicit surface routing command.
In `@cmuxTests/SurfaceSelectionTests.swift`:
- Around line 81-82: Remove the fixed .timeLimit(.minutes(1)) attribute from
browserAndMarkdownPreviewReadLiveDOMSelection, leaving the correctness test’s
existing navigation-completion synchronization unchanged.
- Around line 100-101: Add positive selection coverage in
SurfaceSelectionTests.swift alongside the existing password-input cases: create
a non-password text input or textarea with selected content, then assert
hasSelection is true and the selected text matches the expected value. Keep the
existing password-selection assertions unchanged.
In `@Resources/Localizable.xcstrings`:
- Around line 102-106: Update the socket selection error localizations around
socket.surfaceSelection.tabManagerUnavailable to use stable product-facing
wording instead of internal identifiers such as TabManager or
panel.panelType.rawValue. Use wording equivalent to “Selection reading is
currently unavailable” and “This surface does not support selection reads,”
while retaining raw values only in structured diagnostics or internal logs.
In `@Sources/Panels/SurfaceSelectionSnapshot.swift`:
- Around line 12-26: Make SurfaceSelectionSnapshot construction enforce valid
selected and no-selection states rather than accepting an unrestricted
hasSelection Boolean; update Sources/Panels/SurfaceSelectionSnapshot.swift lines
12-26 accordingly. In SurfaceSelectionLineRange’s initializer at
Sources/Panels/SurfaceSelectionLineRange.swift lines 8-11, require start >= 1
and end >= start, returning or propagating an invalid conversion result so
callers handle failure before creating a snapshot.
In `@Sources/TerminalController`+SurfaceSelection.swift:
- Around line 74-75: Update the surface-selection routing around v2UUID and the
target-resolution logic near lines 157-160 to distinguish an absent pane_id from
a present but unparseable one. When pane_id is supplied and v2UUID returns nil,
return not_found or the established invalid-selector error instead of falling
back to the focused surface; preserve normal resolution for valid or omitted
pane_id values.
🪄 Autofix
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: ASSERTIVE
Plan: Pro Plus
Run ID: 08a13cfd-26ea-4e56-a60d-ad1c5a8433d5
📒 Files selected for processing (27)
CLI/CMUXCLI+CommandSuggestions.swiftCLI/CMUXCLI+SurfaceSelection.swiftCLI/cmux.swiftPackages/macOS/CmuxControlSocket/Sources/CmuxControlSocket/Wire/ControlCommandExecutionPolicy.swiftResources/Localizable.xcstringsSources/Panels/BrowserPanel.swiftSources/Panels/FilePreviewPanel.swiftSources/Panels/MarkdownPanel.swiftSources/Panels/MarkdownWebRenderer.swiftSources/Panels/MarkdownWebSupport.swiftSources/Panels/NativeTextSurfaceSelectionReader.swiftSources/Panels/Panel.swiftSources/Panels/SurfaceSelectionLineRange.swiftSources/Panels/SurfaceSelectionReadResult.swiftSources/Panels/SurfaceSelectionSnapshot.swiftSources/Panels/TerminalPanel.swiftSources/Panels/WebSurfaceSelectionReader.swiftSources/SurfaceSelectionSocketCapture.swiftSources/SurfaceSelectionSocketCaptureOutcome.swiftSources/SurfaceSelectionSocketFailure.swiftSources/TerminalController+SurfaceSelection.swiftSources/TerminalController.swiftcmux.xcodeproj/project.pbxprojcmuxTests/CLIExplicitSurfaceRoutingTests.swiftcmuxTests/SurfaceSelectionTests.swiftcmuxTests/TerminalControllerSocketSecurityTests.swiftdocs/cli-contract.md
There was a problem hiding this comment.
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
docs/cli-contract.md (1)
243-243: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick winUse a UUID for
surface_idin the raw RPC example.
surface.read_selectionvalidatessurface_idas a UUID.surface:2returnsinvalid_params, so this documented command fails. Use a UUID value in the JSON parameters.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@docs/cli-contract.md` at line 243, Update the surface.read_selection raw RPC example to use a valid UUID for the surface_id JSON parameter instead of surface:2, while preserving the command structure and parameter name.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Outside diff comments:
In `@docs/cli-contract.md`:
- Line 243: Update the surface.read_selection raw RPC example to use a valid
UUID for the surface_id JSON parameter instead of surface:2, while preserving
the command structure and parameter name.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: ASSERTIVE
Plan: Pro Plus
Run ID: 35c5cc92-eaf1-4c39-8bb8-3a463dc939ef
📒 Files selected for processing (14)
CLI/CMUXCLI+SurfaceSelection.swiftResources/Localizable.xcstringsSources/Panels/Panel.swiftSources/Panels/SurfaceSelectionLineRange.swiftSources/Panels/SurfaceSelectionReadResult.swiftSources/Panels/SurfaceSelectionSnapshot.swiftSources/Sidebar/SidebarWorkspaceDescriptionText.swiftSources/TerminalController+ControlSurfaceDock.swiftSources/TerminalController+SurfaceSelection.swiftSources/Workspace+RemoteTmuxControlTopology.swiftcmuxTests/CLIExplicitSurfaceRoutingTests.swiftcmuxTests/SurfaceSelectionTests.swiftcmuxTests/TerminalControllerSocketSecurityTests.swiftdocs/cli-contract.md
|
Fixed the latest CodeRabbit documentation finding in 64428da. The raw |
Bugbot is paused — on-demand spend limit reachedBugbot uses usage-based billing for this team and has hit its on-demand spend limit. A team admin can raise the spend limit in the Cursor dashboard, or wait for the next billing cycle to continue. |
Greptile SummaryThis PR adds a unified, read-only surface-selection API across terminal, browser, file-preview, and Markdown panels.
Confidence Score: 5/5The PR appears safe to merge. No blocking failure remains. Important Files Changed
Sequence DiagramsequenceDiagram
participant CLI as cmux CLI / Socket Client
participant Worker as Socket Worker
participant Main as Main Actor
participant Panel as Target Panel
participant Provider as Ghostty / NSTextView / WKWebView
CLI->>Worker: surface.read_selection(target)
Worker->>Main: Resolve workspace, pane, and surface
Main->>Panel: readSurfaceSelection()
Panel->>Provider: Read live or retained selection
Provider-->>Panel: Immutable selection snapshot
Panel-->>Worker: Sendable capture outcome
Worker->>Worker: Encode common payload and base64
Worker-->>CLI: Structured selection response
Reviews (5): Last reviewed commit: "Make selection timeout and RPC routing s..." | Re-trigger Greptile |
…election-context # Conflicts: # Packages/macOS/CmuxControlSocket/Sources/CmuxControlSocket/Wire/ControlCommandExecutionPolicy.swift # Resources/Localizable.xcstrings # cmux.xcodeproj/project.pbxproj
|
Deployment failed for project cmux166 with the following error: Learn More: https://vercel.com/manaflow?upgradeToPro=build-rate-limit |
|
Deployment failed for project cmux41 with the following error: Learn More: https://vercel.com/manaflow?upgradeToPro=build-rate-limit |
|
recheck |
…election-context # Conflicts: # Resources/Localizable.xcstrings # Sources/TerminalController+ControlSocketAsync.swift # cmux.xcodeproj/project.pbxproj
|
All contributors have signed the CLA ✍️ ✅ |
Bugbot is paused — on-demand spend limit reachedBugbot uses usage-based billing for this team and has hit its on-demand spend limit. A team admin can raise the spend limit in the Cursor dashboard, or wait for the next billing cycle to continue. |
Bugbot is paused — on-demand spend limit reachedBugbot uses usage-based billing for this team and has hit its on-demand spend limit. A team admin can raise the spend limit in the Cursor dashboard, or wait for the next billing cycle to continue. |
Bugbot is paused — on-demand spend limit reachedBugbot uses usage-based billing for this team and has hit its on-demand spend limit. A team admin can raise the spend limit in the Cursor dashboard, or wait for the next billing cycle to continue. |
Final review audit (HEAD
|
| comment id | author | file:line | ask | disposition | commit sha |
|---|---|---|---|---|---|
| 3763317633 | coderabbitai[bot] | CLI/CMUXCLI+SurfaceSelection.swift:87 | Make a valid no-selection response succeed and distinguish it in plain output | fix | 2f6ba70 |
| 3763317637 | coderabbitai[bot] | cmuxTests/CLIExplicitSurfaceRoutingTests.swift:149 | Assert the complete no-selection JSON payload | fix | 2f6ba70 |
| 3763317640 | coderabbitai[bot] | cmuxTests/SurfaceSelectionTests.swift:— | Remove the fixed one-minute test time limit | fix | 2f6ba70 |
| 3763317643 | coderabbitai[bot] | cmuxTests/SurfaceSelectionTests.swift:167 | Add positive editable-control selection coverage | fix | 2f6ba70 |
| 3763317645 | coderabbitai[bot] | Resources/Localizable.xcstrings:— | Keep client-visible selection errors in stable product wording | fix | 2f6ba70 |
| 3763317649 | coderabbitai[bot] | Sources/Panels/SurfaceSelectionSnapshot.swift:— | Enforce valid selected/no-selection states and line ranges | fix | 2f6ba70 |
| 3763317651 | coderabbitai[bot] | Sources/TerminalController+SurfaceSelection.swift:161 | Reject malformed pane_id values and fail closed | fix | 2f6ba70 |
| 4912686293 | coderabbitai[bot] | review body | Seven inline findings, all listed above with replies and confirmations | already-fixed | 2f6ba70 |
| 4912819431 (outside-diff) | coderabbitai[bot] | docs/cli-contract.md:243 | Use a UUID in the raw surface.read_selection RPC example | fix | 64428da |
| 5260702852 | coderabbitai[bot] | review body | Avoid a semaphore-blocking runtime path; the live socket path is async and the sync adapter is in-process only | disagree | 927ed85 |
| 5260702852 | coderabbitai[bot] | review body | Extract selection models into a new SwiftPM package; these app-target types have no second package consumer | disagree | 2f6ba70 |
| 5260702852 | coderabbitai[bot] | review body | Translate every new key into all catalog locales; new cmux keys follow the en/ja policy and the audit passes | disagree | 2f6ba70 |
| 5260702852 | coderabbitai[bot] | review body | Remove the SidebarWorkspaceDescriptionText warning change; it is absent from the current diff against origin/main | already-fixed | f34cd66 |
| 5260702852 | coderabbitai[bot] | review body | Raise app-target docstring coverage to 80%; this heuristic is not a repository requirement for app-target code | disagree | f34cd66 |
| 5417694774 | greptile-apps[bot] | review body | No blocking ask; confidence 5/5 | already-fixed | 3593e6c |
| — | cubic | review body | No actionable review body; check passed | already-fixed | 3593e6c |
| — | Codex | review body | No Codex review body was published | already-fixed | 3593e6c |
All seven inline threads have an Austin reply and a CodeRabbit acknowledgement; the GraphQL review-thread audit reports 0 unresolved threads. No CHANGES_REQUESTED review exists.
436fc1f Add unified surface selection context API (manaflow-ai#10022) d028302 Merge pull request manaflow-ai#11696 from manaflow-ai/audit-tui-graphics-diff-wave202 11af2f2 perf(cmux-tui): index graphics pointer routes 60d06c2 Merge pull request manaflow-ai#11687 from manaflow-ai/feat-config-key-dispatch-complexity 0358c30 Merge pull request manaflow-ai#11684 from manaflow-ai/feat-tui-tab-scroll-range-fit 2c0c8a4 test(cmux-tui): bound graphics diff comparisons 8187531 Merge pull request manaflow-ai#11672 from manaflow-ai/issue-remote-tree-cache-race f0ff9cf Merge pull request manaflow-ai#11630 from manaflow-ai/feat-journal-group-commit f1e2d3f perf(tui): cache key dispatch maps 96897a7 test(tui): cover cached key dispatch refresh e40f6ca fix tab scroll range fitting complexity 275cf7c test tab scroll range fitting at scale 995638d Merge pull request manaflow-ai#11640 from manaflow-ai/feat-tui-worker-cancellation 790b53f perf(tui): avoid pane area frame clone (manaflow-ai#11404) 6a5dd4d fix(tui): reap PTY child on startup failure (manaflow-ai#11414) 2ebb5a5 fix(tui): clear stale agent updates at snapshot boundary 3b91c0c test(tui): prevent stale agent resurrection after omission 789b350 fix(tui): retain agent updates across topology races 85cbff1 test(tui): retain agent updates across topology omission 586a2a6 perf(cmux-tui): resolve resource selectors without the registry lock 7fb5414 fix(cmux-tui): cancel blocked machine worker sends dd7347e test(cmux-tui): cover cancellable machine completion send
Closes #10020
Summary
This PR adds one discoverable selection capability across selectable cmux surfaces:
surface.read_selectionfor socket clientscmux read-selection --surface <ref>for source-aware CLI outputcmux read-screen --selectionas a text-only terminal-compatible aliassurface.read_selectioninsystem.capabilities/cmux capabilitiesThe implementation is read-only: it does not focus a window, activate a workspace, move a pane, or mutate selection state.
Uniform selection model
Every supported panel implements the same async
Panel.readSurfaceSelection()capability and returns an immutable Sendable snapshot. The socket response always includes:has_selectionkindtextbase64nullIt conditionally includes
file_path, one-based inclusiveline_range, andurlwhen that source context exists. A supported surface with no selection succeeds withhas_selection: falseand empty text/base64. Unsupported panel kinds returnnot_supported; a supported surface whose live backing object is unavailable returnsunavailable.Snapshot construction makes selected and no-selection states distinct, and line ranges reject zero, negative, or reversed bounds. Explicit window/group/workspace/surface/pane selectors are validated before routing and fail closed instead of degrading to the focused surface. Pane-to-surface resolution lives in the shared workspace/Dock topology helpers rather than in the selection command.
Surface providers
ghostty_surface_read_selection, without requiring a mountedGhosttySurfaceScrollView.NSTextView.selectedRange(), extracts UTF-16-safe text, normalizes the file path, and maps the range to one-based inclusive source lines, including CRLF and selected line terminators.WKWebViewand returns the Markdown file path.Selectionobject into isolated content worlds, so the tracker deliberately lives in the same world that observes the selection. It owns an immutable last-selection snapshot so native focus can move to the neighboring agent surface without WebKit erasing the context before the socket read. Real page selection/focus/input changes and navigation reconcile or clear that snapshot; password controls clear it immediately, and cross-origin frame contents remain inaccessible by design. The response reports the live user-visible URL and returns immediately for an uncommitted document.Socket and concurrency design
surface.read_selectionis a worker-lane method withmainThreadCallable: false. The typed request remains intact until the async handler resolves the target. AppKit, WebKit, workspace routing, Ghostty access, and ref minting remain on the main actor. Only the immutable Sendable capture crosses back to the socket worker, where the common payload and base64 are encoded.The async payload builder has an explicit compiler-compatible
@concurrentboundary. The existing transport processes each connection on its own dedicated OS thread specifically because synchronous request/reply commands can wait on main-actor or async bridges; selection reuses that contract without blocking the listener or Swift cooperative executor. It introduces no polling, sleeps, accessibility scraping, or per-kind socket methods.CLI and discoverability
cmux read-selectionprints kind plus any file/line/URL context followed by the selected text.--jsonpreserves the complete common response, including a successful no-selection result. Plain output also exits zero for this valid state and prints the explicitHas selection: falsemarker.cmux read-screen --selectionuses the same RPC and prints selected text or the no-selection marker without source metadata. It rejects--scrollbackand--lines. Both read commands avoid global--windowprefocus so querying another surface does not steal focus.The CLI contract, command help, command suggestions, and capabilities output make the method discoverable.
Related terminal work
This includes the terminal socket lane and
read-screen --selectionbehavior requested by #7500. It intentionally does not close that issue because this PR does not add its DEBUGselect_allend-to-end fixture. The PR provides the production terminal read capability and alias while leaving any remaining test-seam work to that issue.Selection-change events
Push events are intentionally separated from this authoritative read path. AppKit, Ghostty, and WebKit expose selection changes through different owners and replacement lifecycles; adding per-panel listeners here would fragment ownership and risk leaks or accidental default disclosure of selected text.
The opt-in, debounced, polling-free event design is tracked at #10027. It will reuse this common result shape while owning observer and WebView teardown centrally.
Tests and validation
Behavior coverage includes:
The first commit contains only the regression tests; the second contains the production implementation and expanded behavior coverage.
Permitted local validation completed successfully: Swift frontend parsing, project normalization/checking, test-wiring lint, JSON validation, localization audit, file-length audit, and
git diff --check. Per repository guidance, no localxcodebuild, XCUITest, build, launch, or dogfood run was performed on this machine. Required GitHub checks are the compilation and test source of truth.Localization and budgets
All new CLI help, output labels, and socket error strings use
String(localized:defaultValue:)and have English and Japanese catalog entries. The touched user-facing strings and docs were audited. Every new Swift file is below 500 lines. A clean Xcode 26 compile exposed one existing unbudgeted deprecatedonChangeoverload inSidebarWorkspaceDescriptionText; the source was migrated to the modern two-argument callback instead of increasing the warning budget. No warning-budget or file-length-budget TSV was edited or regenerated.Summary by CodeRabbit
read-selectionCLI command for retrieving selected text from terminals, browsers, files, and Markdown surfaces.read-screen --selectionas a compatible alias with workspace, window, surface, and pane targeting.surface.read_selectionsocket capability with structured routing and error responses.