Wrap deleted content in a span

Hello everyone!

I am trying to build a track changes editor. I’ve searched high and low and found wonderful implementation by @TeemuKoivisto (kudos mate, you helped me a lot), but alas I am looking for something different.

I need to mark the changes done in the editor (both insertions, and deletions) permanently, not just decorate them.

I’ve made a plugin which basically appends a compensating transaction like so:

appendTransaction(transactions, oldState, newState) {
      console.log('appended transaction');
      const targetTransaction = newState.tr;
      targetTransaction.setMeta('compensating-transaction', true);
      transactions.forEach((transaction) => {
        transaction.steps.forEach((step) => {
          if (step instanceof ReplaceStep) {
            step
              .getMap()
              .forEach((fromA: number, toA: number, fromB: number, toB: number) => {
                if (fromA !== toA) {
                   console.log('this is a delete change');
                }
                if (fromB !== toB) {
                  console.log('this is a add change');
                }

                if (fromA !== toA && fromB !== toB) {
                  console.log('this is a modify (delete+insert) change');
                }

              });
          }
        });
      });
      if (targetTransaction.docChanged) {
        return targetTransaction;
      }
      return null;
    },

This is just handling one case (ReplaceStep), I’d have to research a bit more to figure out how to handle ReplaceAroundStep.

I have this in my schema

  deletion: {
    content: 'inline*',
    attrs: {
      'data-change-type': { default: 'deletion' },
      class: 'track-changes-delete'
    },
    parseDOM: [
      {
        tag: 'span',
      },
    ],
    toDOM() {
      return ['span', {
        class: 'track-changes-delete'
      }, 0];
    },
  },
  paragraph: {
    content: 'inline*',
    group: 'block',
    parseDOM: [
      {
        tag: 'p',
      },
    ],
    toDOM() {
      const children = [0];
      return ['p', {}, ...children];
    },
  },
  text: {
    group: 'inline',
  },

But I cannot for the life of me figure out how to replace the deleted text with a wrapped version of it. I need it to be inline span with some attributes attached to it. I understand that this gets complicated really fast (how to handle a situation where you delete a range between paragraphs, multiple different nodes etc.)

Reason I cannot figure this out is because I have a lot of new concepts battling in my head for attention (Fragment, Slice, Node etc.) and I am still learning how to use all of this.

Can anybody just point me into right direction?

I find these libraries really well written and I really want to understand all of this.

Update:

I guess I can do something with marks, that will also modify the state. I will am now exploring if I can add marks on each addition or deletion.

I managed to bring back the deleted content and mark it with deleteMark.

const content = oldState.doc.slice(fromA, toA);
targetTransaction.replace(fromB, toB, content);
const deleteMark = schema.marks.deletion.create();
targetTransaction.addMark(fromB, fromB + content.content.size, deleteMark);
deletion: {
    attrs: {
      'data-change-type': { default: 'deletion' },
      class: {default: 'track-changes-delete'}
    },
    parseDOM: [
      {

        tag: 'span[data-change-type="deletion"]',
        getAttrs() {
          return {
            'data-change-type': 'deletion',
            class: 'track-changes-delete',
          };
        },
      },
    ],
    toDOM(node) {
      return ['span', node.attrs, 0];
    },
  },

Hope this helps somebody. Cheers!

Hi, I’m also facing these same issue, if you get any solution’s please inform me