I’m currently facing a ProseMirror behavior/design question and I’m looking for the best solution.
I have a document structure like the following (two paragraphs, each paragraph has attributes such as align):
{
“type”: “doc”,
“content”: [
{
“type”: “paragraph”,
“attr”: { “align”: “center” },
“content”: [
{ "type": "text", "text": "It was a quiet evening." }
\]
},
{
“type”: “paragraph”,
“attr”: { “align”: “left” },
“content”: [
{ "type": "text", "text": "The sun was slowly setting beyond the hills." }
\]
}
]
}
What I want is to support two editor “modes” when working with the same underlying structure:
-
Paragraph-aware editor (normal behavior):
When the cursor is at the start of the second paragraph and the user presses Backspace (delete one character backward), ProseMirror merges the paragraphs (the second paragraph node disappears and the content is joined into the first paragraph).
This is fine for this editor.
-
Paragraph-unaware editor (custom behavior):
In this mode, I don’t want the paragraph node to be removed/merged at the boundary.
Instead, when the cursor is at the start of the second paragraph and the user presses Backspace, I want it to behave as if there is no real line break there, and the editor should delete the last character of the first paragraph (without removing the paragraph node).
For the “paragraph-unaware” editor, I’m currently using NodeViews to visually make it look like there’s no line break and also make it feel like the boundary is not a “character” the user can select/delete.
I tried implementing this by overriding Backspace via a keymap plugin, but I’m not sure what the best approach is to handle all the corner cases (especially things like deleting a selection range across paragraphs, or other complex deletion scenarios).
At the same time, I still want to represent line breaks using actual paragraph nodes, because structurally I need paragraph-level attributes like align.
Question:
What would be the recommended way in ProseMirror to implement this kind of mode-dependent deletion behavior while safely covering selection/range deletion and other edge cases?
Is overriding keymaps the right direction, or is there a more robust approach (e.g. custom transaction filtering, plugins that intercept replace steps, etc.)?