You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
In our current exploration of sync engines, @chriszarate and I are testing three contenders that handle conflicts in different ways:
yjs-server: CRDT-based, uses “conflict-free” data structures. Conflicts happen, but Yjs’s merge algorithm guarantees a deterministic answer. This is most similar to the current RTC implementation in Gutenberg. There’s no concept of a user-facing merge conflict.
Distributed Editing (DE-RTC) takes a merge-friendly approach. On each sync, clients send the entire post content (and base version), and the server merges the user’s changed content into a canonical copy. Any unmergeable block state (e.g. both users changed the same part of a block) is parked for review.
intent-log: Operational transform engine that speaks Gutenberg-specific verbs like block splitting and transformations. Users communicate individual actions (e.g. insert text “i” at position 4, split block 123e4567 at position 6) and the server orders the actions in a canonical log. Actions that conflict with earlier transforms (e.g. adding text to a deleted block) are parked for review.
Let's explore a possible editor UI for block sequestration and human-guided merges when edits from multiple users are incompatible. It’s not guaranteed that we’ll need all of these states depending on which sync engine we use.
Note that while these UI experiments are based on sync engine capabilities, they are currently only implemented as one-off visual experiments (read “staged demos”) in the experimental/merge-dialog-prototype branch. The videos below were recorded using the intent-log engine.
Visual Format for Conflicts
Rather than a new sidebar or trying to fit information into the Notes UI, we opt to show a static conflict alert within the block editor:
After clicking “Review conflict”, we view the incompatible incoming changes, along with a rich editor for making manual adjustments:
All text conflicts and block sequestration follow this format.
Let’s look at some examples!
Text Conflicts
While most text changes should be resolvable without human interaction, the DE-RTC and intent-log engines escalate situations where there’s no obviously correct merge path. Here’s a text conflict example where changes between users overlap:
As mentioned above, this sort of text conflict would not apply to the yjs-server engine, as its CRDT data structure never creates a merge conflict and instead produces a deterministic (and sometimes unexpected) result.
Block Sequestration
All three engines make use of sequestration or sanitization for block changes that need elevated permissions from users. Using the same review flow as merge conflicts, here’s what sequestration can look like for a newly inserted block from a user without sufficient permissions. Below, the right user is an admin and the left user is an author without unfiltered_html permissions:
demo2-sequestration.mov
Sequestration Diffing
It might be difficult to see what’s changed in a larger block of code, e.g. a long snippet of JS analytics code. In the case where a previously approved code block is changed, we can also show a diff view of the changed code:
demo2.1-sequestration-diff.mov
Multi-block Conflicts
Complex operations like block splitting, transformations, and ordering changes mixed with content edits can cause a merge to span multiple blocks. Here’s an example of a conflict that involves the left user splitting a block and changing content at the same time as another user:
demo3-multi-block.mov
Custom Merging
Some block types like core/table have complex inner data structures that don’t map well to simple text merges. We can also provide block-aware diffing modes for these cases:
demo4-tables.mov
This was an attempt to test a robust merge UI without crowding the editor canvas or sidebar, or cramming too much information into a small space. So far the UI for these is implemented in experimental/merge-dialog-prototype, but have not been adapted for each engine. When we make an engine choice, if the result is a system that requires merge conflict handling, we may bring this work into the engine choice.
In our current exploration of sync engines, @chriszarate and I are testing three contenders that handle conflicts in different ways:
123e4567at position 6) and the server orders the actions in a canonical log. Actions that conflict with earlier transforms (e.g. adding text to a deleted block) are parked for review.Let's explore a possible editor UI for block sequestration and human-guided merges when edits from multiple users are incompatible. It’s not guaranteed that we’ll need all of these states depending on which sync engine we use.
Note that while these UI experiments are based on sync engine capabilities, they are currently only implemented as one-off visual experiments (read “staged demos”) in the experimental/merge-dialog-prototype branch. The videos below were recorded using the intent-log engine.
Visual Format for Conflicts
Rather than a new sidebar or trying to fit information into the Notes UI, we opt to show a static conflict alert within the block editor:
After clicking “Review conflict”, we view the incompatible incoming changes, along with a rich editor for making manual adjustments:
All text conflicts and block sequestration follow this format.
Let’s look at some examples!
Text Conflicts
While most text changes should be resolvable without human interaction, the DE-RTC and intent-log engines escalate situations where there’s no obviously correct merge path. Here’s a text conflict example where changes between users overlap:
demo1-text-conflict.mov
Note that this reuses the excellent revisions diff viewer introduced to Gutenberg this year in In-editor revisions: add visual diffing.
As mentioned above, this sort of text conflict would not apply to the yjs-server engine, as its CRDT data structure never creates a merge conflict and instead produces a deterministic (and sometimes unexpected) result.
Block Sequestration
All three engines make use of sequestration or sanitization for block changes that need elevated permissions from users. Using the same review flow as merge conflicts, here’s what sequestration can look like for a newly inserted block from a user without sufficient permissions. Below, the right user is an admin and the left user is an author without
unfiltered_htmlpermissions:demo2-sequestration.mov
Sequestration Diffing
It might be difficult to see what’s changed in a larger block of code, e.g. a long snippet of JS analytics code. In the case where a previously approved code block is changed, we can also show a diff view of the changed code:
demo2.1-sequestration-diff.mov
Multi-block Conflicts
Complex operations like block splitting, transformations, and ordering changes mixed with content edits can cause a merge to span multiple blocks. Here’s an example of a conflict that involves the left user splitting a block and changing content at the same time as another user:
demo3-multi-block.mov
Custom Merging
Some block types like
core/tablehave complex inner data structures that don’t map well to simple text merges. We can also provide block-aware diffing modes for these cases:demo4-tables.mov
This was an attempt to test a robust merge UI without crowding the editor canvas or sidebar, or cramming too much information into a small space. So far the UI for these is implemented in experimental/merge-dialog-prototype, but have not been adapted for each engine. When we make an engine choice, if the result is a system that requires merge conflict handling, we may bring this work into the engine choice.