fix(web): Compose input no longer adds stray quotes#20350
Problem
In the web/desktop composer, Right Alt (Compose), ", then o can leave ö·" instead of just ö. Composing é can also leave stray quotes. The editor mistakes the replacement range of provisional composition text for a selection to surround with delimiters.
Change
Only surround a matching, nonempty TextSelection when composition is inactive. This lets the editor replace provisional text normally, including changes processed after compositionend, while preserving quote/bracket wrapping of explicitly selected text. Existing chip, styled-text, and literal-answer protections remain in place.
The handler is shared by plain/rich composer modes and web/desktop. Native mobile uses a different editor.
Scope and approval
Fixes #16859. This is the focused guard described in the maintainer response, with regression tests and native Compose recordings. Related prior fix: #12644.
Verification
Five new regression cases fail before the fix; all seven input cases pass after it. All 24 focused tests pass, along with web typecheck and targeted lint/format checks:
| 1 | vp test run \ |
| 2 | apps/web/src/components/ComposerPromptEditorTiptap.input.test.tsx \ |
| 3 | apps/web/src/components/ComposerPromptEditorTiptap.navigation.test.tsx \ |
| 4 | apps/web/src/components/ComposerPromptEditorTiptap.citationDraft.test.tsx \ |
| 5 | apps/web/src/composer-undo-grouping.test.ts |
| 6 | (cd apps/web && vp run typecheck) |
| 7 | vp lint apps/web/src/components/ComposerPromptEditorTiptap.tsx apps/web/src/components/ComposerPromptEditorTiptap.input.test.tsx |
| 8 | vp fmt --check apps/web/src/components/ComposerPromptEditorTiptap.tsx apps/web/src/components/ComposerPromptEditorTiptap.input.test.tsx |
Manually recorded on Linux/GNOME Wayland using US-International with dead keys disabled and Right Alt as Compose. The same sequences were typed on baseline cfafb4bb54 and fixed 5b77b5d7c1:
- Compose →
"→o, then Compose →'→e: stray characters before; cleanö éafter. - Select
wordand type": still produces"word". - Select
testand type(: still produces(test).
Both composer modes are covered by automated tests; the recordings do not show a mode switch. Native dead-key quote+Space was not manually tested. The tests replay composition replacements, including a final quote arriving after compositionend; they are not a native dead-key event capture. Wrapping with a dead quote key during composition now replaces the selection, as does typing a delimiter over a whole-document selection. Native Electron was not separately exercised.
Before
https://github.com/user-attachments/assets/9619e630-8705-45a6-ba74-2e28e54c8ba9
After
https://github.com/user-attachments/assets/244a31e0-a726-4422-ad8d-bfb2858ddcf9
Implemented with GPT-6 in Codex. Reviewed with Claude Opus 5.5 at high effort in Claude Code.
Checks
Added 1 commit
- fix(web): preserve Compose and dead-key input5b77b5d
Approvability
Verdict: Approved at 5b77b5d
Macroscope's review found this PR approvable — This is a small, self-contained editor bug fix that preserves normal quote wrapping while preventing composition updates from adding stray surrounding characters. The accompanying tests cover the affected composition and selection cases, with no product-default or static-analysis configuration changes.
You can add or adjust custom eligibility rules. Learn more.
No actionable comments were generated in the recent review. 🎉
ℹ️ Recent review info
⚙️ Run configuration
- Configuration used: Path: .coderabbit.config.ts
- Review profile: CHILL
- Plan: Advanced
- Run ID:
f32741e4-a4db-46ee-b77d-b318b6a4673f
📥 Commits
Reviewing files that changed from the base of the PR and between e58a4857130a93cc58b45dd6ebee14cf08df04ec and 5b77b5d7c14b667e567d61710c093e0c4f1abb03.
📒 Files selected for processing: 2
apps/web/src/components/ComposerPromptEditorTiptap.input.test.tsxapps/web/src/components/ComposerPromptEditorTiptap.tsx
Included review availability: This review used your included allowance. Your plan provides up to 10 included reviews per hour; 9 remain after this review.
📝 Walkthrough
The composer now skips quote wrapping during composition. It wraps selected text only when the editor selection matches the text input replacement range. New tests cover composition and text replacement in both rich-text modes.
Changes: Composer text input
Layer / File(s) Summary Selection-aware quote wrapping
apps/web/src/components/ComposerPromptEditorTiptap.tsx,apps/web/src/components/ComposerPromptEditorTiptap.input.test.tsxThe input handler skips wrapping during composition and wraps text only when the non-empty editor selection matches the replacement range. Tests cover composed characters, replacement over and outside a selection, and quote wrapping in both rich-text modes. Priority: ➖ Normal
Estimated code review effort: 2 (Simple) | ~10 minutes
Change: Bug fix · Severity of issue fixed: Medium
Suggested reviewers:
stienswout
Merge Risk: ⚪ Minimal · up to 5b77b
The composer no longer inserts stray quotes during composition, and wrapping of explicitly selected text is preserved. No merge-blocking risk was found.
Pre-merge checks: Passed 4
Check name Status Explanation Title check Passed The title clearly and concisely describes the main fix: stray quotes caused by Compose input in the web composer. Description check Passed The description includes all required sections. It explains the problem, implementation, scope approval, focused verification, limitations, screenshots, and the tools used. Linked Issues check Passed The change addresses active issue #16859.handleTextInputnow skips delimiter wrapping whileview.composingis true and requires a nonemptyTextSelectionthat exactly matches the replacement ran…Out of Scope Changes check Passed The pull request changes only the shared composer input handler and adds focused regression tests for composition and selection wrapping. These changes directly support #16859. No unrelated product be…Full details: Linked Issues check
Explanation
The change addresses active issue
#16859.handleTextInputnow skips delimiter wrapping whileview.composingis true and requires a nonemptyTextSelectionthat exactly matches the replacement range. This prevents provisional dead-key or Compose text from being wrapped. The shared handler covers the web and desktop composer. Tests cover plain and rich text composition, composed quotes, composition over selected text, and explicit quote wrapping. Closed issue#12644supplies historical context only.
Full details: Out of Scope Changes check
Explanation
The pull request changes only the shared composer input handler and adds focused regression tests for composition and selection wrapping. These changes directly support
#16859. No unrelated product behavior or files are included.
✨ Finishing Touches
🧪 Generate unit tests (beta)
- Create a new PR
- Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts
Comment @coderabbitai help to get the list of available commands.