ueberdosis / tiptap

The headless rich text editor framework for web artisans.
https://tiptap.dev
MIT License
27.12k stars 2.26k forks source link

@tiptap/extension-collaboration is not compatible with ySyncPlugin renderSnapshot #3583

Open martarf opened 1 year ago

martarf commented 1 year ago

What’s the bug you are facing?

I am working on a project built on top of Tiptap and @tiptap/extension-collaboration. (Huge thank you for the libraries, they are amazing). I am trying to leverage YJS snapshots to build a feature that renders the document history (showing who edited what part of the doc) as described in the prosemirror demo by @dmonad.

However, @tiptap/extension-collaboration does not allow me to configure the options object needed for ySyncPlugin so when it tries to read the permanentUserData prop it throws an error

https://github.com/yjs/y-prosemirror/blob/7a3afbc0d8de35bc01bd63bfc9911318c115c4f7/src/plugins/sync-plugin.js#L447
sync-plugin.js:446

Uncaught TypeError: Cannot read properties of null (reading 'getUserByClientId')
    at computeYChange (sync-plugin.js:446:1)
    at YText.js:1028:1
    at transact (Transaction.js:398:1)
    at YXmlText.toDelta (YText.js:1008:1)
    at createTextNodesFromYText (sync-plugin.js:706:1)
    at createChildren (sync-plugin.js:638:1)
    at Array.forEach (<anonymous>)
    at createNodeFromYElement (sync-plugin.js:658:1)
    at sync-plugin.js:468:1
    at Array.map (<anonymous>)

You can see here that the current extension only includes

return [
      ySyncPlugin(fragment),
      yUndoPlugin(),
    ]

vs

return [
      ySyncPlugin(fragment, { permanentUserData, colors }),
      yUndoPlugin(),
    ]

So I am unable to leverage renderSnapshot and unrenderSnapshot unless I fork the extension to be able to inject my own permanentUserData. I'm happy to send a PR but I wanted to understand if there was a reason behind this choice.

Thanks!

Which browser was this experienced in? Are any special extensions installed?

Chrome, Firefox, Safari. Not a browser issue.

How can we reproduce the bug on our side?

  const editor = useEditor({
    extensions: [
      // your extensions here ...
      Collaboration.configure({
        document: messagingProvider.doc,
      }),
      CollaborationCursor.configure({
        provider: messagingProvider,
      }),
     ....
    ]
  });
const renderDocHistory = () => {
    const binding = ySyncPluginKey.getState(editor.view.state).binding;
    const doc = messagingProvider.doc;
    const prevSnapshot = Y.emptySnapshot;
    const snapshot = Y.snapshot(doc);
    binding.renderSnapshot(snapshot, prevSnapshot);
  };

<div className="editor" role="document">
     <EditorContent className="editor__content" editor={editor} />
          <div className="editor__footer">
               <button onClick={renderDocHistory}>Show History</button>
          </div>
</div>

fixed by updating https://github.com/ueberdosis/tiptap/tree/main/packages/extension-collaboration to

export const Collaboration = Extension.create<CollaborationOptions>({

...

  addProseMirrorPlugins() {
    const fragment = this.options.fragment
      ? this.options.fragment
      : this.options.document.getXmlFragment(this.options.field)

    const permanentUserData = this.options.permanentUserData;
    const colors = this.options.colors;
    return [
      ySyncPlugin(fragment, { permanentUserData , colors  }),
      yUndoPlugin(),
    ]
  },
})

Can you provide a CodeSandbox?

https://codesandbox.io/s/smoosh-dew-4vt1x8?file=/src/App.js

What did you expect to happen?

I would expect @tiptap/extension-collaboration to be fully configurable with all the options allowed by ySyncPlugin in y-prosemirror

const options = {
    colors,
    onFirstRender
    permanentUserData
};
Collaboration.configure({
    document,
    options
}),

Anything to add? (optional)

No response

Did you update your dependencies?

Are you sponsoring us?

iarmankhan commented 1 year ago

@martarf You can extend the plugin like this, this should work.

Collaboration.extend({
    addProseMirrorPlugins() {
      const yXmlFragment = yDoc.get('default', Y.XmlFragment) as Y.XmlFragment;

      return [
        ySyncPlugin(yXmlFragment, {
          permanentUserData,
          colors: [
            { light: '#ecd44433', dark: '#ecd444' },
            { light: '#ee635233', dark: '#ee6352' },
            { light: '#6eeb8333', dark: '#6eeb83' },
          ],
        }),
      ];
    },
}).configure({
  document: yDoc,
}),

I am also trying the same thing, using versions in tiptap, so far I don't get any success. Do you have example for this? Thanks

iarmankhan commented 1 year ago

Any updates here?

martarf commented 1 year ago

@iarmankhan Following the example written by dmonad here you can store and show the authors of the content by using

const prevSnapshot = Y.emptySnapshot;
const snapshot = Y.snapshot(binding.doc);
binding.renderSnapshot(snapshot, prevSnapshot);

and

binding.unrenderSnapshot()

But since I got no response here I still had to extend the Collaboration plugin and add the missing permanentUserData option myself as described in the issue.

drFerg commented 7 months ago

@martarf @iarmankhan Did you successful use snapshots with tiptap? I've been trying to integrate the versions demo by dmonad with the above suggestions but can't seem to unrender a snapshot without losing the current editors contents. Any tips for getting it to work?


edit: I've figure this out in the end - turned out I wasn't including the necessary Mark for displaying ychange text.