Implementing google docs like "suggest edit" mode

Hi!

I’m currently working on a collaborative editor using tiptap (based on prosemirror) and I’m trying to achieve an editing experience similar to google docs (but limited mostly to markdown). Implementing comments was easy enough, however I’m a bit at a loss when it comes to implementing something like Google Doc’s “suggest edit” mode.

In theory it sounds simple enough. For example, when the user deletes text while in this mode, prevent the delete or undo it, and apply a mark to the deleted text instead. But since I’m still very new to ProseMirror I’m not sure how to achieve this in a custom prosemirror plugin. I found methods like appendTransaction and filterTransaction. Using the latter it’s easy to prevent the deletion when in the mode. And using appendTransaction I could add a new transaction that undos the delete and sets the mark instead.

But is this really the way one would implement this properly? I feel like the more correct way would be to “amend” the transaction instead. I found this post amendTransaction - #2 by marijn but it didn’t seem to go anywhere.

I also found this post about the very same feature Suggestions which also didn’t seem to result in anything.

Can anyone more experienced with ProseMirror maybe give me a pointer into the right direction, or share how they would implement a feature like this?

1 Like

For starters I’m trying to handle deletions. My naive implementation right now looks like this:

new Plugin({
key: new PluginKey('suggestEdit'),
appendTransaction(transactions, oldState, newState) {
    if (!useEditorState.getState().suggestMode) return;

    const deletion = transactions.find(tr => {
            return tr.steps.find(step => step instanceof ReplaceStep)
        })?.steps[0] as ReplaceStep|undefined;
    if (!deletion) return null;

    // that's my own comments extension
    const marker = newState.schema.marks['comments'].create({
        threadId: uuidv4(),
        type: 'delete'
    });

    const tr = newState.tr;

    return tr
        .step(deletion.invert(oldState.doc))
        .addMark(deletion.from, deletion.to, marker)
        .setSelection(Selection.near(tr.doc.resolve(deletion.from)));
},
})

This partially works, at least for deleting selected text.

1 Like