Checklist
Describe the bug
I want to start off that I am unsure if what I am reporting is a bug or an expected behavior.
I am seeing different outcomes when remote updates for a Y.Text struct (specifically attribute updates) are applied locally but after another local change happened to text attributes.
The following code has three cases:
- Single text struct leading to the expected outcome
- Two text structs where remote updates are applied immediately and leading to the expected outcome
- Two text structs where remote updates are applied after another local change and leading to an unexpected outcome
To Reproduce
Steps to reproduce the behavior:
- Execute code or go to https://codesandbox.io/s/busy-cannon-59l6t?file=/src/index.ts
- Open console
- Check differences in logged text deltas
Example code:
import * as Y from "yjs";
// Create doc with text and initial text
const doc = new Y.Doc();
const text = doc.getText("text");
text.insert(0, "helloworld");
const initialState = Y.encodeStateAsUpdate(doc);
function getDoc(): Y.Doc {
const doc = new Y.Doc();
Y.applyUpdate(doc, initialState);
return doc;
}
// 1. Single text struct
function oneText() {
const doc1 = getDoc();
const text1 = doc1.getText("text");
text1.format(3, 3, { bold: true });
text1.format(0, 4, { bold: true });
console.log({
test: "oneText",
text1: text1.toDelta()
});
}
// 2. Two text structs synced locally without and with a
// timeout.
//
// The timeout will delay applying the change on the other
// struct by putting it in a macro task. This means that
// both local changes will be applied before the update from
// the respective other struct.
//
// Without timeout
// text1 will apply format 3, 3, { bold: true }
// text2 will receive format 3, 3, { bold: true }
// text2 will apply format 0, 4, { bold: true }
// text1 will receive format 0, 4, { bold: true }
//
// Expected output: [{"insert":"hellow","attributes":{"bold":true}},{"insert":"orld"}]
// Actual output: [{"insert":"hellow","attributes":{"bold":true}},{"insert":"orld"}]
//
// With timeout
// text1 will apply format 3, 3, { bold: true }
// text2 will apply format 0, 4, { bold: true }
// text2 will receive format 3, 3, { bold: true }
// text1 will receive format 0, 4, { bold: true }
//
// Expected output: [{"insert":"hellow","attributes":{"bold":true}},{"insert":"orld"}]
// Actual ouput: [{"insert":"hell","attributes":{"bold":true}},{"insert":"oworld"}]
function twoText(withTimeout: boolean = false) {
const doc1 = getDoc();
const text1 = doc1.getText("text");
const doc2 = getDoc();
const text2 = doc2.getText("text");
doc1.on("update", (update: Uint8Array) => {
if (withTimeout) {
setTimeout(() => {
Y.applyUpdate(doc2, update);
}, 0);
} else {
Y.applyUpdate(doc2, update);
}
});
doc2.on("update", (update: Uint8Array) => {
if (withTimeout) {
setTimeout(() => {
Y.applyUpdate(doc1, update);
}, 0);
} else {
Y.applyUpdate(doc1, update);
}
});
doc1.transact(() => {
text1.format(3, 3, { bold: true });
});
doc2.transact(() => {
text2.format(0, 4, { bold: true });
});
if (withTimeout) {
setTimeout(() => {
console.log({
test: "twoText",
text1: text1.toDelta(),
text2: text2.toDelta()
});
}, 0);
} else {
console.log({
test: "twoText",
text1: text1.toDelta(),
text2: text2.toDelta()
});
}
}
oneText();
twoText();
twoText(true);
Screenshots
The screenshot shows the rendered console output. Compare the top text1/text2 combo with the bottom text1/text2 combo. The expected outcome is that the bottom combo would be the same as the top combo.

Environment Information
- Browser Chrome Version 89.0.4389.114 (Official Build) (x86_64)
- Node.js v12.16.3
- Yjs 13.5.3
Checklist
Describe the bug
I want to start off that I am unsure if what I am reporting is a bug or an expected behavior.
I am seeing different outcomes when remote updates for a Y.Text struct (specifically attribute updates) are applied locally but after another local change happened to text attributes.
The following code has three cases:
To Reproduce
Steps to reproduce the behavior:
Example code:
Screenshots
The screenshot shows the rendered console output. Compare the top text1/text2 combo with the bottom text1/text2 combo. The expected outcome is that the bottom combo would be the same as the top combo.
Environment Information