0
# Y-ProseMirror
1
2
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.
3
4
## Package Information
5
6
- **Package Name**: y-prosemirror
7
- **Package Type**: npm
8
- **Language**: JavaScript with TypeScript definitions
9
- **Installation**: `npm install y-prosemirror`
10
11
## Core Imports
12
13
```javascript
14
import { ySyncPlugin, yCursorPlugin, yUndoPlugin } from "y-prosemirror";
15
```
16
17
For CommonJS:
18
19
```javascript
20
const { ySyncPlugin, yCursorPlugin, yUndoPlugin } = require("y-prosemirror");
21
```
22
23
Utility functions:
24
25
```javascript
26
import {
27
prosemirrorToYDoc,
28
yXmlFragmentToProseMirrorRootNode,
29
absolutePositionToRelativePosition
30
} from "y-prosemirror";
31
```
32
33
## Basic Usage
34
35
```javascript
36
import * as Y from "yjs";
37
import { EditorView } from "prosemirror-view";
38
import { EditorState } from "prosemirror-state";
39
import { DOMParser, Schema } from "prosemirror-model";
40
import { Awareness } from "y-protocols/awareness";
41
import {
42
ySyncPlugin,
43
yCursorPlugin,
44
yUndoPlugin,
45
undo,
46
redo,
47
initProseMirrorDoc
48
} from "y-prosemirror";
49
import { keymap } from "prosemirror-keymap";
50
51
// Define your ProseMirror schema
52
const schema = new Schema({
53
nodes: {
54
doc: { content: "paragraph+" },
55
paragraph: { content: "text*", toDOM: () => ["p", 0] },
56
text: {}
57
}
58
});
59
60
// Create Yjs document and awareness
61
const ydoc = new Y.Doc();
62
const yXmlFragment = ydoc.getXmlFragment("prosemirror");
63
const awareness = new Awareness(ydoc);
64
65
// Initialize document from Yjs content (or create empty)
66
const { doc, mapping } = initProseMirrorDoc(yXmlFragment, schema);
67
68
// Create ProseMirror editor with collaborative plugins
69
const view = new EditorView(document.querySelector("#editor"), {
70
state: EditorState.create({
71
doc,
72
schema,
73
plugins: [
74
ySyncPlugin(yXmlFragment, { mapping }),
75
yCursorPlugin(awareness),
76
yUndoPlugin(),
77
keymap({
78
'Mod-z': undo,
79
'Mod-y': redo,
80
'Mod-Shift-z': redo
81
})
82
]
83
})
84
});
85
86
// Set user information for collaborative cursors
87
awareness.setLocalStateField('user', {
88
name: 'User Name',
89
color: '#ff6b6b'
90
});
91
```
92
93
## Architecture
94
95
Y-ProseMirror is built around several key components:
96
97
- **Synchronization Plugin**: Maintains bidirectional sync between Yjs and ProseMirror documents
98
- **Cursor Plugin**: Displays real-time cursors and selections from other users via awareness protocol
99
- **Undo Plugin**: Provides collaborative undo/redo with per-user history tracking
100
- **Binding System**: Core `ProsemirrorBinding` class managing the synchronization lifecycle
101
- **Conversion Utilities**: Functions for converting between Yjs and ProseMirror data structures
102
- **Position Mapping**: Utilities for converting between absolute and relative positions
103
104
## Capabilities
105
106
### Core Plugins
107
108
Essential ProseMirror plugins that enable collaborative editing with synchronization, cursors, and undo functionality.
109
110
```javascript { .api }
111
function ySyncPlugin(yXmlFragment: Y.XmlFragment, opts?: {
112
colors?: Array<ColorDef>;
113
colorMapping?: Map<string, ColorDef>;
114
permanentUserData?: Y.PermanentUserData | null;
115
mapping?: ProsemirrorMapping;
116
onFirstRender?: () => void;
117
}): Plugin;
118
119
function yCursorPlugin(awareness: Awareness, opts?: {
120
awarenessStateFilter?: (currentClientId: number, userClientId: number, user: any) => boolean;
121
cursorBuilder?: (user: any, clientId: number) => HTMLElement;
122
selectionBuilder?: (user: any, clientId: number) => DecorationAttrs;
123
getSelection?: (state: EditorState) => Selection;
124
}, cursorStateField?: string): Plugin;
125
126
function yUndoPlugin(options?: {
127
protectedNodes?: Set<string>;
128
trackedOrigins?: any[];
129
undoManager?: UndoManager | null;
130
}): Plugin;
131
```
132
133
[Core Plugins](./plugins.md)
134
135
### Document Conversion
136
137
Utilities for converting between ProseMirror and Yjs document formats, enabling data migration and persistence.
138
139
```javascript { .api }
140
function prosemirrorToYDoc(doc: Node, xmlFragment?: string): Y.Doc;
141
function yXmlFragmentToProseMirrorRootNode(yXmlFragment: Y.XmlFragment, schema: Schema): Node;
142
function prosemirrorJSONToYDoc(schema: Schema, state: object, xmlFragment?: string): Y.Doc;
143
```
144
145
[Document Conversion](./utilities.md)
146
147
### Position and Selection Utilities
148
149
Functions for managing positions and selections in collaborative editing contexts with relative position support.
150
151
```javascript { .api }
152
function absolutePositionToRelativePosition(pos: number, type: Y.AbstractType, mapping: ProsemirrorMapping): Y.RelativePosition;
153
function relativePositionToAbsolutePosition(y: Y.Doc, documentType: Y.AbstractType, relPos: Y.RelativePosition, mapping: ProsemirrorMapping): number | null;
154
function getRelativeSelection(pmbinding: ProsemirrorBinding, state: EditorState): {
155
type: string;
156
anchor: Y.RelativePosition;
157
head: Y.RelativePosition;
158
};
159
```
160
161
[Position Utilities](./utilities.md)
162
163
### Plugin Key Constants
164
165
Plugin keys for accessing plugin state and configuration.
166
167
```javascript { .api }
168
const ySyncPluginKey: PluginKey;
169
const yCursorPluginKey: PluginKey;
170
const yUndoPluginKey: PluginKey;
171
```
172
173
## Types
174
175
```javascript { .api }
176
interface ColorDef {
177
light: string;
178
dark: string;
179
}
180
181
type ProsemirrorMapping = Map<Y.AbstractType<any>, PModel.Node | Array<PModel.Node>>;
182
183
interface BindingMetadata {
184
mapping: ProsemirrorMapping;
185
isOMark: Map<import('prosemirror-model').MarkType, boolean>;
186
}
187
188
interface UndoPluginState {
189
undoManager: import('yjs').UndoManager;
190
prevSel: ReturnType<typeof getRelativeSelection> | null;
191
hasUndoOps: boolean;
192
hasRedoOps: boolean;
193
}
194
195
interface YSyncOpts {
196
colors?: Array<ColorDef>;
197
colorMapping?: Map<string, ColorDef>;
198
permanentUserData?: Y.PermanentUserData | null;
199
mapping?: ProsemirrorMapping;
200
onFirstRender?: () => void;
201
}
202
203
class ProsemirrorBinding {
204
type: Y.XmlFragment;
205
prosemirrorView: EditorView | null;
206
mapping: ProsemirrorMapping;
207
doc: Y.Doc;
208
mux: any;
209
isOMark: Map<MarkType, boolean>;
210
isDestroyed: boolean;
211
beforeTransactionSelection: any;
212
213
constructor(yXmlFragment: Y.XmlFragment, mapping?: Map);
214
initView(prosemirrorView: EditorView): void;
215
destroy(): void;
216
renderSnapshot(snapshot: Snapshot, prevSnapshot?: Snapshot): void;
217
unrenderSnapshot(): void;
218
}
219
```