Collaboration – inksane
inksane
Introduction

Collaboration

Sync a document between clients with Yjs and render remote cursors.

@inksane/extension-collaboration connects an editor to a shared Yjs document. Document changes sync through a Y.Text, and remote cursors render through Yjs awareness with the user’s name next to the caret.

pnpm add @inksane/extension-collaboration yjs

Usage

import { Editor } from "@inksane/core";
import { BaseKit } from "@inksane/core/extensions/base-kit";
import { CollaborationExtension } from "@inksane/extension-collaboration";
import * as Y from "yjs";

const ydoc = new Y.Doc();

const editor = new Editor({
  element: document.getElementById("editor"),
  content: "# Shared document",
  extensions: [
    BaseKit({ history: false }),
    CollaborationExtension({
      ydoc,
      user: { name: "Alice", color: "#30bced" },
    }),
  ],
});

BaseKit provides the Markdown features and standard editing shortcuts. Disable its history feature because Yjs owns undo and redo. You can also choose individual extensions, leaving out HistoryExtension.

Options

OptionTypeDefaultDescription
ydocY.DocrequiredThe document shared by every collaborator.
fieldstring"content"Name of the shared Y.Text field.
awarenessAwarenessnew one on ydocBroadcasts cursors. Pass your provider’s instance.
userPartial<CollaborationUser>anonymousIdentity shown on your cursor to everyone else.

user accepts name, color, and an optional colorLight for the selection highlight. When you omit them, the extension derives a color from the awareness client id and uses it for the caret and the colorLight tint.

Transport is up to you

The extension only connects the editor to a Y.Doc and an Awareness. Sending updates over the network is the job of a provider, for example y-websocket, y-webrtc, or your own server:

import { WebsocketProvider } from "y-websocket";

const ydoc = new Y.Doc();
const provider = new WebsocketProvider("wss://example.com", "my-room", ydoc);

const editor = new Editor({
  element: document.getElementById("editor"),
  extensions: [
    BaseKit({ history: false }),
    CollaborationExtension({
      ydoc,
      awareness: provider.awareness,
      user: { name: "Alice", color: "#30bced" },
    }),
  ],
});

Who seeds the document

The Y.Doc owns the document. When the shared text is still empty and the editor was created with content, the extension seeds the shared text with it, so the first client creates the document. A client joining an existing room should pass the current shared text as content.

Local checks

Two editors in the same page that share a Y.Doc sync immediately, which is a quick way to try the extension without a server. The extension tests in the repository do exactly that: they link two documents and two awareness instances.

The extension sets priority: 100, so it initializes before BaseKit’s default-priority features.