I’m attempting to use ProseMirror to replace my current note-taking workflow, which involves constantly switching between Typora/Overleaf/OneNote, with a single text editor capable of supporting blocks of text with semantic meaning.
My notes are full of inline and display math, written in LaTeX notation, so it is important that math be treated as a “first-class object” in my editor. I would like to replicate Typora’s math editing behavior as closely as possible using ProseMirror.
Typora-style Math Editing
Desired behavior:
- Property A: Show math as LaTeX code while the cursor is inside the math node. As soon as the cursor leaves the node, render using KaTeX
- Property B: Changing the cursor position by clicking / arrow keys should seamlessly transition between these two modes.
Below are some screenscaptures demonstrating the desired behavior in Typora (click to expand).
creating display math
editing display math
creating inline math
editing inline math
My Attempts
Let’s focus on inline math for now. Reading through the documentation / examples, it seems like NodeViews are the right tool to use. I have tried two approaches:
Attempt #1: Using contentDOM
Set a contentDOM
in the NodeView, so that ProseMirror is responsible for managing the appearance / editing of the NodeView, as in the . This successfully achieves Property B (cursor movement with arrow keys behaves as expected), but I cannot get Property A to work (toggling katex/code views).
The reason seems to be that NodeViews with a contentDOM have no way of knowing when the editing cursor has left the NodeView. (they only receive setSelection
events, not selectNode
and deselectNode
).
Attempt #2: Without contentDOM
(demo)
Without contentDOM
, the NodeView is responsible for its own rendering / editing, as in the footnotes example. Setting atom:true
in the schema for my inline math node ensures that selectNode
and deselectNode
are called when the user selects/deslects the NodeView, allowing me to toggle the inline math editor as needed.
So, this approach succeeds at Property A (toggle math editor), but unfortunately I am struggling to implement Property B, as the cursor is always set to the first position in the NodeView, regardless of which side I approach from:
cursor approaching from the left (good)
cursor approaching from the right (bad -- cursor should enter from the right)
Attempt #3: Plugin with arrowHandler
I have also tried adapting the arrowHandler
from the CodeMirror example to detect from which side the cursor enters / leaves the NodeView. However, I’m not sure how to pass this information along to the NodeView. I’ve seen other posts suggest using decorators for this purpose, but I’ve not yet been able to make that work.
Minimal Working Example
I set up a minimal working example (matching attempt #2) on codesandbox.
(if you have trouble getting the ProseMirror instance to appear, try typing a bit in the codesandbox editor to get it to refresh!)
Other Relevant Discussions
Regarding math in ProseMirror:
Regarding decorations to communicate with NodeViews:
- How can I communicate from a plugin to a custom NodeView?
- Updating NodeViews on node moved
- How to handle events inside decorations?
- Expanding / Collapsing view content
Miscellaneous:
- CodeMirror integration and selections
- How to create non-editable text segments in the document
- Cursor appears inside inline node when at end of preceding text node
- How can I attach attribute with dynamic value when new paragraph is inserted?
Questions
I’m still new to ProseMirror, so in addition to replicating the desired behavior, I also want to know if my solution follows ProseMirror “best practices”.
- How should I modify Attempts #1, #2, or #3 to achieve the desired behavior?
- If there are multiple solutions, what is the recommended way to achieve this behavior? Attempt #1, #2, or #3, or something else entirely?
- Based on other discussions, it seems like many people have tried to add math support to their ProseMirror editors. Unfortunately most askers seem to vanish after finding a solution to their problems! Do you know of anyone who has implemented similar behavior and posted their code publicly?
Thanks so much for this toolkit! I’m still learning, but I’ve really enjoyed working with ProseMirror so far!