Add replace-with-answer button to selection tools
SE
1d ago+859+859+012345678901234567890123456789-18-18-01234567890123456789
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.
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 clonedRangeinside acontenteditableregion. 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 asCopyButton). - 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/textareaselections viawindow.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
contenteditablethe 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 aninputevent so pages with controlled inputs (React and similar) observe the change.
Containment
- New util
src/utils/selection-replace.mjs+ new componentsrc/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 SelectionandReplace Failedtosrc/_locales/en/main.jsonand 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'sreplaceCapturedSelectionimport and test stub after master switched the floating toolbar toDraggableCore; the earlier conflict insrc/content-script/index.jsxroutes master'screationVersionstale-toolbar guard and this PR's captured selection throughcreateSelectionTools. npm test: 1138 tests pass, including 30 unit tests forselection-replace.mjsand a selection-toolbar lifecycle test for the Firefox keydown case.npm run lint,npm run pretty:checkandnpm run build: clean.- Manual testing in Firefox 157 with a signed build of this branch: replace in textarea, text input and
contenteditable(including undo andinputevent 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.