Localstorage projects using Y.js #1236
Draft
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
What this adds
Approach
Storage
There is a list of projects, stored in an IndexedDB instance. This contains the project names, last edited dates, against a fixed GUID.
There are the projects themselves, which consist of a Y.Doc per project, backed by y-indexeddb which creates a DB per doc.
The revisions themselves are serialised Y.js Updates which act as a complete binary delta since their parent revision. They are stored in a separate DB again, as a linked list.
Note that the "working copy" of the project (in a version control analogy) is a
Y.Docbut all the "commits" are Uint8Arrays and have to be unpacked to a working copy before they will work.Revisioning
Currently the only way to create a revision is to click the clock button then "Save as new revision." This calculates the diff between the previously-saved revision (if any) and the current working copy, and adds it to the end of the linked list of revisions for the project.
Loading a previous revision creates a new project, so that it does not revert any of the other project's changes, but this is not necessarily a proposed behaviour for the future.
Code structure
I've tried to keep everything reusable inside the
src/project-persistencefolder so that it can be extracted as a modular add-on to other apps in future.ProjectStorageProvider.tsx- underlying state, not intended to be used by consuming apps directly (this is a decision that helped me see clearly what was being used in the investigative work but might not be relevant for you)persistent-project-hooks.ts- if a project is loaded, this provides it, as well as itsawarenesswhich is Y.js' way of managing multiple interactive users on a document, where their cursors are etc.project-list-hooks.ts- create, load, delete, and list user projectsproject-history-hooks.ts- view and interact with a project's history.There are also some chakra components for displaying common information about a project, although loading a project also has to do app-specific things so I didn't try to force it into a complete component that works everywhere.
Python Editor specifics
CodeMirror
CodeMirror has its own easy yjs integration that makes this really intuitive to implement. Check out the diff in
src/editor/CodeMirror.tsxfor the directness of the implementation.Project Browser
There was no existing project browser, so I hacked the routing to allow it in
src/ProjectPageRouting.tsx. This was not a designed solution, but the Python editor already assumes it has control over the router for event information and I didn't want to get caught up in picking it apart. Currently the only way to get to the project view is via the root path/.