Agentic UI: add a native text context menu - #4388
Conversation
…ok up Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
📊 Performance Test ResultsComparing 7fdcf3e vs trunk app-size
site-editor
site-startup
Results are median values from multiple test runs. Legend: 🟢 Improvement (faster) | 🔴 Regression (slower) | ⚪ No change (<50ms diff) |
bcotrim
left a comment
There was a problem hiding this comment.
Looks good!
Some comments:
- On the composer only "Paste" is available, should we also offer "Copy"?
-
"Copy All" is a translated string. Should the other options also be translated?
menu.ts:364-366already does{ label: __( 'Copy' ), role: 'copy' }— macOS
localizes roles natively, so this would only show on Windows/Linux. -
conversation/index.tsx:473usescopyText ?? text, butcopyTextis only set on
the last text block (line 281). On a reply split by tool calls, right-clicking an
earlier paragraph makes "Copy All" copy just that fragment — the comment on line 262 says it should yield the whole message.
Suggestion: what do you think about an option to quote the selected text into the
composer? Could be useful for referencing part of the agent's reply. Could be done in a follow-up PR if you think it's a good idea
Related issues
How AI was used in this PR
AI wrote the implementation and tests from a scope I set interactively — we went back and forth on what the menu should contain and how the message text should reach the main process. I reviewed the approach and tested it in the app.
Proposed Changes
Right-click did nothing anywhere in Studio. No Copy, no Look Up, no Paste in the composer — the app just felt inert in a way desktop apps aren't.
That's because Electron ships no default context menu. The menu Chrome shows is built by Chrome's own browser layer, which isn't part of the content layer Electron embeds, so every Electron app has to declare its own. Studio never did.
This adds one. It's a real native menu on every platform — NSMenu on macOS, a Win32 popup on Windows, GTK on Linux — with the same appearance, keyboard handling and accessibility the OS gives any other app. Only the item list is ours.
What you get:
Look Up is the one that only makes sense on a Mac — it opens the system Dictionary panel for the selected word. Windows and Linux expose no system dictionary to apps; their native text menus really are just the edit commands, so gating it on platform gives each OS what it would natively show rather than a lowest-common-denominator menu.
Copy All copies the whole message rather than just the highlighted part, which is usually what you actually want in a transcript. It works on your own prompts as well as the agent's replies — it would be strange for right-click to work on one bubble and not the other.
Every item is conditional on whether it would actually do something, so nothing appears greyed out: no dead Paste in the read-only transcript, no Copy without a selection. If nothing applies, no menu opens at all rather than an empty one.
Deliberately not included: undo, redo, cut and delete. The transcript is read-only and the composer is a plain field where they'd be noise.
The browser builds are untouched — they already have a real context menu, so the connector method is simply absent there and the right-click falls through to Chrome's own.
Testing Instructions
This is a main-process change, so it needs a full app restart —
rs/ hot reload won't pick it up.Look Up "…", a divider,Copy,Copy All.Copy,Copy All.Copy All.Copy Allworks there too.Pasteappears. Clear the clipboard and it doesn't.Unit tests:
npm test -- apps/studio/src/tests/text-context-menu.test.tsPre-merge Checklist
Notes for reviewers:
apps/studioandapps/uitypecheck clean; eslint clean; 9 new unit tests plus the fullapps/uisuite (66 files, 422 tests) pass.npm run typecheckcurrently fails inapps/clion@earendil-works/pi-ai— that's pre-existing on trunk (stalenode_modulesafter the recentpackages/commonbump), confirmed by stashing this branch's changes.npm installclears it.conversation/index.tsx, which Agentic UI: fix copy button space in chat #4334 also changes. Expect a one-line conflict inAssistantTextfor whichever merges second.