Skip to content

[lexical-website] Documentation Update: Add the limit of text nodes made again with other text to the @lexical/y docs - #9556

Merged
zurfyx merged 2 commits into
mainfrom
zurfyx/fix-doc-replaced-text-node-limit
Oct 30, 2026
Merged

zurfyx merged 2 commits into
mainfrom
zurfyx/fix-doc-replaced-text-node-limit

Conversation

@zurfyx

@zurfyx zurfyx commented Oct 30, 2026

Copy link
Copy Markdown
Member

Description

When an update replaces a line's text nodes with new ones and the line's text changed (an AI rewrite that makes a block's text nodes again from their JSON with one word changed, or removes a block's children and appends one new text node), the binding writes a replaced node whose text is not all there unchanged again whole: a removed node's text is aligned with what replaced it whole or not at all. A collaborator's concurrent letter typed into that text, which the rewriting client had not seen, then stays where it was and shows in front of the rewritten text, out of its word (for example zt63t71z t13z t81z t15z where the collaborator typed the z of t63z). The concurrent typed text is kept and every client converges.

The "Edits made by code" section already says that text made where the line's text changed is new where it is put, and points to Limits for replaced blocks, but Limits did not list the case. This adds it there, with what to do instead (edit the changed words in place with spliceText or insertText, which writes only the changed characters).

Aligning a removed node's text in part instead (by the common start and end, as a text set is) was tried and is not taken here: it fixes this case but breaks cases the whole-or-nothing rule protects in the existing suite (a peer's concurrent delete written back, a character duplicated after an entity move and a set, a collaborator's typing moved to the start of a rewritten line).

Test plan

Documentation only; pnpm run ci-check passes (prettier, website typecheck). The anchor it links (#edits-made-by-code-agents-tools) is the slug of the existing "Edits made by code (agents, tools)" heading.

…ade again with other text to the @lexical/y docs

## Description

When an update replaces a line's text nodes with new ones and the line's text changed (an AI rewrite that makes a block's text nodes again from their JSON with one word changed, or removes a block's children and appends one new text node), the binding writes a replaced node whose text is not all there unchanged again whole: a removed node's text is aligned with what replaced it whole or not at all. A collaborator's concurrent letter typed into that text, which the rewriting client had not seen, then stays where it was and shows in front of the rewritten text, out of its word (for example `zt63t71z t13z t81z t15z` where the collaborator typed the `z` of `t63z`). The concurrent typed text is kept and every client converges.

The "Edits made by code" section already says that text made where the line's text changed is new where it is put, and points to Limits for replaced blocks, but Limits did not list the case. This adds it there, with what to do instead (edit the changed words in place with `spliceText` or `insertText`, which writes only the changed characters).

Aligning a removed node's text in part instead (by the common start and end, as a text set is) was tried and is not taken here: it fixes this case but breaks cases the whole-or-nothing rule protects in the existing suite (a peer's concurrent delete written back, a character duplicated after an entity move and a set, a collaborator's typing moved to the start of a rewritten line).

## Test plan

Documentation only; `pnpm run ci-check` passes (prettier, website typecheck). The anchor it links (`#edits-made-by-code-agents-tools`) is the slug of the existing "Edits made by code (agents, tools)" heading.
@vercel

vercel Bot commented Oct 30, 2026 •

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated
lexical Ready Ready Preview Oct 30, 2026 5:44pm UTC
lexical-playground Ready Ready Preview Oct 30, 2026 5:44pm UTC

Request Review

@meta-cla meta-cla Bot added the CLA Signed This label is managed by the Facebook bot. Authors need to sign the CLA before a PR can be reviewed. label Oct 30, 2026
@pkg-pr-new

pkg-pr-new Bot commented Oct 30, 2026 •

Copy link
Copy Markdown
lexical

npm i https://pkg.pr.new/lexical@9556

@lexical/a11y

npm i https://pkg.pr.new/@lexical/a11y@9556

@lexical/clipboard

npm i https://pkg.pr.new/@lexical/clipboard@9556

@lexical/code

npm i https://pkg.pr.new/@lexical/code@9556

@lexical/code-core

npm i https://pkg.pr.new/@lexical/code-core@9556

@lexical/code-prism

npm i https://pkg.pr.new/@lexical/code-prism@9556

@lexical/code-shiki

npm i https://pkg.pr.new/@lexical/code-shiki@9556

@lexical/compiler

npm i https://pkg.pr.new/@lexical/compiler@9556

@lexical/devtools-core

npm i https://pkg.pr.new/@lexical/devtools-core@9556

@lexical/dragon

npm i https://pkg.pr.new/@lexical/dragon@9556

@lexical/eslint-plugin

npm i https://pkg.pr.new/@lexical/eslint-plugin@9556

@lexical/extension

npm i https://pkg.pr.new/@lexical/extension@9556

@lexical/fast-check

npm i https://pkg.pr.new/@lexical/fast-check@9556

@lexical/file

npm i https://pkg.pr.new/@lexical/file@9556

@lexical/hashtag

npm i https://pkg.pr.new/@lexical/hashtag@9556

@lexical/headless

npm i https://pkg.pr.new/@lexical/headless@9556

@lexical/history

npm i https://pkg.pr.new/@lexical/history@9556

@lexical/html

npm i https://pkg.pr.new/@lexical/html@9556

@lexical/internal

npm i https://pkg.pr.new/@lexical/internal@9556

@lexical/link

npm i https://pkg.pr.new/@lexical/link@9556

@lexical/list

npm i https://pkg.pr.new/@lexical/list@9556

@lexical/mark

npm i https://pkg.pr.new/@lexical/mark@9556

@lexical/markdown

npm i https://pkg.pr.new/@lexical/markdown@9556

@lexical/mdast

npm i https://pkg.pr.new/@lexical/mdast@9556

@lexical/offset

npm i https://pkg.pr.new/@lexical/offset@9556

@lexical/overflow

npm i https://pkg.pr.new/@lexical/overflow@9556

@lexical/plain-text

npm i https://pkg.pr.new/@lexical/plain-text@9556

@lexical/react

npm i https://pkg.pr.new/@lexical/react@9556

@lexical/rich-text

npm i https://pkg.pr.new/@lexical/rich-text@9556

@lexical/selection

npm i https://pkg.pr.new/@lexical/selection@9556

@lexical/table

npm i https://pkg.pr.new/@lexical/table@9556

@lexical/tailwind

npm i https://pkg.pr.new/@lexical/tailwind@9556

@lexical/text

npm i https://pkg.pr.new/@lexical/text@9556

@lexical/utils

npm i https://pkg.pr.new/@lexical/utils@9556

@lexical/y

npm i https://pkg.pr.new/@lexical/y@9556

@lexical/yjs

npm i https://pkg.pr.new/@lexical/yjs@9556

commit: 9ffbf33

## Description

This commit changes no file. The repository's review bot reviews a pull request when its branch is pushed; this branch was last pushed before the bot started reviewing the repository, so it had no review yet.
that outdents an item of such a list is a delete in the nested list's
container and an insert in the list's: text a collaborator types in those
items at the same time is lost (only lists stored in that older shape).
- **Text nodes made again with other text.** Where an update replaces a

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

IIRC the text merge transform is going to splice TextNode back together at least in a left-biased way if the other matching conditions are met before the update is committed and then relayed to collab, so a split-then-join should be indistinguishable from a setTextContent splice unless there's something blocking the merge altogether or the changed content isn't a prefix. We could add a different bias in $mergeTextNodes to prefer preserving an existing node on merge even if it is on the right.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

And if the problem is that it doesn't call TextNode.spliceText (after the bias heuristic is changed), we could probably have TextNode.mergeWithSibling call it?

@meta-code-review meta-code-review Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This looks good to me. The new Limits bullet matches how the @lexical/y binding behaves today: a text node that is made again with changed text is deleted and inserted whole, what a collaborator types into it at the same time is kept and every client converges, and TextNode#spliceText or the selection's insertText write only the characters they replace. Three optional, non-blocking wording suggestions, all in packages/lexical-website/docs/collaboration/y.md:

  1. Unchanged nodes between two changed ones are rewritten too (lines 816-818). “a replaced node whose text is not all there unchanged is written again whole” reads as if a remade node whose text did not change keeps its characters. It does only where it lines up from the start or the end of the replaced text: with aa · bb · cc remade as aX · bb · cY, the whole line aa bb cc is deleted and aX bb cY inserted, and a z a collaborator types inside the unchanged bb at the same time lands in front of the line. With only the last word changed, only cc is rewritten and the z stays in its word. So an update that changes two words of a block rewrites everything between them. Possible wording: “…every replaced node from the first whose text changed to the last, also one whose text did not change, is written again whole…”.

  2. Concurrent formatting at the end of the replaced text (lines 822-823). The bullet says the collaborator's formatting of that text “is not kept”. Formatting that reaches the end of the replaced text can instead cover all of the new text, on every client. For example, with the line t63 t71 t13 t81 t15 made again as one node with t72, a collaborator who bolds t15 at the same time leaves the whole rewritten line bold when the author has the lower client id; in a line head · t63 t71 · t13 tail whose nodes are remade from JSON, bolding t71 makes all of the new t63 t72 bold whichever id is lower. Formatting that ends inside the text (bolding t63) is dropped, as written. The page's “Other races” bullet already describes a related case, text typed at the start of a range a collaborator formats at the same time. Possible wording: “…is not kept where it was; formatting that reaches the end of the replaced text can cover all of the new text instead.” The setTextContent sentence under “Edits made by code” (lines 684-686) could take the same clause.

  3. Typing at the very end of the replaced text (lines 819-821). “lands outside the new text, before it” holds for a letter typed inside the text; one typed at the very end of the replaced text lands before or after the new text depending on client ids (z typed after t15 gives t63 t72 t13 t81 t15z when the author has the lower id). Possible wording: “outside the new text: before it, or after it where it was typed at the very end of the replaced text (by the order Yjs gives the two inserts)”.

On the open thread about merge bias: the binding already replays a text-node merge with both nodes' characters (the merge entry in packages/lexical-y/src/internal/TextEdits.ts), so in a split-then-join only the part that was made again is rewritten whole, whichever node survives the merge. That looks like a separate follow-up rather than something this page needs to wait for.

No changeset is needed for a website-only change (CONTRIBUTING.md, “Changesets”).

Comment on lines +816 to +818
block's children and appends one new text node), a replaced node whose
text is not all there unchanged is written again whole: its characters are
deleted, and the new text is inserted. What a collaborator types into that

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Optional, non-blocking: a remade node whose text did not change keeps its characters only where it lines up from the start or the end of the replaced text. With aa · bb · cc remade as aX · bb · cY, the whole line aa bb cc is deleted and aX bb cY inserted, and a z a collaborator types inside the unchanged bb at the same time lands in front of the line; with only the last word changed, only cc is rewritten. So an update that changes two words of a block rewrites everything between them. Possible wording: “…every replaced node from the first whose text changed to the last, also one whose text did not change, is written again whole…”.

Comment on lines +819 to +821
text at the same time, which the update's author has not seen, stays where
it was, so it lands outside the new text, before it (a letter typed at the
end of a word ends up in front of the rewritten text, out of its word),

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Optional, non-blocking: a letter typed at the very end of the replaced text can land after the new text instead: z typed after t15 (the end of the line) gives t63 t72 t13 t81 t15z when the author has the lower client id, and zt63 t72 t13 t81 t15 otherwise. Possible wording: “outside the new text: before it, or after it where it was typed at the very end of the replaced text (by the order Yjs gives the two inserts)”.

Comment on lines +822 to +823
and the collaborator's formatting of that text at the same time is not
kept. The concurrent typed text is kept, and every client shows the same

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Optional, non-blocking: formatting that reaches the end of the replaced text is not dropped but can cover all of the new text. With t63 t71 t13 t81 t15 made again as one node with t72, a collaborator bolding t15 at the same time leaves the whole rewritten line bold on every client when the author has the lower client id; in head · t63 t71 · t13 tail remade from JSON, bolding t71 makes all of the new t63 t72 bold in either id order. Formatting that ends inside the text (bolding t63) is dropped, as written. Possible wording: “…is not kept where it was; formatting that reaches the end of the replaced text can cover all of the new text instead.” The “Other races” bullet already describes a related case, text typed at the start of a range a collaborator formats at the same time.

@zurfyx
zurfyx added this pull request to the merge queue Oct 30, 2026
Merged via the queue into main with commit 3cab41a Oct 30, 2026
61 checks passed

This branch was successfully deployed

2 active deployments
Preview – lexical — 9ffbf330 Deployed Oct 30, 2026 by vercel[bot]
Preview – lexical-playground — 9ffbf330 Deployed Oct 30, 2026 by vercel[bot]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

CLA Signed This label is managed by the Facebook bot. Authors need to sign the CLA before a PR can be reviewed.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants