I’m building a template editor and a preview editor. Both are editable ProseMirror/Tiptap editors.
A simplified example:
Template
Hello @FirstName
where @FirstName is a semantic placeholder.
Preview
Hello Jake
The preview needs to preserve the relationship between "Jake" and the original @FirstName placeholder.
For example:
-
If the underlying FirstName value changes from
JaketoJohn, the preview should automatically update toHello John. -
The user must also be able to place the cursor inside
"Jake"and edit it manually. -
Once the user manually edits that value, I need to keep track of that fact so later automatic updates do not overwrite the user’s text.
My initial idea was to represent the resolved value as an inline node with editable content, conceptually:
paragraph
text("Hello ")
firstName
text("Jake")
However, I ran into cursor/selection issues around the boundaries of the inline node. For example, typing immediately before/after Jake, moving the cursor across the boundary, etc. could behave unexpectedly.
Because of this, I currently represent the resolved value using a mark instead:
text("Jake", marks=[firstName])
This behaves much more naturally while editing.
The downside is that in large previews generated from sophisticated templates, the document JSON becomes difficult to inspect because the semantic information is spread across/repeated on marked inline content.
So my questions are:
-
Is an editable inline node with content the wrong model for this use case?
-
Are the cursor/boundary problems I encountered an expected limitation of inline nodes with editable content, or am I likely implementing the node incorrectly?
-
Would a mark generally be the recommended representation for this kind of editable-but-semantically-tracked range?
-
Is there another ProseMirror pattern that fits this use case better?