Place the caret before an inline decorator when Gecko drops the selection - #1204
Conversation
425a956 to
5b51fb1
Compare
There was a problem hiding this comment.
Pull request overview
Fixes a Firefox/Gecko-specific edge case where clicking immediately before a leading inline decorator (mention) leaves Lexical with no selection, causing subsequent typing to be dropped. The change detects the null-selection + inline-decorator-from-point condition and explicitly places the caret at the decorator’s index in its parent, matching the element-point behavior seen in Blink.
Tip
If you aren't ready for review, convert to a draft PR.
Click "Convert to draft" or run gh pr ready --undo.
Click "Ready for review" or run gh pr ready to reengage.
Changes:
- Extend
CLICK_COMMANDhandling to recover from Gecko’s “null selection” by placing the caret before an inline decorator found at the click point. - Add a Playwright regression test covering typing before a leading mention on Firefox, plus a guard test for clicking after a trailing mention.
Reviewed changes
Copilot reviewed 2 out of 2 changed files in this pull request and generated no comments.
| File | Description |
|---|---|
src/editor/selection.js |
Adds a null-selection recovery path for Gecko clicks that resolve inside inline decorator DOM, placing the caret before the decorator. |
test/browser/tests/prompts/typing_before_mention.test.js |
Adds browser-level coverage to reproduce and prevent regressions for typing adjacent to mentions (especially Firefox). |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
…tion Clicking just before a mention that starts a paragraph left the editor with no selection in Firefox, so anything typed afterwards was silently dropped. Gecko resolves such a click to a text node inside the decorator's contenteditable="false" subtree. That node is not part of the editor state, so no selection point maps to it and Lexical ends up with none. Blink resolves the same click to the paragraph itself at offset 0. Place the caret before the decorator ourselves when a click leaves us without a selection, which matches what Blink does natively.
5b51fb1 to
88df0d5
Compare
Fixes #1149.
The bug
With a mention at the start of a paragraph, clicking just before it and typing did nothing in Firefox. The keystrokes were silently dropped.
Why
The click leaves the editor with no selection at all, so there is nothing for typing to act on.
document.caretPositionFromPoint()at the same coordinates explains why:P @0— the paragraph itselfRangeSelection, element point, offset 0#text:"Zacharias" @0— inside the mentionnull(rangeCount: 0)Gecko descends into the decorator's
contenteditable="false"subtree. That text node is rendered by the decorator and is not part of the editor state, so no selection point maps to it and Lexical ends up with none. There is no text node before the mention to hold a caret, so nothing recovers it.The fix
CLICK_COMMANDalready handles clicks that land on a decorator. This adds the neighbouring case: when a click leaves us with no selection and the clicked point resolves inside an inline decorator, place the caret at that decorator's index in its parent — the same element point Blink produces natively.The guard is
$getSelection() !== null, so Blink and WebKit never reach this path.Scope
Deliberately limited to inline decorators (mentions), which is the reported bug. Block attachments (e.g. an uploaded file) have an analogous Gecko caret-drop at their vertical boundary, but the correct before/after placement there depends on the click's Y position rather than X, so it is a separate change rather than something to fold in here. The
isInline()guard makes the boundary explicit, and the method is named#placeCaretBeforeInlineDecoratorbecause the null-selection case only ever occurs on the leading edge (clicking after an inline decorator resolves natively in Gecko).Tests
test/browser/tests/prompts/typing_before_mention.test.js, driven with a realpage.mouse.click():Both assert the mention still exists (
toHaveCount(1)) so the ordering check can't pass vacuously. Verified on Chromium, Firefox and WebKit.prompts/andattachments/are green on Firefox;yarn lintandyarn testare clean.