Skip to content

feat: right click selection translation#1171

Merged
mengxi-ream merged 3 commits intomainfrom
feat/right-click-selection-translation
Mar 20, 2026
Merged

feat: right click selection translation#1171
mengxi-ream merged 3 commits intomainfrom
feat/right-click-selection-translation

Conversation

@ananaBMaster
Copy link
Copy Markdown
Collaborator

@ananaBMaster ananaBMaster commented Mar 20, 2026

Type of Changes

  • โœจ New feature (feat)
  • ๐Ÿ› Bug fix (fix)
  • ๐Ÿ“ Documentation change (docs)
  • ๐Ÿ’„ UI/style change (style)
  • โ™ป๏ธ Code refactoring (refactor)
  • โšก Performance improvement (perf)
  • โœ… Test related (test)
  • ๐Ÿ”ง Build or dependencies update (build)
  • ๐Ÿ”„ CI/CD related (ci)
  • ๐ŸŒ Internationalization (i18n)
  • ๐Ÿง  AI model related (ai)
  • ๐Ÿ”„ Revert a previous commit (revert)
  • ๐Ÿ“ฆ Other changes that do not modify src or test files (chore)

Description

  • add selection translation support from the browser right-click menu while preserving the existing page translation flow
  • add custom AI action entries to the selection context menu and reuse the same popover / execution flow as the selection toolbar
  • share selection snapshot recovery between translation and custom actions so right-click actions can reopen against the same captured selection
  • add tests around context-menu open flows, cross-node selection reuse, and custom action execution behavior

Related Issue

Closes #292

How Has This Been Tested?

  • Added unit tests
  • Verified through manual testing

Test Commands

SKIP_FREE_API=true pnpm test -- src/entrypoints/background/__tests__/context-menu.test.ts src/entrypoints/background/__tests__/analytics.test.ts src/entrypoints/selection.content/selection-toolbar/__tests__/request-rerun.test.tsx src/utils/__tests__/analytics.test.ts
pnpm type-check

Screenshots

  • selection translation can now open from the browser right-click menu
  • custom AI actions now appear directly in the extension's selection context menu

Checklist

  • I have tested these changes locally
  • I have updated the documentation accordingly if necessary
  • My code follows the code style of this project
  • My changes do not break existing functionality
  • If my code was generated by AI, I have proofread and improved it as necessary.

Additional Information

  • custom AI actions are intentionally flattened inside the extension context menu instead of adding another submenu layer
  • the local docs/pr-right-click-context-menu.md file is intentionally left uncommitted for copy/paste

Summary by cubic

Adds rightโ€‘click selection translation and inline custom AI actions to the browser context menu, while keeping the existing page translation flow. This lets users translate selected text or run custom actions directly from the context menu with consistent popover behavior.

  • New Features

    • Adds context menu items: โ€œTranslateโ€, โ€œTranslate โ€˜%sโ€™โ€, and inline entries for enabled custom actions.
    • Opens selection translation and custom actions from rightโ€‘click with proper tab/frame routing and popover anchoring.
    • Reuses the same captured selection across nodes; toolbar remains visible on rightโ€‘click for stable reuse.
    • Updates i18n strings for selection translate.
  • Refactors

    • Introduces a unified selection session and dedicated providers for translation and custom actions to share popover, state, and recovery logic.
    • Expands analytics to include action_id and action_name, and tracks the context_menu surface for these flows.
    • Adds message handlers openSelectionTranslationFromContextMenu and openSelectionCustomActionFromContextMenu; exports menu IDs and handles selection-specific clicks in background.
    • Adds tests for context menu creation, routing, selection reuse, and action execution.

Written for commit 18ef68f. Summary will update on new commits.

@changeset-bot
Copy link
Copy Markdown

changeset-bot bot commented Mar 20, 2026

๐Ÿฆ‹ Changeset detected

Latest commit: 18ef68f

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 1 package
Name Type
@read-frog/extension Patch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@dosubot dosubot bot added the size:XXL This PR changes 1000+ lines, ignoring generated files. label Mar 20, 2026
@github-actions github-actions bot added the feat label Mar 20, 2026
@dosubot dosubot bot added the app: browser extension Related to browser extension label Mar 20, 2026
@dosubot
Copy link
Copy Markdown

dosubot bot commented Mar 20, 2026

Documentation Updates

1 document(s) were updated by changes in this PR:

Internationalization and Localization
View Changes
@@ -2,9 +2,9 @@
 The project supports internationalization (i18n) and localization (l10n) for both the browser extension and website, enabling a scalable, maintainable multi-language UI.
 
 ## Localized UI Elements and Content
-All UI elements, configuration options, provider descriptions, provider groupings, sidebar group labels, translation mode selectors, error recovery interfaces, and blog content are localized using structured locale files. The browser extension uses YAML files (e.g., `en.yml`), while the website uses JSON files (e.g., `en.json`, `zh.json`).
-
-Translations are organized hierarchically by UI section, such as popup dialogs, options pages, side panels, provider descriptions, sidebar navigation, error recovery, and blog pages. For example, the extension's locale files include keys for popup labels (`autoLang`, `sourceLang`, `targetLang`), configuration options (`options.apiProviders.title`, `options.general.title`), translation mode selector labels (`options.translation.translationMode.title`, `options.translation.translationMode.description`, and `options.translation.translationMode.mode.<mode>` for each mode), sidebar group labels (`options.sidebar.settings`, `options.sidebar.product`), command palette UI (`options.commandPalette.placeholder`, `options.commandPalette.noResults`), error recovery UI (`errorRecovery.title`, `errorRecovery.description`), and provider descriptions under `options.apiProviders.description` for each supported provider.
+All UI elements, configuration options, provider descriptions, provider groupings, sidebar group labels, translation mode selectors, error recovery interfaces, context menu items, and blog content are localized using structured locale files. The browser extension uses YAML files (e.g., `en.yml`), while the website uses JSON files (e.g., `en.json`, `zh.json`).
+
+Translations are organized hierarchically by UI section, such as popup dialogs, options pages, side panels, provider descriptions, sidebar navigation, error recovery, context menus, and blog pages. For example, the extension's locale files include keys for popup labels (`autoLang`, `sourceLang`, `targetLang`), configuration options (`options.apiProviders.title`, `options.general.title`), translation mode selector labels (`options.translation.translationMode.title`, `options.translation.translationMode.description`, and `options.translation.translationMode.mode.<mode>` for each mode), sidebar group labels (`options.sidebar.settings`, `options.sidebar.product`), command palette UI (`options.commandPalette.placeholder`, `options.commandPalette.noResults`), error recovery UI (`errorRecovery.title`, `errorRecovery.description`), context menu items (`contextMenu.translate`, `contextMenu.translateSelection`), and provider descriptions under `options.apiProviders.description` for each supported provider.
 
 #### Model Selector UI Keys
 The model selector UI uses several i18n keys under `options.apiProviders.form.models`, including:
@@ -978,6 +978,44 @@
 - **PR #1127**: Added localized provider descriptions for three new AI providers (alibaba, moonshotai, huggingface) across all 8 supported languages
 - **PR #1126**: Added selection toolbar opacity configuration with 2 new keys under `options.floatingButtonAndToolbar.selectionToolbar.opacity` to control transparency of the toolbar and popovers
 - **PR #1152**: Added provider options recommendation keys with 5 new keys under `options.apiProviders.form.providerOptionsRecommendation*` to support manual preview and application of recommended provider options
+  - **PR #1171**: Added context menu selection translation and custom action support with 1 new key (`contextMenu.translateSelection`) and updated context menu configuration description
+
+### Context Menu Localization
+The browser context menu (right-click menu) supports page translation, selection translation, and custom AI actions. Context menu entries are localized under the `contextMenu` namespace:
+
+**Context Menu Keys:**
+- **`contextMenu.translate`**: Label for page translation ("Translate")
+- **`contextMenu.translateSelection`**: Label for selection translation with selected text placeholder ("Translate \"%s\"")
+
+When the context menu translate option is enabled in settings (`options.general.contextMenu.translate.title`), users will see different context menu entries depending on the context:
+- **"Translate"** option when right-clicking on the page (for full page translation)
+- **"Translate \"%s\""** option when right-clicking on selected text (for selection translation via the selection toolbar)
+- **Custom action entries** when right-clicking on selected text (if custom actions are configured and enabled)
+
+Custom AI actions appear as individual entries in the browser's context menu when text is selected. These entries use the user-defined action names and trigger the same popover UI and execution flow as the selection toolbar. The context menu integration allows users to access both built-in translation and custom AI actions directly from the right-click menu.
+
+**Context Menu Configuration:**
+The context menu translate feature is configured under `options.general.contextMenu.translate`:
+- **`title`**: "Enable Context Menu Translate"
+- **`description`**: "Add translate options to the browser right-click menu for quick page and selection translation"
+
+**Example:**
+
+```yaml
+contextMenu:
+  translate: Translate
+  translateSelection: Translate "%s"
+
+options:
+  general:
+    contextMenu:
+      title: Context Menu
+      translate:
+        title: Enable Context Menu Translate
+        description: Add translate options to the browser right-click menu for quick page and selection translation
+```
+
+The context menu entries are dynamically updated when configuration changes. Selection translation and custom actions share the same selection snapshot and recovery mechanism, allowing seamless interaction between the context menu and selection toolbar.
 
 ### Example: Adding a New Provider Group
 **English:**

How did I do? Any feedback?ย ย Join Discord

Copy link
Copy Markdown
Contributor

@cubic-dev-ai cubic-dev-ai bot left a comment

Choose a reason for hiding this comment

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

1 issue found across 29 files

Prompt for AI agents (unresolved issues)

Check if these issues are valid โ€” if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="src/components/ui/selection-popover/index.tsx">

<violation number="1" location="src/components/ui/selection-popover/index.tsx:117">
P2: `setAnchor` is callback-based now, but `contextValue` memo deps omit it, which can freeze a stale `setAnchor` in context.</violation>
</file>

Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.

onOpenChange?.(nextOpen)
}, [onOpenChange, open, openProp])

const setAnchor = React.useCallback((value: SelectionPopoverPosition | null) => {
Copy link
Copy Markdown
Contributor

@cubic-dev-ai cubic-dev-ai bot Mar 20, 2026

Choose a reason for hiding this comment

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

P2: setAnchor is callback-based now, but contextValue memo deps omit it, which can freeze a stale setAnchor in context.

Prompt for AI agents
Check if this issue is valid โ€” if so, understand the root cause and fix it. At src/components/ui/selection-popover/index.tsx, line 117:

<comment>`setAnchor` is callback-based now, but `contextValue` memo deps omit it, which can freeze a stale `setAnchor` in context.</comment>

<file context>
@@ -109,6 +114,14 @@ function SelectionPopoverRoot({
     onOpenChange?.(nextOpen)
   }, [onOpenChange, open, openProp])
 
+  const setAnchor = React.useCallback((value: SelectionPopoverPosition | null) => {
+    if (anchorProp === undefined) {
+      setUncontrolledAnchor(value)
</file context>
Fix with Cubic

Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector bot left a comment

Choose a reason for hiding this comment

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

๐Ÿ’ก Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 18ef68fafd

โ„น๏ธ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with ๐Ÿ‘.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment on lines +439 to +440
return onMessage("openSelectionTranslationFromContextMenu", () => {
openFromContextMenu()
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Consume context-menu payload when opening translation

The message handler drops message.data.selectionText and always calls openFromContextMenu() with no payload, so translation depends entirely on selectionSession cached from toolbar state. If the current selection was created without a mouseup-driven session refresh (for example keyboard selection, or selection cleared before click), the request resolves to stale/null session and either translates the wrong text or shows missingSelection even though the background click already provided the selected text. Please use the message payload as a fallback source when building the context-menu request.

Useful? React with ๐Ÿ‘ย / ๐Ÿ‘Ž.

Comment on lines +296 to +297
return onMessage("openSelectionCustomActionFromContextMenu", (message) => {
openContextMenuCustomAction(message.data.actionId)
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Consume context-menu payload when opening custom action

The custom-action handler only forwards actionId and ignores message.data.selectionText, which means execution still relies on selectionSession being previously captured by toolbar logic. In contexts where that session is absent or stale (e.g., non-mouse selection paths), users can get a false missingSelection error or run the action on old text even though the context-menu click delivered the selected text. The open path should incorporate the payload text as a recovery/fallback input.

Useful? React with ๐Ÿ‘ย / ๐Ÿ‘Ž.

@mengxi-ream mengxi-ream merged commit 05f49c1 into main Mar 20, 2026
10 checks passed
@mengxi-ream mengxi-ream deleted the feat/right-click-selection-translation branch March 20, 2026 17:14
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

app: browser extension Related to browser extension feat size:XXL This PR changes 1000+ lines, ignoring generated files.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[FEATURE] ๅธŒๆœ›่ƒฝๆทปๅŠ ๅณ้”ฎ่œๅ•ๅŠŸ่ƒฝ

2 participants