ueberdosis / tiptap

The headless rich text editor framework for web artisans.
https://tiptap.dev
MIT License
27.63k stars 2.3k forks source link

How do I listen for caret/cursor/selection change? #346

Closed kshitizshankar closed 5 years ago

kshitizshankar commented 5 years ago

How do get the current cursor/caret position and get a callback when it changes?

I've been playing around with the Collaboration extension code and it seems straight forward. I got a majority of it working and I am struggling with trying to figure out how to show cursor positions of other users.

One approach that I've been exploring is to draw the marker on top of the page and pass the positions back and forth. At least that's what I did for another collaboration related thing where I had to show the mouse positions with mouse move events.

So -

  1. Is there a better way to get this done?
  2. How do I actually listen for cursor change and selection change specifically?

Thanks!

kshitizshankar commented 5 years ago

Update - I played around with the MenuBubble Plugin code to understand how it works and the overall plugin system in Prosemirror ecosystem.

For now, I've created a new plugin same as the MenuBubble Plugin - that resolves the position of the selection's endpoint (instead of the middle position for the Menu). I am using that to render the cursor marker (similar to MenuBubble).

df90e630-fcec-4c89-91ef-623bdc264bf9

This also gave me a way to trigger other actions when caret position or selection changes (like sending over the position of the caret over a socket which can then be used for showing the cursor of other active users and change in real-time)

Not sure if this is the right way to do this or if there was a direct way to actually listen for selection update - but it works for now!

philippkuehn commented 5 years ago

I also worked in a branch for supporting cursors in the collaboration extension a few weeks ago. The main difference is to watch the transaction event instead of update event (because a selection change is no change to the document). But I don't found a solution to keep cursors in sync with the different collab versions of all clients for now. How you you solved this?

kshitizshankar commented 5 years ago

Haven't implemented the cursor sync yet - Will add my findings here once I reach there - Overall concept was to Get the Cursor Position on Editor 1 - Broadcast it to other connected users - Other editors handle how to draw it (so maybe a plugin that takes in cursor positions and draws them in the UI - At least thats my base to go on with). For my use case, it isn't important to be "100% accurate" - so there is some room there I think..

rdlh commented 5 years ago

Hi @kshitizshankar !

Any news on this? Would love to get some information about this feature! :)

kshitizshankar commented 5 years ago

Sorry haven't gotten around to it yet. I did an initial mockup where I broadcast the selection of a user to all other clients and draw decorations using a custom extension. Seemed to work but it wasn't crucial so I've parked it for now. Will be revisiting it shortly though. I'll reopen the issue and add my findings when I implement it.

kshitizshankar commented 4 years ago

@rdlh @philippkuehn Finally got around to this again - so adding an update here.

Few resources that actually helped in figuring out the frontend as well as the backend bits -

  1. I am using the existing collab plugin from prosemirror/tiptap and kept the cursors and other metadata separate.
  2. A good starting point for getting a better understanding of the server for my use-case (FeathersJS with Rest/Socket) - was https://github.com/jure/pubsweet-blogger/blob/master/README.md
  3. Next, I found this https://github.com/mms-gianni/tiptap-collaboration-demo/blob/master/src/components/extensions/Collaboration.js which actually implements a basic setup for sharing cursors inside Tiptap.
  4. I ended up dissecting the AtlasKit's Collaborative editor to get a better understanding of how we can keep the server trivial and handle the mapping etc on the editor itself. The pubsweet-blogger helped in filling the gaps a bit for the server and the AtlasKit's CollabEdit plugin helped with the frontend (It's in TS+React - which I haven't used much so might be a lot easier for someone who is familiar with that stack)

So with all that, I created a separate service that sends/receives participants and their selections. I created a prosemirror plugin to process the cursors and draw them. It maps them if the transaction did any changes to the document. With that, I was able to sync the cursors and text selections (still working on node selections and some edge cases)

Here is what I have so far - cursor-prosemirror

I will add my findings as I explore it further. Will try to extract the code into a gist which might be useful for others working on something similar.

dokudoki commented 4 years ago

@kshitizshankar Thank you for the resources, it is really helpful. I would love to look at the gist if they are available.