@kerebron/extension-yjs 0.6.7 → 0.7.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/esm/ExtensionYjs.d.ts +3 -11
- package/esm/ExtensionYjs.d.ts.map +1 -1
- package/esm/ExtensionYjs.js +71 -45
- package/esm/ExtensionYjs.js.map +1 -1
- package/esm/WebsocketProvider.d.ts +70 -0
- package/esm/WebsocketProvider.d.ts.map +1 -0
- package/esm/WebsocketProvider.js +377 -0
- package/esm/WebsocketProvider.js.map +1 -0
- package/esm/YjsProvider.d.ts +48 -0
- package/esm/YjsProvider.d.ts.map +1 -0
- package/esm/YjsProvider.js +12 -0
- package/esm/YjsProvider.js.map +1 -0
- package/esm/_dnt.shims.d.ts +2 -0
- package/esm/_dnt.shims.d.ts.map +1 -0
- package/esm/_dnt.shims.js +58 -0
- package/esm/_dnt.shims.js.map +1 -0
- package/esm/binding/BindingMetadata.d.ts +6 -0
- package/esm/binding/BindingMetadata.d.ts.map +1 -0
- package/esm/binding/BindingMetadata.js +2 -0
- package/esm/binding/BindingMetadata.js.map +1 -0
- package/esm/binding/DiffViewer.d.ts +17 -0
- package/esm/binding/DiffViewer.d.ts.map +1 -0
- package/esm/binding/DiffViewer.js +96 -0
- package/esm/binding/DiffViewer.js.map +1 -0
- package/esm/binding/PmYjsBinding.d.ts +45 -0
- package/esm/binding/PmYjsBinding.d.ts.map +1 -0
- package/esm/binding/PmYjsBinding.js +230 -0
- package/esm/binding/PmYjsBinding.js.map +1 -0
- package/esm/binding/convertUtils.d.ts +48 -0
- package/esm/binding/convertUtils.d.ts.map +1 -0
- package/esm/binding/convertUtils.js +80 -0
- package/esm/binding/convertUtils.js.map +1 -0
- package/esm/{createNodeFromYElement.d.ts → binding/createNodeFromYElement.d.ts} +2 -2
- package/esm/binding/createNodeFromYElement.d.ts.map +1 -0
- package/esm/{createNodeFromYElement.js → binding/createNodeFromYElement.js} +2 -2
- package/esm/binding/createNodeFromYElement.js.map +1 -0
- package/esm/{updateYFragment.d.ts → binding/updateYFragment.d.ts} +3 -3
- package/esm/binding/updateYFragment.d.ts.map +1 -0
- package/esm/{updateYFragment.js → binding/updateYFragment.js} +10 -7
- package/esm/binding/updateYFragment.js.map +1 -0
- package/esm/debug.d.ts.map +1 -1
- package/esm/debug.js +11 -0
- package/esm/debug.js.map +1 -1
- package/esm/lib.d.ts +1 -7
- package/esm/lib.d.ts.map +1 -1
- package/esm/lib.js +1 -200
- package/esm/lib.js.map +1 -1
- package/esm/position.d.ts +8 -0
- package/esm/position.d.ts.map +1 -0
- package/esm/position.js +165 -0
- package/esm/position.js.map +1 -0
- package/esm/ui/selection.d.ts +29 -0
- package/esm/ui/selection.d.ts.map +1 -0
- package/esm/ui/selection.js +129 -0
- package/esm/ui/selection.js.map +1 -0
- package/esm/utils.d.ts +1 -1
- package/esm/utils.d.ts.map +1 -1
- package/esm/utils.js.map +1 -1
- package/esm/yPositionPlugin.d.ts +6 -1
- package/esm/yPositionPlugin.d.ts.map +1 -1
- package/esm/yPositionPlugin.js +91 -50
- package/esm/yPositionPlugin.js.map +1 -1
- package/esm/ySyncPlugin.d.ts +5 -22
- package/esm/ySyncPlugin.d.ts.map +1 -1
- package/esm/ySyncPlugin.js +70 -101
- package/esm/ySyncPlugin.js.map +1 -1
- package/esm/yUndoPlugin.d.ts +11 -10
- package/esm/yUndoPlugin.d.ts.map +1 -1
- package/esm/yUndoPlugin.js +90 -52
- package/esm/yUndoPlugin.js.map +1 -1
- package/package.json +9 -6
- package/src/ExtensionYjs.ts +98 -67
- package/src/WebsocketProvider.ts +528 -0
- package/src/YjsProvider.ts +75 -0
- package/src/_dnt.shims.ts +60 -0
- package/src/binding/BindingMetadata.ts +6 -0
- package/src/binding/DiffViewer.ts +138 -0
- package/src/binding/PmYjsBinding.ts +360 -0
- package/src/binding/convertUtils.ts +124 -0
- package/src/{createNodeFromYElement.ts → binding/createNodeFromYElement.ts} +4 -4
- package/src/{updateYFragment.ts → binding/updateYFragment.ts} +15 -8
- package/src/debug.ts +21 -0
- package/src/lib.ts +4 -230
- package/src/position.ts +191 -0
- package/src/ui/selection.ts +218 -0
- package/src/utils.ts +1 -1
- package/src/yPositionPlugin.ts +122 -74
- package/src/ySyncPlugin.ts +111 -155
- package/src/yUndoPlugin.ts +113 -62
- package/esm/ProsemirrorBinding.d.ts +0 -60
- package/esm/ProsemirrorBinding.d.ts.map +0 -1
- package/esm/ProsemirrorBinding.js +0 -405
- package/esm/ProsemirrorBinding.js.map +0 -1
- package/esm/createNodeFromYElement.d.ts.map +0 -1
- package/esm/createNodeFromYElement.js.map +0 -1
- package/esm/updateYFragment.d.ts.map +0 -1
- package/esm/updateYFragment.js.map +0 -1
- package/esm/userColors.d.ts +0 -5
- package/esm/userColors.d.ts.map +0 -1
- package/esm/userColors.js +0 -11
- package/esm/userColors.js.map +0 -1
- package/src/ProsemirrorBinding.ts +0 -607
- package/src/userColors.ts +0 -10
package/src/yPositionPlugin.ts
CHANGED
|
@@ -1,24 +1,50 @@
|
|
|
1
|
-
import * as Y from 'yjs';
|
|
2
1
|
import { Awareness } from 'y-protocols/awareness';
|
|
2
|
+
import * as Y from 'yjs';
|
|
3
3
|
|
|
4
4
|
import { EditorState, Plugin, Transaction } from 'prosemirror-state';
|
|
5
5
|
import { EditorView } from 'prosemirror-view';
|
|
6
6
|
|
|
7
7
|
import type { CoreEditor } from '@kerebron/editor';
|
|
8
|
-
import
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
8
|
+
import {
|
|
9
|
+
type ColorMapper,
|
|
10
|
+
defaultColorMapper,
|
|
11
|
+
generateBlankUser,
|
|
12
|
+
type User,
|
|
13
|
+
} from '@kerebron/editor/user';
|
|
14
|
+
import type { SelectionState } from '@kerebron/extension-basic-editor/ExtensionRemoteSelection';
|
|
13
15
|
|
|
16
|
+
import { yPositionPluginKey, ySyncPluginKey } from './keys.js';
|
|
14
17
|
import {
|
|
15
18
|
absolutePositionToRelativePosition,
|
|
16
19
|
relativePositionToAbsolutePosition,
|
|
17
|
-
|
|
18
|
-
} from './lib.js';
|
|
19
|
-
import { yPositionPluginKey, ySyncPluginKey } from './keys.js';
|
|
20
|
+
} from './position.js';
|
|
20
21
|
import type { YSyncPluginState } from './ySyncPlugin.js';
|
|
21
22
|
|
|
23
|
+
/**
|
|
24
|
+
* Is null if no timeout is in progress.
|
|
25
|
+
* Is defined if a timeout is in progress.
|
|
26
|
+
* Maps from view
|
|
27
|
+
*/
|
|
28
|
+
let viewsToUpdate: Map<EditorView, Map<any, any>> | null = null;
|
|
29
|
+
|
|
30
|
+
const updateMetas = () => {
|
|
31
|
+
const ups: Map<EditorView, Map<any, any>> | null = viewsToUpdate;
|
|
32
|
+
viewsToUpdate = null;
|
|
33
|
+
if (!ups) {
|
|
34
|
+
return;
|
|
35
|
+
}
|
|
36
|
+
ups.forEach((metas, view) => {
|
|
37
|
+
const tr = view.state.tr;
|
|
38
|
+
const syncState = ySyncPluginKey.getState(view.state);
|
|
39
|
+
if (syncState && syncState.binding) { // && !syncState.binding.isDestroyed
|
|
40
|
+
metas.forEach((val, key) => {
|
|
41
|
+
tr.setMeta(key, val);
|
|
42
|
+
});
|
|
43
|
+
view.dispatch(tr);
|
|
44
|
+
}
|
|
45
|
+
});
|
|
46
|
+
};
|
|
47
|
+
|
|
22
48
|
type AwarenessListener = (
|
|
23
49
|
{ added, updated, removed }: {
|
|
24
50
|
added: number[];
|
|
@@ -36,20 +62,24 @@ interface PositionPluginConfig {
|
|
|
36
62
|
export interface YPositionPluginState {
|
|
37
63
|
awareness?: Awareness;
|
|
38
64
|
awarenessListener?: AwarenessListener;
|
|
65
|
+
cursorStateField: string;
|
|
66
|
+
userStateField: string;
|
|
67
|
+
me: User;
|
|
68
|
+
colorMapper: ColorMapper;
|
|
39
69
|
}
|
|
40
70
|
|
|
41
71
|
function destroyAwareness(
|
|
42
|
-
|
|
43
|
-
cursorStateField: string,
|
|
72
|
+
pluginState: YPositionPluginState,
|
|
44
73
|
) {
|
|
45
|
-
if (!
|
|
74
|
+
if (!pluginState.awareness) {
|
|
46
75
|
return;
|
|
47
76
|
}
|
|
48
|
-
const awareness =
|
|
49
|
-
if (
|
|
50
|
-
awareness.off('change',
|
|
77
|
+
const awareness = pluginState.awareness;
|
|
78
|
+
if (pluginState.awarenessListener) {
|
|
79
|
+
awareness.off('change', pluginState.awarenessListener);
|
|
51
80
|
}
|
|
52
|
-
awareness.setLocalStateField(cursorStateField, null);
|
|
81
|
+
awareness.setLocalStateField(pluginState.cursorStateField, null);
|
|
82
|
+
pluginState.awareness = undefined;
|
|
53
83
|
}
|
|
54
84
|
|
|
55
85
|
function initAwareness(state: YPositionPluginState, editor: CoreEditor) {
|
|
@@ -63,10 +93,14 @@ function initAwareness(state: YPositionPluginState, editor: CoreEditor) {
|
|
|
63
93
|
{ added, updated, removed },
|
|
64
94
|
) => {
|
|
65
95
|
const ystate: YSyncPluginState = ySyncPluginKey.getState(view.state)!;
|
|
66
|
-
if (!ystate.
|
|
96
|
+
if (!ystate.binding) {
|
|
67
97
|
return;
|
|
68
98
|
}
|
|
69
|
-
const
|
|
99
|
+
const yjs = ystate.binding.getYjs();
|
|
100
|
+
if (!yjs) {
|
|
101
|
+
return;
|
|
102
|
+
}
|
|
103
|
+
|
|
70
104
|
const clients = added.concat(updated).concat(removed);
|
|
71
105
|
if (
|
|
72
106
|
clients.findIndex((id: number) => id !== awareness.doc.clientID) ===
|
|
@@ -75,46 +109,37 @@ function initAwareness(state: YPositionPluginState, editor: CoreEditor) {
|
|
|
75
109
|
return;
|
|
76
110
|
}
|
|
77
111
|
|
|
78
|
-
|
|
79
|
-
setMeta(view, remoteSelectionPluginKey, {
|
|
80
|
-
remotePositionUpdated: true,
|
|
81
|
-
});
|
|
82
|
-
}
|
|
83
|
-
|
|
112
|
+
const { ydoc, xmlFragment } = yjs;
|
|
84
113
|
const remoteStates: SelectionState[] = [];
|
|
85
|
-
|
|
86
|
-
const ydoc = ystate.ydoc;
|
|
87
|
-
|
|
88
114
|
awareness.getStates().forEach((aw, clientId) => {
|
|
89
115
|
if (!defaultAwarenessStateFilter(ydoc.clientID, clientId, aw)) {
|
|
90
116
|
return;
|
|
91
117
|
}
|
|
92
118
|
|
|
93
|
-
|
|
119
|
+
const cursor = aw[state.cursorStateField];
|
|
120
|
+
const user: User | undefined = aw[state.userStateField];
|
|
121
|
+
|
|
122
|
+
if (!cursor || !user) {
|
|
94
123
|
return;
|
|
95
124
|
}
|
|
96
125
|
|
|
97
126
|
const anchor = relativePositionToAbsolutePosition(
|
|
98
127
|
ydoc,
|
|
99
|
-
|
|
100
|
-
Y.createRelativePositionFromJSON(
|
|
101
|
-
ystate.binding.
|
|
128
|
+
xmlFragment,
|
|
129
|
+
Y.createRelativePositionFromJSON(cursor.anchor),
|
|
130
|
+
ystate.binding.getMapping(),
|
|
102
131
|
);
|
|
103
132
|
const head = relativePositionToAbsolutePosition(
|
|
104
133
|
ydoc,
|
|
105
|
-
|
|
106
|
-
Y.createRelativePositionFromJSON(
|
|
107
|
-
ystate.binding.
|
|
134
|
+
xmlFragment,
|
|
135
|
+
Y.createRelativePositionFromJSON(cursor.head),
|
|
136
|
+
ystate.binding.getMapping(),
|
|
108
137
|
);
|
|
109
138
|
|
|
110
139
|
if (anchor !== null && head !== null) {
|
|
111
140
|
remoteStates.push({
|
|
112
141
|
clientId,
|
|
113
|
-
user:
|
|
114
|
-
name: aw.user?.name,
|
|
115
|
-
color: aw.user?.color,
|
|
116
|
-
colorLight: aw.user?.colorLight,
|
|
117
|
-
},
|
|
142
|
+
user: user,
|
|
118
143
|
cursor: {
|
|
119
144
|
anchor,
|
|
120
145
|
head,
|
|
@@ -122,12 +147,10 @@ function initAwareness(state: YPositionPluginState, editor: CoreEditor) {
|
|
|
122
147
|
});
|
|
123
148
|
}
|
|
124
149
|
});
|
|
125
|
-
const extension: ExtensionRemoteSelection = editor.getExtension(
|
|
126
|
-
'remote-selection',
|
|
127
|
-
)!;
|
|
128
150
|
|
|
129
|
-
|
|
130
|
-
|
|
151
|
+
const tr = editor.state.tr;
|
|
152
|
+
tr.setMeta('remoteSelectionChange', { remoteStates });
|
|
153
|
+
editor.dispatchTransaction(tr);
|
|
131
154
|
};
|
|
132
155
|
|
|
133
156
|
awareness.on('change', state.awarenessListener);
|
|
@@ -147,40 +170,49 @@ export const yPositionPlugin = (
|
|
|
147
170
|
{
|
|
148
171
|
getSelection = (state: EditorState) => state.selection,
|
|
149
172
|
}: PositionPluginConfig = {},
|
|
150
|
-
cursorStateField: string = 'cursor',
|
|
151
173
|
) => {
|
|
152
174
|
return new Plugin<YPositionPluginState>({
|
|
153
175
|
key: yPositionPluginKey,
|
|
154
176
|
state: {
|
|
155
|
-
init: (
|
|
177
|
+
init: (): YPositionPluginState => {
|
|
156
178
|
return {
|
|
157
179
|
awareness: undefined,
|
|
180
|
+
cursorStateField: 'kerebron:cursor',
|
|
181
|
+
userStateField: 'kerebron:user',
|
|
182
|
+
me: generateBlankUser(),
|
|
183
|
+
colorMapper: defaultColorMapper,
|
|
158
184
|
};
|
|
159
185
|
},
|
|
160
186
|
apply: (tr: Transaction, pluginState: YPositionPluginState) => {
|
|
161
|
-
const
|
|
187
|
+
const changeUser = tr.getMeta('changeUser');
|
|
188
|
+
if (changeUser) {
|
|
189
|
+
pluginState.me = { ...changeUser.user };
|
|
190
|
+
}
|
|
191
|
+
const setColorMapper = tr.getMeta('setColorMapper');
|
|
192
|
+
if (setColorMapper) {
|
|
193
|
+
pluginState.colorMapper = setColorMapper.colorMapper;
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
const awareness = tr.getMeta('yjs:setAwareness');
|
|
162
197
|
if (awareness) {
|
|
163
198
|
if (pluginState.awareness) {
|
|
164
|
-
destroyAwareness(pluginState
|
|
199
|
+
destroyAwareness(pluginState);
|
|
165
200
|
}
|
|
166
201
|
pluginState.awareness = awareness;
|
|
167
202
|
if (pluginState.awareness) {
|
|
168
203
|
initAwareness(pluginState, editor);
|
|
169
204
|
}
|
|
170
205
|
}
|
|
206
|
+
|
|
207
|
+
if (tr.getMeta('yjs:removeAwareness')) {
|
|
208
|
+
if (pluginState.awareness) {
|
|
209
|
+
destroyAwareness(pluginState);
|
|
210
|
+
}
|
|
211
|
+
}
|
|
171
212
|
return pluginState;
|
|
172
213
|
},
|
|
173
214
|
},
|
|
174
215
|
view: (view: EditorView) => {
|
|
175
|
-
// const ystate: YSyncPluginState = ySyncPluginKey.getState(view.state)!;
|
|
176
|
-
// if (
|
|
177
|
-
// ystate.snapshot != null || ystate.prevSnapshot != null ||
|
|
178
|
-
// ystate.binding.mapping.size === 0
|
|
179
|
-
// ) {
|
|
180
|
-
// // do not render cursors while snapshot is active
|
|
181
|
-
// return DecorationSet.empty;
|
|
182
|
-
// }
|
|
183
|
-
|
|
184
216
|
const updateAwareness = (
|
|
185
217
|
selectionAnchor: number,
|
|
186
218
|
selectionHead: number,
|
|
@@ -195,30 +227,37 @@ export const yPositionPlugin = (
|
|
|
195
227
|
const current = awareness.getLocalState() || {};
|
|
196
228
|
|
|
197
229
|
const ystate: YSyncPluginState = ySyncPluginKey.getState(view.state)!;
|
|
230
|
+
const yjs = ystate.binding.getYjs();
|
|
231
|
+
if (!yjs) {
|
|
232
|
+
return;
|
|
233
|
+
}
|
|
234
|
+
const { xmlFragment } = yjs;
|
|
198
235
|
|
|
199
236
|
const anchor: Y.RelativePosition = absolutePositionToRelativePosition(
|
|
200
237
|
selectionAnchor,
|
|
201
|
-
|
|
202
|
-
ystate.binding.
|
|
238
|
+
xmlFragment,
|
|
239
|
+
ystate.binding.getMapping(),
|
|
203
240
|
);
|
|
204
241
|
const head: Y.RelativePosition = absolutePositionToRelativePosition(
|
|
205
242
|
selectionHead,
|
|
206
|
-
|
|
207
|
-
ystate.binding.
|
|
243
|
+
xmlFragment,
|
|
244
|
+
ystate.binding.getMapping(),
|
|
208
245
|
);
|
|
209
246
|
|
|
247
|
+
const cursor = current[state.cursorStateField];
|
|
248
|
+
|
|
210
249
|
if (
|
|
211
|
-
|
|
250
|
+
cursor == null ||
|
|
212
251
|
!Y.compareRelativePositions(
|
|
213
|
-
Y.createRelativePositionFromJSON(
|
|
252
|
+
Y.createRelativePositionFromJSON(cursor.anchor),
|
|
214
253
|
anchor,
|
|
215
254
|
) ||
|
|
216
255
|
!Y.compareRelativePositions(
|
|
217
|
-
Y.createRelativePositionFromJSON(
|
|
256
|
+
Y.createRelativePositionFromJSON(cursor.head),
|
|
218
257
|
head,
|
|
219
258
|
)
|
|
220
259
|
) {
|
|
221
|
-
awareness.setLocalStateField(cursorStateField, {
|
|
260
|
+
awareness.setLocalStateField(state.cursorStateField, {
|
|
222
261
|
anchor,
|
|
223
262
|
head,
|
|
224
263
|
});
|
|
@@ -232,22 +271,31 @@ export const yPositionPlugin = (
|
|
|
232
271
|
if (!state.awareness) {
|
|
233
272
|
return;
|
|
234
273
|
}
|
|
235
|
-
const awareness = state.awareness;
|
|
236
274
|
|
|
237
|
-
const ystate = ySyncPluginKey.getState(view.state)!;
|
|
275
|
+
const ystate: YSyncPluginState = ySyncPluginKey.getState(view.state)!;
|
|
276
|
+
const yjs = ystate.binding.getYjs();
|
|
277
|
+
if (!yjs) {
|
|
278
|
+
return;
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
const awareness = state.awareness;
|
|
238
282
|
const current = awareness.getLocalState() || {};
|
|
239
283
|
|
|
284
|
+
const { ydoc, xmlFragment } = yjs;
|
|
285
|
+
|
|
286
|
+
const cursor = current[state.cursorStateField];
|
|
287
|
+
|
|
240
288
|
if (
|
|
241
|
-
|
|
289
|
+
cursor &&
|
|
242
290
|
relativePositionToAbsolutePosition(
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
Y.createRelativePositionFromJSON(
|
|
246
|
-
ystate.binding.
|
|
291
|
+
ydoc,
|
|
292
|
+
xmlFragment,
|
|
293
|
+
Y.createRelativePositionFromJSON(cursor.anchor),
|
|
294
|
+
ystate.binding.getMapping(),
|
|
247
295
|
) !== null
|
|
248
296
|
) {
|
|
249
297
|
// delete cursor information if current cursor information is owned by this editor binding
|
|
250
|
-
awareness.setLocalStateField(cursorStateField, null);
|
|
298
|
+
awareness.setLocalStateField(state.cursorStateField, null);
|
|
251
299
|
}
|
|
252
300
|
};
|
|
253
301
|
|
|
@@ -286,7 +334,7 @@ export const yPositionPlugin = (
|
|
|
286
334
|
view.state,
|
|
287
335
|
);
|
|
288
336
|
if (pluginState) {
|
|
289
|
-
destroyAwareness(pluginState
|
|
337
|
+
destroyAwareness(pluginState);
|
|
290
338
|
}
|
|
291
339
|
|
|
292
340
|
editor.removeEventListener(
|
package/src/ySyncPlugin.ts
CHANGED
|
@@ -2,57 +2,54 @@ import * as Y from 'yjs';
|
|
|
2
2
|
import { Plugin } from 'prosemirror-state';
|
|
3
3
|
|
|
4
4
|
import { ySyncPluginKey, yUndoPluginKey } from './keys.js';
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
7
|
-
import
|
|
8
|
-
|
|
9
|
-
export type TransactFunc<T> = (
|
|
10
|
-
f: (arg0?: Y.Transaction) => T,
|
|
11
|
-
origin?: any,
|
|
12
|
-
) => T;
|
|
13
|
-
|
|
14
|
-
export interface ColorDef {
|
|
15
|
-
light: string;
|
|
16
|
-
dark: string;
|
|
17
|
-
}
|
|
5
|
+
import type { CreateYjsProvider } from './YjsProvider.js';
|
|
6
|
+
import { PmYjsBinding } from './binding/PmYjsBinding.js';
|
|
7
|
+
import { CoreEditor } from '@kerebron/editor';
|
|
18
8
|
|
|
19
9
|
interface YSyncOpts {
|
|
20
|
-
colors?: Array<ColorDef>;
|
|
21
|
-
colorMapping?: Map<string, ColorDef>;
|
|
22
10
|
permanentUserData?: Y.PermanentUserData;
|
|
23
11
|
onFirstRender?: () => void;
|
|
24
12
|
roomId?: string;
|
|
25
13
|
}
|
|
26
14
|
|
|
27
15
|
export interface YSyncPluginState {
|
|
28
|
-
|
|
29
|
-
provider?: YjsProvider;
|
|
30
|
-
type: Y.XmlFragment;
|
|
31
|
-
ydoc: Y.Doc;
|
|
32
|
-
|
|
33
|
-
binding: ProsemirrorBinding;
|
|
34
|
-
addToHistory: boolean;
|
|
16
|
+
binding: PmYjsBinding;
|
|
35
17
|
isChangeOrigin: boolean;
|
|
36
|
-
|
|
37
|
-
snapshot?: Y.Snapshot;
|
|
38
|
-
prevSnapshot?: Y.Snapshot;
|
|
39
|
-
isUndoRedoOperation: boolean;
|
|
40
|
-
colors: Array<ColorDef>;
|
|
41
|
-
colorMapping: Map<string, ColorDef>;
|
|
18
|
+
isUndoRedoOperation: boolean; // Used in y-history.test.ts
|
|
42
19
|
permanentUserData?: Y.PermanentUserData;
|
|
43
20
|
}
|
|
44
21
|
|
|
22
|
+
interface YSyncMeta {
|
|
23
|
+
getYDoc?: {
|
|
24
|
+
resolve: (doc: Y.Doc) => void;
|
|
25
|
+
reject: (reason: any) => void;
|
|
26
|
+
};
|
|
27
|
+
changeRoom?: {
|
|
28
|
+
roomId: string;
|
|
29
|
+
};
|
|
30
|
+
leaveRoom?: boolean;
|
|
31
|
+
isChangeOrigin?: boolean;
|
|
32
|
+
isUndoRedoOperation?: boolean;
|
|
33
|
+
getYSnapshot?: {
|
|
34
|
+
resolve: (snapshot: Uint8Array) => void;
|
|
35
|
+
reject: (reason: any) => void;
|
|
36
|
+
};
|
|
37
|
+
setYSnapshot?: {
|
|
38
|
+
prevSnapshot?: Uint8Array;
|
|
39
|
+
snapshot: Uint8Array;
|
|
40
|
+
};
|
|
41
|
+
resetYSnapshot?: boolean;
|
|
42
|
+
}
|
|
43
|
+
|
|
45
44
|
/**
|
|
46
45
|
* This plugin listens to changes in prosemirror view and keeps yXmlState and view in sync.
|
|
47
46
|
*
|
|
48
47
|
* This plugin also keeps references to the type and the shared document so other plugins can access it.
|
|
49
48
|
*/
|
|
50
49
|
export const ySyncPlugin = (
|
|
51
|
-
|
|
52
|
-
|
|
50
|
+
editor: CoreEditor,
|
|
51
|
+
createYjsProvider: CreateYjsProvider,
|
|
53
52
|
{
|
|
54
|
-
colors = defaultColors,
|
|
55
|
-
colorMapping = new Map(),
|
|
56
53
|
onFirstRender = () => {
|
|
57
54
|
},
|
|
58
55
|
}: YSyncOpts = {},
|
|
@@ -60,132 +57,105 @@ export const ySyncPlugin = (
|
|
|
60
57
|
let initialContentChanged = false;
|
|
61
58
|
const plugin: Plugin<YSyncPluginState> = new Plugin<YSyncPluginState>({
|
|
62
59
|
props: {
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
60
|
+
editable: (state) => {
|
|
61
|
+
const syncState = ySyncPluginKey.getState(state)!;
|
|
62
|
+
return syncState.binding.isEditable();
|
|
63
|
+
},
|
|
67
64
|
},
|
|
68
65
|
key: ySyncPluginKey,
|
|
69
66
|
state: {
|
|
70
67
|
init: (_initargs, state): YSyncPluginState => {
|
|
71
|
-
const ydoc = new Y.Doc();
|
|
72
|
-
const yXmlFragment: Y.XmlFragment = ydoc.getXmlFragment('prosemirror');
|
|
73
|
-
const binding = new ProsemirrorBinding(yXmlFragment, new Map());
|
|
74
|
-
|
|
75
68
|
return {
|
|
76
|
-
|
|
77
|
-
provider: undefined,
|
|
78
|
-
binding,
|
|
79
|
-
type: yXmlFragment,
|
|
80
|
-
ydoc: ydoc,
|
|
81
|
-
snapshot: undefined,
|
|
82
|
-
prevSnapshot: undefined,
|
|
69
|
+
binding: new PmYjsBinding(editor),
|
|
83
70
|
isChangeOrigin: false,
|
|
84
71
|
isUndoRedoOperation: false,
|
|
85
|
-
addToHistory: true,
|
|
86
|
-
restore: undefined,
|
|
87
|
-
colors,
|
|
88
|
-
colorMapping,
|
|
89
72
|
permanentUserData: undefined,
|
|
90
73
|
};
|
|
91
74
|
},
|
|
92
75
|
apply: (tr, pluginState: YSyncPluginState) => {
|
|
93
|
-
const
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
);
|
|
107
|
-
pluginState.type = yXmlFragment, pluginState.ydoc = ydoc;
|
|
108
|
-
|
|
109
|
-
if (pluginState.provider) {
|
|
110
|
-
if (pluginState.binding.prosemirrorView) {
|
|
111
|
-
const view = pluginState.binding.prosemirrorView;
|
|
112
|
-
const tr = view.state.tr.setMeta(
|
|
113
|
-
'yjs:awareness',
|
|
114
|
-
pluginState.provider.awareness,
|
|
115
|
-
);
|
|
116
|
-
view.dispatch(tr);
|
|
117
|
-
}
|
|
118
|
-
}
|
|
76
|
+
const pluginMeta: Partial<YSyncMeta> = tr.getMeta(ySyncPluginKey);
|
|
77
|
+
|
|
78
|
+
const changeUser = tr.getMeta('changeUser');
|
|
79
|
+
if (changeUser) {
|
|
80
|
+
pluginState.binding.changeUser(changeUser.user);
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
if (pluginMeta?.getYDoc) {
|
|
84
|
+
const yjs = pluginState.binding.getYjs();
|
|
85
|
+
if (yjs) {
|
|
86
|
+
pluginMeta.getYDoc.resolve(yjs.ydoc);
|
|
87
|
+
} else {
|
|
88
|
+
if (pluginMeta.getYDoc.reject) {
|
|
89
|
+
pluginMeta.getYDoc.reject(new Error('No yjs'));
|
|
119
90
|
} else {
|
|
120
|
-
|
|
121
|
-
const yXmlFragment: Y.XmlFragment = ydoc.getXmlFragment(
|
|
122
|
-
'prosemirror',
|
|
123
|
-
);
|
|
124
|
-
pluginState.type = yXmlFragment, pluginState.ydoc = ydoc;
|
|
125
|
-
pluginState.provider = undefined;
|
|
91
|
+
throw new Error('No yjs');
|
|
126
92
|
}
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
pluginState.isUndoRedoOperation = false;
|
|
131
|
-
pluginState.addToHistory = true;
|
|
132
|
-
pluginState.restore = undefined;
|
|
93
|
+
}
|
|
94
|
+
return pluginState;
|
|
95
|
+
}
|
|
133
96
|
|
|
134
|
-
|
|
97
|
+
if (pluginMeta?.leaveRoom) {
|
|
98
|
+
pluginState.isChangeOrigin = false;
|
|
99
|
+
pluginState.isUndoRedoOperation = false;
|
|
135
100
|
|
|
136
|
-
|
|
137
|
-
setTimeout(() => {
|
|
138
|
-
pluginState.binding._forceRerender();
|
|
139
|
-
}, 0);
|
|
101
|
+
initialContentChanged = false;
|
|
140
102
|
|
|
141
|
-
|
|
142
|
-
|
|
103
|
+
pluginState.binding.leaveRoom(tr);
|
|
104
|
+
|
|
105
|
+
return pluginState;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
if (pluginMeta?.changeRoom) {
|
|
109
|
+
pluginState.isChangeOrigin = false;
|
|
110
|
+
pluginState.isUndoRedoOperation = false;
|
|
111
|
+
|
|
112
|
+
initialContentChanged = false;
|
|
113
|
+
|
|
114
|
+
const roomId = pluginMeta.changeRoom.roomId;
|
|
115
|
+
pluginState.binding.changeRoom(
|
|
116
|
+
roomId,
|
|
117
|
+
createYjsProvider,
|
|
118
|
+
tr,
|
|
119
|
+
);
|
|
120
|
+
|
|
121
|
+
return pluginState;
|
|
143
122
|
}
|
|
144
123
|
|
|
145
|
-
pluginState.
|
|
124
|
+
pluginState.binding.addToYjsHistory =
|
|
125
|
+
tr.getMeta('addToYjsHistory') !== false;
|
|
146
126
|
// always set isChangeOrigin. If undefined, this is not change origin.
|
|
147
|
-
pluginState.isChangeOrigin = !!
|
|
148
|
-
pluginState.isUndoRedoOperation = !!
|
|
149
|
-
!!
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
change.snapshot,
|
|
163
|
-
change.prevSnapshot,
|
|
164
|
-
pluginState,
|
|
165
|
-
);
|
|
166
|
-
} else {
|
|
167
|
-
binding._renderSnapshot(
|
|
168
|
-
change.snapshot,
|
|
169
|
-
change.snapshot,
|
|
170
|
-
pluginState,
|
|
171
|
-
);
|
|
172
|
-
// reset to current prosemirror state
|
|
173
|
-
delete pluginState.restore;
|
|
174
|
-
delete pluginState.snapshot;
|
|
175
|
-
delete pluginState.prevSnapshot;
|
|
176
|
-
initialContentChanged = false;
|
|
177
|
-
binding.mux(() => {
|
|
178
|
-
if (!binding.prosemirrorView) {
|
|
179
|
-
return;
|
|
180
|
-
}
|
|
181
|
-
binding.prosemirrorChanged(
|
|
182
|
-
binding.prosemirrorView.state.doc,
|
|
183
|
-
);
|
|
184
|
-
});
|
|
185
|
-
}
|
|
186
|
-
}, 0);
|
|
127
|
+
pluginState.isChangeOrigin = !!pluginMeta?.isChangeOrigin;
|
|
128
|
+
pluginState.isUndoRedoOperation = !!pluginMeta?.isChangeOrigin &&
|
|
129
|
+
!!pluginMeta?.isUndoRedoOperation;
|
|
130
|
+
|
|
131
|
+
if (pluginMeta?.getYSnapshot) {
|
|
132
|
+
const yjs = pluginState.binding.getYjs();
|
|
133
|
+
if (yjs) {
|
|
134
|
+
const snapshot = Y.snapshot(yjs.ydoc);
|
|
135
|
+
pluginMeta.getYSnapshot.resolve(Y.encodeSnapshotV2(snapshot));
|
|
136
|
+
} else {
|
|
137
|
+
if (pluginMeta.getYSnapshot.reject) {
|
|
138
|
+
pluginMeta.getYSnapshot.reject(new Error('No yjs'));
|
|
139
|
+
} else {
|
|
140
|
+
throw new Error('No yjs');
|
|
141
|
+
}
|
|
187
142
|
}
|
|
143
|
+
return pluginState;
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
if (pluginMeta?.resetYSnapshot) {
|
|
147
|
+
pluginState.binding.diffViewer.reset();
|
|
148
|
+
return pluginState;
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
if (pluginMeta?.setYSnapshot) {
|
|
152
|
+
const { prevSnapshot, snapshot } = pluginMeta?.setYSnapshot;
|
|
153
|
+
setTimeout(() => { // Prevent from snapshot being overwritten by current tr
|
|
154
|
+
pluginState.binding.setSnapshot(snapshot, prevSnapshot);
|
|
155
|
+
}, 0);
|
|
156
|
+
return pluginState;
|
|
188
157
|
}
|
|
158
|
+
|
|
189
159
|
return pluginState;
|
|
190
160
|
},
|
|
191
161
|
},
|
|
@@ -195,11 +165,6 @@ export const ySyncPlugin = (
|
|
|
195
165
|
)!;
|
|
196
166
|
const binding = pluginState.binding;
|
|
197
167
|
|
|
198
|
-
binding.initView(view);
|
|
199
|
-
if (binding.mapping.size === 0) {
|
|
200
|
-
// force rerender to update the bindings mapping
|
|
201
|
-
binding._forceRerender();
|
|
202
|
-
}
|
|
203
168
|
onFirstRender();
|
|
204
169
|
return {
|
|
205
170
|
update: () => {
|
|
@@ -211,9 +176,7 @@ export const ySyncPlugin = (
|
|
|
211
176
|
}
|
|
212
177
|
|
|
213
178
|
const binding = pluginState.binding;
|
|
214
|
-
if (
|
|
215
|
-
!pluginState.snapshot && !pluginState.prevSnapshot
|
|
216
|
-
) {
|
|
179
|
+
if (binding.isEditable()) {
|
|
217
180
|
if (
|
|
218
181
|
// If the content doesn't change initially, we don't render anything to Yjs
|
|
219
182
|
// If the content was cleared by a user action, we want to catch the change and
|
|
@@ -225,7 +188,7 @@ export const ySyncPlugin = (
|
|
|
225
188
|
) {
|
|
226
189
|
initialContentChanged = true;
|
|
227
190
|
if (
|
|
228
|
-
pluginState.
|
|
191
|
+
pluginState.binding.addToYjsHistory === false &&
|
|
229
192
|
!pluginState.isChangeOrigin
|
|
230
193
|
) {
|
|
231
194
|
const yUndoPluginState = yUndoPluginKey.getState(view.state);
|
|
@@ -233,15 +196,8 @@ export const ySyncPlugin = (
|
|
|
233
196
|
yUndoPluginState.undoManager.stopCapturing();
|
|
234
197
|
}
|
|
235
198
|
}
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
return;
|
|
239
|
-
}
|
|
240
|
-
pluginState.ydoc.transact((tr) => {
|
|
241
|
-
tr.meta.set('addToHistory', pluginState.addToHistory);
|
|
242
|
-
binding.prosemirrorChanged(view.state.doc);
|
|
243
|
-
}, ySyncPluginKey);
|
|
244
|
-
});
|
|
199
|
+
|
|
200
|
+
binding.pmChanged();
|
|
245
201
|
}
|
|
246
202
|
}
|
|
247
203
|
},
|