Repository navigation
Conversation
…nge splits or shortens its text node ## Description A collaborator's change that shortens, splits or replaces the text node a range selection is in collapsed the selection to a caret on the other client: a delete before the range at least as long as the range, a format, comment or link starting at the range's start, a link on a word before it, or Enter right after it. It happened in paragraphs, list items, table cells and code lines, and for a headless writer's edit too. The read changes that node before the binding puts the selection back, and Lexical moves a point past the end of a changed node to that end: both points of the range could land on one offset. The binding then took the range for a caret the user had collapsed (which it must never widen back into a range) and left it collapsed. Whether the selection is a caret is now decided on what the editor showed when the read began (`$showsCaret`, captured before the read changes any node), for every read that puts a selection back: a remote change, a re-read of containers, and the release of a held composition. A caret the user collapsed before a remote change arrives still stays a caret. Closes #9516 Closes #9526 ## Changeset `.changeset/lexical-y-range-selection-reads.md` (`[fix]`). ## Test plan New `packages/lexical-y/src/__tests__/unit/RangeSelectionReads.test.ts`: deletes before the range (forward and backward ranges, both client id orders), bold, comment and link at the range's start, a link on a word before it, Enter after it, a bold in a list item, a table cell and a code line, and a headless writer's comment. Controls (a shorter delete, a format starting inside the range, Enter before it) pass on both. ### Before ``` $ pnpm exec vitest run --project unit packages/lexical-y/src/__tests__/unit/RangeSelectionReads.test.ts ❯ |unit| packages/lexical-y/src/__tests__/unit/RangeSelectionReads.test.ts (19 tests | 16 failed) × "aaa bbb ccc": B selects "ccc", A deletes "bbb" × "aaa bbbb ccc": B selects "ccc", A deletes "bbbb" × "aaa bbbbbbb ccc": B selects "ccc", A deletes "bbbbbbb" × "one two three": B selects "three", A deletes "one two" × B selects "t2z t3z", A bolds the range × B selects "t2z t3z", A bolds its first word × B selects "t2z t3z", A comments the range × B selects "t2z t3z", A comments its first word × B selects "t2z t3z", A links the range × B selects "t2z t3z", A links its first word × B selects "t2z t3z", A links a word before it × B selects "t2z t3z", A presses Enter right after it × in a list item: B selects "t2z", A bolds it × in a table cell: B selects "t2z", A bolds it × in a code line: B selects "t2z", A bolds it × a headless writer's comment on the range keeps it AssertionError: expected '' to be 'ccc' // Object.is equality ``` ### After ``` $ pnpm exec vitest run --project unit packages/lexical-y/src/__tests__/unit/RangeSelectionReads.test.ts ✓ |unit| packages/lexical-y/src/__tests__/unit/RangeSelectionReads.test.ts (19 tests) Test Files 1 passed (1) Tests 19 passed (19) ``` The `@lexical/y` and `@lexical/yjs` unit suites pass, the randomized ones included (`Fuzz`, `FollowUpFuzz`, `ConcurrentShapesFuzz`, `NestedFuzz`, `SlotsFuzz`), also with `LEXICAL_Y_BLOCK_ID=1` for the new test and `Fuzz`: ``` $ pnpm exec vitest run --project unit packages/lexical-y Test Files 166 passed | 2 skipped (168) Tests 7502 passed | 46 expected fail | 4 skipped (7552) ``` `pnpm run ci-check` passes.
zurfyx
requested review from
acywatson,
etrepum,
ivailop7 and
potatowagon
as code owners
October 29, 2026 22:43
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
lexical
@lexical/a11y
@lexical/clipboard
@lexical/code
@lexical/code-core
@lexical/code-prism
@lexical/code-shiki
@lexical/compiler
@lexical/devtools-core
@lexical/dragon
@lexical/eslint-plugin
@lexical/extension
@lexical/fast-check
@lexical/file
@lexical/hashtag
@lexical/headless
@lexical/history
@lexical/html
@lexical/internal
@lexical/link
@lexical/list
@lexical/mark
@lexical/markdown
@lexical/mdast
@lexical/offset
@lexical/overflow
@lexical/plain-text
@lexical/react
@lexical/rich-text
@lexical/selection
@lexical/table
@lexical/tailwind
@lexical/text
@lexical/utils
@lexical/y
@lexical/yjs
commit: |
This branch was successfully deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Description
A collaborator's change that shortens, splits or replaces the text node a
range selection is in collapsed the selection to a caret on the other client:
a delete before the range at least as long as the range, a format, comment
or link starting at the range's start, a link on a word before it, or Enter
right after it. It happened in paragraphs, list items, table cells and code
lines, and for a headless writer's edit too.
The read changes that node before the binding puts the selection back, and
Lexical moves a point past the end of a changed node to that end: both
points of the range could land on one offset. The binding then took the
range for a caret the user had collapsed (which it must never widen back
into a range) and left it collapsed.
Whether the selection is a caret is now decided on what the editor showed
when the read began (
$showsCaret, captured before the read changes anynode), for every read that puts a selection back: a remote change, a
re-read of containers, and the release of a held composition. A caret the
user collapsed before a remote change arrives still stays a caret.
Closes #9516
Closes #9526
Changeset
.changeset/lexical-y-range-selection-reads.md([fix]).Test plan
New
packages/lexical-y/src/__tests__/unit/RangeSelectionReads.test.ts:deletes before the range (forward and backward ranges, both client id
orders), bold, comment and link at the range's start, a link on a word
before it, Enter after it, a bold in a list item, a table cell and a code
line, and a headless writer's comment. Controls (a shorter delete, a format
starting inside the range, Enter before it) pass on both.
Before
After
The
@lexical/yand@lexical/yjsunit suites pass, the randomized onesincluded (
Fuzz,FollowUpFuzz,ConcurrentShapesFuzz,NestedFuzz,SlotsFuzz), also withLEXICAL_Y_BLOCK_ID=1for the new test andFuzz:pnpm run ci-checkpasses.