### Y-Websocket Server with LevelDB Persistence Source: https://github.com/yjs/docs/blob/main/ecosystem/connection-provider/y-websocket.md Starts a y-websocket server that persists document updates to a LevelDB database. Ensure LevelDB is installed and configured. ```bash PORT=1234 YPERSISTENCE=./dbDir node ./node_modules/y-websocket/bin/server.js ``` -------------------------------- ### Install Monaco Editor and Yjs Dependencies Source: https://github.com/yjs/docs/blob/main/ecosystem/editor-bindings/monaco.md Install the necessary packages for Monaco editor, Yjs, and the y-monaco binding using npm. ```bash npm i monaco-editor yjs y-monaco ``` -------------------------------- ### Install y-indexeddb Source: https://github.com/yjs/docs/blob/main/ecosystem/database-provider/y-indexeddb.md Install the y-indexeddb package using npm. ```bash npm i --save y-indexeddb ``` -------------------------------- ### Start Basic Y-Websocket Server Source: https://github.com/yjs/docs/blob/main/ecosystem/connection-provider/y-websocket.md Launches a y-websocket server. The PORT environment variable defaults to 1234. ```bash HOST=localhost PORT=1234 npx y-websocket ``` -------------------------------- ### Install js-base64 Library Source: https://github.com/yjs/docs/blob/main/api/document-updates.md Install the js-base64 library using npm to enable Base64 encoding and decoding for Yjs document updates. ```bash npm install js-base64 ``` -------------------------------- ### Install Yjs Providers Source: https://github.com/yjs/docs/blob/main/getting-started/a-collaborative-editor.md Install the necessary Yjs provider package for your project using npm. Choose the provider that best suits your needs. ```bash npm i y-webrtc # or npm i y-websocket # or npm i y-dat ``` -------------------------------- ### Install y-websocket Package Source: https://github.com/yjs/docs/blob/main/ecosystem/connection-provider/y-websocket.md Installs the y-websocket package using npm. This is a prerequisite for using the WebSocket provider in your JavaScript project. ```bash npm i y-websocket ``` -------------------------------- ### Install Yjs and y-quill Source: https://github.com/yjs/docs/blob/main/getting-started/a-collaborative-editor.md Installs the necessary Yjs and y-quill packages for integrating Yjs with the Quill editor. ```bash npm i yjs y-quill ``` -------------------------------- ### Send relative position to a remote client (Uint8Array Example) Source: https://github.com/yjs/docs/blob/main/api/relative-positions.md Shows how to transmit a relative position efficiently using Uint8Array encoding. The example covers encoding on the sender side and decoding/resolving on the receiver side. ```javascript const relPos = Y.createRelativePositionFromTypeIndex(ytext, 2) const encodedRelPos = Y.encodeRelativePosition(relPos) // send encodedRelPos to remote client.. const parsedRelPos = Y.decodeRelativePosition(encodedRelPos) const pos = Y.createAbsolutePositionFromRelativePosition(parsedRelPos, remoteDoc) console.assert(pos.type === remoteytext) console.assert(pos.index === 2) ``` -------------------------------- ### Quick Start: Yjs Document Synchronization Source: https://github.com/yjs/docs/blob/main/README.md Demonstrates how to create Yjs documents, define shared maps, and sync changes between documents simulating remote users. Ensure Yjs is imported correctly. ```javascript import * as Y from 'yjs' // Yjs documents are collections of // shared objects that sync automatically. const ydoc = new Y.Doc() // Define a shared Y.Map instance const ymap = ydoc.getMap() ymap.set('keyA', 'valueA') // Create another Yjs document (simulating a remote user) // and create some conflicting changes const ydocRemote = new Y.Doc() const ymapRemote = ydocRemote.getMap() ymapRemote.set('keyB', 'valueB') // Merge changes from remote const update = Y.encodeStateAsUpdate(ydocRemote) Y.applyUpdate(ydoc, update) // Observe that the changes have merged console.log(ymap.toJSON()) // => { keyA: 'valueA', keyB: 'valueB' } ``` -------------------------------- ### Install ws Package for NodeJS WebSocket Polyfill Source: https://github.com/yjs/docs/blob/main/ecosystem/connection-provider/y-websocket.md Installs the 'ws' package, which provides WebSocket support for Node.js. This is required when using y-websocket in a Node.js environment. ```bash npm i ws ``` -------------------------------- ### Include a Copy of a Subdocument using GUID Source: https://github.com/yjs/docs/blob/main/api/subdocuments.md Shows how to include a copy of a subdocument by assigning it the same GUID. Documents with identical GUIDs will automatically synchronize. ```javascript const rootDoc = new Y.Doc() const doc = new Y.Doc() doc.guid // => "123e4567-e89b-12d3-a456-426614174000" - A random UUIDv4 rootDoc.getMap().set('file.txt', doc) // we can include a copy of a subdocument by giving it the same UUIDv4 const copy = new Y.Doc({ guid: doc.guid }) rootDoc.getMap().set('copy.txt', copy) // `doc` and `copy` will automatically sync because they have the same guid ``` -------------------------------- ### Setup Monaco Editor with Yjs and y-monaco Source: https://github.com/yjs/docs/blob/main/ecosystem/editor-bindings/monaco.md Initialize a Yjs document, set up a WebRTC provider for collaboration, and create a MonacoBinding to connect the Yjs text type with the Monaco editor model. Note that editor initialization is a prerequisite. ```javascript import * as Y from 'yjs' import { WebrtcProvider } from 'y-webrtc' import { MonacoBinding } from 'y-monaco' const ydoc = new Y.Doc() const provider = new WebrtcProvider('monaco', ydoc) const type = ydoc.getText('monaco') // There are some steps missing to initialize the editor // The editor requires a webpack build-step // See the complete example: // https://github.com/yjs/yjs-demos/blob/master/monaco/monaco.js const editor = monaco.editor.create( document.getElementById('monaco-editor'), { value: '', language: 'javascript', theme: 'vs-dark' } ) const monacoBinding = new MonacoBinding( type, editor.getModel(), new Set([editor]), provider.awareness ) ``` -------------------------------- ### Implement Lazy-Loading with Providers Source: https://github.com/yjs/docs/blob/main/api/subdocuments.md A method to implement lazy-loading of documents by creating a provider instance to the `doc.guid`-room once a document is loaded. This example uses WebrtcProvider. ```javascript doc.on('subdocs', ({ loaded }) => { loaded.forEach(subdoc => { new WebrtcProvider(subdoc.guid, subdoc) }) }) doc.getSubdocs() // Get the Set of all subdocuments ``` -------------------------------- ### Register an Event Handler Source: https://github.com/yjs/docs/blob/main/api/y.doc.md Event handlers can be registered using `doc.on()`. This example shows how to listen for the 'update' event, which is triggered after each transaction. ```javascript doc.on('update', (update, origin, doc) => { console.log('Document updated!') }) ``` -------------------------------- ### Transform to RelativePosition and back (JavaScript Example) Source: https://github.com/yjs/docs/blob/main/api/relative-positions.md Demonstrates how to create a relative position from a type and index, and then resolve it back to an absolute position within a document. ```javascript const relPos = Y.createRelativePositionFromTypeIndex(ytext, 2) const pos = Y.createAbsolutePositionFromRelativePosition(relPos, doc) console.assert(pos.type === ytext) console.assert(pos.index === 2) ``` -------------------------------- ### Creating and Using Y.Map Source: https://github.com/yjs/docs/blob/main/api/shared-types/y.map.md Demonstrates how to create a Y.Map, either as a top-level type or nested within another shared type. Shows basic operations like setting, getting, and deleting entries. ```javascript import * as Y from 'yjs' const ydoc = new Y.Doc() // You can define a Y.Map as a top-level type or a nested type // Method 1: Define a top-level type const ymap = ydoc.getMap('my map type') // Method 2: Define Y.Map that can be included into the Yjs document const ymapNested = new Y.Map() // Nested types can be included as content into any other shared type ymap.set('my nested map', ymapNested) // Common methods ymap.set('prop-name', 'value') // value can be anything json-encodable ymap.get('prop-name') // => 'value' ymap.delete('prop-name') ``` -------------------------------- ### Setup Quill Editor Source: https://github.com/yjs/docs/blob/main/getting-started/a-collaborative-editor.md Initializes a Quill rich-text editor with cursor support and basic formatting options. Ensure the '#editor' element exists in your HTML. ```javascript import Quill from 'quill' import QuillCursors from 'quill-cursors' Quill.register('modules/cursors', QuillCursors); const quill = new Quill(document.querySelector('#editor'), { modules: { cursors: true, toolbar: [ // adding some basic Quill content features [{ header: [1, 2, false] }], ['bold', 'italic', 'underline'], ['image', 'code-block'] ], history: { // Local undo shouldn't undo changes // from remote users userOnly: true } }, placeholder: 'Start collaborating...', theme: 'snow' // 'bubble' is also great }) ``` ```markup
``` ```bash npm i quill quill-cursors ``` -------------------------------- ### Connect with y-websocket Provider Source: https://github.com/yjs/docs/blob/main/getting-started/a-collaborative-editor.md Connects to a Yjs WebSocket server to synchronize the document. This example uses a public demo server; use your own for production. ```javascript import { WebsocketProvider } from 'y-websocket' // connect to the public demo server (not in production!) const provider = new WebsocketProvider( 'wss://demos.yjs.dev/ws', 'quill-demo-room', ydoc ) ``` -------------------------------- ### Send relative position to a remote client (JSON Example) Source: https://github.com/yjs/docs/blob/main/api/relative-positions.md Illustrates sending a relative position to a remote client by first encoding it to a JSON string, then decoding it on the receiving end and resolving it to an absolute position. ```javascript const relPos = Y.createRelativePositionFromTypeIndex(ytext, 2) const encodedRelPos = JSON.stringify(relPos) // send encodedRelPos to remote client.. const parsedRelPos = JSON.parse(encodedRelPos) const pos = Y.createAbsolutePositionFromRelativePosition(parsedRelPos, remoteDoc) console.assert(pos.type === remoteytext) console.assert(pos.index === 2) ``` -------------------------------- ### get Method Source: https://github.com/yjs/docs/blob/main/ecosystem/database-provider/y-indexeddb.md Retrieves a stored value associated with a given key from the IndexedDB. ```APIDOC ## provider.get(key: any): Promise ### Description Retrieve a stored value. ### Parameters #### Path Parameters - **key** (any) - Required - The key of the value to retrieve. ### Response #### Success Response (200) - **any** - The retrieved value. ``` -------------------------------- ### Get All Awareness States Source: https://github.com/yjs/docs/blob/main/api/about-awareness/README.md Retrieve all awareness states, including local and remote. The result is a Map from clientID to their respective awareness state. ```javascript awareness.getStates(): Map> ``` -------------------------------- ### Yjs Transaction Example Source: https://github.com/yjs/docs/blob/main/getting-started/working-with-shared-types.md Demonstrates how to use transactions to group multiple changes to shared types. Observers are called after each transaction, and bundling changes reduces observer calls and network updates. ```javascript const ydoc = new Y.Doc() const ymap = ydoc.getMap('favorites') // set an initial value - to demonstrate the how changes in ymap are represented ymap.set('food', 'pizza') // observers are called after each transaction ymap.observe(event => { console.log('changes', event.changes.keys) }) ydoc.transact(() => { ymap.set('food', 'pencake') ymap.set('number', 31) }) // => changes: Map({ number: { action: 'added' }, food: { action: 'updated', oldValue: 'pizza' } }) ``` -------------------------------- ### Define and Use Y.XmlElement Source: https://github.com/yjs/docs/blob/main/api/shared-types/y.xmlelement.md Demonstrates how to define Y.XmlElement as a top-level or nested type and common methods for manipulation. Includes an example of converting to a DOM element. ```javascript import * as Y from 'yjs' const ydoc = new Y.Doc() // You can define a Y.XmlElement as a top-level type or a nested type // Method 1: Define a top-level type // Note that the nodeName is always "undefined" // when defining an XmlElement as a top-level type. const yxmlElement = ydoc.get('prop-name', Y.XmlElement) // Method 2: Define Y.XmlFragment that can be included into the Yjs document const yxmlNested = new Y.XmlElement('node-name') // Common methods const yxmlText = new Y.XmlText() yxmlFragment.insert(0, [yxmlText]) yxmlFragment.firstChild === yxmlText yxmlFragment.insertAfter(yxmlText, [new Y.XmlElement('node-name')]) yxmlFragment.get(0) === yxmlText // => true //show result in dev console console.log(yxmlFragment.toDOM()) ``` -------------------------------- ### Delta Description: Retain and Delete Source: https://github.com/yjs/docs/blob/main/api/delta-format.md Demonstrates a Delta operation combining 'retain' and 'delete'. This example shows retaining one item and then deleting three. ```javascript delta = [{ retain: 1 }, { delete: 3 }] ``` -------------------------------- ### Execute a Transaction Source: https://github.com/yjs/docs/blob/main/api/y.doc.md All changes to a Y.Doc must occur within a transaction. This example demonstrates how to bundle multiple operations into a single transaction to minimize event calls. ```javascript doc.transact(() => { // Perform multiple operations here, e.g.: // yarray.insert(0, [1, 2]) // ymap.set('key', 'value') }) ``` -------------------------------- ### Connect to WebSocket Server Source: https://github.com/yjs/docs/blob/main/ecosystem/connection-provider/y-websocket.md Establish a WebSocket connection to the server. This method is useful if you have previously disconnected or initialized the provider with `connect: false`. ```javascript wsProvider.connect() ``` -------------------------------- ### Initialize a Y.Doc Instance Source: https://github.com/yjs/docs/blob/main/api/y.doc.md This snippet shows how to import Yjs and create a new Y.Doc instance, which is the foundation for any shared document. ```javascript import * as Y from 'yjs' const doc = new Y.Doc() ``` -------------------------------- ### Initialize DatProvider Source: https://github.com/yjs/docs/blob/main/getting-started/a-collaborative-editor.md Initialize a DatProvider to sync Yjs documents using a Dat key. Ensure the Dat key is unique for each document you want to sync. ```javascript import { DatProvider } from 'y-dat' // set null in order to create a fresh const datKey = '7b0d584fcdaf1de2e8c473393a31f52327793931e03b330f7393025146dc02fb' const provider = new DatProvider(datKey, ydoc) ``` -------------------------------- ### Get Y.Text Delta State Source: https://github.com/yjs/docs/blob/main/api/delta-format.md Demonstrates how to get the current state of a Y.Text object as a Delta. This is useful for serializing or inspecting the text content and its formatting. ```javascript const ytext = ydoc.getText() ytext.toDelta() // => [] ytext.insert(0, 'World', { bold: true }) ytext.insert(0, 'Hello ') ytext.toDelta() // => [{ insert: 'Hello ' }, { insert: 'World', attributes: { bold: true } }] ``` -------------------------------- ### Delete Content from Y.XmlFragment Source: https://github.com/yjs/docs/blob/main/api/shared-types/y.xmlfragment.md Delete a specified number of elements starting from a given index. ```javascript yxmlFragment.delete(index, length) ``` -------------------------------- ### Get JSON Representation of Y.XmlFragment Source: https://github.com/yjs/docs/blob/main/api/shared-types/y.xmlfragment.md Retrieve the JSON representation of this type. The result is a concatenated string of XML elements. ```javascript const validDocument = `${yXmlFragment.toJSON()}` ``` -------------------------------- ### Get Local Awareness State Source: https://github.com/yjs/docs/blob/main/api/about-awareness.md Retrieve the current local awareness state, which is a key-value store of JSON-encodable values. ```javascript awareness.getLocalState() ``` -------------------------------- ### Y.Map Methods Source: https://github.com/yjs/docs/blob/main/api/shared-types/y.map.md Core methods for manipulating and querying the Y.Map, including setting, getting, deleting, and checking for keys. ```APIDOC ## `ymap.set(key: string, value: object|boolean|string|number|Uint8Array|Y.AbstractType)` ### Description Add or update an entry with a specified key. This method works similarly to the [Map.set](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map/set) method. The value can be a shared type, an Uint8Array, or anything JSON-encodable. ### Parameters - **key** (string) - The key of the entry to add or update. - **value** (object|boolean|string|number|Uint8Array|Y.AbstractType) - The value to associate with the key. ## `ymap.get(key: string): object|boolean|Array|string|number|Uint8Array|Y.AbstractType` ### Description Returns an entry with the specified key. This method works similarly to the [Map.get](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map/get) method. ### Parameters - **key** (string) - The key of the entry to retrieve. ### Returns - (object|boolean|Array|string|number|Uint8Array|Y.AbstractType) - The value associated with the key, or `undefined` if the key does not exist. ## `ymap.delete(key: string)` ### Description Deletes an entry with the specified key. This method works similarly to the [Map.delete](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map/delete) method. ### Parameters - **key** (string) - The key of the entry to delete. ## `ymap.has(key: string): boolean` ### Description Returns true if an entry with the specified key exists. This method works similarly to the[ Map.has](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map/has) method. ### Parameters - **key** (string) - The key to check for. ### Returns - (boolean) - `true` if the key exists, `false` otherwise. ## `ymap.clear()` ### Description Removes all elements from this `ymap`. ``` -------------------------------- ### Get Local Awareness State Source: https://github.com/yjs/docs/blob/main/api/about-awareness/README.md Retrieve the current local awareness state. Returns `null` if the client is marked as offline. ```javascript awareness.getLocalState(): Object | null ``` -------------------------------- ### Syncing clients without loading the Y.Doc Source: https://github.com/yjs/docs/blob/main/api/document-updates.md Demonstrates how to sync two clients by encoding their current states, computing differences using state vectors derived from these encoded states, and then merging these differences without needing to load the full Y.Doc objects. ```APIDOC ## Example: Syncing clients without loading the Y.Doc ### Description This example shows how to sync two clients by computing the differences using their state vectors, which are derived directly from their encoded states. This method allows for efficient syncing by minimizing the data exchanged, as it avoids loading the complete Y.Doc objects into memory. ### Code Example ```javascript // encode the current state as a binary buffer let currentState1 = Y.encodeStateAsUpdate(ydoc1) let currentState2 = Y.encodeStateAsUpdate(ydoc2) // now we can continue syncing clients without // using the Y.Doc // ydoc1.destroy() // ydoc2.destroy() const stateVector1 = Y.encodeStateVectorFromUpdate(currentState1) const stateVector2 = Y.encodeStateVectorFromUpdate(currentState2) const diff1 = Y.diffUpdate(currentState1, stateVector2) const diff2 = Y.diffUpdate(currentState2, stateVector1) // sync clients currentState1 = Y.mergeUpdates([currentState1, diff2]) currentState2 = Y.mergeUpdates([currentState2, diff1]) ``` ``` -------------------------------- ### Awareness CRDT API Source: https://github.com/yjs/docs/blob/main/api/about-awareness/README.md Provides methods for managing local and remote awareness states, including setting, getting, and listening to changes. ```APIDOC ## Awareness CRDT API ### Description Manages user status and awareness information like cursor location, username, or email address. Clients can update their local state and listen to remote state changes. ### Methods - **`new Awareness(ydoc: Y.Doc)`**: Creates a new awareness instance. - **`destroy()`**: Destroys the awareness instance and associated state and event-handlers. - **`getLocalState(): Object | null`**: Gets the local awareness state. - **`setLocalState(state: Object)`**: Sets or updates the local awareness state. Set to `null` to mark the client as offline. The state object must map to JSON-encodable values. - **`setLocalStateField(string, any)`**: Sets or updates a single key-value pair in the local awareness state. - **`getStates(): Map>`**: Gets all awareness states (remote and local), mapping from `clientID` to awareness state. ### Events - **`on('update', ({ added: Array, updated: Array, removed: Array }, [transactionOrigin:any]) => ..)`**: Listens to remote and local state changes. Notifies when a state is added, updated, or removed. - **`on('change', ({ added: Array, updated: Array, removed: Array }, [transactionOrigin:any]) => ..)`**: Listens to remote and local awareness changes. This event is also triggered when the awareness state is updated to notify other users that the client is still online. ``` -------------------------------- ### Instantiate WebsocketProvider Source: https://github.com/yjs/docs/blob/main/ecosystem/connection-provider/y-websocket.md Create a new instance of WebsocketProvider to connect to a Yjs document. Provide the server URL, room name, and the Yjs document instance. Optional WebSocket options can be passed to customize the connection. ```javascript const wsProvider = new WebsocketProvider(serverUrl, room, ydoc) ``` -------------------------------- ### Creating and Using Y.Text Source: https://github.com/yjs/docs/blob/main/api/shared-types/y.text.md Demonstrates how to create Y.Text instances, both as top-level types and nested within other shared types. Includes common methods for insertion, formatting, and conversion to Delta format. ```javascript import * as Y from 'yjs' const ydoc = new Y.Doc() // You can define a Y.Text as a top-level type or a nested type // Method 1: Define a top-level type const ytext = ydoc.getText('my text type') // Method 2: Define Y.Text that can be included into the Yjs document const ytextNested = new Y.Text() // Nested types can be included as content into any other shared type ydoc.getMap('another shared structure').set('my nested text', ytextNested) // Common methods ytext.insert(0, 'abc') // insert three elements ytext.format(1, 2, { bold: true }) // delete second element ytext.toString() // => 'abc' ytext.toDelta() // => [{ insert: 'a' }, { insert: 'bc', attributes: { bold: true }}] ``` -------------------------------- ### Import Awareness Protocol Source: https://github.com/yjs/docs/blob/main/api/about-awareness/README.md Import the necessary awareness protocol module. This is typically done at the beginning of your JavaScript file. ```javascript import * as awarenessProtocol from 'y-protocols/awareness.js' ``` -------------------------------- ### Adding Meta Information to Undo/Redo Stack Items Source: https://github.com/yjs/docs/blob/main/api/undo-manager.md Demonstrates how to attach and restore meta-information, such as cursor location, to stack items for preserving context during undo/redo operations. ```javascript const ytext = doc.getText('text') const undoManager = new Y.UndoManager(ytext, { trackedOrigins: new Set([42, CustomBinding]) }) undoManager.on('stack-item-added', event => { // save the current cursor location on the stack-item event.stackItem.meta.set('cursor-location', getRelativeCursorLocation()) }) undoManager.on('stack-item-popped', event => { // restore the current cursor location on the stack-item restoreCursorLocation(event.stackItem.meta.get('cursor-location')) }) ``` -------------------------------- ### Basic Undo/Redo with Y.UndoManager Source: https://github.com/yjs/docs/blob/main/api/undo-manager.md Demonstrates the fundamental usage of Y.UndoManager to undo and redo text insertions. ```javascript import * as Y from 'yjs' const ytext = doc.getText('text') const undoManager = new Y.UndoManager(ytext) ytext.insert(0, 'abc') undoManager.undo() ytext.toString() // => '' undoManager.redo() ytext.toString() // => 'abc' ``` -------------------------------- ### Listen for 'updateV2' Event (Experimental) Source: https://github.com/yjs/docs/blob/main/api/y.doc.md This experimental event handler listens for update messages using an alternative, more efficient format. Use with caution as it's not production-ready. ```javascript doc.on('updateV2', (update, origin, doc, tr) => { console.log('Received updateV2 message.') }) ``` -------------------------------- ### Get a Shared Type Instance Source: https://github.com/yjs/docs/blob/main/api/y.doc.md Use `doc.get()` to retrieve a top-level shared type instance from the document. You must specify the type class (e.g., Y.Array, Y.Map). ```javascript const yarray = doc.get('myArray', Y.Array) const ymap = doc.get('myMap', Y.Map) ``` -------------------------------- ### Initialize Yjs Document and Quill Binding Source: https://github.com/yjs/docs/blob/main/getting-started/a-collaborative-editor.md Sets up a Yjs document and a Y.Text shared type, then binds it to the Quill editor instance. This enables real-time synchronization of text content. ```javascript import * as Y from 'yjs' import { QuillBinding } from 'y-quill' // A Yjs document holds the shared data const ydoc = new Y.Doc() // Define a shared text type on the document const ytext = ydoc.getText('quill') // Create an editor-binding which // "binds" the quill editor to a Y.Text type. const binding = new QuillBinding(ytext, quill) ``` -------------------------------- ### WebsocketProvider Configuration Options Source: https://github.com/yjs/docs/blob/main/ecosystem/connection-provider/y-websocket.md Customize the behavior of the WebsocketProvider using the wsOpts object. Options include manual connection control, URL parameters, WebSocket polyfills, and awareness protocol integration. ```javascript const wsOpts = { // Set this to `false` if you want to connect manually using wsProvider.connect() connect: true, // Specify a query-string that will be url-encoded and attached to the `serverUrl` // I.e. params = { auth: "bearer" } will be transformed to "?auth=bearer" params: {}, // You may polyill the Websocket object (https://developer.mozilla.org/en-US/docs/Web/API/WebSocket). // E.g. In nodejs, you could specify WebsocketPolyfill = require('ws') WebsocketPolyfill: Websocket, // Specify an existing Awareness instance - see https://github.com/yjs/y-protocols awareness: new awarenessProtocol.Awareness(ydoc) } ``` -------------------------------- ### Listen to Update Events and Apply Remotely Source: https://github.com/yjs/docs/blob/main/api/document-updates.md Listen to 'update' events on one Yjs document and apply them to another. This setup ensures that changes made to doc1 are reflected in doc2, and vice versa. ```javascript const doc1 = new Y.Doc() const doc2 = new Y.Doc() doc1.on('update', update => { Y.applyUpdate(doc2, update) }) doc2.on('update', update => { Y.applyUpdate(doc1, update) }) // All changes are also applied to the other document doc1.getArray('myarray').insert(0, ['Hello doc2, you got this?']) doc2.getArray('myarray').get(0) // => 'Hello doc2, you got this?' ``` -------------------------------- ### Y.Array Insert and Delete Operations Source: https://github.com/yjs/docs/blob/main/api/shared-types/y.array.md Demonstrates basic insertion and deletion operations on a Y.Array and shows the resulting delta format for each operation. ```javascript yarray.insert(0, [1, 2, 3]) // => [{ insert: [1, 2, 3] }] yarray.delete(2, 1) // [{ retain: 2 }, { delete: 1 }] ``` -------------------------------- ### Event Handling Source: https://github.com/yjs/docs/blob/main/ecosystem/connection-provider/y-websocket.md Listen for synchronization events to be notified when the client's document state is synced with the server. ```APIDOC ## Event Handling ### `wsProvider.on('sync', function(isSynced: boolean))` Add an event listener for the `sync` event that is fired when the client received content from the server. ``` -------------------------------- ### Listen for Sync Events Source: https://github.com/yjs/docs/blob/main/ecosystem/connection-provider/y-websocket.md Add an event listener to react to the 'sync' event. This event is triggered whenever the client successfully synchronizes content with the server, indicating the current synchronization status. ```javascript wsProvider.on('sync', function(isSynced) { console.log('Document synced:', isSynced) }) ``` -------------------------------- ### Y-Websocket Server with HTTP Callback Source: https://github.com/yjs/docs/blob/main/ecosystem/connection-provider/y-websocket.md Configures a y-websocket server to send debounced callbacks to a specified HTTP server on document updates. Environment variables control the callback URL, debounce timing, timeouts, and the specific shared objects to include in the callback payload. ```bash CALLBACK_URL=http://localhost:3000/ CALLBACK_OBJECTS='{"prosemirror":"XmlFragment"}' npm start ``` -------------------------------- ### Define and Use Y.XmlFragment Source: https://github.com/yjs/docs/blob/main/api/shared-types/y.xmlfragment.md Demonstrates how to define a Y.XmlFragment as a top-level type and a nested type. Includes common methods for inserting and retrieving nodes, and converting to a DOM element. ```javascript import * as Y from 'yjs' const ydoc = new Y.Doc() // You can define a Y.XmlFragment as a top-level type or a nested type // Method 1: Define a top-level type const yxmlFragment = ydoc.getXmlFragment('my xml fragment') // Method 2: Define Y.XmlFragment that can be included into the Yjs document const yxmlNested = new Y.XmlFragment() // Common methods const yxmlText = new Y.XmlText() yxmlFragment.insert(0, [yxmlText]) yxmlFragment.firstChild === yxmlText yxmlFragment.insertAfter(yxmlText, [new Y.XmlElement('node-name')]) yxmlFragment.get(0) === yxmlText // => true //show result in dev console console.log(yxmlFragment.toDOM()) ``` -------------------------------- ### Initialize IndexedDB Provider Source: https://github.com/yjs/docs/blob/main/ecosystem/database-provider/y-indexeddb.md Import and create an IndexeddbPersistence provider for a Yjs document. The provider will automatically sync data with the browser's IndexedDB. The 'synced' event is fired when content is loaded from the database. ```javascript import { IndexeddbPersistence } from 'y-indexeddb' const provider = new IndexeddbPersistence(docName, ydoc) provider.on('synced', () => { console.log('content from the database is loaded') }) ``` -------------------------------- ### Y.XmlText Constructor Source: https://github.com/yjs/docs/blob/main/api/shared-types/y.xmltext.md Creates a new Y.XmlText instance. This method inherits from Y.Text. ```APIDOC ## Y.XmlText() ### Description Creates a new Y.XmlText instance. This method inherits from Y.Text. ### Method Constructor ### Endpoint N/A (Class constructor) ### Parameters None ``` -------------------------------- ### Y.Text Constructor Source: https://github.com/yjs/docs/blob/main/api/shared-types/y.text.md Creates a new Y.Text instance. It can be initialized with existing content. ```APIDOC ## new Y.Text(initialContent) ### Description Create an instance of Y.Text with existing content. ### Parameters - **initialContent** (string) - Optional initial content for the text type. ``` -------------------------------- ### WebsocketProvider Constructor Source: https://github.com/yjs/docs/blob/main/ecosystem/connection-provider/y-websocket.md Instantiate a new WebsocketProvider to connect a Yjs document to a WebSocket server. This allows for real-time synchronization of document changes across clients. ```APIDOC ## new WebsocketProvider(serverUrl: string, room: string, ydoc: Y.Doc [, wsOpts: WsOpts]) ### Description Creates a new websocket-provider instance. As long as this provider, or the connected `ydoc`, is not destroyed, the changes will be synced to other clients via the connected server. Optionally, you may specify a configuration object. ### Parameters * **serverUrl** (string) - The URL of the WebSocket server. * **room** (string) - The name of the room to join for synchronization. * **ydoc** (Y.Doc) - The Yjs document to synchronize. * **wsOpts** (WsOpts, optional) - Configuration options for the WebSocket provider. ### WsOpts Configuration ```javascript { // Set this to `false` if you want to connect manually using wsProvider.connect() connect: true, // boolean // Specify a query-string that will be url-encoded and attached to the `serverUrl` // I.e. params = { auth: "bearer" } will be transformed to "?auth=bearer" params: {}, // Object // You may polyfill the Websocket object (https://developer.mozilla.org/en-US/docs/Web/API/WebSocket). // E.g. In nodejs, you could specify WebsocketPolyfill = require('ws') WebsocketPolyfill: Websocket, // WebSocket constructor // Specify an existing Awareness instance - see https://github.com/yjs/y-protocols awareness: new awarenessProtocol.Awareness(ydoc) // awarenessProtocol.Awareness } ``` ``` -------------------------------- ### Sync Clients Without Loading Y.Doc Source: https://github.com/yjs/docs/blob/main/api/document-updates.md Synchronize Yjs clients by encoding the current state as binary buffers and computing differences using state vectors derived from these updates. This approach allows for syncing without maintaining the full Y.Doc in memory, but requires loading the document later for garbage collection. ```javascript // encode the current state as a binary buffer let currentState1 = Y.encodeStateAsUpdate(ydoc1) let currentState2 = Y.encodeStateAsUpdate(ydoc2) // now we can continue syncing clients without // using the Y.Doc ydoc1.destroy() ydoc2.destroy() const stateVector1 = Y.encodeStateVectorFromUpdate(currentState1) const stateVector2 = Y.encodeStateVectorFromUpdate(currentState2) const diff1 = Y.diffUpdate(currentState1, stateVector2) const diff2 = Y.diffUpdate(currentState2, stateVector1) // sync clients currentState1 = Y.mergeUpdates([currentState1, diff2]) currentState2 = Y.mergeUpdates([currentState2, diff1]) ``` -------------------------------- ### Connect with y-webrtc Provider Source: https://github.com/yjs/docs/blob/main/getting-started/a-collaborative-editor.md Establishes a direct peer-to-peer connection for syncing the Yjs document using WebRTC. Ideal for demo applications. ```javascript import { WebrtcProvider } from 'y-webrtc' const provider = new WebrtcProvider('quill-demo-room', ydoc) ``` -------------------------------- ### Listen for IndexedDB Sync Event Source: https://github.com/yjs/docs/blob/main/getting-started/allowing-offline-editing.md Listen for the 'synced' event, which fires when the client has loaded initial content from the IndexedDB database. ```javascript persistence.once('synced', () => { console.log('initial content loaded') }) ``` -------------------------------- ### Sync Two Clients by Computing Differences Using State Vectors Source: https://github.com/yjs/docs/blob/main/api/document-updates.md Synchronize two Yjs documents by exchanging only the differences, computed using state vectors. This method requires an additional roundtrip but significantly reduces bandwidth usage compared to exchanging the complete document structure. ```javascript const stateVector1 = Y.encodeStateVector(ydoc1) const stateVector2 = Y.encodeStateVector(ydoc2) const diff1 = Y.encodeStateAsUpdate(ydoc1, stateVector2) const diff2 = Y.encodeStateAsUpdate(ydoc2, stateVector1) Y.applyUpdate(ydoc1, diff2) Y.applyUpdate(ydoc2, diff1) ``` -------------------------------- ### Prepend Content to Y.XmlFragment Source: https://github.com/yjs/docs/blob/main/api/shared-types/y.xmlfragment.md Prepend content to the beginning of the Y.Array. Same as `yxmlFragment.insert(0, content)`. ```javascript yxmlFragment.unshift([new Y.XmlElement()]) ``` -------------------------------- ### Implement a Custom Yjs Provider Source: https://github.com/yjs/docs/blob/main/api/document-updates.md A template for creating a custom Yjs provider that synchronizes document updates. It listens for local updates and forwards them, while also applying received updates from other sources. It uses transaction origin to filter updates applied by the provider itself. ```javascript import * as Y from 'yjs' import { Observable } from 'lib0/observable' class Provider extends Observable { /** * @param {Y.Doc} ydoc */ constructor (ydoc) { super() ydoc.on('update', (update, origin) => { // ignore updates applied by this provider if (origin !== this) { // this update was produced either locally or by another provider. this.emit('update', [update]) } }) // listen to an event that fires when a remote update is received this.on('update', update => { Y.applyUpdate(ydoc, update, this) // the third parameter sets the transaction-origin }) } .. } ``` -------------------------------- ### Observe Y.XmlElement Changes Source: https://github.com/yjs/docs/blob/main/api/shared-types/y.xmlelement.md Shows how to observe changes to Y.XmlElement, including child element additions/deletions and attribute modifications. It logs changes in a delta format and demonstrates attribute change observation. ```javascript yxmlFragment.observe(yxmlEvent => { yxmlEvent.target === yarray // => true // Observe when child-elements are added or deleted. // Log the Xml-Delta Format to calculate the difference to the last observe-event console.log(yxmlEvent.changes.delta) // Observe attribute changes. // Option 1: A set of keys that changed yxmlEvent.keysChanged // => Set // Option 2: Compute the differences yxmlEvent.changes.keys // => Map // The change format is equivalent to the Y.MapEvent change format. yxmlEvent.changes.keys.forEach((change, key) => { if (change.action === 'add') { console.log(`Attribute "${key}" was added. Initial value: "${ymap.get(key)}".`) } else if (change.action === 'update') { console.log(`Attribute "${key}" was updated. New value: "${ymap.get(key)}". Previous value: "${change.oldValue}".`) } else if (change.action === 'delete') { console.log(`Attribute "${key}" was deleted. New value: undefined. Previous value: "${change.oldValue}".`) } }) }) yxmlElement.insert(0, [new Y.XmlText()]) // => [{ insert: [yxmlText] }] yxmlElement.delete(0, 1) // [{ delete: 1 }] yxmlElement.setAttribute('key', 'value') // Attribute "key" was added. Initial value: "undefined". yxmlElement.setAttribute('key', 'new value') // Attribute "key" was updated. New value: "new value". Previous value: "value" yxmlElement.deleteAttribute('key') // Attribute "key" was deleted. New value: undefined. Previous value: "new value" ``` -------------------------------- ### Connect to a Yjs Document via WebSocket Source: https://github.com/yjs/docs/blob/main/ecosystem/connection-provider/y-websocket.md Connects a Yjs document to a WebSocket server for real-time synchronization. This is the standard way to initiate a connection from a client. ```javascript import * as Y from 'yjs' import { WebsocketProvider } from 'y-websocket' const doc = new Y.Doc() const wsProvider = new WebsocketProvider('ws://localhost:1234', 'my-roomname', doc) wsProvider.on('status', event => { console.log(event.status) // logs "connected" or "disconnected" }) ``` -------------------------------- ### Delta Format for Y.Array & Y.XmlFragment Inserts Source: https://github.com/yjs/docs/blob/main/api/delta-format.md Demonstrates the structure of the 'insert' operation in the Delta format when used with Y.Array and Y.XmlFragment, where inserted content is an array. ```javascript yarray.observe(event => { console.log(event.changes.delta) }) yarray.insert(0, [1, 2, 3]) // => [{ insert: [1, 2, 3] }] yarray.insert(2, ["abc"]) // => [{ retain: 2 }, { insert: ["abc"] }] yarray.delete(0, 1) // => [{ delete: 1 }] ``` -------------------------------- ### Delta Description: Retain with Attributes on Y.Text Source: https://github.com/yjs/docs/blob/main/api/delta-format.md Illustrates how to apply formatting attributes to existing text using the 'retain' operation with attributes in the Delta format for Y.Text. ```javascript delta = [{ retain: 5, attributes: { italic: true } }] ``` -------------------------------- ### Create and Resolve Relative Position Source: https://github.com/yjs/docs/blob/main/api/relative-positions.md Demonstrates creating a relative position from a text type and index, then resolving it to an absolute position within the document. ```javascript const relPos = Y.createRelativePositionFromTypeIndex(ytext, 2) const pos = Y.createAbsolutePositionFromRelativePosition(relPos, doc) pos.type === ytext // => true pos.index === 2 // => true ``` -------------------------------- ### Observing Changes in Y.Text Source: https://github.com/yjs/docs/blob/main/api/shared-types/y.text.md Shows how to observe changes made to a Y.Text instance using the `observe` method. It details how to access change information like keys and actions (add, update, delete) within the event handler. ```javascript yarray.observe(yarrayEvent => { yarrayEvent.target === yarray // => true // Find out what changed: // Option 1: A set of keys that changed ymapEvent.keysChanged // => Set // Option 2: Compute the differences ymapEvent.changes.keys // => Map // sample code. yarrayEvent.changes.keys.forEach((change, key) => { if (change.action === 'add') { console.log(`Property "${key}" was added. Initial value: "${ymap.get(key)}".`) } else if (change.action === 'update') { console.log(`Property "${key}" was updated. New value: "${ymap.get(key)}". Previous value: "${change.oldValue}".`) } else if (change.action === 'delete') { console.log(`Property "${key}" was deleted. New value: undefined. Previous value: "${change.oldValue}".`) } }) }) ymap.set('key', 'value') // => Property "key" was added. Initial value: "value". ymap.set('key', 'new') // => Property "key" was updated. New value: "new". Previous value: "value". ymap.delete('key') // => Property "key" was deleted. New value: undefined. Previous Value: "new". ``` -------------------------------- ### Connection Management Methods Source: https://github.com/yjs/docs/blob/main/ecosystem/connection-provider/y-websocket.md Methods to manually control the WebSocket connection and manage the provider's lifecycle. ```APIDOC ## Connection Management Methods ### `wsProvider.disconnect()` Disconnect from the server and don't try to reconnect. ### `wsProvider.connect()` Establish a websocket connection to the websocket-server. Call this if you recently disconnected or if you set `wsOpts.connect = false`. ### `wsProvider.destroy()` Destroy this `wsProvider` instance. Disconnects from the server and removes all event handlers. ``` -------------------------------- ### Creating and Manipulating Y.Array Source: https://github.com/yjs/docs/blob/main/api/shared-types/y.array.md Demonstrates how to create a Y.Array, either as a top-level type or nested within another Yjs document. It also shows common operations like inserting and deleting elements, and converting the array to a standard JavaScript array. ```javascript import * as Y from 'yjs' const ydoc = new Y.Doc() // You can define a Y.Array as a top-level type or a nested type // Method 1: Define a top-level type const yarray = ydoc.getArray('my array type') // Method 2: Define Y.Array that can be included into the Yjs document const yarrayNested = new Y.Array() // Nested types can be included as content into any other shared type, // notice that a shared type can only exist once in a document. yarray.insert(0, [yarrayNested]) // Common methods yarray.insert(0, [1, 2, 3]) // insert three elements yarray.delete(1, 1) // delete second element yarray.toArray() // => [1, 3] ``` -------------------------------- ### Listen to Awareness Changes Source: https://github.com/yjs/docs/blob/main/api/about-awareness/README.md Subscribe to 'change' events, which are triggered for any awareness state change, including updates that merely confirm a client is still online. This event is useful for propagating awareness state. ```javascript awareness.on('change', ({ added: Array, updated: Array removed: Array }, [transactionOrigin:any]) => ..) ``` -------------------------------- ### Create and Embed a Subdocument Source: https://github.com/yjs/docs/blob/main/api/subdocuments.md Client One creates a root document and embeds a new subdocument into a map. The subdocument is initialized with some text content. ```javascript const rootDoc = new Y.Doc() const folder = rootDoc.getMap() const subDoc = new Y.Doc() subDoc.getText().insert(0, 'some initial content') folder.set('my-document.txt', subDoc) ``` -------------------------------- ### Listen to Awareness Updates Source: https://github.com/yjs/docs/blob/main/api/about-awareness.md Subscribe to the 'update' event to be notified of any changes to awareness states, including when a client is still online. This is useful for propagating awareness state. ```javascript awareness.on('update', ({ added: Array, updated: Array, removed: Array }, [transactionOrigin:any]) => ..) ``` -------------------------------- ### Register an Event Handler Once Source: https://github.com/yjs/docs/blob/main/api/y.doc.md Use `doc.once()` to register an event handler that will only be called the first time the event is triggered. ```javascript doc.once('update', (update, origin, doc) => { console.log('This will only be logged once.') }) ``` -------------------------------- ### Y.Doc Methods Source: https://github.com/yjs/docs/blob/main/api/y.doc.md Core methods for interacting with Y.Doc instances, including transactions, type retrieval, and destruction. ```APIDOC ## Y.Doc Methods **`doc.transact(function(Transaction): void [, origin:any])`** Every change on the shared document happens in a transaction. Observer calls and the `update` event are called after each transaction. You should bundle changes into a single transaction to reduce event calls. You can specify an optional `origin` parameter that is stored on `transaction.origin`. **`doc.get(string, Y.[TypeClass]): [Type]`** Get a top-level instance of a shared type. **`doc.getArray(string = ''): Y.Array`** Define a shared Y.Array type. Is equivalent to `y.get(string, Y.Array)`. **`doc.getMap(string = ''): Y.Map`** Define a shared Y.Map type. Is equivalent to `y.get(string, Y.Map)`. **`doc.getXmlFragment(string = ''): Y.XmlFragment`** Define a shared Y.XmlFragment type. Is equivalent to `y.get(string, Y.XmlFragment)`. **`doc.destroy()`** Destroy this Y.Doc instance. All event handlers are cleared and the content is cleared from memory unless it is still being referenced. Bindings and providers that are attached to this document are also destroyed. ``` -------------------------------- ### Encode Complete Awareness State Source: https://github.com/yjs/docs/blob/main/api/about-awareness/README.md Use this snippet to encode the complete awareness state when connecting to a new client. It's important to share the complete state, and receiving duplicate updates is acceptable. ```javascript const encodedAwState = awarenessProtocol.encodeAwarenessUpdate( provider.awareness, Array.from(provider.awareness.getStates().keys()) ) ``` -------------------------------- ### Import WebsocketProvider Source: https://github.com/yjs/docs/blob/main/ecosystem/connection-provider/y-websocket.md Import the WebsocketProvider class from the y-websocket library. This is the primary class used to establish WebSocket connections for Yjs document synchronization. ```javascript import { WebsocketProvider } from 'y-websocket' ``` -------------------------------- ### Destroy and Reload a Subdocument Source: https://github.com/yjs/docs/blob/main/api/subdocuments.md Demonstrates how to explicitly load a subdocument and then destroy it to free memory and data bindings. A destroyed document should not be accessed directly; instead, it should be re-requested to reload its content. ```javascript const subDoc = rootDoc.getMap().get('my-document.text') subDoc.load() // After some time you might not need to render the document anymore. // You want to destroy the existing data bindings and load a different document ... subDoc.destroy() // free all memory and destroy all data bindings to this document. // You should not access the destroyed document again. // Instead you can create a new one by requesting the document again. const subDocReloaded = rootDoc.getMap().get('my-document.text') subDocReloaded.load() ```