Add replace-with-answer button to selection tools

SE
1d ago
+859-18

Summary

Adds a Replace button to answers in the selection-tools floating toolbar, so the AI response (e.g. a polished or translated version) can be written back over the originally selected text in one click — no more manual copy & paste when polishing text you are writing in an input, textarea, or rich-text editor.

grafik

How it works

  • When the selection toolbar is triggered — by mouse, touch, or a selection tool from the right-click menu — the content script captures the editable target of the selection (captureEditableSelection): a text field (input/textarea) with its selection offsets, or a cloned Range inside a contenteditable region. This happens before the selection is lost by interacting with the toolbar.
  • Completed answers then show a Replace button next to Copy. Clicking it calls replaceCapturedSelection, which writes the answer text over the original selection. A ✓/✗ icon gives success/failure feedback (same pattern as CopyButton).
  • The button only appears when the selection was inside an editable element. Static page content is intentionally not replaceable — replacing arbitrary page DOM is destructive and fragile, and the real use case is text you are writing. Page-level menu tools (which work on the whole page, not the selection) don't offer it either.
  • Firefox does not expose input/textarea selections via window.getSelection(), so the selection-tools flows (mouseup, touchend, and the keydown cleanup) fall back to the captured text-field selection there.

Safety / security considerations

  • No HTML injection: the answer is inserted as plain text only (.value / text nodes / execCommand('insertText')), never parsed as HTML.
  • Stale-capture guard: before replacing (and again after focusing the target, in case page focus handlers changed it), the capture is re-verified — for text fields the whole field value must be unchanged since capture, for contenteditable the range must still contain the original text. Otherwise nothing is overwritten and the button shows a failure state.
  • Sensitive and non-editable fields excluded: password inputs are never captured; read-only and disabled fields are rejected at capture time and re-checked before replacing. The debug log only prints the capture kind, never the selected text.
  • Undo works: document.execCommand('insertText') is preferred so the native undo history keeps working (its result is only trusted when the selected text was actually replaced); a fallback sets the value via the prototype setter and dispatches an input event so pages with controlled inputs (React and similar) observe the change.

Containment

  • New util src/utils/selection-replace.mjs + new component src/components/ReplaceButton/; everything else is optional-prop threading (capturedSelection → FloatingToolbar, onReplaceContent → ConversationCard, onReplace → ConversationItem).
  • Other users of ConversationCard/FloatingToolbar (popup, independent panel, site adapters, menu tools) are unaffected — the capture is only wired up in the selection-tools paths (mouse, touch, and right-click selection tools).

i18n

  • Added keys Replace Selection and Replace Failed to src/_locales/en/main.json and propagated translations to all other locales (de, es, fr, id, it, ja, ko, pt, ru, tr, zh-hans, zh-hant). Native speakers, please feel free to correct.

Validation

  • Up to date with master. The latest merge only had to re-add this PR's replaceCapturedSelection import and test stub after master switched the floating toolbar to DraggableCore; the earlier conflict in src/content-script/index.jsx routes master's creationVersion stale-toolbar guard and this PR's captured selection through createSelectionTools.
  • npm test: 1138 tests pass, including 30 unit tests for selection-replace.mjs and a selection-toolbar lifecycle test for the Firefox keydown case.
  • npm run lint, npm run pretty:check and npm run build: clean.
  • Manual testing in Firefox 157 with a signed build of this branch: replace in textarea, text input and contenteditable (including undo and input event propagation), no Replace button for read-only, password and static text, failure state when the field changed after capture, the right-click selection-tool path, and the toolbar staying open on Shift+Arrow / Ctrl+C inside a text field.
  • Not manually tested in Chromium (covered by the unit tests only).

🤖 Generated with Claude Code

Summary by CodeRabbit

  • New Features
    • Added an option to replace selected text directly from completed answers in the floating toolbar, including selections in text fields and editable content.
    • Replacement restores the cursor after the inserted text and reports whether the action succeeded or failed.
    • Added localized labels and feedback for the replacement action across supported languages.