Skip to content

perf: lazy load runtimes and remove vocabulary insight button#1166

Merged
mengxi-ream merged 4 commits intomainfrom
perf/bundle-size
Mar 20, 2026
Merged

perf: lazy load runtimes and remove vocabulary insight button#1166
mengxi-ream merged 4 commits intomainfrom
perf/bundle-size

Conversation

@mengxi-ream
Copy link
Copy Markdown
Owner

@mengxi-ream mengxi-ream 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

  • lazy-load the host content runtime, subtitles runtime, and options routes so the initial bundles stay smaller
  • remove the deprecated Selection Toolbar AI button / Vocabulary Insight surface and keep Dictionary custom actions as the supported replacement
  • update the v065 to v066 migration so it removes deprecated vocabulary insight config while preserving the new page-translation shortcut migration

Related Issue

None.

How Has This Been Tested?

  • Added unit tests
  • Verified through manual testing

Commands run:

  • SKIP_FREE_API=true pnpm exec vitest run src/utils/config/__tests__/migration-scripts/v065-to-v066.test.ts src/utils/config/__tests__/migration-scripts/all-migrations.test.ts src/entrypoints/host.content/translation-control/__tests__/bind-translation-shortcut.test.ts src/utils/__tests__/page-translation-shortcut.test.ts src/components/__tests__/shortcut-key-recorder.test.tsx
  • pnpm exec eslint src/entrypoints/host.content/index.tsx src/entrypoints/host.content/runtime.ts src/utils/config/migration-scripts/v065-to-v066.ts src/utils/config/__tests__/migration-scripts/v065-to-v066.test.ts src/utils/config/__tests__/example/v066.ts
  • git push -u origin perf/bundle-size pre-push hooks (nx lint, nx type-check, nx test)

Screenshots

N/A

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

  • This branch now includes the latest main via merge commit d824d459.

Summary by cubic

Lazy-loads host and subtitles runtimes and options pages to cut initial bundle size, and removes the deprecated Selection Toolbar β€œVocabulary Insight” button in favor of Dictionary custom actions.

  • Refactors

    • Lazy-load host content runtime and subtitles runtime; options pages now use React lazy + Suspense.
    • Remove Selection Toolbar AI/Vocabulary Insight button, related tests, and i18n; keep Dictionary custom actions as the replacement.
    • Extract resolveModelId to utils/providers/model-id.ts and update imports.
  • Migration

    • v065β†’v066: remove selectionToolbar.features.vocabularyInsight while preserving the page-translation shortcut migration.
    • No user action needed; configs auto-migrate.

Written for commit d824d45. Summary will update on new commits.

@changeset-bot
Copy link
Copy Markdown

changeset-bot bot commented Mar 20, 2026

πŸ¦‹ Changeset detected

Latest commit: d824d45

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 perf label Mar 20, 2026
@dosubot dosubot bot added the app: browser extension Related to browser extension label Mar 20, 2026
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.

2 issues found across 41 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/entrypoints/host.content/index.tsx">

<violation number="1" location="src/entrypoints/host.content/index.tsx:31">
P1: Guard the lazy runtime bootstrap with error handling; otherwise a bootstrap failure can permanently leave the page marked as already injected.</violation>
</file>

<file name="src/entrypoints/host.content/runtime.ts">

<violation number="1" location="src/entrypoints/host.content/runtime.ts:69">
P2: Catch promise rejections when firing `handleUrlChange` from the event listener to avoid unhandled async errors.</violation>
</file>

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

Comment on lines +31 to +32
const { bootstrapHostContent } = await import("./runtime")
await bootstrapHostContent(ctx, initialConfig)
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.

P1: Guard the lazy runtime bootstrap with error handling; otherwise a bootstrap failure can permanently leave the page marked as already injected.

Prompt for AI agents
Check if this issue is valid β€” if so, understand the root cause and fix it. At src/entrypoints/host.content/index.tsx, line 31:

<comment>Guard the lazy runtime bootstrap with error handling; otherwise a bootstrap failure can permanently leave the page marked as already injected.</comment>

<file context>
@@ -39,89 +28,7 @@ export default defineContentScript({
-      // Check if auto-translation should be enabled for initial page load
-      void sendMessage("checkAndAskAutoPageTranslation", { url: window.location.href, detectedCodeOrUnd })
-    }
+    const { bootstrapHostContent } = await import("./runtime")
+    await bootstrapHostContent(ctx, initialConfig)
   },
</file context>
Suggested change
const { bootstrapHostContent } = await import("./runtime")
await bootstrapHostContent(ctx, initialConfig)
try {
const { bootstrapHostContent } = await import("./runtime")
await bootstrapHostContent(ctx, initialConfig)
}
catch (error) {
window.__READ_FROG_HOST_INJECTED__ = false
clearEffectiveSiteControlUrl()
throw error
}
Fix with Cubic


const handleExtensionUrlChange = (e: any) => {
const { from, to } = e.detail
void handleUrlChange(from, to)
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: Catch promise rejections when firing handleUrlChange from the event listener to avoid unhandled async errors.

Prompt for AI agents
Check if this issue is valid β€” if so, understand the root cause and fix it. At src/entrypoints/host.content/runtime.ts, line 69:

<comment>Catch promise rejections when firing `handleUrlChange` from the event listener to avoid unhandled async errors.</comment>

<file context>
@@ -0,0 +1,102 @@
+
+  const handleExtensionUrlChange = (e: any) => {
+    const { from, to } = e.detail
+    void handleUrlChange(from, to)
+  }
+  window.addEventListener("extension:URLChange", handleExtensionUrlChange)
</file context>
Fix with Cubic

@dosubot
Copy link
Copy Markdown

dosubot bot commented Mar 20, 2026

Documentation Updates

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

API Providers Configuration
View Changes
@@ -48,7 +48,7 @@
   // ...additional providers
 ]
 ```
-The schema is validated using Zod, ensuring each provider's configuration matches the expected structure and that both `id` and `name` are unique. Each feature (translate, selection toolbar, input translation, video subtitles, vocabulary insight) independently selects its own provider via the `providerId` field in the respective feature configuration. Custom AI actions in the selection toolbar also specify their own `providerId` to select the LLM provider for processing.
+The schema is validated using Zod, ensuring each provider's configuration matches the expected structure and that both `id` and `name` are unique. Each feature (translate, selection toolbar, input translation, video subtitles) independently selects its own provider via the `providerId` field in the respective feature configuration. Custom AI actions in the selection toolbar also specify their own `providerId` to select the LLM provider for processing.
 
 **Note:** As of schema version 54, TTS (text-to-speech) is no longer part of the feature provider system. TTS is configured separately with built-in Edge TTS as the only synthesis engine. See the TTS Configuration section below for details.
 
@@ -59,7 +59,7 @@
 - `providerOptions` is a JSON object for advanced, provider-specific settings (such as enabling special modes or features). Empty provider options (`{}`) explicitly means "no options" rather than falling back to any defaults. See the [Vercel AI SDK documentation](https://ai-sdk.dev/providers/ai-sdk-providers) or your provider's official docs for supported options.
 
 ### Referencing Providers
-Providers are referenced by their `id` (not `name`) in each feature's configuration. Each feature (translate, selection toolbar, input translation, video subtitles, vocabulary insight) independently selects its own provider, for example:
+Providers are referenced by their `id` (not `name`) in each feature's configuration. Each feature (translate, selection toolbar, input translation, video subtitles) independently selects its own provider, for example:
 ```ts
 translate: {
   providerId: 'openai-default',
@@ -68,7 +68,6 @@
 selectionToolbar: {
   features: {
     translate: { providerId: 'openai-default' },
-    vocabularyInsight: { providerId: 'openai-default' },
   },
   customActions: [
     {
@@ -110,7 +109,7 @@
 - **Delete providers with confirmation dialog.** When deleting a provider, a confirmation dialog warns: "Features using this provider will be automatically reassigned to another available provider." The system enforces that at least one LLM translation provider must remain. If the deleted provider is assigned to any features (including custom AI actions), the system automatically reassigns those features to the next available compatible enabled provider. If deletion would leave no compatible provider for a required feature, the action is blocked and a clear error message is shown.
 - Select a model per provider, with a "use custom model" toggle and model dropdown. Model lists are specific to each provider and are regularly updated to reflect the latest available models. For Volcengine, the default model is `doubao-seed-1-6-flash-250828`. For MiniMax, the default model is `MiniMax-M2`, with `MiniMax-M2-Stable` also available. For Alibaba Cloud, the default model is `qwen3.5-flash`. For Moonshot AI, the default model is `kimi-k2`. For Hugging Face, the default model is `Qwen/Qwen3-32B`.
 - **For custom (OpenAI-compatible) providers, a "Fetch available models" button is available next to the custom model input.** This button retrieves the list of models from the provider's `/models` endpoint (requires baseURL and API key), displays them in a popover, and allows the user to select a model, which is then auto-filled into the custom model field. This helps users quickly pick valid models and avoid configuration errors. The fetch flow includes improved error handling and localized messages for loading, errors, and retry actions.
-- **Assign features to providers via the "Feature Providers" collapsible section:** For each provider, a "Feature Providers" section allows users to toggle which features use this provider. The section displays only features compatible with the provider type (e.g., LLM providers show translate, videoSubtitles, selectionToolbar.translate, selectionToolbar.vocabularyInsight, inputTranslation, and Language Detection; translation-only providers show only translate and videoSubtitles). Toggles are disabled if the feature is already assigned to this provider (indicating the current assignment). Users can toggle ON to assign features to the provider but cannot toggle OFF directlyβ€”instead, they must assign the feature to another provider. Feature labels use i18n keys from `options.general.featureProviders.features.*`, with feature keys containing dots (like "selectionToolbar.translate") mapped to i18n-safe keys with underscores (like "selectionToolbar_translate") via the `FEATURE_KEY_I18N_MAP` constant.
+- **Assign features to providers via the "Feature Providers" collapsible section:** For each provider, a "Feature Providers" section allows users to toggle which features use this provider. The section displays only features compatible with the provider type (e.g., LLM providers show translate, videoSubtitles, selectionToolbar.translate, inputTranslation, and Language Detection; translation-only providers show only translate and videoSubtitles). Toggles are disabled if the feature is already assigned to this provider (indicating the current assignment). Users can toggle ON to assign features to the provider but cannot toggle OFF directlyβ€”instead, they must assign the feature to another provider. Feature labels use i18n keys from `options.general.featureProviders.features.*`, with feature keys containing dots (like "selectionToolbar.translate") mapped to i18n-safe keys with underscores (like "selectionToolbar_translate") via the `FEATURE_KEY_I18N_MAP` constant.
 - **Feature assignment badge:** Each provider card displays a badge showing the number of features assigned to it (e.g., "2 features") using the i18n key `options.apiProviders.badges.featureCount`. Hovering over the badge displays a tooltip listing all assigned features by their localized names. The badge count includes Language Detection assignments (when mode is LLM and the provider is selected). This badge replaces the previous "Translate" badge that indicated the default translation provider.
 - **Configure advanced options for LLM translation providers:** For providers that support LLM-based translation, an "Advanced Options" collapsible section is available in the provider config form. This section includes:
   - **Temperature**: A numeric input controlling the randomness/creativity of AI-generated translations. Lower values (e.g., 0) are more deterministic; higher values are more creative. Leave empty to use the provider's default.
@@ -129,7 +128,6 @@
 
 - **Page Translation**: Select the provider for translating web page content
 - **Selection Toolbar Translation**: Choose the provider for inline translation via the selection toolbar
-- **Selection Toolbar Vocabulary Insight**: Assign the provider for vocabulary definitions and insights via the selection toolbar *(Note: This feature is deprecated and will be replaced by Custom AI Action's Dictionary feature.)*
 - **Input Translation**: Select the provider for translating input fields
 - **Video Subtitles**: Choose the provider for translating video subtitles
 - **Custom AI Actions**: Select the provider for each user-defined custom AI action in the selection toolbar
@@ -407,7 +405,7 @@
 **v052 to v053 migration:**
 - Provider model configuration is unified from `provider.models.read` + `provider.models.translate` to a single `provider.model` field
 - The obsolete `read` config block is removed
-- Selection toolbar now includes per-feature provider configuration for translate and vocabulary insight features
+- Selection toolbar now includes per-feature provider configuration for translate feature
 - Input translation and video subtitles features now include their own `providerId` field
 
 **v053 to v054 migration:**
@@ -424,7 +422,6 @@
 **v055 to v056 migration:**
 - Adds `customFeatures` array to `selectionToolbar` configuration (default: empty array)
 - Custom features enable users to create their own AI-powered actions for selected text with configurable name, icon, prompt, output schema, and provider selection
-- If no vocabularyInsight provider is configured, a default "Dictionary" feature is added to provide backwards-compatible functionality
 
 **v059 to v060 migration:**
 - Renames `selectionToolbar.customFeatures` to `selectionToolbar.customActions`
@@ -446,6 +443,11 @@
   - Selection toolbar custom action prompts: `{{context}}` β†’ `{{paragraphs}}`, `{{targetLang}}` β†’ `{{targetLanguage}}`, `{{title}}` β†’ `{{webTitle}}`
 - Updates dictionary template wording from "context" to "paragraphs" (both English and Chinese prompts, field names, and descriptions)
 - Auto-migrates existing user configs (translate custom prompts, video subtitle custom prompts, selection toolbar custom actions)
+
+**v065 to v066 migration:**
+- Removes the deprecated `selectionToolbar.features.vocabularyInsight` configuration
+- The vocabulary insight feature has been fully removed and replaced by Custom AI Action's Dictionary feature
+- Converts `translate.page.shortcut` from string[] to portable hotkey string
 
 ---
 
@@ -471,7 +473,6 @@
 selectionToolbar: {
   features: {
     translate: { providerId: 'openai-default' },
-    vocabularyInsight: { providerId: 'openai-default' },
   },
   customActions: [
     {
@@ -542,9 +543,6 @@
 - **Define output schemas**: Add structured output fields via dedicated dialogs (not inline editing). Each field dialog includes inputs for name, type (string or number), description, and an optional "Enable speaking" toggle. Field descriptions help guide the AI in generating appropriate values. When speaking is enabled for a field, a Speak button appears next to the field value in the popover. Output schema fields can also be reordered via drag-and-drop within the action configuration form
 - **Preview and test**: Custom actions appear in the selection toolbar when text is selected, showing streaming AI responses
 
-**Vocabulary Insight deprecation notice:**
-The selection toolbar displays an amber deprecation notice for the Vocabulary Insight feature, indicating that it will be replaced by Custom AI Action's Dictionary feature. A link in the notice opens the Add Dictionary action dialog to help users migrate to the new Dictionary custom action.
-
 **Important notes:**
 - Custom actions require at least one enabled LLM provider that supports structured output. If no enabled LLM provider exists, the options page displays a warning: "Enable at least one LLM provider before creating custom AI actions."
 - The selected provider must be enabled and have a valid API key configured
@@ -555,7 +553,7 @@
 
 ### Provider Selection for Custom Actions
 
-Custom actions use the same provider selection system as built-in features (translate, vocabularyInsight). The `ProviderSelector` component accepts a `providers` array directly, allowing the configuration form to pass in a pre-filtered list of enabled LLM providers with structured output support:
+Custom actions use the same provider selection system as built-in features. The `ProviderSelector` component accepts a `providers` array directly, allowing the configuration form to pass in a pre-filtered list of enabled LLM providers with structured output support:
 
 ```tsx
 <ProviderSelector
@@ -584,11 +582,11 @@
   - Loading state during streaming
   - Error messages if the request fails (with improved AI SDK error message extraction)
 
-The custom action popover uses the same UI pattern as built-in features (translate, vocabularyInsight), with streaming responses and graceful error handling.
+The custom action popover uses the same UI pattern as built-in features, with streaming responses and graceful error handling.
 
 #### Selection Popover Component Architecture
 
-Selection toolbar features (translate, vocabulary insight, custom AI actions) use a shared `SelectionPopover` compound component with the following subcomponents:
+Selection toolbar features (translate, custom AI actions) use a shared `SelectionPopover` compound component with the following subcomponents:
 
 - **Root**: Manages popover state (open/closed, pin state, anchor position)
 - **Trigger**: Button that opens the popover and captures the anchor position
Configuration Schema and Migration
View Changes
@@ -213,18 +213,18 @@
 ```
 
 #### Example: v52 to v53 Migration (Unified Provider Model and Per-Feature Provider Selection)
-The migration from v52 to v53 implements major architectural changes to unify the provider model and introduce per-feature provider selection:
+The migration from v52 to v53 implements major architectural changes to unify the provider model and introduce per-feature provider selection. **Note:** The vocabulary insight feature introduced in this migration was later deprecated and removed in v065-to-v066.
 
 **Key changes:**
 1. **Unified provider model:** Each provider now has a single `model` field instead of separate `models.read` and `models.translate` fields.
 2. **Removal of read feature:** The entire `read` configuration block is removed, as the read functionality has been deprecated.
-3. **Per-feature provider selection:** New feature-specific provider configurations are added for `selectionToolbar.features.translate`, `selectionToolbar.features.vocabularyInsight`, `inputTranslation.providerId`, and `videoSubtitles.providerId`.
+3. **Per-feature provider selection:** New feature-specific provider configurations are added for `selectionToolbar.features.translate`, `selectionToolbar.features.vocabularyInsight` (deprecated in v066), `inputTranslation.providerId`, and `videoSubtitles.providerId`.
 
 The migration script:
 - Flattens provider model configuration from `provider.models.translate` to `provider.model`
 - Removes the obsolete `read` config block
-- Adds `selectionToolbar.features` with per-feature provider IDs (translate and vocabularyInsight)
-- Inherits the legacy `read.providerId` for `vocabularyInsight` when available, ensuring a smooth transition
+- Adds `selectionToolbar.features` with per-feature provider IDs (translate and vocabularyInsight - note: vocabularyInsight was later deprecated in v066)
+- Inherits the legacy `read.providerId` for `vocabularyInsight` when available (this feature was later deprecated and removed in v066)
 - Adds `providerId` fields to `inputTranslation` and `videoSubtitles` if not already present
 - Reorders providers to ensure Microsoft Translate appears before Google Translate
 
@@ -352,7 +352,7 @@
   - `prompt`: User prompt template with tokens (`{{selection}}`, `{{context}}`, `{{targetLang}}`, `{{title}}`)
   - `outputSchema`: JSON schema defining the structured output fields (array of `{id, name, type}` objects where type is `'string'` or `'number'`)
 - Custom actions support structured output rendering as key-value cards in the selection toolbar UI
-- The migration conditionally initializes `customActions`: if the vocabulary insight provider **is** configured, it adds a default "Dictionary" action with that provider; otherwise it initializes as an empty array
+- The migration conditionally initializes `customActions`: if the vocabulary insight provider **is** configured (deprecated in v066), it adds a default "Dictionary" action with that provider; otherwise it initializes as an empty array
 - **Order persistence:** The order of items in the `customActions` array is user-controlled and persisted. Users can reorder custom actions via drag-and-drop in the UI, and this order is saved to the configuration. Similarly, output schema fields within each custom action can be reordered via drag-and-drop, with the order persisted in the `outputSchema` array.
 
 The migration script:
@@ -757,11 +757,11 @@
 This migration simplifies language detection configuration by eliminating redundant per-feature settings and providing a single, centralized control that applies across all features using language detection.
 
 #### Example: v62 to v63 Migration (Individual Toggles for Built-in Selection Toolbar Features)
-The migration from v62 to v63 adds individual `enabled` toggles for each built-in selection toolbar feature (translate, speak, vocabulary insight), allowing users to control which features appear in the selection toolbar independently.
+The migration from v62 to v63 adds individual `enabled` toggles for each built-in selection toolbar feature (translate, speak, vocabulary insight), allowing users to control which features appear in the selection toolbar independently. **Note:** The vocabulary insight feature added in this migration was later deprecated and removed in v065-to-v066.
 
 **Key changes:**
 - Adds `enabled: true` field to `selectionToolbar.features.translate`
-- Adds `enabled: true` field to `selectionToolbar.features.vocabularyInsight`
+- Adds `enabled: true` field to `selectionToolbar.features.vocabularyInsight` (deprecated in v066)
 - Adds new `selectionToolbar.features.speak` object with `enabled: true`
 - All built-in features default to enabled for backward compatibility
 - When all features (built-in + custom actions) are disabled, the toolbar is not mounted on text selection
@@ -786,7 +786,7 @@
         speak: {
           enabled: true,
         },
-        vocabularyInsight: {
+        vocabularyInsight: { // Deprecated in v066
           ...oldFeatures.vocabularyInsight,
           enabled: true,
         },
@@ -909,10 +909,15 @@
 
 This migration enables users to adjust selection toolbar visibility based on their preferences, making the toolbar less intrusive when needed while maintaining full functionality.
 
-#### Example: v65 to v66 Migration (Page Translation Shortcut Format)
-The migration from v65 to v66 converts the `translate.page.shortcut` field from a string array format to a portable hotkey string format. This change standardizes keyboard shortcut representation using TanStack Hotkeys conventions.
-
-**Key changes:**
+#### Example: v65 to v66 Migration (Page Translation Shortcut Format and Vocabulary Insight Removal)
+The migration from v65 to v66 performs two changes:
+1. Converts the `translate.page.shortcut` field from a string array format to a portable hotkey string format
+2. Removes the deprecated `selectionToolbar.features.vocabularyInsight` configuration
+
+**Page Translation Shortcut Format:**
+This change standardizes keyboard shortcut representation using TanStack Hotkeys conventions.
+
+**Page Translation Shortcut key changes:**
 - Converts old array format (e.g., `["alt", "e"]`) to portable string format (e.g., `"Alt+E"`)
 - Normalizes modifier keys:
   - `ctrl`, `control`, `command`, and `meta` β†’ `Mod` (portable modifier that maps to Command on macOS, Control elsewhere)
@@ -922,6 +927,11 @@
 - Uses fallback shortcut `"Alt+E"` for invalid configurations
 - Empty strings are allowed (indicating no shortcut configured)
 
+**Vocabulary Insight Removal:**
+- Removes the `selectionToolbar.features.vocabularyInsight` configuration block
+- This feature has been deprecated and replaced by Dictionary custom actions
+- Users who previously used vocabulary insight can create equivalent functionality using custom actions with the "dictionary" template
+
 **Migration behavior:**
 - The migration script converts legacy array shortcuts to the new string format
 - Invalid shortcuts (missing modifiers, multiple keys, etc.) default to `"Alt+E"`
@@ -933,15 +943,31 @@
 ```ts
 // migration-scripts/v065-to-v066.ts
 export function migrate(oldConfig: any): any {
+  const migratedConfig: any = {
+    ...oldConfig,
+  }
+
+  // Remove vocabularyInsight feature from selection toolbar
+  const oldSelectionToolbar = oldConfig?.selectionToolbar
+  if (oldSelectionToolbar) {
+    migratedConfig.selectionToolbar = oldSelectionToolbar.features
+      ? {
+          ...oldSelectionToolbar,
+          features: removeVocabularyInsightFeature(oldSelectionToolbar.features),
+        }
+      : oldSelectionToolbar
+  }
+
+  // Migrate page translation shortcut
   const translate = oldConfig.translate
   const page = translate?.page
 
   if (!page) {
-    return oldConfig
-  }
-
-  return {
-    ...oldConfig,
+    return migratedConfig
+  }
+
+  return {
+    ...migratedConfig,
     translate: {
       ...translate,
       page: {
@@ -952,7 +978,17 @@
   }
 }
 
+function removeVocabularyInsightFeature(oldFeatures: any): any {
+  const { vocabularyInsight: _removedVocabularyInsight, ...features } = oldFeatures ?? {}
+  return features
+}
+
 function migrateLegacyShortcut(legacyShortcut: unknown): string {
+  if (typeof legacyShortcut === "string") {
+    const trimmedShortcut = legacyShortcut.trim()
+    return trimmedShortcut || "Alt+E"
+  }
+
   if (!Array.isArray(legacyShortcut) || legacyShortcut.length === 0) {
     return "Alt+E"
   }
@@ -1013,8 +1049,10 @@
 - The portable `Mod` key ensures cross-platform compatibility (Command on macOS, Control elsewhere)
 - Keyboard recorder preserves physical keys for macOS Option shortcuts (e.g., `Option+3` β†’ `Alt+3`)
 - UI displays shortcuts in platform-native format using `formatPageTranslationShortcut()`
-
-This migration ensures consistent shortcut storage format across the extension while maintaining backward compatibility by automatically converting existing array-based shortcuts to the new string format.
+- The vocabulary insight configuration is removed from `selectionToolbar.features`
+- Custom actions created with the "dictionary" template provide equivalent functionality to the deprecated vocabulary insight feature
+
+This migration ensures consistent shortcut storage format across the extension while maintaining backward compatibility by automatically converting existing array-based shortcuts to the new string format. It also removes the deprecated vocabulary insight feature configuration.
 
 ### Example Configuration Files
 Example configuration files for each schema version are maintained in the `example` directory (e.g., `v037.ts`, `v036.ts`, `v031.ts`, `v030.ts`). Each file contains a `testSeries` object with one or more scenarios for comprehensive migration testing.
@@ -1576,11 +1614,11 @@
 }
 ```
 
-#### v66 Example (Page Translation Shortcut Format)
+#### v66 Example (Page Translation Shortcut Format and Vocabulary Insight Removal)
 ```ts
 export const testSeries = {
   'complex-config-from-v020': {
-    description: 'Converts page translation shortcut from array to portable string format',
+    description: 'Converts page translation shortcut from array to portable string format and removes vocabularyInsight',
     config: {
       // ... other config fields
       translate: {
@@ -1602,6 +1640,22 @@
         },
         // ...
       },
+      selectionToolbar: {
+        enabled: true,
+        disabledSelectionToolbarPatterns: [],
+        opacity: 100,
+        customActions: [...],
+        features: {
+          translate: {
+            enabled: true,
+            providerId: 'openai-default',
+          },
+          speak: {
+            enabled: true,
+          },
+          // vocabularyInsight removed in v066
+        },
+      },
       // ...
     },
   },
@@ -1632,7 +1686,6 @@
 - **Feature provider assignments** for all major features:
   - Page Translation (`translate`)
   - Selection Toolbar Translation (`selectionToolbar.translate`)
-  - Selection Toolbar Vocabulary Insight (`selectionToolbar.vocabularyInsight`)
   - Input Translation (`inputTranslation`)
   - Video Subtitles (`videoSubtitles`)
   - Language Detection (`languageDetection`) - Optional provider assignment for AI-based language detection
Icon Management and Libraries
View Changes
@@ -74,21 +74,16 @@
   ```
   [Source](https://github.com/mengxi-ream/read-frog/blob/b44e9e917b7cd480ce88cb2a6bc34c034c990360/apps/extension/src/components/ui/checkbox.tsx)
 
-- The selection toolbar demonstrates the migration to direct React icon libraries. Trigger buttons use direct icon imports from `@tabler/icons-react` and `@remixicon/react`:
+- The selection toolbar demonstrates the migration to direct React icon libraries. Trigger buttons use direct icon imports from `@remixicon/react`:
   ```tsx
   import { RiTranslate } from '@remixicon/react'
-  import { IconZoomScan } from '@tabler/icons-react'
 
   <SelectionPopover.Trigger title="Translation">
     <RiTranslate className="size-4.5" />
   </SelectionPopover.Trigger>
-
-  <SelectionPopover.Trigger title="Vocabulary insight">
-    <IconZoomScan className="size-4.5" />
-  </SelectionPopover.Trigger>
-  ```
-
-- The selection toolbar uses a compound component pattern with `SelectionPopover` for consistent popover behavior across features (translation, vocabulary insight, custom features). The popover system includes several subcomponents that use Tabler Icons:
+  ```
+
+- The selection toolbar uses a compound component pattern with `SelectionPopover` for consistent popover behavior across features (translation, custom features). The popover system includes several subcomponents that use Tabler Icons:
   - `SelectionPopover.Header` uses `IconGripHorizontal` from `@tabler/icons-react` to indicate drag functionality
   - `SelectionPopover.Pin` and `SelectionPopover.Close` use `IconPin`, `IconPinnedFilled`, and `IconX` from `@tabler/icons-react` for pin/unpin and close actions
   - Icons are passed as Iconify string references to the `SelectionToolbarTitleContent` component, which renders them using the `Icon` component from `@iconify/react`. This provides a unified way to display feature icons in popover headers:
@@ -373,7 +368,7 @@
 
 **Migration to Direct Icon Libraries**
 
-As a further mitigation strategy, components are migrating from `@iconify/react` to direct icon libraries (`@tabler/icons-react`, `@remixicon/react`) to reduce CSP-related issues. Using bundled React components avoids the need for dynamically loading SVG data at runtime, which can be blocked by site CSP policies. For example, the translate button and AI button in the selection toolbar use `@tabler/icons-react` and `@remixicon/react` instead of Iconify's string-based icon references, with icons sized at `size-4.5` for consistent visual appearance.
+As a further mitigation strategy, components are migrating from `@iconify/react` to direct icon libraries (`@tabler/icons-react`, `@remixicon/react`) to reduce CSP-related issues. Using bundled React components avoids the need for dynamically loading SVG data at runtime, which can be blocked by site CSP policies. For example, the translate button in the selection toolbar uses `@remixicon/react` instead of Iconify's string-based icon references, with icons sized at `size-4.5` for consistent visual appearance.
 
 **Dependency Management**
 
Internationalization and Localization
View Changes
@@ -534,9 +534,8 @@
 - **`description`**: Description explaining the feature toggles functionality ("Choose which built-in features to show in the selection toolbar")
 - **`translate`**: Label for the translate feature toggle ("Translate")
 - **`speak`**: Label for the speak feature toggle ("Speak")
-- **`vocabularyInsight`**: Label for the vocabulary insight feature toggle ("Vocabulary Insight")
-
-Each built-in feature (translate, speak, vocabulary insight) can be individually toggled on/off in the options page. When all features (built-in + custom actions) are disabled, the toolbar is not mounted on text selection. The speak toggle is hidden on Firefox where TTS is not supported.
+
+Each built-in feature (translate, speak) can be individually toggled on/off in the options page. When all features (built-in + custom actions) are disabled, the toolbar is not mounted on text selection. The speak toggle is hidden on Firefox where TTS is not supported.
 
 **Example:**
 
@@ -549,7 +548,6 @@
         description: Choose which built-in features to show in the selection toolbar
         translate: Translate
         speak: Speak
-        vocabularyInsight: Vocabulary Insight
 ```
 
 These keys were added across all 8 supported locale files (en, ja, ko, ru, tr, vi, zh-CN, zh-TW) in PR #1115 to support granular control over which selection toolbar features appear to users.
@@ -561,7 +559,7 @@
 - **`title`**: Section title ("Opacity")
 - **`description`**: Description explaining the opacity setting ("Adjust the opacity of the selection toolbar and the expanded window opened from it")
 
-The opacity setting accepts values from 1% to 100%, with 100% being fully opaque (the default). This setting applies to both the selection toolbar and all popover content opened from it, including translation results, vocabulary insights, and custom AI actions.
+The opacity setting accepts values from 1% to 100%, with 100% being fully opaque (the default). This setting applies to both the selection toolbar and all popover content opened from it, including translation results and custom AI actions.
 
 **Example:**
 
@@ -611,7 +609,7 @@
 Inline error alerts replace toast notifications for selection toolbar actions, providing contextual error feedback within the popover UI. Errors are cleared automatically when a user successfully reruns the action or closes the popover.
 
 ### Selection Toolbar Popover Actions
-The selection toolbar uses a refactored `SelectionPopover` compound component to display translation results, vocabulary insights, and custom AI actions. Each popover includes a footer with common action buttons that are localized under the `action` namespace:
+The selection toolbar uses a refactored `SelectionPopover` compound component to display translation results and custom AI actions. Each popover includes a footer with common action buttons that are localized under the `action` namespace:
 
 **Action Button Keys:**
 - **`action.copy`**: Label for the copy button ("Copy")
@@ -620,9 +618,8 @@
 - **`action.speak`**: Label for the speak button ("Speak")
 - **`action.playing`**: Label shown while audio is playing ("Playing")
 - **`action.translation`**: Tooltip label for the translation button in the selection toolbar ("Translation")
-- **`action.vocabularyInsight`**: Tooltip label for the vocabulary insight button in the selection toolbar ("Vocabulary Insight")
-
-These action buttons appear in popover footers with tooltip support. The copy button displays a success state after copying text to the clipboard, while the speak button integrates with the TTS system and shows different states (speak, playing, loading). The `translation` and `vocabularyInsight` keys are used for toolbar button tooltips to provide accessible labels.
+
+These action buttons appear in popover footers with tooltip support. The copy button displays a success state after copying text to the clipboard, while the speak button integrates with the TTS system and shows different states (speak, playing, loading). The `translation` key is used for the toolbar button tooltip to provide an accessible label.
 
 **Example:**
 
@@ -634,7 +631,6 @@
   translation: Translation
   speak: Speak
   playing: Playing
-  vocabularyInsight: Vocabulary Insight
 ```
 
 ### Target Language Selector
@@ -810,7 +806,6 @@
 - `features`: A subsection containing localized labels for each feature:
   - `translate`: "Page Translation"
   - `selectionToolbar_translate`: "Selection Toolbar Translation"
-  - `selectionToolbar_vocabularyInsight`: "Selection Toolbar Vocabulary Insight"
   - `inputTranslation`: "Input Translation"
   - `videoSubtitles`: "Video Subtitles"
 
@@ -826,7 +821,6 @@
       features:
         translate: Page Translation
         selectionToolbar_translate: Selection Toolbar Translation
-        selectionToolbar_vocabularyInsight: Selection Toolbar Vocabulary Insight
         inputTranslation: Input Translation
         videoSubtitles: Video Subtitles
 ```
@@ -841,7 +835,6 @@
 - `translate` β†’ `translate`
 - `videoSubtitles` β†’ `videoSubtitles`
 - `selectionToolbar.translate` β†’ `selectionToolbar_translate`
-- `selectionToolbar.vocabularyInsight` β†’ `selectionToolbar_vocabularyInsight`
 - `inputTranslation` β†’ `inputTranslation`
 
 This mapping ensures that feature labels can be retrieved from locale files using underscore-based keys while maintaining dot notation in the application code.
@@ -975,11 +968,11 @@
 - **PR #1065**: Added theme mode configuration keys for popup and options page appearance settings across all locale files
 - **PR #1075**: Added translation hub card expansion/collapse controls with 4 new i18n keys
 - **PR #1105**: Refactored selection toolbar with `SelectionPopover` compound component, added inline target language selector, and introduced `action.*` keys for popover action buttons (copy, regenerate, speak)
-- **PR #1107**: Added thinking indicator with 4 new `thinking.*` keys, and selection toolbar tooltip keys (`action.translation`, `action.vocabularyInsight`) for improved accessibility
+- **PR #1107**: Added thinking indicator with 4 new `thinking.*` keys, and selection toolbar tooltip key (`action.translation`) for improved accessibility
 - **PR #1108**: Renamed "Custom AI Features" to "Custom AI Actions" across all localization keys (`customFeatures` β†’ `customActions`)
 - **PR #1109**: Added selection toolbar inline error handling with 6 new error keys under `options.floatingButtonAndToolbar.selectionToolbar.errors`
 - **PR #1111**: Added centralized language detection configuration with 9 new keys under `options.general.languageDetection`, added `customActions.form.fieldSpeaking` for TTS dictionary support, and added `languageDetection.llmFailed` error key; removed per-feature detection keys (`translate.page.enableLLMDetection`, `translate.page.enableSkipLanguagesLLMDetection`, `tts.detectLanguageMode`)
-- **PR #1115**: Added selection toolbar feature toggles with 5 new keys under `options.floatingButtonAndToolbar.selectionToolbar.featureToggles` to enable individual control over built-in features (translate, speak, vocabulary insight)
+- **PR #1115**: Added selection toolbar feature toggles with 4 new keys under `options.floatingButtonAndToolbar.selectionToolbar.featureToggles` to enable individual control over built-in features (translate, speak)
 - **PR #1117**: Added drag-and-drop reordering support for custom action cards in the settings sidebar and output schema fields within custom actions using the `SortableList` component
 - **PR #1121**: Updated improve writing template prompt with {{originLanguage}} detection, 4-point language policy (previously 3 points), and revised example format with language annotations; replaced Example B with Japanese original text demonstrating verb conjugation errors
 - **PR #1127**: Added localized provider descriptions for three new AI providers (alibaba, moonshotai, huggingface) across all 8 supported languages

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

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: d824d459b1

ℹ️ About Codex in GitHub

Your team has set up Codex to 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 πŸ‘.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +25 to +27
ctx.onInvalidated(() => {
window.__READ_FROG_SUBTITLES_INJECTED__ = false
})
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Prevent subtitle UI reinjection without teardown

If a YouTube tab survives an extension reload/update, this new onInvalidated handler clears window.__READ_FROG_SUBTITLES_INJECTED__, so the next injection will run the subtitles bootstrap again. The existing subtitles runtime has no teardown path: initYoutubeSubtitles() leaves its yt-navigate-finish listener installed and mountSubtitlesUI() appends a new shadow host without removing the old one. In that reinjection scenario, the same page will accumulate duplicate subtitle controls/overlays until the user refreshes the tab.

Useful? React with πŸ‘Β / πŸ‘Ž.

@mengxi-ream mengxi-ream merged commit eb150b7 into main Mar 20, 2026
10 checks passed
@mengxi-ream mengxi-ream deleted the perf/bundle-size branch March 20, 2026 02:58
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 perf size:XXL This PR changes 1000+ lines, ignoring generated files.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant