Skip to content

Bug: [lexical-y] Undoing a delete of part of a word a collaborator's update made again with a word inserted inside it shows that part twice #9635

Open

Description

@zurfyx

Lexical version: main at fcea4ff (@lexical/y), the 0.53.1-nightly.20261030.0 build (e28f4dd), and #9625.

Steps To Reproduce

  1. Two clients bind one document whose line is one text node, aa bbcc dd.
  2. Client A, offline, makes the node again from its JSON with a word inserted inside the word bbcc: aa bb xxcc dd (an agent rewriting a block).
  3. Client B, offline, deletes aa bb (a range selection from the start of the line into bbcc), then undoes the delete.
  4. The clients sync.
a.update(() => {
  const n = $getRoot().getFirstChildOrThrow<ElementNode>().getFirstChildOrThrow<TextNode>();
  n.replace($parseSerializedNode({...n.exportJSON(), text: 'aa bb xxcc dd'}));
});
b.update(() => {
  // select offsets 0..5 ("aa bb") of the line and removeText()
});
b.undo();
await sync();

The current behavior

Every client shows aa bbbb xxcc dd: bb twice. The same with both client id orders. With bb and cc separate words (aa bb cc dd made again as aa bb xx cc dd) it shows bb once on #9625 (on the nightly, twice there too).

An insertion between two letters of a word in text made again replaces that word whole (#9598, so that an edit computed before the rewrite that replaces the old word takes nothing out of the new one), so A deletes the old bbcc and writes bb xxcc. B's undo brings the bb it deleted back as new content, next to A's new bb: the same shape as two clients removing the same text and both undoing (D50 in packages/lexical-y/DECISIONS.md).

A related shape: if A undoes its rewrite instead while B deletes bb (aa b1zc2z dd made again as aa b1z x9zc2z dd, B deletes b1z, A undoes), A's undo brings the old word back as new content, B's delete included, and on some timings a stray character of the deleted word stays glued to the next one.

The expected behavior

bb once: aa bb xxcc dd.

Notes

Keeping the unchanged characters of a word changed by an insertion inside it would fix the duplicate but brings back the case the whole-word rule prevents (#9608: a stale replace of the old word taking a letter of the new one). Until a design covers both, the binding documents this next to "Text nodes made again with other text" in y.md's Limits.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions