@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.
- package/README.md +3 -89
- package/esm/ExtensionYjs.d.ts +10 -1
- package/esm/ExtensionYjs.d.ts.map +1 -1
- package/esm/ExtensionYjs.js +47 -6
- package/esm/ExtensionYjs.js.map +1 -1
- package/esm/MarkYChange.d.ts +7 -0
- package/esm/MarkYChange.d.ts.map +1 -0
- package/esm/MarkYChange.js +21 -0
- package/esm/MarkYChange.js.map +1 -0
- package/esm/ProsemirrorBinding.d.ts +60 -0
- package/esm/ProsemirrorBinding.d.ts.map +1 -0
- package/esm/ProsemirrorBinding.js +405 -0
- package/esm/ProsemirrorBinding.js.map +1 -0
- package/esm/createNodeFromYElement.d.ts +10 -0
- package/esm/createNodeFromYElement.d.ts.map +1 -0
- package/esm/createNodeFromYElement.js +123 -0
- package/esm/createNodeFromYElement.js.map +1 -0
- package/esm/debug.d.ts +13 -0
- package/esm/debug.d.ts.map +1 -0
- package/esm/debug.js +147 -0
- package/esm/debug.js.map +1 -0
- package/esm/keys.d.ts +5 -8
- package/esm/keys.d.ts.map +1 -1
- package/esm/keys.js +1 -6
- package/esm/keys.js.map +1 -1
- package/esm/lib.d.ts +1 -2
- package/esm/lib.d.ts.map +1 -1
- package/esm/lib.js +12 -2
- package/esm/lib.js.map +1 -1
- package/esm/updateYFragment.d.ts +17 -0
- package/esm/updateYFragment.d.ts.map +1 -0
- package/esm/updateYFragment.js +333 -0
- package/esm/updateYFragment.js.map +1 -0
- package/esm/utils.d.ts +2 -0
- package/esm/utils.d.ts.map +1 -1
- package/esm/utils.js +4 -0
- package/esm/utils.js.map +1 -1
- package/esm/yPositionPlugin.d.ts +12 -4
- package/esm/yPositionPlugin.d.ts.map +1 -1
- package/esm/yPositionPlugin.js +114 -61
- package/esm/yPositionPlugin.js.map +1 -1
- package/esm/ySyncPlugin.d.ts +16 -78
- package/esm/ySyncPlugin.d.ts.map +1 -1
- package/esm/ySyncPlugin.js +81 -848
- package/esm/ySyncPlugin.js.map +1 -1
- package/esm/yUndoPlugin.d.ts +1 -1
- package/esm/yUndoPlugin.d.ts.map +1 -1
- package/esm/yUndoPlugin.js +1 -1
- package/esm/yUndoPlugin.js.map +1 -1
- package/package.json +9 -3
- package/src/ExtensionYjs.ts +65 -9
- package/src/MarkYChange.ts +23 -0
- package/src/ProsemirrorBinding.ts +607 -0
- package/src/createNodeFromYElement.ts +175 -0
- package/src/debug.ts +218 -0
- package/src/keys.ts +9 -9
- package/src/lib.ts +11 -3
- package/src/updateYFragment.ts +439 -0
- package/src/utils.ts +6 -0
- package/src/yPositionPlugin.ts +167 -92
- package/src/ySyncPlugin.ts +135 -1193
- package/src/yUndoPlugin.ts +1 -1
- package/esm/convertUtils.d.ts +0 -59
- package/esm/convertUtils.d.ts.map +0 -1
- package/esm/convertUtils.js +0 -89
- package/esm/convertUtils.js.map +0 -1
- package/src/convertUtils.ts +0 -143
package/src/yPositionPlugin.ts
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import * as Y from 'yjs';
|
|
2
|
-
import
|
|
2
|
+
import { Awareness } from 'y-protocols/awareness';
|
|
3
3
|
|
|
4
|
-
import { EditorState, Plugin,
|
|
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
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
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
|
-
|
|
92
|
-
|
|
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
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
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
|
|
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
|
|
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.
|
|
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:
|
|
198
|
-
|
|
199
|
-
|
|
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
|
-
|
|
216
|
-
|
|
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,
|