Skip to content

feat(browser): add Copy to context menu when text is selected#7159

Merged
nwparker merged 2 commits into
stablyai:mainfrom
sudoeren:fix/browser-copy-clean
Jul 3, 2026
Merged

feat(browser): add Copy to context menu when text is selected#7159
nwparker merged 2 commits into
stablyai:mainfrom
sudoeren:fix/browser-copy-clean

Conversation

@sudoeren

@sudoeren sudoeren commented Jul 2, 2026

Copy link
Copy Markdown
Contributor

Fixes #7036

Summary

Adds a Copy button to the built-in browser's context menu when text is selected. Previously the context menu only showed Back/Forward/Reload and DevTools — users had to open DevTools or use keyboard shortcuts to copy text from a page.

Changes

  • src/main/browser/browser-guest-ui.ts — Forwards selectionText and canCopy from Electron's ContextMenuParams to the renderer
  • src/shared/browser-guest-events.ts — Adds canCopy to BrowserGuestContextMenuEvent
  • src/preload/index.ts — Exposes window.api.ui.writeClipboardText()
  • src/renderer/src/components/browser-pane/BrowserPane.tsx — Renders a Copy button when canCopy is true (both local and remote webviews)

Testing

  1. Open the built-in browser, navigate to any page, select text, right-click → Copy button shown
  2. Click Copy → paste elsewhere → text is copied
  3. Remote webview (SSH host) → same behavior
  4. Right-click without selection → no Copy button

Screenshots

No visual change — standard context menu item.

@coderabbitai

coderabbitai Bot commented Jul 2, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Warning

Review limit reached

@nwparker, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 7 minutes

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: f0101651-8d49-4b55-8de0-94af6745effa

📥 Commits

Reviewing files that changed from the base of the PR and between e2cf68c and 040396d.

📒 Files selected for processing (10)
  • src/main/browser/browser-guest-ui.test.ts
  • src/main/browser/browser-guest-ui.ts
  • src/preload/index.ts
  • src/renderer/src/components/browser-pane/BrowserPane.tsx
  • src/renderer/src/i18n/locales/en.json
  • src/renderer/src/i18n/locales/es.json
  • src/renderer/src/i18n/locales/ja.json
  • src/renderer/src/i18n/locales/ko.json
  • src/renderer/src/i18n/locales/zh.json
  • src/shared/browser-guest-events.ts
📝 Walkthrough

Walkthrough

This change adds clipboard "Copy" support for text selections in the browser context menu. The shared event type, main process context-menu handler, preload IPC bridge, and renderer context-menu components are all extended with selectionText and canCopy fields. When a selection exists, both the remote and local browser pane context menus render a conditional "Copy" item that writes the selected text to the clipboard. Corresponding test expectations were updated to reflect the new payload fields.

Changes

Area Change
Shared types BrowserContextMenuRequestedEvent gains selectionText and canCopy fields
Main process setupGuestContextMenu populates new fields with safe defaults; test updated
Preload onContextMenuRequested callback/listener typing extended
Renderer Context-menu state extended; "Copy" menu item added to remote and local menus

Sequence Diagram(s)

sequenceDiagram
  participant WebContents
  participant browserGuestUi as browser-guest-ui.ts
  participant Preload as preload/index.ts
  participant BrowserPane as BrowserPane.tsx

  WebContents->>browserGuestUi: context-menu event (selectionText, editFlags)
  browserGuestUi->>Preload: browser:context-menu-requested (selectionText, canCopy)
  Preload->>BrowserPane: onContextMenuRequested callback (selectionText, canCopy)
  BrowserPane->>BrowserPane: render Copy menu item if selectionText present
  BrowserPane->>BrowserPane: write selectionText to clipboard on click
Loading

Estimated code review effort: 2/5 (~15 minutes)

Related PRs: None identified.

Related issues: None identified.

Suggested labels: browser, renderer, enhancement

Suggested reviewers: None identified.

🌸 A right-click, a highlighted word,
Now a "Copy" waits, patiently heard.
From guest to preload to pane it flows,
selectionText travels where the clipboard goes.
A small clip, a rabbit's tidy chore. 🐇

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Description check ⚠️ Warning The description covers summary, changes, testing, and screenshots, but it omits the required AI Review Report, Security Audit, and Notes sections. Add the missing template sections with the AI review summary, security audit findings, and any platform-specific notes.
✅ Passed checks (4 passed)
Check name Status Explanation
Linked Issues check ✅ Passed The PR implements the requested ability to copy selected text from the built-in browser context menu, matching #7036.
Out of Scope Changes check ✅ Passed The changes stay focused on context-menu copy support and related plumbing, with no clear unrelated scope added.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Title check ✅ Passed The title matches the main user-visible change: adding a Copy option to the browser context menu when text is selected.

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/renderer/src/components/browser-pane/BrowserPane.tsx (1)

281-288: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Populate the remote context-menu fields

The remote path only sets linkUrl and pageUrl; selectionText and canCopy are never populated, so the state shape is incomplete and the Copy item can’t appear for remote panes. Wire those fields through the remote result or make them optional.

🧹 Nitpick comments (1)
src/renderer/src/components/browser-pane/BrowserPane.tsx (1)

2447-2461: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

canCopy is threaded through the whole stack but never used.

Both Copy menu items gate purely on contextMenu.selectionText truthiness; canCopy (sourced from Electron's editFlags.canCopy) is stored in state but never read. Consider using it (e.g. contextMenu.canCopy && contextMenu.selectionText) to match Electron's own signal for whether copy is actually permitted, or drop the field if it's not needed.

Also applies to: 4840-4854


ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: d770af7a-4a4e-4e92-8198-804adde5a29f

📥 Commits

Reviewing files that changed from the base of the PR and between 801effa and e2cf68c.

📒 Files selected for processing (5)
  • src/main/browser/browser-guest-ui.test.ts
  • src/main/browser/browser-guest-ui.ts
  • src/preload/index.ts
  • src/renderer/src/components/browser-pane/BrowserPane.tsx
  • src/shared/browser-guest-events.ts

@AmethystLiang AmethystLiang self-assigned this Jul 3, 2026
sudoeren and others added 2 commits July 3, 2026 13:36
…lyai#7036)

The built-in browser's custom context menu replaced the native Chromium
menu but lacked a Copy action for selected text. Users could not
right-click to copy text from web pages.

- Forward selectionText and canCopy from Electron's ContextMenuParams
  through the IPC bridge to the renderer
- Add a Copy button to both remote and local webview context menus
  when text is selected
- Update shared types and preload API types for the new fields
The context-menu Copy from stablyai#7159 did not compile and did not work on the
remote/paired-web browser:

- RemoteBrowserContextMenu required a selectionText that the remote setter
  never provided (type error). The remote eval now reads window.getSelection()
  so the SSH/paired-web browser gets the same Copy affordance as the local
  webview.
- Drop the unused canCopy field from the IPC event, preload, and renderer;
  the menu gates purely on a non-empty (trimmed) selection.
- Gate the item on selectionText.trim() and add a trailing separator so Copy
  groups like the existing link section.
- Register the new 'Copy' translate() key in all five locale catalogs
  (es/ja/ko/zh mirror the existing Copy translation); PR left it unsynced.
- Add a unit test asserting the native selection is forwarded to the renderer.

Co-authored-by: Orca <help@stably.ai>
@nwparker nwparker force-pushed the fix/browser-copy-clean branch from e2cf68c to 040396d Compare July 3, 2026 20:57
@nwparker

nwparker commented Jul 3, 2026

Copy link
Copy Markdown
Contributor

Took ownership of this PR (rebased onto latest main, fixed correctness bugs, verified end-to-end). Thanks @sudoeren for the original patch — the approach (read the native selection and write it straight to the clipboard) is the right one.

Description

Issue #7036 is "I can't copy to clipboard in the built-in browser; prevention should at least be opt-in." The real failure mode is page-level copy suppression: sites that call preventDefault() on the copy event block native Cmd/Ctrl+C, and Orca's custom browser context menu had no Copy affordance. Reading selectionText from the native context-menu params and writing it directly to the clipboard bypasses the page's copy handler entirely (we never dispatch a page-visible copy event), so it fixes exactly the reported case.

The original patch was on the right track but did not compile and did not work on the remote/SSH browser. My commit fixes that:

  • Remote/SSH + paired-web browser now actually works. RemoteBrowserContextMenu had been given a required selectionText, but the remote setter never provided it — a real tsc error (error TS2345 … missing selectionText), and the remote Copy item could never appear. The remote context-menu eval now reads window.getSelection() in the guest and threads it through readRemoteContextMenuResult and both setContextMenu sites, so the SSH/paired-web image-streamed browser gets the same Copy affordance as the local webview (per AGENTS.md's SSH requirement).
  • Dropped the dead canCopy field. It was forwarded through the IPC event, preload, and renderer but never read — the menu already gates purely on the selection. Removing it shrinks the webview↔host IPC payload.
  • Gate on selectionText.trim() so a whitespace-only selection doesn't show a pointless Copy, and added a trailing separator so Copy groups like the existing "Copy Link Address" section. The clipboard write uses the raw (untrimmed) selection, matching native copy.
  • Localization: the new translate() "Copy" key was never synced into the catalog (would fail verify:localization-catalog in CI). Added it to all five locales; es/ja/ko/zh mirror the existing Copy translation (Copiar / コピー / 복사 / 复制).
  • Added a unit test asserting the native selection is forwarded to the renderer.

Evidence

Reproduced and verified in a real Electron dev build (/electron) against a test page that blocks copy via document.addEventListener('copy', e => e.preventDefault(), true):

  • Baseline problem reproduced — native copy is blocked: after document.execCommand('copy') and a real Cmd+C keystroke, the OS clipboard stayed at the sentinel (pbpaste unchanged).
  • Fix works — select text → right-click → the menu shows ["Copy", "Back", "Forward", "Reload", "Open Page In Default Browser", "Copy Page URL", "Inspect Page"]; clicking Copy set the clipboard to the exact selection:
    • before: SENTINEL_BEFORE_1783112022
    • after Copy: ORCA_COPY_SENTINEL_2036 the quick brown fox jumps over the lazy dog
  • Gate correct — right-click with no selection shows ["Back", "Forward", "Reload", …] and no Copy item.

Checks:

  • pnpm test src/main/browser/browser-guest-ui.test.ts → 25 passed (incl. new forwards the native selection text so the renderer can offer Copy).
  • pnpm lint → clean (oxlint + switch-exhaustiveness + styled-scrollbars + localization-catalog + localization-coverage). Isolated tsc on BrowserPane.tsx no longer reports the selectionText/SetStateAction errors.
  • Two independent review passes (correctness + security) came back clean.

ELI5

Some websites turn off "copy" so you can't grab their text. Before, Orca's mini-browser had no Copy button in the right-click menu, so those sites left you stuck. Now, when you've highlighted text, right-click shows a Copy button that puts the highlighted text on your clipboard directly — it doesn't ask the webpage's permission, so it works even on pages that try to block copying. It also works over SSH/remote browsers now, and only shows up when you've actually selected something.

Trade-offs / regressions

  • No regressions. The menu only gains a Copy item when there's a real selection; every existing item is unchanged. The canCopy removal is safe (grepped: no remaining references; the only canCopy* hits are an unrelated editor feature).
  • Clipboard-IPC security analysis (webview↔host boundary): clean. window.api.ui.writeClipboardText is pre-existing, not new — this PR adds no clipboard IPC channel. The write goes through clipboard:writeText, which is (a) trusted-sender-gated (rejects webview senders via assertTrustedClipboardSender), (b) size-capped at 16 MB (CLIPBOARD_TEXT_WRITE_MAX_BYTES, no OOM), and (c) write-only (no clipboard read / exfiltration). The untrusted guest page can't reach window.api (contextIsolation, no guest preload) and can't forge the main→renderer context-menu event; the write requires two real user gestures (right-click, then click Copy on Orca chrome). The remote eval expression interpolates only JSON.stringify'd numeric coordinates, so it's injection-safe, and its result is parsed defensively. selectionText is intentionally not redacted (it's the user's own on-screen text, mirroring native copy; it never leaves the local clipboard).

@nwparker nwparker merged commit 2789a67 into stablyai:main Jul 3, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Bug] I cant copy to clipboard in the built in browser, prevention should at least be opt in

3 participants