Y-prosemirror - ignoring specific nodes

Hi, I’m currently adding YJS to our Tiptap editor, and facing a challenge with our current nodes/marks. We have a few nodes/marks that we don’t want to save in the Ydoc, for one of 2 reasons:

  1. If some of them exists, it means that there’s a user action in progress.
  2. Others are related to backend stuff that are currently per-session, so we don’t want them to appear on load.

This is our way of dealing with them now:

  1. We’re adding the addToHistory meta
  2. We’re using DOMSerializer to override the relevant node/mark

What’s the correct way to do it when using YJS?

Sounds non-optimal not to save the nodes/marks. Much easier to just to use a NodeView to store local data and then not displaying anything if user is not the local user.

But yeah, don’t think there’s any specific Yjs way to do that. The whole document is synced between all clients without separating it into parts. You can, of course, use subdocuments but that is a bit of overkill.

Thanks for the response, sorry for the late reply!

I tried implementing such a thing, but there’s a big issue with that solution. When I hide the data from one user, the “hidden” nodes override each other, and it creates wrong behavior when 2 clients use nodes at the same location. There’s also a problem when merging 2 clients using indexedDB, which sometimes duplicates an entire paragraph.

We’ll try to debug a bit the Prosemirror->YJS flow, and will try to understand if there’s an easy workaround for us without using subdocuments. Is there any other way that you thought about?

Weird, they shouldn’t override each other. Oh yeah you are removing them with DOMSerializer? Just sync a dummy node with empty data (or marks with random uuid so that they won’t automatically merge). I think this is just an engineering problem with your ProseMirror & Yjs integration.

Actually that’s exactly what I’m doing, will keep investigate… pasting here the component’s code

import { NodeViewContent, NodeViewWrapper } from '@tiptap/react';
import { NodeViewProps } from '@tiptap/core';
import { FC } from 'react';

export const PerDocumentNode: (actualNode: FC<NodeViewProps>) => FC<NodeViewProps> = (Component: FC<NodeViewProps>) => {
  return (props: NodeViewProps) => {
    if (props.node.attrs['clientId'] === props.editor.storage?.['yjsClientId']?.['clientId']) {
      return <Component {...props} />;
    }

    return <DifferentClientIdNode {...props} />;
  };
};

const DifferentClientIdNode: FC<NodeViewProps> = () => {
  return (
    <NodeViewWrapper as='span'>
      <NodeViewContent as='span' />
    </NodeViewWrapper>
  );
};

I do not very understand Tiptap’s detail.

But every Editor best has the data layer & data mutation emitter for the outer component to listen. (here is the YJS Doc will hear it)

When DOM changed, the data layer had the exact change, the emitter invoked once, and the YJS Doc had a faithful adaptation.

If the Editor has this data layer & emitter, I think your request is easy to arrive:

Just ignore the mutation of nodes/marks in the data layer.

I’m not sure what’s the exact “data layer” you mean, since currently tiptap-collaboration extension might be the one handling it for us. I opened a PR in y-prosemirror that allows passing a meta, in order to ignore the relevant mutations.

1 Like