Collaborative Editing & Monaco
I love Visual Studio Code and I use it everyday. Visual Studio Code uses Monaco as it’s code editor.
Monaco is a really impressive code editor with some amazing features. One of the best features is really how fast it feels. Here is a HN discussion about why Monaco is really really fast. As i started to experiment with the editor more, I realized how amazing it would be if someone used Monaco as the base of a new collaborative editor. Maybe, I should do it.
Thinking about this problem a bit more, if I was going to build a collaborative editor it would need to have the following featureset:
- Reliable editing.
- No centralized datastore for the documents created. The documents should belong to the user who created them.
- Easy access controls. (I don’t want to share my document with the whole world, but only a few users).
- Presence support (Knowing who else is looking at the document in real time).
- Instant Replay (Being able to replay the entire session as it happened would be a differentiating feature).
Choosing my backend #
There are a couple of choices.
Web Sockets / WebRTC #
One approach is the use of Web Sockets / WebRTC, and operational transforms. I could use something like Socket.IO. The client could use the OT model of Monaco editor.
- WebSockets are flaky when there is more than one NAT between the user and the server.
- Need to manage infrastructure for WebSocket servers.
Google Drive Realtime API #
I could use the Google Drive Realtime APIs.
- No need to manage infrastructure. Server-less. Powerful JS API.
- No need to manage access control & permissions model. Can rely on Drive to provide it.
- No need for a datastore. Drive is the datastore. All the documents created also live in the user’s drive folder. They belong to the user.
Firebase Realtime Database and Firebase Cloud Fire Store #
- No need to manage infrastructure. Server-less.
- Need to manage permissions and access control (although its not too much work).
Looking at all the above options, I decided to go with Google Drive Realtime API, given that I get all the permissions management for free. The Firebase Cloud Fire Store API is another great option. It could be another alternative datastore that I could support in the future.
Okay, Let’s really build this #
I realized I need a couple of Cloud functions that will create new documents on Google Drive, using their REST API and annotate them with custom attributes, to help with discovering them.
The official documentation for Google Drive Realtime APIs stated that I need the https://www.googleapis.com/auth/drive scope. That was going to be too broad. So I figured out that I could use shortcut files. One shortcoming of shortcut files however, is they seem to get implicitly filtered by the Drive v3 list files API. So need to attach custom attributes that will help surface these files. Now i can finally use the less scary https://www.googleapis.com/auth/drive.file scope.
I started diving into Monaco editor, to understand the underlying editor model. I discovered Monaco exposes low level change events, that can be transformed into a set of changes that can be applied using the applyEdits API. I use the Google Realtime API to store these changes in Google Drive.
I had also fortunately authored AppAuth-JS. This is the library that I used to help the users log in, and request the drive scopes I need.
Once I got the basic mechanics of synchronizing changes working, I started to work on synchronizing cursor selections which turned into a slightly bigger challenge. I also had to synchronize selection invalidations and some spurious selections while editing a range. But eventually i got that working.
Demo time #
Here is a demo of all of this working.
Try it out #
Try it out here. For more information about the application take a look at this.
Google Drive Realtime API deprecation. #
Google Drive Realtime APIs are now deprecated in favor of the Firebase platform (Firestore and the Realtime database). So I am in the middle of migrating my datastores at the moment.
Video Editing / Recording #
My apologies for the slight cold I had when i recorded the video.