This project demonstrates how certain Yjs → ProseMirror conversions can mutate the underlying Yjs document when the schema doesn’t include all node types present in the fragment (specifically a heading). It also lists a few safe workarounds.
- Node.js 18+ and npm
- Install dependencies:
npm install
- Start the dev server:
npm run dev
- Open the URL shown in your terminal (typically http://localhost:5173).
- In the running app, type a heading anywhere (e.g., “# My Heading”, typing "#" + space will create a heading line) so a heading node is stored in the Yjs fragment.
- In the code, find the “START HERE” section inside the setTimeout.
- Ensure the schema defined there omits the Heading extension (as it already does).
- Uncomment the five lines that call
yXmlFragmentToProseMirrorRootNodewith that schema (the block directly below “faulty or at least unexpected behavior”). - Save and reload the page.
What you’ll see:
- The call to
yXmlFragmentToProseMirrorRootNodewith a schema that lacks Heading will modify the Yjs fragment and remove the heading node. Due to persistence, the removal will stick across reloads.
- Option 1 (Clone first): Clone the fragment’s content into a fresh Yjs Doc and run
yXmlFragmentToProseMirrorRootNodeon the clone to avoid mutating the original fragment. - Option 2 (Validate, don’t mutate): Use
yXmlFragmentToProsemirror, which will throw on schema mismatch but will not modify the fragment. - Option 3 (No validation): Use
yXmlFragmentToProsemirrorJSON, which doesn’t validate against a schema (no error, no mutation).
- The editor is created with content checks enabled. On schema/content errors, it becomes read-only and collaboration is disabled (handled by the configured content error callback).
- However, since all of this runs directly on the yjs, it bypasses any sanity checks inside the editor.
Content is persisted via IndexedDB (database name: testing). To reset:
- In the browser devtools, Application → IndexedDB → delete the “testing” database, or
- Run in the browser console:
indexedDB.deleteDatabase('testing')