Broken table with prosemirro-collab

Hello

I have a collaborative editing setup, with a custom slightly modified vanilla schema and with the tables package on top. In a scenario with two users editing the same document, after pm-collab reconciliation, I end up in a scenario where the < table > head(wrapper) dom node gets substituted with a < paragraph > node, resulting in the following malformed dom structure:

<paragraph>
   <tbody />
</paragraph>

The scenario looks like the following:

User A Send step version 1

{
  "stepType": "replace",
  "from": 2,
  "to": 2,
  "slice": {
    "content": [
      {
        "type": "table",
        ...
      }
     ]
  }
}

Receive from authority step version 1 (ack)

Meanwhile parallel to this: User B

Send step version 1 (the same version as user A, but they are editing on a paragraph in the same pos)

{
  "stepType": "replace",
  "from": 2,
  "to": 2,
  "slice": {
    "content": [
      {
        "type": "paragraph",
        ...
      }
     ]
  }
}

Receive from authority:

Receive step version 1

{
  "stepType": "replace",
  "from": 2,
  "to": 2,
  "slice": {
    "content": [
      {
        "type": "table",
        ...
      }
     ]
  }
}

In terms of steps and authority setup everything looks fine. My expectation here is for User B to simply drop the paragraph they were editing all together and fill in the table received from the authority (User A). That does happen eventually, however the reconciliation results in a broken (hybrid) table for User B as described at start. Any ideas on how can I avoid this “hybrid” table, could it be a schema level problem, an authority/flow problem or simply an edge case with tables and pm-collab?

Thanks in advance

This is odd. If they are on the same version, the same content should exist at a given pos. Are you sure you are implementing the protocol correctly?

In my case it is a race condition. Users are at the same version, same content so far. Both are focused at the same paragraph. One user is undoing a table delete, hence a table will be added. The other user is typing on that paragraph.

Can you provide me with a start document, JSON for the steps being sent, and a description of in which order they get accepted by the server, so I can observe this happening?

Sure, it would have been easier for me to see if I could replicate this in the existing pm-collab example, but as far as I am aware there is no tables bundled in there.

As I said I have a custom schema, probably not that relevant for this topic The starting document

{"type":"doc","attrs":{},"content":[{"type":"paragraph","attrs":{}}]}

Version 70

User A (sendable)

[{"stepType":"replace","from":2,"to":2,"slice":{"content":[{"type":"table","attrs":{"styleId":"TableNormal","borders":true},"content":[{"type":"tableRow","content":[{"type":"tableCell","attrs":{"colspan":1,"rowspan":1,"colwidth":[80]},"content":[{"type":"paragraph","attrs":{"styleId":null,"alignment":null,"indent":null,"spacing":null,"tabs":[],"numbering":null,"dir":"ltr","isSD":true}}]},{"type":"tableCell","attrs":{"colspan":1,"rowspan":1,"colwidth":[80]},"content":[{"type":"paragraph","attrs":{"styleId":null,"alignment":null,"indent":null,"spacing":null,"tabs":[],"numbering":null,"dir":"ltr","isSD":true}}]}]},{"type":"tableRow","content":[{"type":"tableCell","attrs":{"colspan":1,"rowspan":1,"colwidth":[80]},"content":[{"type":"paragraph","attrs":{"styleId":null,"alignment":null,"indent":null,"spacing":null,"tabs":[],"numbering":null,"dir":"ltr","isSD":true}}]},{"type":"tableCell","attrs":{"colspan":1,"rowspan":1,"colwidth":[80]},"content":[{"type":"paragraph","attrs":{"styleId":null,"alignment":null,"indent":null,"spacing":null,"tabs":[],"numbering":null,"dir":"ltr","isSD":true}}]}]}]}]}}]

User A (recieveTransaction)

[{"step":{"stepType":"replace","from":2,"to":2,"slice":{"content":[{"type":"table","attrs":{"styleId":"TableNormal","borders":true},"content":[{"type":"tableRow","content":[{"type":"tableCell","attrs":{"colspan":1,"rowspan":1,"colwidth":[80]},"content":[{"type":"paragraph","attrs":{"styleId":null,"alignment":null,"indent":null,"spacing":null,"tabs":[],"numbering":null,"dir":"ltr","isSD":true}}]},{"type":"tableCell","attrs":{"colspan":1,"rowspan":1,"colwidth":[80]},"content":[{"type":"paragraph","attrs":{"styleId":null,"alignment":null,"indent":null,"spacing":null,"tabs":[],"numbering":null,"dir":"ltr","isSD":true}}]}]},{"type":"tableRow","content":[{"type":"tableCell","attrs":{"colspan":1,"rowspan":1,"colwidth":[80]},"content":[{"type":"paragraph","attrs":{"styleId":null,"alignment":null,"indent":null,"spacing":null,"tabs":[],"numbering":null,"dir":"ltr","isSD":true}}]},{"type":"tableCell","attrs":{"colspan":1,"rowspan":1,"colwidth":[80]},"content":[{"type":"paragraph","attrs":{"styleId":null,"alignment":null,"indent":null,"spacing":null,"tabs":[],"numbering":null,"dir":"ltr","isSD":true}}]}]}]}]}},"clientID":"852266038"}]

Meanwhile at the same version 70, user B is racing their changes

User B (sendable)

[{"stepType":"replace","from":3,"to":3,"slice":{"content":[{"type":"text","text":"d"}]}},{"stepType":"replace","from":4,"to":4,"slice":{"content":[{"type":"text","text":"d"}]}},{"stepType":"replace","from":5,"to":5,"slice":{"content":[{"type":"text","text":"d"}]}}]

User B (recieveTransaction)

[{"step":{"stepType":"replace","from":2,"to":2,"slice":{"content":[{"type":"table","attrs":{"styleId":"TableNormal","borders":true},"content":[{"type":"tableRow","content":[{"type":"tableCell","attrs":{"colspan":1,"rowspan":1,"colwidth":[80]},"content":[{"type":"paragraph","attrs":{"styleId":null,"alignment":null,"indent":null,"spacing":null,"tabs":[],"numbering":null,"dir":"ltr","isSD":true}}]},{"type":"tableCell","attrs":{"colspan":1,"rowspan":1,"colwidth":[80]},"content":[{"type":"paragraph","attrs":{"styleId":null,"alignment":null,"indent":null,"spacing":null,"tabs":[],"numbering":null,"dir":"ltr","isSD":true}}]}]},{"type":"tableRow","content":[{"type":"tableCell","attrs":{"colspan":1,"rowspan":1,"colwidth":[80]},"content":[{"type":"paragraph","attrs":{"styleId":null,"alignment":null,"indent":null,"spacing":null,"tabs":[],"numbering":null,"dir":"ltr","isSD":true}}]},{"type":"tableCell","attrs":{"colspan":1,"rowspan":1,"colwidth":[80]},"content":[{"type":"paragraph","attrs":{"styleId":null,"alignment":null,"indent":null,"spacing":null,"tabs":[],"numbering":null,"dir":"ltr","isSD":true}}]}]}]}]}},"clientID":"852266038"}]

The malformed result table for User B

<div class="paragraph" dir="undefined" issd="undefined" styleid="TableNormal"><tr><td colspan="1" rowspan="1" colwidth="80"><div class="paragraph" dir="ltr" issd="true"><br class="ProseMirror-trailingBreak"></div></td><td colspan="1" rowspan="1" colwidth="80"><div class="paragraph" dir="ltr" issd="true"><br class="ProseMirror-trailingBreak"></div></td></tr><tr><td colspan="1" rowspan="1" colwidth="80"><div class="paragraph" dir="ltr" issd="true"><br class="ProseMirror-trailingBreak"></div></td><td colspan="1" rowspan="1" colwidth="80"><div class="paragraph" dir="ltr" issd="true"><br class="ProseMirror-trailingBreak"></div></td></tr></div>
1 Like

Do you mean it is making concurrent changes, without having seen the step from user A?

That first step for user B has from/to at position 3, which doesn’t exist in the starting document you gave, so I am unsure how to interpret this data.

Yes

Yes, my bad. It is not as easy to replicate this scenario so I needed to do some prep. Before reaching to version 70 in my example I added one paragraph on both users. On first user I also added a table and deleted it immediately.

So, On version 70:

User A → undo table delete

User B → type ‘ddd’

If I simulate that I get the expected result—both users end up with a paragraph containing ‘ddd’ followed by a table. You’re going to have to be more precise here I think.