Pasting with the caret on a quote element crashes with Lexical error #211/#212#1109
Pasting with the caret on a quote element crashes with Lexical error #211/#212#1109jorgemanrubia wants to merge 1 commit into
Conversation
There was a problem hiding this comment.
Pull request overview
Fixes a Lexical crash when pasting while the caret is positioned on the QuoteNode element itself (an element-point selection on a block-container node), by normalizing such range selections down to a leaf position before delegating to Lexical’s RangeSelection.insertNodes.
Changes:
- Add a
BlockContainerNodeInserterto detect range selections whose anchor/focus are element points on a container of block children and normalize the selection before insertion. - Add vitest regression coverage for both the inline and block paste crash variants (Lexical invariants #211 and #212).
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.
Reviewed changes
Copilot reviewed 2 out of 2 changed files in this pull request and generated no comments.
| File | Description |
|---|---|
| test/javascript/unit/editor/paste_with_caret_on_quote.test.js | Adds regression tests ensuring paste into a quote element-point selection no longer crashes and preserves inserted content. |
| src/editor/contents/node_inserter.js | Introduces a new inserter that normalizes range selections on block containers before calling insertNodes. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Lexical's RangeSelection.insertNodes requires every selection point to have a block ancestor with inline children. When the caret sits on the blockquote element itself (an element point on the QuoteNode), a quote holding paragraphs has no such ancestor and Lexical throws invariant errors #211 (inline content) or #212 (block content), losing the paste. Normalize such selections down to a leaf position before inserting.
86e9d86 to
5e10db9
Compare
|
Another lexxy consumer here (a Rails app, lexxy 0.9.12.beta) — confirming this #212 isn't BC3-specific; we see it in production too. Our path matches yours exactly ( Since both entry points funnel through the same |
Fixes BC3-JS-N7AZ — 884 events / 537 users since May 26 (180 / 138 in the last 3 days). Card: https://app.basecamp.com/2914079/buckets/41746046/card_tables/cards/9982982415
What it fixes
Pasting with the caret sitting on the blockquote element itself (an element point on the
QuoteNode, e.g. after clicking in the gap between two paragraphs inside a quote) crashed and lost the paste. Every Sentry event goes through the same path:Contents#insertDOM→insertAtCursor→RangeSelection.insertNodes, throwing Lexical error #211 ("Expected node QuoteNode of type quote to have a block ElementNode ancestor") for inline content or #212 ("…to have a block ancestor") for block content.How
Lexical's
RangeSelection.insertNodesrequires every selection point to have a block ancestor with inline children (INTERNAL_$isBlock). A lexxy quote always holds paragraphs ($ensureQuoteHasParagraphChild), so an element point on the quote has no such ancestor — the quote fails the leaf-block check and its parent is the root.A new
BlockContainerNodeInserterhandles range selections where a point sits on a container of block nodes: it normalizes the selection down to a leaf position ($normalizeSelection__EXPERIMENTAL, the same descent Lexical applies to DOM selections) and then delegates to the regularinsertNodes.Includes Playwright regression tests at
test/browser/tests/paste/paste_with_caret_on_quote.test.jscovering both the #211 (inline paste) and #212 (block paste) variants. They place the caret as an element point on the quote and drive a real clipboard paste. Verified red/green: withsrc/reverted tomainboth tests fail (2 failed); with the fix both pass (2 passed).