Skip to content

Agentic UI: add a native text context menu - #4388

Open
shaunandrews wants to merge 3 commits into
trunkfrom
add-text-context-menu
Open

Agentic UI: add a native text context menu#4388
shaunandrews wants to merge 3 commits into
trunkfrom
add-text-context-menu

Conversation

@shaunandrews

@shaunandrews shaunandrews commented Jul 29, 2026

Copy link
Copy Markdown
Contributor

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

image

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:

macOS Windows / Linux
Look Up "…" ✓ with a selection
Copy ✓ with a selection ✓ with a selection
Copy All ✓ on a message ✓ on a message
Paste ✓ in a text field ✓ in a text field

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 restartrs / hot reload won't pick it up.

  1. Open the Agentic UI and send a message so you have a reply to work with.
  2. Select a few words in a reply, then right-click the selection.
    • macOS: Look Up "…", a divider, Copy, Copy All.
    • Windows/Linux: Copy, Copy All.
  3. Choose Look Up (macOS) — the system Dictionary panel should open on that word.
  4. Choose Copy All and paste somewhere — you should get the whole message, not just what was selected.
  5. Right-click a reply with nothing selected — only Copy All.
  6. Right-click your own prompt bubbleCopy All works there too.
  7. Right-click the composer with something on the clipboard — Paste appears. Clear the clipboard and it doesn't.
  8. Right-click empty canvas with nothing selected — no menu opens at all.
  9. Select a long passage and right-click — the Look Up label should collapse the whitespace and truncate with an ellipsis rather than stretching across the screen.

Unit tests: npm test -- apps/studio/src/tests/text-context-menu.test.ts

Pre-merge Checklist

  • Have you checked for TypeScript, React or other console errors?

Notes for reviewers:

  • apps/studio and apps/ui typecheck clean; eslint clean; 9 new unit tests plus the full apps/ui suite (66 files, 422 tests) pass.
  • npm run typecheck currently fails in apps/cli on @earendil-works/pi-ai — that's pre-existing on trunk (stale node_modules after the recent packages/common bump), confirmed by stashing this branch's changes. npm install clears it.
  • This touches conversation/index.tsx, which Agentic UI: fix copy button space in chat #4334 also changes. Expect a one-line conflict in AssistantText for whichever merges second.

…ok up

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

Copilot AI 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.

Pull request overview

Adds a native, OS-level text context menu for the Agentic UI in Electron by routing right-click context from the renderer to the host (main process) and building an Electron Menu template that conditionally includes Look Up (macOS only), Copy, Copy All, and Paste.

Changes:

  • Add a renderer hook (useTextContextMenu) that captures contextmenu events, detects selection/editability/message text, and calls an optional connector method.
  • Add a main-process menu builder (buildTextContextMenuTemplate) + IPC handler (showTextContextMenu) to pop a native menu and perform actions (Look Up, Copy All, Paste).
  • Wire the IPC connector + preload + handler registration, and add unit tests for both renderer and main-process menu-template behavior.

Reviewed changes

Copilot reviewed 11 out of 11 changed files in this pull request and generated 1 comment.

Show a summary per file
File Description
apps/ui/src/ui-classic/components/session-view/conversation/index.tsx Adds data-message-text attributes to message containers so “Copy All” can target the full message.
apps/ui/src/ui-classic/app.tsx Installs useTextContextMenu() at app root so right-clicks are handled globally.
apps/ui/src/hooks/use-text-context-menu.ts New hook that gathers selection/editability/message context and requests the host-native context menu.
apps/ui/src/hooks/use-text-context-menu.test.tsx Tests hook behavior: message targeting, ignoring unrelated UI, selection intersection, editable detection.
apps/ui/src/data/core/types.ts Extends Connector with optional showTextContextMenu API (absent in browser builds).
apps/ui/src/data/core/connectors/ipc/index.ts Implements showTextContextMenu via ipcApi.showTextContextMenu(...).
apps/studio/src/text-context-menu.ts Implements menu-template construction and the Electron-side popup handler (Look Up, Copy, Copy All, Paste).
apps/studio/src/tests/text-context-menu.test.ts Tests menu-template composition, platform gating, truncation, and section/separator logic.
apps/studio/src/preload.ts Exposes ipcApi.showTextContextMenu using ipcRenderer.send (void handler).
apps/studio/src/ipc-handlers.ts Exports showTextContextMenu from the main-process handler module for IPC registration.
apps/studio/src/constants.ts Registers showTextContextMenu as an IPC void handler.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +439 to +443
function AssistantText( { text, copyText }: { text: string; copyText?: string } ) {
return (
<div className={ styles.assistantTurn }>
// The attribute carries the whole message so a right-click anywhere
// inside it can offer Copy All, not just the selection.
<div className={ styles.assistantTurn } { ...{ [ MESSAGE_TEXT_ATTRIBUTE ]: copyText ?? text } }>
@wpmobilebot

wpmobilebot commented Jul 29, 2026

Copy link
Copy Markdown
Collaborator

📊 Performance Test Results

Comparing 7d78ab7 vs trunk

app-size

Metric trunk 7d78ab7 Diff Change
App Size (Mac) 1378.85 MB 1378.85 MB +0.00 MB ⚪ 0.0%

site-editor

Metric trunk 7d78ab7 Diff Change
load 750 ms 1121 ms +371 ms 🔴 49.5%

site-startup

Metric trunk 7d78ab7 Diff Change
siteCreation 6553 ms 7013 ms +460 ms 🔴 7.0%
siteStartup 2386 ms 2381 ms 5 ms ⚪ 0.0%

Results are median values from multiple test runs.

Legend: 🟢 Improvement (faster) | 🔴 Regression (slower) | ⚪ No change (<50ms diff)

@bcotrim bcotrim 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.

Looks good!

Some comments:

  1. On the composer only "Paste" is available, should we also offer "Copy"?
Image
  1. "Copy All" is a translated string. Should the other options also be translated?
    menu.ts:364-366 already does { label: __( 'Copy' ), role: 'copy' } — macOS
    localizes roles natively, so this would only show on Windows/Linux.

  2. conversation/index.tsx:473 uses copyText ?? text, but copyText is 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

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.

4 participants