or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

docs

index.mdplugins.mdutilities.md
tile.json

tessl/npm-y-prosemirror

ProseMirror bindings for Yjs that enable real-time collaborative editing with synchronization, cursors, and undo/redo

Workspace
tessl
Visibility
Public
Created
Last updated
Describes
npmpkg:npm/y-prosemirror@1.3.x

To install, run

npx @tessl/cli install tessl/npm-y-prosemirror@1.3.0

index.mddocs/

Y-ProseMirror

Y-ProseMirror provides ProseMirror bindings for Yjs that enable real-time collaborative editing. It offers essential collaborative features including document synchronization, shared cursor visibility, and individual undo/redo history for each user. The library handles concurrent editing conflicts and integrates seamlessly with ProseMirror's plugin system.

Package Information

  • Package Name: y-prosemirror
  • Package Type: npm
  • Language: JavaScript with TypeScript definitions
  • Installation: npm install y-prosemirror

Core Imports

import { ySyncPlugin, yCursorPlugin, yUndoPlugin } from "y-prosemirror";

For CommonJS:

const { ySyncPlugin, yCursorPlugin, yUndoPlugin } = require("y-prosemirror");

Utility functions:

import { 
  prosemirrorToYDoc, 
  yXmlFragmentToProseMirrorRootNode,
  absolutePositionToRelativePosition 
} from "y-prosemirror";

Basic Usage

import * as Y from "yjs";
import { EditorView } from "prosemirror-view";
import { EditorState } from "prosemirror-state";
import { DOMParser, Schema } from "prosemirror-model";
import { Awareness } from "y-protocols/awareness";
import { 
  ySyncPlugin, 
  yCursorPlugin, 
  yUndoPlugin,
  undo,
  redo,
  initProseMirrorDoc 
} from "y-prosemirror";
import { keymap } from "prosemirror-keymap";

// Define your ProseMirror schema
const schema = new Schema({
  nodes: {
    doc: { content: "paragraph+" },
    paragraph: { content: "text*", toDOM: () => ["p", 0] },
    text: {}
  }
});

// Create Yjs document and awareness
const ydoc = new Y.Doc();
const yXmlFragment = ydoc.getXmlFragment("prosemirror");
const awareness = new Awareness(ydoc);

// Initialize document from Yjs content (or create empty)
const { doc, mapping } = initProseMirrorDoc(yXmlFragment, schema);

// Create ProseMirror editor with collaborative plugins
const view = new EditorView(document.querySelector("#editor"), {
  state: EditorState.create({
    doc,
    schema,
    plugins: [
      ySyncPlugin(yXmlFragment, { mapping }),
      yCursorPlugin(awareness),
      yUndoPlugin(),
      keymap({
        'Mod-z': undo,
        'Mod-y': redo,
        'Mod-Shift-z': redo
      })
    ]
  })
});

// Set user information for collaborative cursors
awareness.setLocalStateField('user', {
  name: 'User Name',
  color: '#ff6b6b'
});

Architecture

Y-ProseMirror is built around several key components:

  • Synchronization Plugin: Maintains bidirectional sync between Yjs and ProseMirror documents
  • Cursor Plugin: Displays real-time cursors and selections from other users via awareness protocol
  • Undo Plugin: Provides collaborative undo/redo with per-user history tracking
  • Binding System: Core ProsemirrorBinding class managing the synchronization lifecycle
  • Conversion Utilities: Functions for converting between Yjs and ProseMirror data structures
  • Position Mapping: Utilities for converting between absolute and relative positions

Capabilities

Core Plugins

Essential ProseMirror plugins that enable collaborative editing with synchronization, cursors, and undo functionality.

function ySyncPlugin(yXmlFragment: Y.XmlFragment, opts?: {
  colors?: Array<ColorDef>;
  colorMapping?: Map<string, ColorDef>;
  permanentUserData?: Y.PermanentUserData | null;
  mapping?: ProsemirrorMapping;
  onFirstRender?: () => void;
}): Plugin;

function yCursorPlugin(awareness: Awareness, opts?: {
  awarenessStateFilter?: (currentClientId: number, userClientId: number, user: any) => boolean;
  cursorBuilder?: (user: any, clientId: number) => HTMLElement;
  selectionBuilder?: (user: any, clientId: number) => DecorationAttrs;
  getSelection?: (state: EditorState) => Selection;
}, cursorStateField?: string): Plugin;

function yUndoPlugin(options?: {
  protectedNodes?: Set<string>;
  trackedOrigins?: any[];
  undoManager?: UndoManager | null;
}): Plugin;

Core Plugins

Document Conversion

Utilities for converting between ProseMirror and Yjs document formats, enabling data migration and persistence.

function prosemirrorToYDoc(doc: Node, xmlFragment?: string): Y.Doc;
function yXmlFragmentToProseMirrorRootNode(yXmlFragment: Y.XmlFragment, schema: Schema): Node;
function prosemirrorJSONToYDoc(schema: Schema, state: object, xmlFragment?: string): Y.Doc;

Document Conversion

Position and Selection Utilities

Functions for managing positions and selections in collaborative editing contexts with relative position support.

function absolutePositionToRelativePosition(pos: number, type: Y.AbstractType, mapping: ProsemirrorMapping): Y.RelativePosition;
function relativePositionToAbsolutePosition(y: Y.Doc, documentType: Y.AbstractType, relPos: Y.RelativePosition, mapping: ProsemirrorMapping): number | null;
function getRelativeSelection(pmbinding: ProsemirrorBinding, state: EditorState): {
  type: string;
  anchor: Y.RelativePosition;
  head: Y.RelativePosition;
};

Position Utilities

Plugin Key Constants

Plugin keys for accessing plugin state and configuration.

const ySyncPluginKey: PluginKey;
const yCursorPluginKey: PluginKey;
const yUndoPluginKey: PluginKey;

Types

interface ColorDef {
  light: string;
  dark: string;
}

type ProsemirrorMapping = Map<Y.AbstractType<any>, PModel.Node | Array<PModel.Node>>;

interface BindingMetadata {
  mapping: ProsemirrorMapping;
  isOMark: Map<import('prosemirror-model').MarkType, boolean>;
}

interface UndoPluginState {
  undoManager: import('yjs').UndoManager;
  prevSel: ReturnType<typeof getRelativeSelection> | null;
  hasUndoOps: boolean;
  hasRedoOps: boolean;
}

interface YSyncOpts {
  colors?: Array<ColorDef>;
  colorMapping?: Map<string, ColorDef>;
  permanentUserData?: Y.PermanentUserData | null;
  mapping?: ProsemirrorMapping;
  onFirstRender?: () => void;
}

class ProsemirrorBinding {
  type: Y.XmlFragment;
  prosemirrorView: EditorView | null;
  mapping: ProsemirrorMapping;
  doc: Y.Doc;
  mux: any;
  isOMark: Map<MarkType, boolean>;
  isDestroyed: boolean;
  beforeTransactionSelection: any;

  constructor(yXmlFragment: Y.XmlFragment, mapping?: Map);
  initView(prosemirrorView: EditorView): void;
  destroy(): void;
  renderSnapshot(snapshot: Snapshot, prevSnapshot?: Snapshot): void;
  unrenderSnapshot(): void;
}