@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.
Files changed (90) hide show
  1. package/esm/ExtensionYjs.d.ts +3 -11
  2. package/esm/ExtensionYjs.d.ts.map +1 -1
  3. package/esm/ExtensionYjs.js +38 -45
  4. package/esm/ExtensionYjs.js.map +1 -1
  5. package/esm/WebsocketProvider.d.ts +69 -0
  6. package/esm/WebsocketProvider.d.ts.map +1 -0
  7. package/esm/WebsocketProvider.js +354 -0
  8. package/esm/WebsocketProvider.js.map +1 -0
  9. package/esm/YjsProvider.d.ts +48 -0
  10. package/esm/YjsProvider.d.ts.map +1 -0
  11. package/esm/YjsProvider.js +12 -0
  12. package/esm/YjsProvider.js.map +1 -0
  13. package/esm/_dnt.shims.d.ts +2 -0
  14. package/esm/_dnt.shims.d.ts.map +1 -0
  15. package/esm/_dnt.shims.js +58 -0
  16. package/esm/_dnt.shims.js.map +1 -0
  17. package/esm/binding/BindingMetadata.d.ts +6 -0
  18. package/esm/binding/BindingMetadata.d.ts.map +1 -0
  19. package/esm/binding/BindingMetadata.js +2 -0
  20. package/esm/binding/BindingMetadata.js.map +1 -0
  21. package/esm/binding/PmYjsBinding.d.ts +41 -0
  22. package/esm/binding/PmYjsBinding.d.ts.map +1 -0
  23. package/esm/binding/PmYjsBinding.js +190 -0
  24. package/esm/binding/PmYjsBinding.js.map +1 -0
  25. package/esm/binding/convertUtils.d.ts +48 -0
  26. package/esm/binding/convertUtils.d.ts.map +1 -0
  27. package/esm/binding/convertUtils.js +80 -0
  28. package/esm/binding/convertUtils.js.map +1 -0
  29. package/esm/{createNodeFromYElement.d.ts → binding/createNodeFromYElement.d.ts} +1 -1
  30. package/esm/binding/createNodeFromYElement.d.ts.map +1 -0
  31. package/esm/{createNodeFromYElement.js → binding/createNodeFromYElement.js} +2 -2
  32. package/esm/binding/createNodeFromYElement.js.map +1 -0
  33. package/esm/{updateYFragment.d.ts → binding/updateYFragment.d.ts} +3 -3
  34. package/esm/binding/updateYFragment.d.ts.map +1 -0
  35. package/esm/{updateYFragment.js → binding/updateYFragment.js} +10 -7
  36. package/esm/binding/updateYFragment.js.map +1 -0
  37. package/esm/lib.d.ts +1 -7
  38. package/esm/lib.d.ts.map +1 -1
  39. package/esm/lib.js +1 -200
  40. package/esm/lib.js.map +1 -1
  41. package/esm/position.d.ts +8 -0
  42. package/esm/position.d.ts.map +1 -0
  43. package/esm/position.js +165 -0
  44. package/esm/position.js.map +1 -0
  45. package/esm/ui/selection.d.ts +29 -0
  46. package/esm/ui/selection.d.ts.map +1 -0
  47. package/esm/ui/selection.js +129 -0
  48. package/esm/ui/selection.js.map +1 -0
  49. package/esm/yPositionPlugin.d.ts +6 -1
  50. package/esm/yPositionPlugin.d.ts.map +1 -1
  51. package/esm/yPositionPlugin.js +91 -50
  52. package/esm/yPositionPlugin.js.map +1 -1
  53. package/esm/ySyncPlugin.d.ts +5 -22
  54. package/esm/ySyncPlugin.d.ts.map +1 -1
  55. package/esm/ySyncPlugin.js +54 -116
  56. package/esm/ySyncPlugin.js.map +1 -1
  57. package/esm/yUndoPlugin.d.ts +11 -10
  58. package/esm/yUndoPlugin.d.ts.map +1 -1
  59. package/esm/yUndoPlugin.js +90 -52
  60. package/esm/yUndoPlugin.js.map +1 -1
  61. package/package.json +9 -6
  62. package/src/ExtensionYjs.ts +55 -67
  63. package/src/WebsocketProvider.ts +516 -0
  64. package/src/YjsProvider.ts +75 -0
  65. package/src/_dnt.shims.ts +60 -0
  66. package/src/binding/BindingMetadata.ts +6 -0
  67. package/src/binding/PmYjsBinding.ts +300 -0
  68. package/src/binding/convertUtils.ts +124 -0
  69. package/src/{createNodeFromYElement.ts → binding/createNodeFromYElement.ts} +3 -3
  70. package/src/{updateYFragment.ts → binding/updateYFragment.ts} +15 -8
  71. package/src/lib.ts +4 -230
  72. package/src/position.ts +191 -0
  73. package/src/ui/selection.ts +216 -0
  74. package/src/yPositionPlugin.ts +122 -74
  75. package/src/ySyncPlugin.ts +87 -170
  76. package/src/yUndoPlugin.ts +113 -62
  77. package/esm/ProsemirrorBinding.d.ts +0 -60
  78. package/esm/ProsemirrorBinding.d.ts.map +0 -1
  79. package/esm/ProsemirrorBinding.js +0 -405
  80. package/esm/ProsemirrorBinding.js.map +0 -1
  81. package/esm/createNodeFromYElement.d.ts.map +0 -1
  82. package/esm/createNodeFromYElement.js.map +0 -1
  83. package/esm/updateYFragment.d.ts.map +0 -1
  84. package/esm/updateYFragment.js.map +0 -1
  85. package/esm/userColors.d.ts +0 -5
  86. package/esm/userColors.d.ts.map +0 -1
  87. package/esm/userColors.js +0 -11
  88. package/esm/userColors.js.map +0 -1
  89. package/src/ProsemirrorBinding.ts +0 -607
  90. package/src/userColors.ts +0 -10
@@ -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 type {
9
- ExtensionRemoteSelection,
10
- SelectionState,
11
- } from '@kerebron/extension-basic-editor/ExtensionRemoteSelection';
12
- import { remoteSelectionPluginKey } from '@kerebron/extension-basic-editor/ExtensionRemoteSelection';
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
- setMeta,
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
- state: YPositionPluginState,
43
- cursorStateField: string,
72
+ pluginState: YPositionPluginState,
44
73
  ) {
45
- if (!state.awareness) {
74
+ if (!pluginState.awareness) {
46
75
  return;
47
76
  }
48
- const awareness = state.awareness;
49
- if (state.awarenessListener) {
50
- awareness.off('change', state.awarenessListener);
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.provider) {
96
+ if (!ystate.binding) {
67
97
  return;
68
98
  }
69
- const awareness = ystate.provider.awareness;
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
- if (view.docView) {
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
- if (!aw.cursor) {
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
- ystate.type,
100
- Y.createRelativePositionFromJSON(aw.cursor.anchor),
101
- ystate.binding.mapping,
128
+ xmlFragment,
129
+ Y.createRelativePositionFromJSON(cursor.anchor),
130
+ ystate.binding.getMapping(),
102
131
  );
103
132
  const head = relativePositionToAbsolutePosition(
104
133
  ydoc,
105
- ystate.type,
106
- Y.createRelativePositionFromJSON(aw.cursor.head),
107
- ystate.binding.mapping,
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
- extension.setRemoteStates(remoteStates);
130
- // view.dispatch({ annotations: [yRemoteSelectionsAnnotation.of([])] });
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: (_initargs, state): YPositionPluginState => {
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 awareness = tr.getMeta('yjs:awareness');
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, cursorStateField);
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
- ystate.type,
202
- ystate.binding.mapping,
238
+ xmlFragment,
239
+ ystate.binding.getMapping(),
203
240
  );
204
241
  const head: Y.RelativePosition = absolutePositionToRelativePosition(
205
242
  selectionHead,
206
- ystate.type,
207
- ystate.binding.mapping,
243
+ xmlFragment,
244
+ ystate.binding.getMapping(),
208
245
  );
209
246
 
247
+ const cursor = current[state.cursorStateField];
248
+
210
249
  if (
211
- current.cursor == null ||
250
+ cursor == null ||
212
251
  !Y.compareRelativePositions(
213
- Y.createRelativePositionFromJSON(current.cursor.anchor),
252
+ Y.createRelativePositionFromJSON(cursor.anchor),
214
253
  anchor,
215
254
  ) ||
216
255
  !Y.compareRelativePositions(
217
- Y.createRelativePositionFromJSON(current.cursor.head),
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
- current.cursor != null &&
289
+ cursor &&
242
290
  relativePositionToAbsolutePosition(
243
- ystate.ydoc,
244
- ystate.type,
245
- Y.createRelativePositionFromJSON(current.cursor.anchor),
246
- ystate.binding.mapping,
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, cursorStateField);
337
+ destroyAwareness(pluginState);
290
338
  }
291
339
 
292
340
  editor.removeEventListener(
@@ -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 { defaultColors, ProsemirrorBinding } from './ProsemirrorBinding.js';
6
- import { Schema } from 'prosemirror-model';
7
- import type { CreateWsProvider, YjsProvider } from './ExtensionYjs.js';
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
- roomId: string;
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
- restore: any;
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
- schema: Schema,
52
- createWsProvider: CreateWsProvider,
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
- // editable: (state) => {
64
- // const syncState = ySyncPluginKey.getState(state)!;
65
- // return syncState.snapshot && syncState.prevSnapshot;
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
- roomId: '',
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 change: Partial<YSyncPluginState> = tr.getMeta(ySyncPluginKey);
94
- if (change !== undefined) {
95
- pluginState = {
96
- ...pluginState,
97
- ...change,
98
- };
99
-
100
- if ('roomId' in change) {
101
- if (change.roomId) {
102
- const [provider, ydoc] = createWsProvider(change.roomId);
103
- pluginState.provider = provider;
104
- const yXmlFragment: Y.XmlFragment = ydoc.getXmlFragment(
105
- 'prosemirror',
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
- const ydoc = new Y.Doc();
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
- pluginState.snapshot = undefined;
128
- pluginState.prevSnapshot = undefined;
129
- pluginState.isChangeOrigin = false;
130
- pluginState.isUndoRedoOperation = false;
131
- pluginState.addToHistory = true;
132
- pluginState.restore = undefined;
84
+ }
85
+ return pluginState;
86
+ }
133
87
 
134
- initialContentChanged = false;
88
+ if (pluginMeta?.leaveRoom) {
89
+ pluginState.isChangeOrigin = false;
90
+ pluginState.isUndoRedoOperation = false;
135
91
 
136
- pluginState.binding.changeRoom(pluginState.type);
137
- setTimeout(() => {
138
- pluginState.binding._forceRerender();
139
- }, 0);
92
+ initialContentChanged = false;
140
93
 
141
- return pluginState;
142
- }
94
+ pluginState.binding.leaveRoom(tr);
95
+
96
+ return pluginState;
143
97
  }
144
98
 
145
- pluginState.addToHistory = tr.getMeta('addToHistory') !== false;
146
- // always set isChangeOrigin. If undefined, this is not change origin.
147
- pluginState.isChangeOrigin = !!change?.isChangeOrigin;
148
- pluginState.isUndoRedoOperation = !!change?.isChangeOrigin &&
149
- !!change?.isUndoRedoOperation;
150
-
151
- const binding = pluginState.binding;
152
-
153
- if (binding?.prosemirrorView) {
154
- if (change?.snapshot || change?.prevSnapshot) {
155
- // snapshot changed, rerender next
156
- setTimeout(() => {
157
- if (!binding.prosemirrorView) {
158
- return;
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
- !pluginState.snapshot && !pluginState.prevSnapshot
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
- // If the content doesn't change initially, we don't render anything to Yjs
219
- // If the content was cleared by a user action, we want to catch the change and
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
- initialContentChanged = true;
227
- if (
228
- pluginState.addToHistory === false &&
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: () => {