@kerebron/extension-yjs 0.5.3 → 0.5.4

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 (67) hide show
  1. package/README.md +3 -89
  2. package/esm/ExtensionYjs.d.ts +10 -1
  3. package/esm/ExtensionYjs.d.ts.map +1 -1
  4. package/esm/ExtensionYjs.js +47 -6
  5. package/esm/ExtensionYjs.js.map +1 -1
  6. package/esm/MarkYChange.d.ts +7 -0
  7. package/esm/MarkYChange.d.ts.map +1 -0
  8. package/esm/MarkYChange.js +21 -0
  9. package/esm/MarkYChange.js.map +1 -0
  10. package/esm/ProsemirrorBinding.d.ts +60 -0
  11. package/esm/ProsemirrorBinding.d.ts.map +1 -0
  12. package/esm/ProsemirrorBinding.js +405 -0
  13. package/esm/ProsemirrorBinding.js.map +1 -0
  14. package/esm/createNodeFromYElement.d.ts +10 -0
  15. package/esm/createNodeFromYElement.d.ts.map +1 -0
  16. package/esm/createNodeFromYElement.js +123 -0
  17. package/esm/createNodeFromYElement.js.map +1 -0
  18. package/esm/debug.d.ts +13 -0
  19. package/esm/debug.d.ts.map +1 -0
  20. package/esm/debug.js +147 -0
  21. package/esm/debug.js.map +1 -0
  22. package/esm/keys.d.ts +5 -8
  23. package/esm/keys.d.ts.map +1 -1
  24. package/esm/keys.js +1 -6
  25. package/esm/keys.js.map +1 -1
  26. package/esm/lib.d.ts +1 -2
  27. package/esm/lib.d.ts.map +1 -1
  28. package/esm/lib.js +12 -2
  29. package/esm/lib.js.map +1 -1
  30. package/esm/updateYFragment.d.ts +17 -0
  31. package/esm/updateYFragment.d.ts.map +1 -0
  32. package/esm/updateYFragment.js +333 -0
  33. package/esm/updateYFragment.js.map +1 -0
  34. package/esm/utils.d.ts +2 -0
  35. package/esm/utils.d.ts.map +1 -1
  36. package/esm/utils.js +4 -0
  37. package/esm/utils.js.map +1 -1
  38. package/esm/yPositionPlugin.d.ts +12 -4
  39. package/esm/yPositionPlugin.d.ts.map +1 -1
  40. package/esm/yPositionPlugin.js +114 -61
  41. package/esm/yPositionPlugin.js.map +1 -1
  42. package/esm/ySyncPlugin.d.ts +16 -78
  43. package/esm/ySyncPlugin.d.ts.map +1 -1
  44. package/esm/ySyncPlugin.js +81 -848
  45. package/esm/ySyncPlugin.js.map +1 -1
  46. package/esm/yUndoPlugin.d.ts +1 -1
  47. package/esm/yUndoPlugin.d.ts.map +1 -1
  48. package/esm/yUndoPlugin.js +1 -1
  49. package/esm/yUndoPlugin.js.map +1 -1
  50. package/package.json +9 -3
  51. package/src/ExtensionYjs.ts +65 -9
  52. package/src/MarkYChange.ts +23 -0
  53. package/src/ProsemirrorBinding.ts +607 -0
  54. package/src/createNodeFromYElement.ts +175 -0
  55. package/src/debug.ts +218 -0
  56. package/src/keys.ts +9 -9
  57. package/src/lib.ts +11 -3
  58. package/src/updateYFragment.ts +439 -0
  59. package/src/utils.ts +6 -0
  60. package/src/yPositionPlugin.ts +167 -92
  61. package/src/ySyncPlugin.ts +135 -1193
  62. package/src/yUndoPlugin.ts +1 -1
  63. package/esm/convertUtils.d.ts +0 -59
  64. package/esm/convertUtils.d.ts.map +0 -1
  65. package/esm/convertUtils.js +0 -89
  66. package/esm/convertUtils.js.map +0 -1
  67. package/src/convertUtils.ts +0 -143
@@ -1,7 +1,8 @@
1
1
  import * as Y from 'yjs';
2
- import * as awarenessProtocol from 'y-protocols/awareness';
2
+ import { Awareness } from 'y-protocols/awareness';
3
3
 
4
- import { EditorState, Plugin, PluginKey } from 'prosemirror-state';
4
+ import { EditorState, Plugin, Transaction } from 'prosemirror-state';
5
+ import { EditorView } from 'prosemirror-view';
5
6
 
6
7
  import type { CoreEditor } from '@kerebron/editor';
7
8
  import type {
@@ -15,9 +16,8 @@ import {
15
16
  relativePositionToAbsolutePosition,
16
17
  setMeta,
17
18
  } from './lib.js';
18
- import { ySyncPluginKey } from './keys.js';
19
-
20
- export const yPositionPluginKey = new PluginKey('yjs-position');
19
+ import { yPositionPluginKey, ySyncPluginKey } from './keys.js';
20
+ import type { YSyncPluginState } from './ySyncPlugin.js';
21
21
 
22
22
  type AwarenessListener = (
23
23
  { added, updated, removed }: {
@@ -33,6 +33,106 @@ interface PositionPluginConfig {
33
33
  getSelection?: (arg0: any) => any;
34
34
  }
35
35
 
36
+ export interface YPositionPluginState {
37
+ awareness?: Awareness;
38
+ awarenessListener?: AwarenessListener;
39
+ }
40
+
41
+ function destroyAwareness(
42
+ state: YPositionPluginState,
43
+ cursorStateField: string,
44
+ ) {
45
+ if (!state.awareness) {
46
+ return;
47
+ }
48
+ const awareness = state.awareness;
49
+ if (state.awarenessListener) {
50
+ awareness.off('change', state.awarenessListener);
51
+ }
52
+ awareness.setLocalStateField(cursorStateField, null);
53
+ }
54
+
55
+ function initAwareness(state: YPositionPluginState, editor: CoreEditor) {
56
+ if (!state.awareness) {
57
+ return;
58
+ }
59
+ const awareness = state.awareness;
60
+ const view = editor.view as EditorView;
61
+
62
+ state.awarenessListener = (
63
+ { added, updated, removed },
64
+ ) => {
65
+ const ystate: YSyncPluginState = ySyncPluginKey.getState(view.state)!;
66
+ if (!ystate.provider) {
67
+ return;
68
+ }
69
+ const awareness = ystate.provider.awareness;
70
+ const clients = added.concat(updated).concat(removed);
71
+ if (
72
+ clients.findIndex((id: number) => id !== awareness.doc.clientID) ===
73
+ -1
74
+ ) {
75
+ return;
76
+ }
77
+
78
+ if (view.docView) {
79
+ setMeta(view, remoteSelectionPluginKey, {
80
+ remotePositionUpdated: true,
81
+ });
82
+ }
83
+
84
+ const remoteStates: SelectionState[] = [];
85
+
86
+ const ydoc = ystate.ydoc;
87
+
88
+ awareness.getStates().forEach((aw, clientId) => {
89
+ if (!defaultAwarenessStateFilter(ydoc.clientID, clientId, aw)) {
90
+ return;
91
+ }
92
+
93
+ if (!aw.cursor) {
94
+ return;
95
+ }
96
+
97
+ const anchor = relativePositionToAbsolutePosition(
98
+ ydoc,
99
+ ystate.type,
100
+ Y.createRelativePositionFromJSON(aw.cursor.anchor),
101
+ ystate.binding.mapping,
102
+ );
103
+ const head = relativePositionToAbsolutePosition(
104
+ ydoc,
105
+ ystate.type,
106
+ Y.createRelativePositionFromJSON(aw.cursor.head),
107
+ ystate.binding.mapping,
108
+ );
109
+
110
+ if (anchor !== null && head !== null) {
111
+ remoteStates.push({
112
+ clientId,
113
+ user: {
114
+ name: aw.user?.name,
115
+ color: aw.user?.color,
116
+ colorLight: aw.user?.colorLight,
117
+ },
118
+ cursor: {
119
+ anchor,
120
+ head,
121
+ },
122
+ });
123
+ }
124
+ });
125
+ const extension: ExtensionRemoteSelection = editor.getExtension(
126
+ 'remote-selection',
127
+ )!;
128
+
129
+ extension.setRemoteStates(remoteStates);
130
+ // view.dispatch({ annotations: [yRemoteSelectionsAnnotation.of([])] });
131
+ };
132
+
133
+ awareness.on('change', state.awarenessListener);
134
+ }
135
+
36
136
  /**
37
137
  * Default awareness state filter
38
138
  */
@@ -43,101 +143,59 @@ export const defaultAwarenessStateFilter = (
43
143
  ): boolean => currentClientId !== userClientId;
44
144
 
45
145
  export const yPositionPlugin = (
46
- awareness: awarenessProtocol.Awareness,
47
146
  editor: CoreEditor,
48
147
  {
49
148
  getSelection = (state: EditorState) => state.selection,
50
149
  }: PositionPluginConfig = {},
51
150
  cursorStateField: string = 'cursor',
52
151
  ) => {
53
- return new Plugin({
152
+ return new Plugin<YPositionPluginState>({
54
153
  key: yPositionPluginKey,
55
- view: (view) => {
56
- const extension: ExtensionRemoteSelection = editor.getExtension(
57
- 'remote-selection',
58
- )!;
59
-
60
- const awarenessListener: AwarenessListener = (
61
- { added, updated, removed },
62
- ) => {
63
- const clients = added.concat(updated).concat(removed);
64
- if (
65
- clients.findIndex((id: number) => id !== awareness.doc.clientID) ===
66
- -1
67
- ) {
68
- return;
69
- }
70
-
71
- if (view.docView) {
72
- setMeta(view, remoteSelectionPluginKey, {
73
- remotePositionUpdated: true,
74
- });
75
- }
76
-
77
- const remoteStates: SelectionState[] = [];
78
-
79
- const ystate = ySyncPluginKey.getState(view.state);
80
- const y = ystate.doc;
81
-
82
- awareness.getStates().forEach((aw, clientId) => {
83
- if (!defaultAwarenessStateFilter(y.clientID, clientId, aw)) {
84
- return;
85
- }
86
-
87
- if (!aw.cursor) {
88
- return;
154
+ state: {
155
+ init: (_initargs, state): YPositionPluginState => {
156
+ return {
157
+ awareness: undefined,
158
+ };
159
+ },
160
+ apply: (tr: Transaction, pluginState: YPositionPluginState) => {
161
+ const awareness = tr.getMeta('yjs:awareness');
162
+ if (awareness) {
163
+ if (pluginState.awareness) {
164
+ destroyAwareness(pluginState, cursorStateField);
89
165
  }
90
-
91
- let anchor = relativePositionToAbsolutePosition(
92
- y,
93
- ystate.type,
94
- Y.createRelativePositionFromJSON(aw.cursor.anchor),
95
- ystate.binding.mapping,
96
- );
97
- let head = relativePositionToAbsolutePosition(
98
- y,
99
- ystate.type,
100
- Y.createRelativePositionFromJSON(aw.cursor.head),
101
- ystate.binding.mapping,
102
- );
103
-
104
- if (anchor !== null && head !== null) {
105
- remoteStates.push({
106
- clientId,
107
- user: {
108
- name: aw.user?.name,
109
- color: aw.user?.color,
110
- colorLight: aw.user?.colorLight,
111
- },
112
- cursor: {
113
- anchor,
114
- head,
115
- },
116
- });
166
+ pluginState.awareness = awareness;
167
+ if (pluginState.awareness) {
168
+ initAwareness(pluginState, editor);
117
169
  }
118
- });
119
-
120
- extension.setRemoteStates(remoteStates);
121
- // view.dispatch({ annotations: [yRemoteSelectionsAnnotation.of([])] });
122
- };
123
-
124
- {
125
- // if (
126
- // ystate.snapshot != null || ystate.prevSnapshot != null ||
127
- // ystate.binding.mapping.size === 0
128
- // ) {
129
- // // do not render cursors while snapshot is active
130
- // return DecorationSet.create(state.doc, []);
131
- // }
132
- }
170
+ }
171
+ return pluginState;
172
+ },
173
+ },
174
+ 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
+ // }
133
183
 
134
184
  const updateAwareness = (
135
185
  selectionAnchor: number,
136
186
  selectionHead: number,
137
187
  ) => {
138
- const ystate = ySyncPluginKey.getState(view.state);
188
+ const state: YPositionPluginState = yPositionPluginKey.getState(
189
+ view.state,
190
+ )!;
191
+ if (!state.awareness) {
192
+ return;
193
+ }
194
+ const awareness = state.awareness;
139
195
  const current = awareness.getLocalState() || {};
140
196
 
197
+ const ystate: YSyncPluginState = ySyncPluginKey.getState(view.state)!;
198
+
141
199
  const anchor: Y.RelativePosition = absolutePositionToRelativePosition(
142
200
  selectionAnchor,
143
201
  ystate.type,
@@ -168,13 +226,21 @@ export const yPositionPlugin = (
168
226
  };
169
227
 
170
228
  const clearAwareness = () => {
171
- const ystate = ySyncPluginKey.getState(view.state);
229
+ const state: YPositionPluginState = yPositionPluginKey.getState(
230
+ view.state,
231
+ )!;
232
+ if (!state.awareness) {
233
+ return;
234
+ }
235
+ const awareness = state.awareness;
236
+
237
+ const ystate = ySyncPluginKey.getState(view.state)!;
172
238
  const current = awareness.getLocalState() || {};
173
239
 
174
240
  if (
175
241
  current.cursor != null &&
176
242
  relativePositionToAbsolutePosition(
177
- ystate.doc,
243
+ ystate.ydoc,
178
244
  ystate.type,
179
245
  Y.createRelativePositionFromJSON(current.cursor.anchor),
180
246
  ystate.binding.mapping,
@@ -194,9 +260,11 @@ export const yPositionPlugin = (
194
260
  }
195
261
  };
196
262
 
197
- const localPositionChangedListener = (event: CustomEvent) => {
198
- const { detail } = event;
199
- updateAwareness(detail.anchor, detail.head);
263
+ const localPositionChangedListener = (event: Event) => {
264
+ if ('detail' in event) {
265
+ const { detail } = event as CustomEvent;
266
+ updateAwareness(detail.anchor, detail.head);
267
+ }
200
268
  };
201
269
 
202
270
  editor.addEventListener(
@@ -204,16 +272,23 @@ export const yPositionPlugin = (
204
272
  localPositionChangedListener,
205
273
  );
206
274
 
207
- awareness.on('change', awarenessListener);
208
275
  view.dom.addEventListener('focusin', updateCursorInfo);
209
276
  view.dom.addEventListener('focusout', updateCursorInfo);
277
+
210
278
  return {
211
279
  update: updateCursorInfo,
212
280
  destroy: () => {
213
281
  view.dom.removeEventListener('focusin', updateCursorInfo);
214
282
  view.dom.removeEventListener('focusout', updateCursorInfo);
215
- awareness.off('change', awarenessListener);
216
- awareness.setLocalStateField(cursorStateField, null);
283
+
284
+ const pluginState: YPositionPluginState | undefined =
285
+ yPositionPluginKey.getState(
286
+ view.state,
287
+ );
288
+ if (pluginState) {
289
+ destroyAwareness(pluginState, cursorStateField);
290
+ }
291
+
217
292
  editor.removeEventListener(
218
293
  'localPositionChanged',
219
294
  localPositionChangedListener,