You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
[Bug]: Dead-key apostrophe inserts extra quotes in the composer #16859
Use a keyboard layout where the apostrophe key is a dead key for accents.
Type s in the composer with the caret at the end and no selected text.
Press the single-quote key once, then Space to commit a literal apostrophe.
Type il.
Expected behavior
The composer contains s' after step 3 and s'il after step 4. Accented letters should continue to compose normally.
Actual behavior
The composer contains s'´' after step 3, then s'il' after step 4. Accented letters work normally; committing an apostrophe is the broken case.
Impact
Frequent incorrect text when writing French with a dead-key keyboard layout.
Version and environment
Linux x64 desktop, 0.0.46-nightly.20261007.2774, also present in main at 611132c171f3a821bd2e32f22261135cef6330ac.
Investigation
The composer's handleTextInput treats any nonempty replacement range as selected text to surround with delimiters. ProseMirror can report a replacement range covering a provisional dead-key accent while the current editor selection is empty. Surrounding that accent with ' produces exactly s'´', then selects the accent so subsequent typing leaves a trailing apostrophe.
A focused test mounting the actual controlled ComposerPromptEditorTiptap in jsdom reproduces s'´' in both plain and rich-text modes when replaying this replacement. This is a replay of the editor's text-input boundary, not a native Linux keyboard-event capture.
Existing issues and PRs were searched for a duplicate. This report is specifically about committing a literal apostrophe, rather than accent composition or Markdown grave accents.
Thanks for the clear report. This is a confirmed bug on current main.
Root cause handleTextInput in apps/web/src/components/ComposerPromptEditorTiptap.tsx (~L1020–1045) wraps text in a delimiter pair whenever the typed character is in SURROUND_CLOSE (L178, which includes ', `, ") and the replacement range is non-empty (if (!closer || from === to) return false;, L1023). It never checks view.composing or view.state.selection. With a dead-key layout (e.g. US-International), ProseMirror passes a range covering the provisional accent even though the selection is empty, so the accent gets "wrapped" ('´') instead of being replaced. That's what produces s'´' and then s'il'.
Fix
Only surround when the user actually has a selection: bail out early if view.composing is true, or if view.state.selection is empty, isn't a TextSelection, or doesn't match from/to. Otherwise return false so native composition can finish.
Prior work #12644 (@SpizzyCoder) added exactly this guard but was closed unmerged because it had no verification recording, which CONTRIBUTING.md requires. As @adolago noted there, this has affected US-International users since v0.0.43 (#12160). A new PR with this guard is welcome. Please include a before/after recording that shows quote + Space, quote + vowel, and wrapping an explicit selection on a dead-key layout. Related: #3142 (dead-key backtick for inline code, still open).
Activity
juliusmarminge commentedon Oct 7, 2026
Note
Grok responding on behalf of Julius.
Thanks for the clear report. This is a confirmed bug on current
main.Root cause
handleTextInputinapps/web/src/components/ComposerPromptEditorTiptap.tsx(~L1020–1045) wraps text in a delimiter pair whenever the typed character is inSURROUND_CLOSE(L178, which includes',`,") and the replacement range is non-empty (if (!closer || from === to) return false;, L1023). It never checksview.composingorview.state.selection. With a dead-key layout (e.g. US-International), ProseMirror passes a range covering the provisional accent even though the selection is empty, so the accent gets "wrapped" ('´') instead of being replaced. That's what producess'´'and thens'il'.Fix
Only surround when the user actually has a selection: bail out early if
view.composingis true, or ifview.state.selectionis empty, isn't aTextSelection, or doesn't matchfrom/to. Otherwise returnfalseso native composition can finish.Prior work
#12644 (@SpizzyCoder) added exactly this guard but was closed unmerged because it had no verification recording, which CONTRIBUTING.md requires. As @adolago noted there, this has affected US-International users since v0.0.43 (#12160). A new PR with this guard is welcome. Please include a before/after recording that shows quote + Space, quote + vowel, and wrapping an explicit selection on a dead-key layout. Related: #3142 (dead-key backtick for inline code, still open).