Getting useful positions from a node

My users want some way to remove a table from the editor state after pasting it in from another program. Searching around, I found Unable to delete tables? - #2 by marijn and some other issues it links to to resolve this - basically, it seems like the current best-practice approach is to simply prepend a blank text node before the pasted table markup, as this allows selection to be certain the user intends to select and delete an entire table rather than only its markup. Unfortunately, my users tested this locally and found some limitations - the deletion behavior could somehow be disabled if the users did the wrong thing in the table in a way I can’t even really describe. So, that solution was out.

Instead, I thought of an alternative. The editor we’re using doesn’t have a widget for users to make new tables - they can only paste them from a different program. So, we don’t really need to support empty tables - why not just have a plugin that immediately removes empty tables from the document whenever they come into existence?

Unfortunately, I can’t get this to work!

   public static makeEmptyTableRemoverPlugin(): Plugin{
        let firstEmptyTable = function(state: EditorState) : {offset: number, node: Node} {
          let tableType = state.schema.nodes['table'];
          let tables : {offset: number, node: Node}[] = [];
          state.doc.forEach((node, offset, index) => { if (node.type === tableType) { tables.push({offset, node})}});
          for (let table of tables) {
            if (table.node.textContent === '') { 
              return table;
          return null;
        return new Plugin({
            appendTransaction: function(transactions: readonly Transaction[], oldState: EditorState, newState: EditorState): Transaction {
              let empty = firstEmptyTable(newState);
              if (!!empty) {
                let tr =;
                //this doesn't work!
                tr.deleteRange(empty.offset, empty.offset + empty.node.nodeSize + 1);
                return tr;
              else {
                return null;

The code as written here does not work as I want it to! The parameters I’m giving to deleteRange, or delete as I was trying earlier, don’t cover the entire table - the contents can be removed, but I want to remove the <table></table> part itself.

I’m assuming that this is simply because I’m not sure completely what I’m doing about resolved positions, offsets, and indices. Honestly, when I originally started working on this I assumed there was some delete transaction/transformation that took in Nodes as a parameter - of course, this was folly, and transactions really want to deal with positions. But I’m not really sure what I’m supposed to do to convert the table node into a useful position! What exactly does the “pos” parameter to the resolve method even mean? And can I even use methods on the returned ResolvedPosition to encompass the whole table container, or am I barking up the wrong tree?