I’m working a tool where I’d like to have many code editors and RichText editors on a canvas. I’d like to try having a single global UndoManager instead of having a per editor undomanager as per the built in ones in most of the example integrations. I’ve banged my head against this for a few days. I stripped out the undo/redo and tried just using an UndoManager on a top level component, but the tiptap integration doesn’t seem to handle changes happening that didn’t come from the sync plugin or the initial indexeddb sync.
TLDR: I came across at some point a comment from I think Kevin mentioning having undo managers that are outside a single editor instance. Does anyone have an example or recommendations for how I’d implement this?
For context:
I’m building a zoomable canvas where you can place/resize as many code editors (Monaco) and RichText (tiptap or prosemirror) on the canvas. I can easily get multiple instances going at least for tiptap editors right now (I’m having issues with embedding a Monaco in next.js with existing example code).
You can create a single undo-manager to track multiple editors. All editor bindings accept a undoManager option that allows you to define a custom undo manager. You just need to make sure that the shared type of the editor is tracked. In short, define an undo manager like this undoManager = new Y.UndoManager([yquill, yprosemirror]) and then add it to the editor binding.
There is an exhaustive documentation on the inner working of the UndoManager on the documentation website: https://docs.yjs.dev/api/undo-manager If you still have trouble, maybe you can post some source-code here?
Thanks for the reply. I was able to get my y-codemirror instances working, had missed that you can provide your undomanager. However I can’t get my richtext editors using my global undomanager because y-prosemirror does not support providing an undo manager. It creates it’s own, see:
Inlined for convenience:
export const yUndoPlugin = ({ protectedNodes = new Set(['paragraph']), trackedOrigins = [] } = {}) => new Plugin({
I have a feeling trying to use this less traveled path is a bad idea, looks like there is some nice details that are baked into the undo logic when you let the editor internals have an isolated undomanager. I’d like to try a bit more though, as I prefer a global undo/redo for my tool.
You are right. Feel free to open a PR that adds undoManager as one of the options. If undoManager is set, the other parameters should be ignored.
I think this is the way to go. I implemented something similar with the codemirror editor. The editor bindings are supposed to accept a custom undoManager, and then simply add a origin to the undoManager.
I went with Quill and CodeMirror for my use case. I haven’t worked on this in a few months and am swamped, so I forget exactly the issues I was having. I just reviewed the code quickly to make sure that information was correct.
Code mirror worked like this: const binding = new CodemirrorBinding(props.text, editor, null, { yUndoManager: yUndoManager })
Quill I am just not having this issue even though I didn’t prove an undo manager, not following what that works exactly. Maybe it’s not.
I have editors with multiple different ydocs. but I want to have single history stack, so user can undo last changes from all editors. How can we achieve this? I am using tiptap editor