@kerebron/extension-yjs 0.6.6 → 0.7.0
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 +38 -45
- package/esm/ExtensionYjs.js.map +1 -1
- package/esm/WebsocketProvider.d.ts +69 -0
- package/esm/WebsocketProvider.d.ts.map +1 -0
- package/esm/WebsocketProvider.js +354 -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/PmYjsBinding.d.ts +41 -0
- package/esm/binding/PmYjsBinding.d.ts.map +1 -0
- package/esm/binding/PmYjsBinding.js +190 -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} +1 -1
- 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/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/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 +54 -116
- 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 +55 -67
- package/src/WebsocketProvider.ts +516 -0
- package/src/YjsProvider.ts +75 -0
- package/src/_dnt.shims.ts +60 -0
- package/src/binding/BindingMetadata.ts +6 -0
- package/src/binding/PmYjsBinding.ts +300 -0
- package/src/binding/convertUtils.ts +124 -0
- package/src/{createNodeFromYElement.ts → binding/createNodeFromYElement.ts} +3 -3
- package/src/{updateYFragment.ts → binding/updateYFragment.ts} +15 -8
- package/src/lib.ts +4 -230
- package/src/position.ts +191 -0
- package/src/ui/selection.ts +216 -0
- package/src/yPositionPlugin.ts +122 -74
- package/src/ySyncPlugin.ts +87 -170
- 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,46 @@ 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
|
+
restore?: any;
|
|
34
|
+
}
|
|
35
|
+
|
|
45
36
|
/**
|
|
46
37
|
* This plugin listens to changes in prosemirror view and keeps yXmlState and view in sync.
|
|
47
38
|
*
|
|
48
39
|
* This plugin also keeps references to the type and the shared document so other plugins can access it.
|
|
49
40
|
*/
|
|
50
41
|
export const ySyncPlugin = (
|
|
51
|
-
|
|
52
|
-
|
|
42
|
+
editor: CoreEditor,
|
|
43
|
+
createYjsProvider: CreateYjsProvider,
|
|
53
44
|
{
|
|
54
|
-
colors = defaultColors,
|
|
55
|
-
colorMapping = new Map(),
|
|
56
45
|
onFirstRender = () => {
|
|
57
46
|
},
|
|
58
47
|
}: YSyncOpts = {},
|
|
@@ -60,132 +49,76 @@ export const ySyncPlugin = (
|
|
|
60
49
|
let initialContentChanged = false;
|
|
61
50
|
const plugin: Plugin<YSyncPluginState> = new Plugin<YSyncPluginState>({
|
|
62
51
|
props: {
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
// },
|
|
52
|
+
editable: (state) => {
|
|
53
|
+
return true;
|
|
54
|
+
},
|
|
67
55
|
},
|
|
68
56
|
key: ySyncPluginKey,
|
|
69
57
|
state: {
|
|
70
58
|
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
59
|
return {
|
|
76
|
-
|
|
77
|
-
provider: undefined,
|
|
78
|
-
binding,
|
|
79
|
-
type: yXmlFragment,
|
|
80
|
-
ydoc: ydoc,
|
|
81
|
-
snapshot: undefined,
|
|
82
|
-
prevSnapshot: undefined,
|
|
60
|
+
binding: new PmYjsBinding(editor),
|
|
83
61
|
isChangeOrigin: false,
|
|
84
62
|
isUndoRedoOperation: false,
|
|
85
|
-
addToHistory: true,
|
|
86
|
-
restore: undefined,
|
|
87
|
-
colors,
|
|
88
|
-
colorMapping,
|
|
89
63
|
permanentUserData: undefined,
|
|
90
64
|
};
|
|
91
65
|
},
|
|
92
66
|
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
|
-
}
|
|
67
|
+
const pluginMeta: Partial<YSyncMeta> = tr.getMeta(ySyncPluginKey);
|
|
68
|
+
|
|
69
|
+
const changeUser = tr.getMeta('changeUser');
|
|
70
|
+
if (changeUser) {
|
|
71
|
+
pluginState.binding.changeUser(changeUser.user);
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
if (pluginMeta?.getYDoc) {
|
|
75
|
+
const yjs = pluginState.binding.getYjs();
|
|
76
|
+
if (yjs) {
|
|
77
|
+
pluginMeta.getYDoc.resolve(yjs.ydoc);
|
|
78
|
+
} else {
|
|
79
|
+
if (pluginMeta.getYDoc.reject) {
|
|
80
|
+
pluginMeta.getYDoc.reject(new Error('No yjs'));
|
|
119
81
|
} else {
|
|
120
|
-
|
|
121
|
-
const yXmlFragment: Y.XmlFragment = ydoc.getXmlFragment(
|
|
122
|
-
'prosemirror',
|
|
123
|
-
);
|
|
124
|
-
pluginState.type = yXmlFragment, pluginState.ydoc = ydoc;
|
|
125
|
-
pluginState.provider = undefined;
|
|
82
|
+
throw new Error('No yjs');
|
|
126
83
|
}
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
pluginState.isUndoRedoOperation = false;
|
|
131
|
-
pluginState.addToHistory = true;
|
|
132
|
-
pluginState.restore = undefined;
|
|
84
|
+
}
|
|
85
|
+
return pluginState;
|
|
86
|
+
}
|
|
133
87
|
|
|
134
|
-
|
|
88
|
+
if (pluginMeta?.leaveRoom) {
|
|
89
|
+
pluginState.isChangeOrigin = false;
|
|
90
|
+
pluginState.isUndoRedoOperation = false;
|
|
135
91
|
|
|
136
|
-
|
|
137
|
-
setTimeout(() => {
|
|
138
|
-
pluginState.binding._forceRerender();
|
|
139
|
-
}, 0);
|
|
92
|
+
initialContentChanged = false;
|
|
140
93
|
|
|
141
|
-
|
|
142
|
-
|
|
94
|
+
pluginState.binding.leaveRoom(tr);
|
|
95
|
+
|
|
96
|
+
return pluginState;
|
|
143
97
|
}
|
|
144
98
|
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
}
|
|
160
|
-
if (change.restore == null) {
|
|
161
|
-
binding._renderSnapshot(
|
|
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);
|
|
187
|
-
}
|
|
99
|
+
if (pluginMeta?.changeRoom) {
|
|
100
|
+
pluginState.isChangeOrigin = false;
|
|
101
|
+
pluginState.isUndoRedoOperation = false;
|
|
102
|
+
|
|
103
|
+
initialContentChanged = false;
|
|
104
|
+
|
|
105
|
+
const roomId = pluginMeta.changeRoom.roomId;
|
|
106
|
+
pluginState.binding.changeRoom(
|
|
107
|
+
roomId,
|
|
108
|
+
createYjsProvider,
|
|
109
|
+
tr,
|
|
110
|
+
);
|
|
111
|
+
|
|
112
|
+
return pluginState;
|
|
188
113
|
}
|
|
114
|
+
|
|
115
|
+
pluginState.binding.addToYjsHistory =
|
|
116
|
+
tr.getMeta('addToYjsHistory') !== false;
|
|
117
|
+
// always set isChangeOrigin. If undefined, this is not change origin.
|
|
118
|
+
pluginState.isChangeOrigin = !!pluginMeta?.isChangeOrigin;
|
|
119
|
+
pluginState.isUndoRedoOperation = !!pluginMeta?.isChangeOrigin &&
|
|
120
|
+
!!pluginMeta?.isUndoRedoOperation;
|
|
121
|
+
|
|
189
122
|
return pluginState;
|
|
190
123
|
},
|
|
191
124
|
},
|
|
@@ -195,11 +128,6 @@ export const ySyncPlugin = (
|
|
|
195
128
|
)!;
|
|
196
129
|
const binding = pluginState.binding;
|
|
197
130
|
|
|
198
|
-
binding.initView(view);
|
|
199
|
-
if (binding.mapping.size === 0) {
|
|
200
|
-
// force rerender to update the bindings mapping
|
|
201
|
-
binding._forceRerender();
|
|
202
|
-
}
|
|
203
131
|
onFirstRender();
|
|
204
132
|
return {
|
|
205
133
|
update: () => {
|
|
@@ -212,37 +140,26 @@ export const ySyncPlugin = (
|
|
|
212
140
|
|
|
213
141
|
const binding = pluginState.binding;
|
|
214
142
|
if (
|
|
215
|
-
|
|
143
|
+
// If the content doesn't change initially, we don't render anything to Yjs
|
|
144
|
+
// If the content was cleared by a user action, we want to catch the change and
|
|
145
|
+
// represent it in Yjs
|
|
146
|
+
initialContentChanged ||
|
|
147
|
+
view.state.doc.content.findDiffStart(
|
|
148
|
+
view.state.doc.type.createAndFill()!.content,
|
|
149
|
+
) !== null
|
|
216
150
|
) {
|
|
151
|
+
initialContentChanged = true;
|
|
217
152
|
if (
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
// represent it in Yjs
|
|
221
|
-
initialContentChanged ||
|
|
222
|
-
view.state.doc.content.findDiffStart(
|
|
223
|
-
view.state.doc.type.createAndFill()!.content,
|
|
224
|
-
) !== null
|
|
153
|
+
pluginState.binding.addToYjsHistory === false &&
|
|
154
|
+
!pluginState.isChangeOrigin
|
|
225
155
|
) {
|
|
226
|
-
|
|
227
|
-
if (
|
|
228
|
-
|
|
229
|
-
!pluginState.isChangeOrigin
|
|
230
|
-
) {
|
|
231
|
-
const yUndoPluginState = yUndoPluginKey.getState(view.state);
|
|
232
|
-
if (yUndoPluginState?.undoManager) {
|
|
233
|
-
yUndoPluginState.undoManager.stopCapturing();
|
|
234
|
-
}
|
|
156
|
+
const yUndoPluginState = yUndoPluginKey.getState(view.state);
|
|
157
|
+
if (yUndoPluginState?.undoManager) {
|
|
158
|
+
yUndoPluginState.undoManager.stopCapturing();
|
|
235
159
|
}
|
|
236
|
-
binding.mux(() => {
|
|
237
|
-
if (!pluginState.ydoc) {
|
|
238
|
-
return;
|
|
239
|
-
}
|
|
240
|
-
pluginState.ydoc.transact((tr) => {
|
|
241
|
-
tr.meta.set('addToHistory', pluginState.addToHistory);
|
|
242
|
-
binding.prosemirrorChanged(view.state.doc);
|
|
243
|
-
}, ySyncPluginKey);
|
|
244
|
-
});
|
|
245
160
|
}
|
|
161
|
+
|
|
162
|
+
binding.pmChanged();
|
|
246
163
|
}
|
|
247
164
|
},
|
|
248
165
|
destroy: () => {
|