ShareDB Integration

I’m interested in integrating ProseMirror collab with ShareDB. This question has been asked by others before but didn’t gain much traction, so I’ll ask again with a little more detail.

At a glance, a ShareDB OT & a ProseMirror Step appear quite similar. I’m wondering if it’s possible to wrap a Step within a custom OT type? This would be similar to how rich-text does for a QuillDelta.

I tried an implementation myself but got stumped implementing transform (op1, op2, side) as it doesn’t seem like we can apply a Step to another Step. I thought about using a Transform instead, though it doesn’t seem possible to Transform a Transform either.

I’m not an expert in this space, so I’m perhaps misunderstanding the difference between the ShareDB & ProseMirror collab model. I’ve read marijn’s collab post probably forty times & believe I understand why OTs weren’t used to begin with.

Is a ShareDB integration feasible? Or is this a dead end?

Thanks.

1 Like

I know nothing of ShareDB and whether combining it with ProseMirror is going to work, but something like step1.map(step2.getMap()) might be what you’re looking for here (though step mapping does not take a parameter like side, and as such might not cover what you want).

1 Like

@marijn, thank you so much for your reply.

Does .map actually transform the Step itself? I thought it just transformed the character position mappings? Or am i thinking about this wrong?

The quill example defined transform like so:

const a = new Delta().insert('a');
const b = new Delta().insert('b').retain(5).insert('c');

a.transform(b, true);  // new Delta().retain(1).insert('b').retain(5).insert('c');
a.transform(b, false); // new Delta().insert('b').retain(6).insert('c');

Maybe i should be considering wrapping a Step[] inside a ShareDB OT?

@marijn You’re right. I misread what transform is supposed to do.

I ended up wrapping an array of Step’s. I think i have it working? Are there any red flags that anyone sees?

Update: Removed broken code to not lead anyone else astray

Indeed, with map lacking a side, it doesn’t seem to work. This is my latest attempt. After this post, i’ll stop live coding. But would love to talk with anyone more about this approach.

function mapToSide (source: Mappable, side: 'left' | 'right'): Mappable {
  const assoc = side === 'left' ? -1 : 1

  return {
    map (pos: number): number {
      return source.map(pos, assoc)
    },

    mapResult (pos: number): MapResult {
      return source.mapResult(pos, assoc)
    }
  }
}

function transform (a: unknown[], b: unknown[], side: 'left' | 'right'): unknown[] {
  const mapping = mapToSide(getMapping(b), side)

  return a.map(step => {
    const result = deserializeStep(step).map(mapping)
    if (result == null) throw new Error('Could not transform ProseMirror Step')
    return result.toJSON()
  })
}