Merging changes from one document into another

Hey Sam,

Sure you can do that. Maybe I misunderstood, but it might make sense to change the perspective a bit and work purely with document updates.

Yjs documents can be transformed to document updates (representing some state of the document). E.g. using update = Y.encodeStateAsUpdate(ydoc). Document updates are commutative (which allows, for example, p2p network protocols) and idempotent. Idempotency is a neat property because it allows you to create a powerful templating engine. Basically, it says that updates can be merged even if they contain duplicate information (the duplicate information is then stripped away in the output).

Here is a templating engine for text documents:

import * as Y from 'yjs'

// basic template that should be inherited by all
const basicTemplate  = new Y.Doc()
basicTemplate.getText().insert(0, '# Headline\n')

console.log('Basic template: \n' + basicTemplate.getText(''))

// meeting notes template change the headline and add a bullet list
const meetingNotes = new Y.Doc()
// inherit changes from basicTemplate
Y.applyUpdate(meetingNotes, Y.encodeStateAsUpdate(basicTemplate))
meetingNotes.getText().applyDelta([
  { retain: 2 }, // skip "# "
  { delete: 8 }, // delete headline
  { insert: "Meeting Notes" }, // add new headline
  { retain: 1 }, // skip newline
  { insert: "• \n• \n" } // add two bullet items
])

console.log('Meeting notes template: \n' + meetingNotes.getText())

// Now we create a document based on the meetingNotes template
const ydoc = new Y.Doc()
Y.applyUpdate(ydoc, Y.encodeStateAsUpdate(meetingNotes))
console.log('Document state:\n' + ydoc.getText())

// A client can perform some operations on any template. E.g. add a citation to the basic template
basicTemplate.getText().insert(10, "\n> citation")

// We need to apply the changes on all documents that use basicTemplate
Y.applyUpdate(ydoc, Y.encodeStateAsUpdate(basicTemplate))

// the new paragraph is added in the final document
console.log('New state:\n' + ydoc.getText())

Maybe you can adapt this for your own scenario. The above example works on text because it is the most complex scenario. But I think you will see that working with updates makes sense in this scenario. Let me know what you think.

2 Likes