NodeSelection of an inline selectable atom

I’m trying to build a feature that inserts “snippets” into the document. These are predefined structures that contain a mix of placeholders and predefined (but still editable!) content.

I’m struggling with correctly setting the selection in some cases.

A case that works:

structure to insert:

    const nodeToInsert = schema.node(
      'title',
      { __rdfaId: uuid() },
      schema.node(
        'paragraph',
        null,
        schema.node('placeholder', {
          placeholderText,
        })
      )
    );

A case that doesn’t work:

structure to insert:

    const nodeToInsert = schema.node('motivering', {__rdfaId: uuid()}, [
      schema.node('heading', {level: 5}, schema.text('Bevoegdheid')),
      schema.node(
        'paragraph',
        null,
        schema.node('placeholder', {
          placeholderText: 'motivation',
        })
      )
    ]);

In each case, I’m trying to nodeselect the placeholder node. This is a node of our own making, with the following spec:

export const placeholder: NodeSpec = {
  attrs: { placeholderText: { default: 'placeholder' } },
  inline: true,
  group: 'inline',
  selectable: true,
  draggable: false,
  atom: true,
  defining: false,
  toDOM(node) {
    return [
      'span',
      { class: PLACEHOLDER_CLASS, ...node.attrs, contenteditable: false },
      node.attrs.placeholderText,
    ];
  },
  leafText(node) {
    return node.attrs.placeholderText as string;
  },
  parseDOM: [
    {
      tag: 'span',
      getAttrs(node: HTMLElement) {
        if (node.classList.contains(PLACEHOLDER_CLASS)) {
          return { placeholderText: node.innerText };
        }
        return false;
      },
    },
  ],
};

In each case, after insertion, I’m simply making a new NodeSelection with NodeSelection.create(tr.doc, insertionPos + hardcodedOffset). In case 1, that offset is 2, in case 2, that offset is 15.

In case 2, the heading gets nodeselected instead of the placeholder. I’ve confirmed that the hardcoded offset is correct, and tried all manner of +1s and -1s, as wel as using NodeSelection.near and family. In no way can I get it to select the placeholder, whereas in case 1 (and another similar case) it worked like a charm. I also have another broken case, where the offset is 4. In each case, the inner structure is the same: a paragraph with a single placeholder node.

While writing this out, I realized we can simply use a block-level placeholder node in these cases. However, to me the fact I cannot nodeselect an inline atom consistently still merits discussion.

Clicking manually on the placeholder works as expected, triggering the special node-selection styling we have for it. I have a gut feeling I’m bumping into some less-than-fully-supported combinations of nodespec properties here, but maybe I’m missing something.

It is likely, depending on the context into which you insert this, that the inserting ends up creating a wrapper node and/or moving out of incompatible parent nodes, which will cause the position of the node you insert to be different than the actual position at which you inserted it. Look at the new document and see where the placeholder ended up, possibly by iterating through the range around the insertion.