I am working on an interactive scientific writing platform called curvenote.com, which integrates with Jupyter. We recently released a few open source projects that we have been working on over the past year that might be interesting/useful to this community.
Happy to answer questions! Let me know what you think! The prosemirror community & library is fantastic, thanks!
-Rowan
@curvenote/editor
I think this is pretty well trod territory by now: another react wrapper of prosemirror with suggestions, so I will stick to what is different, our inline interactive widgets. Our goal is to make it easy to write content like this - with inline sliders, data, variables, etc. that hook up to visualizations.
We went with the approach of having prosemirror wrap web-components that contain the interactivity, which means that there is very little custom view from the prosemirror perspective - just manipulating XML. This made the integration with prosemirror really simple (mostly around right clicking on the widgets and popping up a menu). We communicate state back to react using a shared redux store.
Suggestion framework
The other piece that is currently buried in there is our suggestions, which combine an input rule trigger and then a decoration, I don’t think I have seen this approach yet, and it works quite well. Happy to spin this out if it is useful to folks.
sidenotes
sidenotes is a library for placing notes/comments to the side of a document. Just the placement, no comment UI included. Allows inline references as well as “block” based reference fallbacks.
@curvenote/schema is a library to convert between markdown and the prosemirror internal format. There is some support for latex export here, as well as MyST flavoured markdown import and export.
Looks great. Are you doing anything special with the interaction between sidenotes and copy/paste? (Such as do notes move along with cut/pasted content? Do they duplicate when you copy?)
No, we haven’t solved copy/paste references at the moment - we will update the thread here as well when we do some work on this!
Right now the inline references are a decoration, but if that reference gets lost (e.g. through a cut/delete), the comments fall back to being referenced at the “block” level, which seems ok for now. The sidenote framework does work with multiple inline references pointing to the same note/comment though, so looking forward to getting this feature in. I will see if there is anything we can do to make it generalizable.
Something I have recently come to wonder with PM editors - how do you manage syncing? Do you use some library that utilizes IndexedDB (such as RxDB) and then apply/poll changes over a websocket or use some custom implementation? And do you aim to support offline mode with possible large numbers of conflicting changes that must be rebased/cherry picked somehow?
Using web components seems interesting - not sure do you use them as nodeViews or just nodes. Probably more appropriate than using React for the same purpose. Wiring them to the application logic seems a bit of hassle but evidently it can be done.
Making everything flow through Redux seems appropriate as it avoids managing various event listeners. It seems you’ve separated the UI & API state into Redux stores and PM specific state into plugins?
Btw there are https://curevnote.com/ links in editor README.
That is entirely possible on the math node selections. We have been having some problems with our math nodes specifically - especially with the cursor (at least in chrome) looking like it was on the wrong side. We are probably going to move something a bit closer/help improve to prosemirror-math.
With regard to syncing, we are currently just using the standard prosemirror collaboration module. Our product is built on top of firebase at the moment, and we are using their real-time infrastructure to help keep things in sync. All of our changes are dispatched to our redux state as a middleware of sorts to prosemirror - and that is where we add the collaboration, and keep various views of the editor in sync.
With regard to the web-components, we were using them as just other nodes (not even node-views) for a while, and then wrapped them in this class which basically adds right click ability. The only reason we didn’t do that in the web-components directly was that it is a different library and that was out of scope. I actually quite like the approach of doing this through web-components, and it makes the integration with prosemirror really, really easy.
To connect back up to application logic, we are just using the same redux store in both places (including relevant prosemirror-plugin state). That redux store is also responsible for the editor state (just holds a reference to prosemirror-state). This also means that the state is available for other application components through selectors/actions/etc.
The curvenote.dev vs curvenote.com are our open source work and our scientific writing product that we are building.
Something I have recently come to wonder with PM editors - how do you manage syncing? Do you use some library that utilizes IndexedDB (such as RxDB) and then apply/poll changes over a websocket or use some custom implementation?
Similar to what rowanc1 said, for persisting a document state I have found success in overloading the collaboration module to also help persist the latest state of the document wherever you want : indexdb, local storage, or some DB.
Have a look at what the Tiptap folks are doing with yjs.
At present I’m doing very simple Pmr collaboration in an app I’m developing (Clibu Notes) which uses Logux.io to handle full offline use and sync when back online.
Both yjs and Logux are impressive and help greatly with developing offline apps and CRDT eventual consistency.
@rowanc1 Impressive work indeed. IMO it is a pity that so many folks using Prosemirror are using React, however I sort of understand why.
I’m personally using Lit-Element and Lit-Html which is much lighter weight than React and work really well for developing Web Components. Including embedding Web Components in Prosemirror.
Hey thanks y’all for the replies and the links, that Logux seems very intriguing. I’m bit undecided about yjs since it is still CRDT which seems a quite heavy abstraction.
At one point I also intended to use Redux for the whole thing (until I decided it’s too much work to setup and I should just use what works and get going) and one thing I wondered was how to avoid duplicating plugin state to the stores. I guess it wasn’t that big of a deal after all.
And I probably should have specified, I think curevnote.com (in verbatim) is a typo.