Content affinity via read from DOM

I’m working on trying to improve caret positioning for our inline-code (mark) text. The current situation has the caret positioned offset from where typed characters appear:

Ideally the caret renders in the position where text will actually appear. The above example using the CSS:

.code {
  padding: 2px 4px;
  background: #fefefe;
  border: 1px solid #ccc;
  font-family: monospace;
}

The situation can be improved by avoiding horizontal padding, and opting for spacer pseudo elements instead:

.code::before,
.code::after {
  display: inline-block;
  width: 5px;
  background: transparent;
  height: 1px;
  content: '';
}

.code {
  padding: 2px 0;
  background: #fefefe;
  border: 1px solid #ccc;
  font-family: monospace;
}

This yields the improved behaviour:

All is good until you:

  1. Make a selection containing a few leading characters.
  2. Delete those characters.
  3. Type a letter.

After step (2), the browser places the caret outside the element:

However ProseMirror overrides that and places the caret back inside the element, and upon typing a character inserts that character outside the element.

Apart from the ProseMirror behaviour, the experience is pretty decent. It’s a bit awkward in Chrome because caret positions are collapsed at node boundaries, but in Firefox (which doesn’t seem to collapse caret positions) it’s quite pleasant:

So a couple of questions:

  1. Is it viable to improve ProseMirror’s reading from DOM behaviour to better interpret text insertion around the boundaries of an element (in this case some marked text) so that the native caret position and insertion location is honored?
  2. Even if (1) was successful, the behaviour would remain awkward in Chrome. Ideally there’d be two caret positions straddling the boundary of the element (as Firefox does) so that the user can decide whether to insert text inside or outside a mark. Do we have an idiomatic / recommended approach to this on the horizon? (e.g. perhaps something using node views and zero-width characters)
1 Like

ProseMirror doesn’t have a concept of cursor positions being inside or outside a given mark. A text position points at a given offset into a textblock, and which marks are applied when text is inserted there depends on the marks present and those marks’ inclusive option.

For most marks, this is simply a bad idea, since the two wouldn’t be visually distinct, and this’d just be a source of invisible state and unpredictable behavior.

Does your gif show this? I’m not seeing this here NOR in my very similar implementation of inline-code. My pseudo elements use:

    letter-spacing: -4px;
    content: "\00a0";

Hmmm, I was having issues with cursor placement not being correct in chrome but I can’t reproduce it now… maybe it is fixed o_O. In general I agree that it’d be better if there were pseudo caret positions that could be placed at the front and end of the code snippet. This would also potentially solve some issues around <figure>'s that I have.

1 Like

In firefox the behaviour is quite weird either. Even though you can place cursor before mark or before first character inside the mark, when you type characters jump out of the mark and appear in front of it (the same is true for the end of the mark):

And if text node with a mark is the first child of a paragraph it looks even worse (it’s true for Chrome, Firefox and Safari, and probably for others as well):

Maybe approach as for keep cursor next to the last node in paragraph might work here. I mean adding some empty span in front of the node with a mark might help here.