ProseMirror bindings for Yjs that enable real-time collaborative editing with synchronization, cursors, and undo/redo
Y-ProseMirror provides utility functions for converting between ProseMirror and Yjs data structures, managing positions in collaborative contexts, and handling document persistence.
Convert between ProseMirror and Yjs document formats for data migration, persistence, and initialization.
/**
* Convert ProseMirror document to Yjs document
* @param doc - ProseMirror document node
* @param xmlFragment - XML fragment name (default: 'prosemirror')
* @returns Yjs document containing the converted content
*/
function prosemirrorToYDoc(doc: Node, xmlFragment?: string): Y.Doc;
/**
* Convert ProseMirror document to Yjs XML fragment
* @param doc - ProseMirror document node
* @param xmlFragment - Yjs XML fragment to populate
* @returns Yjs XML fragment with converted content
*/
function prosemirrorToYXmlFragment(doc: Node, xmlFragment: Y.XmlFragment): Y.XmlFragment;
/**
* Convert ProseMirror JSON state to Yjs document
* @param schema - ProseMirror schema for document structure
* @param state - JSON representation of ProseMirror state
* @param xmlFragment - XML fragment name (default: 'prosemirror')
* @returns Yjs document containing the converted content
*/
function prosemirrorJSONToYDoc(schema: Schema, state: object, xmlFragment?: string): Y.Doc;
/**
* Convert ProseMirror JSON state to Yjs XML fragment
* @param schema - ProseMirror schema for document structure
* @param state - JSON representation of ProseMirror state
* @param xmlFragment - Yjs XML fragment to populate
* @returns Yjs XML fragment with converted content
*/
function prosemirrorJSONToYXmlFragment(
schema: Schema,
state: object,
xmlFragment: Y.XmlFragment
): Y.XmlFragment;/**
* Convert Yjs XML fragment to ProseMirror fragment
* @param yXmlFragment - Yjs XML fragment to convert
* @param schema - ProseMirror schema for document structure
* @returns ProseMirror fragment with converted content
*/
function yXmlFragmentToProseMirrorFragment(
yXmlFragment: Y.XmlFragment,
schema: Schema
): Fragment;
/**
* Convert Yjs XML fragment to ProseMirror root node
* @param yXmlFragment - Yjs XML fragment to convert
* @param schema - ProseMirror schema for document structure
* @returns ProseMirror document node with converted content
*/
function yXmlFragmentToProseMirrorRootNode(
yXmlFragment: Y.XmlFragment,
schema: Schema
): Node;
/**
* Initialize ProseMirror document from Yjs fragment with mapping metadata
* @param yXmlFragment - Yjs XML fragment to convert
* @param schema - ProseMirror schema for document structure
* @returns Object containing document, metadata, and mapping information
*/
function initProseMirrorDoc(yXmlFragment: Y.XmlFragment, schema: Schema): {
doc: Node;
meta: BindingMetadata;
mapping: ProsemirrorMapping;
};Usage Examples:
import * as Y from "yjs";
import { Schema } from "prosemirror-model";
import {
prosemirrorToYDoc,
yXmlFragmentToProseMirrorRootNode,
initProseMirrorDoc
} from "y-prosemirror";
// Convert ProseMirror document to Yjs for persistence
const ydoc = prosemirrorToYDoc(pmDoc);
// Initialize ProseMirror from existing Yjs content
const yXmlFragment = ydoc.getXmlFragment("prosemirror");
const { doc, meta, mapping } = initProseMirrorDoc(yXmlFragment, schema);
// Convert Yjs content back to ProseMirror
const pmDoc = yXmlFragmentToProseMirrorRootNode(yXmlFragment, schema);Manage positions and selections in collaborative editing contexts using relative positions that remain valid across document changes.
/**
* Convert ProseMirror absolute position to Yjs relative position
* @param pos - Absolute position in ProseMirror document
* @param type - Yjs type containing the position
* @param mapping - Mapping between Yjs and ProseMirror nodes
* @returns Yjs relative position that persists across changes
*/
function absolutePositionToRelativePosition(
pos: number,
type: Y.AbstractType,
mapping: ProsemirrorMapping
): Y.RelativePosition;
/**
* Convert Yjs relative position to ProseMirror absolute position
* @param y - Yjs document instance
* @param documentType - Yjs document type
* @param relPos - Yjs relative position
* @param mapping - Mapping between Yjs and ProseMirror nodes
* @returns Absolute position in ProseMirror document, or null if invalid
*/
function relativePositionToAbsolutePosition(
y: Y.Doc,
documentType: Y.AbstractType,
relPos: Y.RelativePosition,
mapping: ProsemirrorMapping
): number | null;
/**
* Get current selection as relative positions for collaboration
* @param pmbinding - ProseMirror binding instance
* @param state - Current editor state
* @returns Selection object with relative positions
*/
function getRelativeSelection(pmbinding: ProsemirrorBinding, state: EditorState): {
type: string;
anchor: Y.RelativePosition;
head: Y.RelativePosition;
};
/**
* Set metadata on ProseMirror view with deferred update
* @param view - ProseMirror editor view
* @param key - Metadata key
* @param value - Metadata value
*/
function setMeta(view: EditorView, key: any, value: any): void;Usage Examples:
import {
absolutePositionToRelativePosition,
relativePositionToAbsolutePosition,
getRelativeSelection
} from "y-prosemirror";
// Store cursor position that persists across document changes
const relativePos = absolutePositionToRelativePosition(
state.selection.anchor,
yXmlFragment,
binding.mapping
);
// Restore cursor position from stored relative position
const absolutePos = relativePositionToAbsolutePosition(
ydoc,
yXmlFragment,
relativePos,
binding.mapping
);
// Get current selection for sharing with other users
const selection = getRelativeSelection(binding, state);Legacy conversion functions that are maintained for backwards compatibility but should be avoided in new code.
/**
* @deprecated Use yXmlFragmentToProseMirrorRootNode instead
* Convert Yjs document to ProseMirror node
* @param schema - ProseMirror schema for document structure
* @param ydoc - Yjs document to convert
* @param xmlFragment - XML fragment name (optional)
*/
function yDocToProsemirror(schema: Schema, ydoc: Y.Doc, xmlFragment?: string): Node;
/**
* @deprecated Use yXmlFragmentToProseMirrorRootNode instead
* Convert Yjs XML fragment to ProseMirror node
* @param schema - ProseMirror schema for document structure
* @param yXmlFragment - Yjs XML fragment to convert
*/
function yXmlFragmentToProsemirror(schema: Schema, yXmlFragment: Y.XmlFragment): Node;
/**
* @deprecated Use appropriate conversion functions instead
* Convert Yjs document to ProseMirror JSON
* @param schema - ProseMirror schema for document structure
* @param ydoc - Yjs document to convert
* @param xmlFragment - XML fragment name (optional)
*/
function yDocToProsemirrorJSON(schema: Schema, ydoc: Y.Doc, xmlFragment?: string): object;
/**
* @deprecated Use appropriate conversion functions instead
* Convert Yjs XML fragment to ProseMirror JSON
* @param schema - ProseMirror schema for document structure
* @param yXmlFragment - Yjs XML fragment to convert
*/
function yXmlFragmentToProsemirrorJSON(schema: Schema, yXmlFragment: Y.XmlFragment): object;Internal helper functions that are exported for advanced usage scenarios.
/**
* Update Yjs fragment from ProseMirror node
* @param y - Yjs document
* @param yDomFragment - Yjs fragment to update
* @param pNode - ProseMirror node
* @param meta - Binding metadata
*/
function updateYFragment(y: Y.Doc, yDomFragment: Y.XmlFragment, pNode: Node, meta: BindingMetadata): void;
/**
* Create ProseMirror node from Yjs element
* @param yElement - Yjs element
* @param schema - ProseMirror schema
* @param meta - Binding metadata
* @returns ProseMirror node
*/
function createNodeFromYElement(yElement: Y.XmlElement, schema: Schema, meta: BindingMetadata): Node;
/**
* Create empty binding metadata
* @returns Empty binding metadata object
*/
function createEmptyMeta(): BindingMetadata;
/**
* Check if Yjs item is visible in snapshot
* @param item - Yjs item to check
* @param snapshot - Snapshot to check against
* @returns true if item is visible
*/
function isVisible(item: any, snapshot?: Snapshot): boolean;
/**
* Generate hash of JSON object for content comparison
* @param obj - JSON object to hash
* @returns String hash of the object
*/
function hashOfJSON(obj: object): string;/** Mapping between Yjs types and ProseMirror nodes */
type ProsemirrorMapping = Map<Y.AbstractType<any>, PModel.Node | Array<PModel.Node>>;
/** Metadata structure for binding state */
interface BindingMetadata {
mapping: ProsemirrorMapping;
isOMark: Map<import('prosemirror-model').MarkType, boolean>;
}Install with Tessl CLI
npx tessl i tessl/npm-y-prosemirror