How to initialize document in ProseMirror with Y.js

A doc JSON string has been persistenced in my backend database, but I found it difficult to load up in my frontend editor view, I used the utility function prosemirrorToYDoc, and I can see the doc rendered, but only one second…

It seems that a new state has been applied and I found meta $y_sync in console…

Where I create initDoc:

const sampleInitDoc = Node.fromJSON(EditorSchema, {
  type: 'doc',
  content: [
    {
      type: 'paragraph',
      text: ''
    }
  ]
});
function createInitDoc(schema: Schema, initContent: string) {
  try {
    return Node.fromJSON(schema, JSON.parse(initContent));
  } catch (err) {
    return sampleInitDoc
  }
}

when I do have a options.initContent (that’s a string)

if (!options.readonly) {  // if my prosemirror is readonly mode, I would not turn on anything of Y.js ...
    // Y.js 协同配置:
    ydoc = options.initContent
      ? prosemirrorToYDoc(createInitDoc(EditorSchema, options.initContent))
      : new Y.Doc();
  const provider = new WebsocketProvider(
    'ws://localhost:2048',
    options.docName,
    ydoc  
 );
 // ...
}

and some code snippet for init EditorView:

new EditorView(el as HTMLDivElement, {
      state: EditorState.create({
        doc: createInitDoc(EditorSchema, options.initContent), // is here something wrong ???
        schema: EditorSchema,
        plugins
      }),
// ...
})

I know that my code’s logic is somewhat a mess… so I would like to learn a best practice for initialize docs for ProseMirror with existing JSON data…

Continuing the discussion from Prosemirror serializer:

I saw some discussion about this cases…
I wonder how I can just update Yjs Doc by applying a new PM doc JSON ??

does plugins include

import { ySyncPlugin } from 'y-prosemirror'
ySyncPlugin(ydoc.getXmlFragment("prosemirror")),
1 Like

of course I did include this plugin…

Hmm, if that didn’t work…

Try this?

      let ydocInit = prosemirrorJSONToYDoc(editor.schema, options.initContent);
      applyUpdate(ydoc, encodeStateAsUpdate(ydocInit));

Still Need Help please :hot_face:

I was debuging for a long time… BTW, I use Vue 3 for View layer …

What I got in debuging:

First, I found in sync-plugin.js there is a ReplaceStep, from my init doc structure to { type: 'doc', content: [{ type: 'paragraph', text: '' }] }

Conclusion May be:

When an EditorView is creating with ySyncPlugin,

  1. an initial state ( call it StateA ) was created in new EditorView() -->
state: EditorState.create({
    doc: pmNodeFromJSONString(options.initContent),
    schema: EditorSchema,
    plugins
}),
  1. when StateA was just rendered but plugins not started --> sync-plugin created a tr which replaced my initial content to an empty paragraph… after applying this tr there is a new state: StateB

  2. render view of StateB, and I just saw a flash between A and B…

@dmonad Can you provide any suggestions for me, thanks a lot !! :hot_face:

I’m sorry, but this is quite a lot for me to go through. It would take a while for me to tell you exactly what is going wrong.

A few recommendations though:

Do not set the ProseMirror state AND the initial Yjs state. Ideally, just remove this line:

doc: pmNodeFromJSONString(options.initContent),

When you have the EditorView that is made collaborative using the ySyncPlugin, then there is no need to use prosemirrorToYDoc. Simply create an editor view with the Yjs document that is connected to the server using y-websocket. Then you can replace the whole editor state with a new editor state (using the ProseMirror API).

Note, however, that I discouraged “initializing” on the client-side in several threads on this forum. I’m not sure how one would prevent the case that two clients “initialize” at the same time. Yjs guarantees that all peers will always end up with the same state. But it really only can do that. Ideally, you initialize the content on the server, if you have to.

THANKSSSSSSSS For you kind reply !!

What’s my question now…

Now I found that I just need to maintain a yjs service separately with nodejs,
So I turn to integrate y-websocket and y-leveldb together,

but what should I do for readonly situation?
For example, user just get into the document view page, not editing page…
I don’t want the view page be synchronized … just a frozen one (latest to when getting into that page) is fine…

Do I need to read the persistence data ?? desperately seeking your help for my university graduation project, Thanks a lot !!!

If I do permit the readonly view page to show updates in real-time, I tried to add ySyncPlugin for non-editable ProseMirror …

Theoretically, as I provide the same docName to ydoc.getXmlFragment for both editable and read-only ProseMirror, it would all sync from the same persistence data… I don’t know why it didn’t show anything when I already have something in leveldb…

y-prosemirror Read-out persistence data on server issue:

I made a quick prototype server extending y-websocket-server
I provide a route for frontend app to fetch document from persistence, but while debugging, I found that there is actually some structure stored inside sharing ydoc, but the method yDocToProsemirrorJSON output an empty structure… How does that happen?? is that an issue ??

Oh I made it !!! WOW :exploding_head: Belowing my mind !!!

const docJson = yDocToProsemirrorJSON(yDocByName, docName);

I need to pass the docName into this method…

this post really helped me a lot, thanks @dmonad

Glad you figured it out @ShenQingchuan! Regarding the read-only feature, you can maybe have a look at this thread: "read-only" or one-way only sync

How about you post your graduation project here when you are done? Use a “show” and tell us what you learned.

sure!I will ~
Thanks for your great effort !!!:heart::heart::heart: