@kerebron/extension-yjs 0.2.1 → 0.3.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/editor/src/CoreEditor.d.ts +11 -5
- package/esm/editor/src/CoreEditor.d.ts.map +1 -1
- package/esm/editor/src/CoreEditor.js +64 -61
- package/esm/editor/src/DummyEditorView.d.ts +60 -0
- package/esm/editor/src/DummyEditorView.d.ts.map +1 -0
- package/esm/editor/src/DummyEditorView.js +277 -0
- package/esm/editor/src/Extension.d.ts +9 -9
- package/esm/editor/src/Extension.d.ts.map +1 -1
- package/esm/editor/src/Extension.js +2 -2
- package/esm/editor/src/ExtensionManager.d.ts +8 -7
- package/esm/editor/src/ExtensionManager.d.ts.map +1 -1
- package/esm/editor/src/ExtensionManager.js +58 -39
- package/esm/editor/src/Mark.d.ts +8 -6
- package/esm/editor/src/Mark.d.ts.map +1 -1
- package/esm/editor/src/Mark.js +8 -2
- package/esm/editor/src/Node.d.ts +14 -12
- package/esm/editor/src/Node.d.ts.map +1 -1
- package/esm/editor/src/Node.js +10 -4
- package/esm/editor/src/commands/CommandManager.d.ts +5 -9
- package/esm/editor/src/commands/CommandManager.d.ts.map +1 -1
- package/esm/editor/src/commands/CommandManager.js +7 -6
- package/esm/editor/src/commands/mod.d.ts +12 -6
- package/esm/editor/src/commands/mod.d.ts.map +1 -1
- package/esm/editor/src/commands/mod.js +0 -45
- package/esm/editor/src/mod.d.ts +1 -0
- package/esm/editor/src/mod.d.ts.map +1 -1
- package/esm/editor/src/mod.js +1 -0
- package/esm/editor/src/nodeToTreeString.d.ts +8 -2
- package/esm/editor/src/nodeToTreeString.d.ts.map +1 -1
- package/esm/editor/src/nodeToTreeString.js +47 -29
- package/esm/editor/src/plugins/input-rules/InputRulesPlugin.js +2 -2
- package/esm/editor/src/plugins/keymap/keymap.d.ts +11 -0
- package/esm/editor/src/plugins/keymap/keymap.d.ts.map +1 -0
- package/esm/editor/src/plugins/keymap/keymap.js +125 -0
- package/esm/editor/src/plugins/keymap/w3c-keyname.d.ts +4 -0
- package/esm/editor/src/plugins/keymap/w3c-keyname.d.ts.map +1 -0
- package/esm/editor/src/plugins/keymap/w3c-keyname.js +124 -0
- package/esm/editor/src/types.d.ts +10 -5
- package/esm/editor/src/types.d.ts.map +1 -1
- package/esm/editor/src/utilities/SmartOutput.d.ts +39 -0
- package/esm/editor/src/utilities/SmartOutput.d.ts.map +1 -0
- package/esm/editor/src/utilities/SmartOutput.js +213 -0
- package/esm/editor/src/utilities/createNodeFromContent.d.ts +4 -3
- package/esm/editor/src/utilities/createNodeFromContent.d.ts.map +1 -1
- package/esm/editor/src/utilities/createNodeFromContent.js +4 -5
- package/esm/editor/src/utilities/getHtmlAttributes.d.ts +8 -3
- package/esm/editor/src/utilities/getHtmlAttributes.d.ts.map +1 -1
- package/esm/extension-yjs/src/ExtensionYjs.d.ts +5 -5
- package/esm/extension-yjs/src/ExtensionYjs.d.ts.map +1 -1
- package/esm/extension-yjs/src/ExtensionYjs.js +3 -2
- package/esm/extension-yjs/src/keys.d.ts +15 -0
- package/esm/extension-yjs/src/keys.d.ts.map +1 -0
- package/esm/extension-yjs/src/keys.js +13 -0
- package/esm/extension-yjs/src/lib.js +269 -0
- package/esm/extension-yjs/src/utils.d.ts +0 -3
- package/esm/extension-yjs/src/utils.d.ts.map +1 -1
- package/esm/extension-yjs/src/utils.js +0 -8
- package/esm/extension-yjs/src/yCursorPlugin.d.ts +3 -17
- package/esm/extension-yjs/src/yCursorPlugin.d.ts.map +1 -1
- package/esm/extension-yjs/src/yCursorPlugin.js +21 -13
- package/esm/extension-yjs/src/ySyncPlugin.d.ts +54 -91
- package/esm/extension-yjs/src/ySyncPlugin.d.ts.map +1 -1
- package/esm/extension-yjs/src/ySyncPlugin.js +99 -222
- package/esm/extension-yjs/src/yUndoPlugin.d.ts +21 -0
- package/esm/extension-yjs/src/yUndoPlugin.d.ts.map +1 -0
- package/esm/extension-yjs/src/yUndoPlugin.js +86 -0
- package/package.json +2 -4
- package/LICENSE +0 -23
- package/README.md +0 -57
- package/esm/package.json +0 -3
|
@@ -1,71 +1,33 @@
|
|
|
1
|
-
|
|
2
|
-
* @module bindings/prosemirror
|
|
3
|
-
*/
|
|
1
|
+
import * as Y from 'yjs';
|
|
4
2
|
import { createMutex } from 'lib0/mutex';
|
|
5
3
|
import * as PModel from 'prosemirror-model';
|
|
6
|
-
import { AllSelection, NodeSelection, Plugin, TextSelection, } from 'prosemirror-state';
|
|
4
|
+
import { AllSelection, NodeSelection, Plugin, TextSelection, } from 'prosemirror-state';
|
|
7
5
|
import * as math from 'lib0/math';
|
|
8
6
|
import * as object from 'lib0/object';
|
|
9
7
|
import * as set from 'lib0/set';
|
|
10
8
|
import { simpleDiff } from 'lib0/diff';
|
|
11
9
|
import * as error from 'lib0/error';
|
|
12
|
-
import { ySyncPluginKey, yUndoPluginKey } from 'y-prosemirror';
|
|
13
|
-
import * as Y from 'yjs';
|
|
14
|
-
import { absolutePositionToRelativePosition, relativePositionToAbsolutePosition, } from 'y-prosemirror';
|
|
15
10
|
import * as random from 'lib0/random';
|
|
16
11
|
import * as environment from 'lib0/environment';
|
|
17
12
|
import * as dom from 'lib0/dom';
|
|
18
13
|
import * as eventloop from 'lib0/eventloop';
|
|
19
14
|
import * as map from 'lib0/map';
|
|
15
|
+
import { ySyncPluginKey, yUndoPluginKey } from './keys.js';
|
|
20
16
|
import * as utils from './utils.js';
|
|
21
|
-
|
|
22
|
-
* @typedef {Object} BindingMetadata
|
|
23
|
-
* @property {ProsemirrorMapping} BindingMetadata.mapping
|
|
24
|
-
* @property {Map<import('prosemirror-model').MarkType, boolean>} BindingMetadata.isOMark - is overlapping mark
|
|
25
|
-
*/
|
|
26
|
-
/**
|
|
27
|
-
* @return {BindingMetadata}
|
|
28
|
-
*/
|
|
17
|
+
import { absolutePositionToRelativePosition, relativePositionToAbsolutePosition, } from './lib.js';
|
|
29
18
|
export const createEmptyMeta = () => ({
|
|
30
19
|
mapping: new Map(),
|
|
31
20
|
isOMark: new Map(),
|
|
32
21
|
});
|
|
33
|
-
/**
|
|
34
|
-
* @param {Y.Item} item
|
|
35
|
-
* @param {Y.Snapshot} [snapshot]
|
|
36
|
-
*/
|
|
37
22
|
export const isVisible = (item, snapshot) => snapshot === undefined
|
|
38
23
|
? !item.deleted
|
|
39
24
|
: (snapshot.sv.has(item.id.client) && /** @type {number} */
|
|
40
25
|
(snapshot.sv.get(item.id.client)) > item.id.clock &&
|
|
41
26
|
!Y.isDeleted(snapshot.ds, item.id));
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
/**
|
|
47
|
-
* @typedef {Object} ColorDef
|
|
48
|
-
* @property {string} ColorDef.light
|
|
49
|
-
* @property {string} ColorDef.dark
|
|
50
|
-
*/
|
|
51
|
-
/**
|
|
52
|
-
* @typedef {Object} YSyncOpts
|
|
53
|
-
* @property {Array<ColorDef>} [YSyncOpts.colors]
|
|
54
|
-
* @property {Map<string,ColorDef>} [YSyncOpts.colorMapping]
|
|
55
|
-
* @property {Y.PermanentUserData|null} [YSyncOpts.permanentUserData]
|
|
56
|
-
* @property {ProsemirrorMapping} [YSyncOpts.mapping]
|
|
57
|
-
* @property {function} [YSyncOpts.onFirstRender] Fired when the content from Yjs is initially rendered to ProseMirror
|
|
58
|
-
*/
|
|
59
|
-
/**
|
|
60
|
-
* @type {Array<ColorDef>}
|
|
61
|
-
*/
|
|
62
|
-
const defaultColors = [{ light: '#ecd44433', dark: '#ecd444' }];
|
|
63
|
-
/**
|
|
64
|
-
* @param {Map<string,ColorDef>} colorMapping
|
|
65
|
-
* @param {Array<ColorDef>} colors
|
|
66
|
-
* @param {string} user
|
|
67
|
-
* @return {ColorDef}
|
|
68
|
-
*/
|
|
27
|
+
const defaultColors = [{
|
|
28
|
+
light: '#ecd44433',
|
|
29
|
+
dark: '#ecd444',
|
|
30
|
+
}];
|
|
69
31
|
const getUserColor = (colorMapping, colors, user) => {
|
|
70
32
|
// @todo do not hit the same color twice if possible
|
|
71
33
|
if (!colorMapping.has(user)) {
|
|
@@ -82,11 +44,9 @@ const getUserColor = (colorMapping, colors, user) => {
|
|
|
82
44
|
* This plugin listens to changes in prosemirror view and keeps yXmlState and view in sync.
|
|
83
45
|
*
|
|
84
46
|
* This plugin also keeps references to the type and the shared document so other plugins can access it.
|
|
85
|
-
* @param {Y.XmlFragment} yXmlFragment
|
|
86
|
-
* @param {YSyncOpts} opts
|
|
87
|
-
* @return {any} Returns a prosemirror plugin that binds to this type
|
|
88
47
|
*/
|
|
89
|
-
export const ySyncPlugin = (yXmlFragment, { colors = defaultColors, colorMapping = new Map(), permanentUserData = null, onFirstRender = () => {
|
|
48
|
+
export const ySyncPlugin = (yXmlFragment, { colors = defaultColors, colorMapping = new Map(), permanentUserData = null, onFirstRender = () => {
|
|
49
|
+
}, mapping, } = {}) => {
|
|
90
50
|
let initialContentChanged = false;
|
|
91
51
|
const binding = new ProsemirrorBinding(yXmlFragment, mapping);
|
|
92
52
|
const plugin = new Plugin({
|
|
@@ -178,10 +138,8 @@ export const ySyncPlugin = (yXmlFragment, { colors = defaultColors, colorMapping
|
|
|
178
138
|
if (pluginState.addToHistory === false &&
|
|
179
139
|
!pluginState.isChangeOrigin) {
|
|
180
140
|
const yUndoPluginState = yUndoPluginKey.getState(view.state);
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
*/
|
|
184
|
-
const um = yUndoPluginState && yUndoPluginState.undoManager;
|
|
141
|
+
const um = yUndoPluginState &&
|
|
142
|
+
yUndoPluginState.undoManager;
|
|
185
143
|
if (um) {
|
|
186
144
|
um.stopCapturing();
|
|
187
145
|
}
|
|
@@ -203,11 +161,6 @@ export const ySyncPlugin = (yXmlFragment, { colors = defaultColors, colorMapping
|
|
|
203
161
|
});
|
|
204
162
|
return plugin;
|
|
205
163
|
};
|
|
206
|
-
/**
|
|
207
|
-
* @param {import('prosemirror-state').Transaction} tr
|
|
208
|
-
* @param {ReturnType<typeof getRelativeSelection>} relSel
|
|
209
|
-
* @param {ProsemirrorBinding} binding
|
|
210
|
-
*/
|
|
211
164
|
const restoreRelativeSelection = (tr, relSel, binding) => {
|
|
212
165
|
if (relSel !== null && relSel.anchor !== null && relSel.head !== null) {
|
|
213
166
|
if (relSel.type === 'all') {
|
|
@@ -227,10 +180,6 @@ const restoreRelativeSelection = (tr, relSel, binding) => {
|
|
|
227
180
|
}
|
|
228
181
|
}
|
|
229
182
|
};
|
|
230
|
-
/**
|
|
231
|
-
* @param {ProsemirrorBinding} pmbinding
|
|
232
|
-
* @param {import('prosemirror-state').EditorState} state
|
|
233
|
-
*/
|
|
234
183
|
export const getRelativeSelection = (pmbinding, state) => ({
|
|
235
184
|
type: /** @type {any} */ (state.selection).jsonID,
|
|
236
185
|
anchor: absolutePositionToRelativePosition(state.selection.anchor, pmbinding.type, pmbinding.mapping),
|
|
@@ -242,42 +191,101 @@ export const getRelativeSelection = (pmbinding, state) => ({
|
|
|
242
191
|
* @protected
|
|
243
192
|
*/
|
|
244
193
|
export class ProsemirrorBinding {
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
194
|
+
get beforeTransactionSelection() {
|
|
195
|
+
return this._beforeTransactionSelection;
|
|
196
|
+
}
|
|
197
|
+
set beforeTransactionSelection(value) {
|
|
198
|
+
this._beforeTransactionSelection = value;
|
|
199
|
+
}
|
|
249
200
|
constructor(yXmlFragment, mapping = new Map()) {
|
|
201
|
+
Object.defineProperty(this, "doc", {
|
|
202
|
+
enumerable: true,
|
|
203
|
+
configurable: true,
|
|
204
|
+
writable: true,
|
|
205
|
+
value: void 0
|
|
206
|
+
});
|
|
207
|
+
Object.defineProperty(this, "type", {
|
|
208
|
+
enumerable: true,
|
|
209
|
+
configurable: true,
|
|
210
|
+
writable: true,
|
|
211
|
+
value: void 0
|
|
212
|
+
});
|
|
213
|
+
Object.defineProperty(this, "mux", {
|
|
214
|
+
enumerable: true,
|
|
215
|
+
configurable: true,
|
|
216
|
+
writable: true,
|
|
217
|
+
value: void 0
|
|
218
|
+
});
|
|
219
|
+
Object.defineProperty(this, "prosemirrorView", {
|
|
220
|
+
enumerable: true,
|
|
221
|
+
configurable: true,
|
|
222
|
+
writable: true,
|
|
223
|
+
value: void 0
|
|
224
|
+
});
|
|
225
|
+
Object.defineProperty(this, "isOMark", {
|
|
226
|
+
enumerable: true,
|
|
227
|
+
configurable: true,
|
|
228
|
+
writable: true,
|
|
229
|
+
value: void 0
|
|
230
|
+
});
|
|
231
|
+
Object.defineProperty(this, "_observeFunction", {
|
|
232
|
+
enumerable: true,
|
|
233
|
+
configurable: true,
|
|
234
|
+
writable: true,
|
|
235
|
+
value: void 0
|
|
236
|
+
});
|
|
237
|
+
Object.defineProperty(this, "mapping", {
|
|
238
|
+
enumerable: true,
|
|
239
|
+
configurable: true,
|
|
240
|
+
writable: true,
|
|
241
|
+
value: void 0
|
|
242
|
+
});
|
|
243
|
+
Object.defineProperty(this, "_beforeTransactionSelection", {
|
|
244
|
+
enumerable: true,
|
|
245
|
+
configurable: true,
|
|
246
|
+
writable: true,
|
|
247
|
+
value: void 0
|
|
248
|
+
});
|
|
249
|
+
Object.defineProperty(this, "beforeAllTransactions", {
|
|
250
|
+
enumerable: true,
|
|
251
|
+
configurable: true,
|
|
252
|
+
writable: true,
|
|
253
|
+
value: void 0
|
|
254
|
+
});
|
|
255
|
+
Object.defineProperty(this, "afterAllTransactions", {
|
|
256
|
+
enumerable: true,
|
|
257
|
+
configurable: true,
|
|
258
|
+
writable: true,
|
|
259
|
+
value: void 0
|
|
260
|
+
});
|
|
261
|
+
Object.defineProperty(this, "_domSelectionInView", {
|
|
262
|
+
enumerable: true,
|
|
263
|
+
configurable: true,
|
|
264
|
+
writable: true,
|
|
265
|
+
value: void 0
|
|
266
|
+
});
|
|
250
267
|
this.type = yXmlFragment;
|
|
251
|
-
/**
|
|
252
|
-
* this will be set once the view is created
|
|
253
|
-
* @type {any}
|
|
254
|
-
*/
|
|
255
268
|
this.prosemirrorView = null;
|
|
256
269
|
this.mux = createMutex();
|
|
257
270
|
this.mapping = mapping;
|
|
258
271
|
/**
|
|
259
272
|
* Is overlapping mark - i.e. mark does not exclude itself.
|
|
260
|
-
*
|
|
261
|
-
* @type {Map<import('prosemirror-model').MarkType, boolean>}
|
|
262
273
|
*/
|
|
263
274
|
this.isOMark = new Map();
|
|
264
275
|
this._observeFunction = this._typeChanged.bind(this);
|
|
265
|
-
/**
|
|
266
|
-
* @type {Y.Doc}
|
|
267
|
-
*/
|
|
268
|
-
// @ts-ignore
|
|
269
276
|
this.doc = yXmlFragment.doc;
|
|
270
277
|
/**
|
|
271
278
|
* current selection as relative positions in the Yjs model
|
|
272
279
|
*/
|
|
273
|
-
this.
|
|
280
|
+
this._beforeTransactionSelection = null;
|
|
274
281
|
this.beforeAllTransactions = () => {
|
|
275
|
-
if (this.
|
|
276
|
-
this.
|
|
282
|
+
if (this._beforeTransactionSelection === null &&
|
|
283
|
+
this.prosemirrorView != null) {
|
|
284
|
+
this._beforeTransactionSelection = getRelativeSelection(this, this.prosemirrorView.state);
|
|
277
285
|
}
|
|
278
286
|
};
|
|
279
287
|
this.afterAllTransactions = () => {
|
|
280
|
-
this.
|
|
288
|
+
this._beforeTransactionSelection = null;
|
|
281
289
|
};
|
|
282
290
|
this._domSelectionInView = null;
|
|
283
291
|
}
|
|
@@ -302,7 +310,7 @@ export class ProsemirrorBinding {
|
|
|
302
310
|
return this._domSelectionInView;
|
|
303
311
|
}
|
|
304
312
|
_isDomSelectionInView() {
|
|
305
|
-
const selection = this.prosemirrorView.
|
|
313
|
+
const selection = this.prosemirrorView.root.getSelection(); // https://stackoverflow.com/questions/62054839/shadowroot-getselection
|
|
306
314
|
if (selection == null || selection.anchorNode == null)
|
|
307
315
|
return false;
|
|
308
316
|
const range = dom.doc.createRange(); // https://github.com/yjs/y-prosemirror/pull/193
|
|
@@ -325,10 +333,6 @@ export class ProsemirrorBinding {
|
|
|
325
333
|
(globalThis.innerWidth || documentElement.clientWidth || 0) &&
|
|
326
334
|
bounding.top <= (globalThis.innerHeight || documentElement.clientHeight || 0);
|
|
327
335
|
}
|
|
328
|
-
/**
|
|
329
|
-
* @param {Y.Snapshot} snapshot
|
|
330
|
-
* @param {Y.Snapshot} prevSnapshot
|
|
331
|
-
*/
|
|
332
336
|
renderSnapshot(snapshot, prevSnapshot) {
|
|
333
337
|
if (!prevSnapshot) {
|
|
334
338
|
prevSnapshot = Y.createSnapshot(Y.createDeleteSet(), new Map());
|
|
@@ -352,7 +356,7 @@ export class ProsemirrorBinding {
|
|
|
352
356
|
// If this is a forced rerender, this might neither happen as a pm change nor within a Yjs
|
|
353
357
|
// transaction. Then the "before selection" doesn't exist. In this case, we need to create a
|
|
354
358
|
// relative position before replacing content. Fixes #126
|
|
355
|
-
const sel = this.
|
|
359
|
+
const sel = this._beforeTransactionSelection !== null
|
|
356
360
|
? null
|
|
357
361
|
: this.prosemirrorView.state.selection;
|
|
358
362
|
const fragmentContent = this.type.toArray().map((t) => createNodeFromYElement(
|
|
@@ -372,15 +376,9 @@ export class ProsemirrorBinding {
|
|
|
372
376
|
this.prosemirrorView.dispatch(tr.setMeta(ySyncPluginKey, { isChangeOrigin: true, binding: this }));
|
|
373
377
|
});
|
|
374
378
|
}
|
|
375
|
-
/**
|
|
376
|
-
* @param {Y.Snapshot|Uint8Array} snapshot
|
|
377
|
-
* @param {Y.Snapshot|Uint8Array} prevSnapshot
|
|
378
|
-
* @param {Object} pluginState
|
|
379
|
-
*/
|
|
380
379
|
_renderSnapshot(snapshot, prevSnapshot, pluginState) {
|
|
381
380
|
/**
|
|
382
381
|
* The document that contains the full history of this document.
|
|
383
|
-
* @type {Y.Doc}
|
|
384
382
|
*/
|
|
385
383
|
let historyDoc = this.doc;
|
|
386
384
|
let historyType = this.type;
|
|
@@ -432,10 +430,6 @@ export class ProsemirrorBinding {
|
|
|
432
430
|
Y.iterateDeletedStructs(transaction, ds, (_item) => { });
|
|
433
431
|
});
|
|
434
432
|
}
|
|
435
|
-
/**
|
|
436
|
-
* @param {'removed'|'added'} type
|
|
437
|
-
* @param {Y.ID} id
|
|
438
|
-
*/
|
|
439
433
|
const computeYChange = (type, id) => {
|
|
440
434
|
const user = type === 'added'
|
|
441
435
|
? pud.getUserByClientId(id.client)
|
|
@@ -464,10 +458,6 @@ export class ProsemirrorBinding {
|
|
|
464
458
|
}, ySyncPluginKey);
|
|
465
459
|
});
|
|
466
460
|
}
|
|
467
|
-
/**
|
|
468
|
-
* @param {Array<Y.YEvent<any>>} events
|
|
469
|
-
* @param {Y.Transaction} transaction
|
|
470
|
-
*/
|
|
471
461
|
_typeChanged(events, transaction) {
|
|
472
462
|
if (this.prosemirrorView == null)
|
|
473
463
|
return;
|
|
@@ -479,10 +469,6 @@ export class ProsemirrorBinding {
|
|
|
479
469
|
return;
|
|
480
470
|
}
|
|
481
471
|
this.mux(() => {
|
|
482
|
-
/**
|
|
483
|
-
* @param {any} _
|
|
484
|
-
* @param {Y.AbstractType<any>} type
|
|
485
|
-
*/
|
|
486
472
|
const delType = (_, type) => this.mapping.delete(type);
|
|
487
473
|
Y.iterateDeletedStructs(transaction, transaction.deleteSet, (struct) => {
|
|
488
474
|
if (struct.constructor === Y.Item) {
|
|
@@ -498,29 +484,25 @@ export class ProsemirrorBinding {
|
|
|
498
484
|
/** @type {Y.XmlElement | Y.XmlHook} */ (t), this.prosemirrorView.state.schema, this)).filter((n) => n !== null);
|
|
499
485
|
// @ts-ignore
|
|
500
486
|
let tr = this._tr.replace(0, this.prosemirrorView.state.doc.content.size, new PModel.Slice(PModel.Fragment.from(fragmentContent), 0, 0));
|
|
501
|
-
restoreRelativeSelection(tr, this.
|
|
487
|
+
restoreRelativeSelection(tr, this._beforeTransactionSelection, this);
|
|
502
488
|
tr = tr.setMeta(ySyncPluginKey, {
|
|
503
489
|
isChangeOrigin: true,
|
|
504
490
|
isUndoRedoOperation: transaction.origin instanceof Y.UndoManager,
|
|
505
491
|
});
|
|
506
|
-
if (this.
|
|
492
|
+
if (this._beforeTransactionSelection !== null && this._isLocalCursorInView()) {
|
|
507
493
|
tr.scrollIntoView();
|
|
508
494
|
}
|
|
509
495
|
this.prosemirrorView.dispatch(tr);
|
|
510
496
|
});
|
|
511
497
|
}
|
|
512
|
-
/**
|
|
513
|
-
* @param {import('prosemirror-model').Node} doc
|
|
514
|
-
*/
|
|
515
498
|
_prosemirrorChanged(doc) {
|
|
516
499
|
this.doc.transact(() => {
|
|
517
500
|
updateYFragment(this.doc, this.type, doc, this);
|
|
518
|
-
this.
|
|
501
|
+
this._beforeTransactionSelection = getRelativeSelection(this, this.prosemirrorView.state);
|
|
519
502
|
}, ySyncPluginKey);
|
|
520
503
|
}
|
|
521
504
|
/**
|
|
522
505
|
* View is ready to listen to changes. Register observers.
|
|
523
|
-
* @param {any} prosemirrorView
|
|
524
506
|
*/
|
|
525
507
|
initView(prosemirrorView) {
|
|
526
508
|
if (this.prosemirrorView != null)
|
|
@@ -539,16 +521,6 @@ export class ProsemirrorBinding {
|
|
|
539
521
|
this.doc.off('afterAllTransactions', this.afterAllTransactions);
|
|
540
522
|
}
|
|
541
523
|
}
|
|
542
|
-
/**
|
|
543
|
-
* @private
|
|
544
|
-
* @param {Y.XmlElement | Y.XmlHook} el
|
|
545
|
-
* @param {PModel.Schema} schema
|
|
546
|
-
* @param {BindingMetadata} meta
|
|
547
|
-
* @param {Y.Snapshot} [snapshot]
|
|
548
|
-
* @param {Y.Snapshot} [prevSnapshot]
|
|
549
|
-
* @param {function('removed' | 'added', Y.ID):any} [computeYChange]
|
|
550
|
-
* @return {PModel.Node | null}
|
|
551
|
-
*/
|
|
552
524
|
const createNodeIfNotExists = (el, schema, meta, snapshot, prevSnapshot, computeYChange) => {
|
|
553
525
|
const node = /** @type {PModel.Node} */ (meta.mapping.get(el));
|
|
554
526
|
if (node === undefined) {
|
|
@@ -561,21 +533,8 @@ const createNodeIfNotExists = (el, schema, meta, snapshot, prevSnapshot, compute
|
|
|
561
533
|
}
|
|
562
534
|
return node;
|
|
563
535
|
};
|
|
564
|
-
/**
|
|
565
|
-
* @private
|
|
566
|
-
* @param {Y.XmlElement} el
|
|
567
|
-
* @param {any} schema
|
|
568
|
-
* @param {BindingMetadata} meta
|
|
569
|
-
* @param {Y.Snapshot} [snapshot]
|
|
570
|
-
* @param {Y.Snapshot} [prevSnapshot]
|
|
571
|
-
* @param {function('removed' | 'added', Y.ID):any} [computeYChange]
|
|
572
|
-
* @return {PModel.Node | null} Returns node if node could be created. Otherwise it deletes the yjs type and returns null
|
|
573
|
-
*/
|
|
574
536
|
export const createNodeFromYElement = (el, schema, meta, snapshot, prevSnapshot, computeYChange) => {
|
|
575
537
|
const children = [];
|
|
576
|
-
/**
|
|
577
|
-
* @param {Y.XmlElement | Y.XmlText} type
|
|
578
|
-
*/
|
|
579
538
|
const createChildren = (type) => {
|
|
580
539
|
if (type instanceof Y.XmlElement) {
|
|
581
540
|
const n = createNodeIfNotExists(type, schema, meta, snapshot, prevSnapshot, computeYChange);
|
|
@@ -646,13 +605,6 @@ export const createNodeFromYElement = (el, schema, meta, snapshot, prevSnapshot,
|
|
|
646
605
|
};
|
|
647
606
|
/**
|
|
648
607
|
* @private
|
|
649
|
-
* @param {Y.XmlText} text
|
|
650
|
-
* @param {import('prosemirror-model').Schema} schema
|
|
651
|
-
* @param {BindingMetadata} _meta
|
|
652
|
-
* @param {Y.Snapshot} [snapshot]
|
|
653
|
-
* @param {Y.Snapshot} [prevSnapshot]
|
|
654
|
-
* @param {function('removed' | 'added', Y.ID):any} [computeYChange]
|
|
655
|
-
* @return {Array<PModel.Node>|null}
|
|
656
608
|
*/
|
|
657
609
|
const createTextNodesFromYText = (text, schema, _meta, snapshot, prevSnapshot, computeYChange) => {
|
|
658
610
|
const nodes = [];
|
|
@@ -675,9 +627,6 @@ const createTextNodesFromYText = (text, schema, _meta, snapshot, prevSnapshot, c
|
|
|
675
627
|
};
|
|
676
628
|
/**
|
|
677
629
|
* @private
|
|
678
|
-
* @param {Array<any>} nodes prosemirror node
|
|
679
|
-
* @param {BindingMetadata} meta
|
|
680
|
-
* @return {Y.XmlText}
|
|
681
630
|
*/
|
|
682
631
|
const createTypeFromTextNodes = (nodes, meta) => {
|
|
683
632
|
const type = new Y.XmlText();
|
|
@@ -692,9 +641,6 @@ const createTypeFromTextNodes = (nodes, meta) => {
|
|
|
692
641
|
};
|
|
693
642
|
/**
|
|
694
643
|
* @private
|
|
695
|
-
* @param {any} node prosemirror node
|
|
696
|
-
* @param {BindingMetadata} meta
|
|
697
|
-
* @return {Y.XmlElement}
|
|
698
644
|
*/
|
|
699
645
|
const createTypeFromElementNode = (node, meta) => {
|
|
700
646
|
const type = new Y.XmlElement(node.type.name);
|
|
@@ -710,21 +656,11 @@ const createTypeFromElementNode = (node, meta) => {
|
|
|
710
656
|
};
|
|
711
657
|
/**
|
|
712
658
|
* @private
|
|
713
|
-
* @param {PModel.Node|Array<PModel.Node>} node prosemirror text node
|
|
714
|
-
* @param {BindingMetadata} meta
|
|
715
|
-
* @return {Y.XmlElement|Y.XmlText}
|
|
716
659
|
*/
|
|
717
660
|
const createTypeFromTextOrElementNode = (node, meta) => node instanceof Array
|
|
718
661
|
? createTypeFromTextNodes(node, meta)
|
|
719
662
|
: createTypeFromElementNode(node, meta);
|
|
720
|
-
/**
|
|
721
|
-
* @param {any} val
|
|
722
|
-
*/
|
|
723
663
|
const isObject = (val) => typeof val === 'object' && val !== null;
|
|
724
|
-
/**
|
|
725
|
-
* @param {any} pattrs
|
|
726
|
-
* @param {any} yattrs
|
|
727
|
-
*/
|
|
728
664
|
const equalAttrs = (pattrs, yattrs) => {
|
|
729
665
|
const keys = Object.keys(pattrs).filter((key) => pattrs[key] !== null);
|
|
730
666
|
let eq = keys.length ===
|
|
@@ -740,13 +676,6 @@ const equalAttrs = (pattrs, yattrs) => {
|
|
|
740
676
|
}
|
|
741
677
|
return eq;
|
|
742
678
|
};
|
|
743
|
-
/**
|
|
744
|
-
* @typedef {Array<Array<PModel.Node>|PModel.Node>} NormalizedPNodeContent
|
|
745
|
-
*/
|
|
746
|
-
/**
|
|
747
|
-
* @param {any} pnode
|
|
748
|
-
* @return {NormalizedPNodeContent}
|
|
749
|
-
*/
|
|
750
679
|
const normalizePNodeContent = (pnode) => {
|
|
751
680
|
const c = pnode.content.content;
|
|
752
681
|
const res = [];
|
|
@@ -766,25 +695,17 @@ const normalizePNodeContent = (pnode) => {
|
|
|
766
695
|
}
|
|
767
696
|
return res;
|
|
768
697
|
};
|
|
769
|
-
/**
|
|
770
|
-
* @param {Y.XmlText} ytext
|
|
771
|
-
* @param {Array<any>} ptexts
|
|
772
|
-
*/
|
|
773
698
|
const equalYTextPText = (ytext, ptexts) => {
|
|
774
699
|
const delta = ytext.toDelta();
|
|
775
700
|
return delta.length === ptexts.length &&
|
|
776
|
-
delta.every(
|
|
701
|
+
delta.every((d, i) => d.insert === /** @type {any} */ (ptexts[i]).text &&
|
|
777
702
|
object.keys(d.attributes || {}).length === ptexts[i].marks.length &&
|
|
778
703
|
object.every(d.attributes, (attr, yattrname) => {
|
|
779
704
|
const markname = yattr2markname(yattrname);
|
|
780
705
|
const pmarks = ptexts[i].marks;
|
|
781
|
-
return equalAttrs(attr, pmarks.find(
|
|
706
|
+
return equalAttrs(attr, pmarks.find((mark) => mark.type.name === markname)?.attrs);
|
|
782
707
|
}));
|
|
783
708
|
};
|
|
784
|
-
/**
|
|
785
|
-
* @param {Y.XmlElement|Y.XmlText|Y.XmlHook} ytype
|
|
786
|
-
* @param {any|Array<any>} pnode
|
|
787
|
-
*/
|
|
788
709
|
const equalYTypePNode = (ytype, pnode) => {
|
|
789
710
|
if (ytype instanceof Y.XmlElement && !(pnode instanceof Array) &&
|
|
790
711
|
matchNodeName(ytype, pnode)) {
|
|
@@ -796,20 +717,10 @@ const equalYTypePNode = (ytype, pnode) => {
|
|
|
796
717
|
return ytype instanceof Y.XmlText && pnode instanceof Array &&
|
|
797
718
|
equalYTextPText(ytype, pnode);
|
|
798
719
|
};
|
|
799
|
-
/**
|
|
800
|
-
* @param {PModel.Node | Array<PModel.Node> | undefined} mapped
|
|
801
|
-
* @param {PModel.Node | Array<PModel.Node>} pcontent
|
|
802
|
-
*/
|
|
803
720
|
const mappedIdentity = (mapped, pcontent) => mapped === pcontent ||
|
|
804
721
|
(mapped instanceof Array && pcontent instanceof Array &&
|
|
805
722
|
mapped.length === pcontent.length &&
|
|
806
723
|
mapped.every((a, i) => pcontent[i] === a));
|
|
807
|
-
/**
|
|
808
|
-
* @param {Y.XmlElement} ytype
|
|
809
|
-
* @param {PModel.Node} pnode
|
|
810
|
-
* @param {BindingMetadata} meta
|
|
811
|
-
* @return {{ foundMappedChild: boolean, equalityFactor: number }}
|
|
812
|
-
*/
|
|
813
724
|
const computeChildEqualityFactor = (ytype, pnode, meta) => {
|
|
814
725
|
const yChildren = ytype.toArray();
|
|
815
726
|
const pChildren = normalizePNodeContent(pnode);
|
|
@@ -844,9 +755,6 @@ const computeChildEqualityFactor = (ytype, pnode, meta) => {
|
|
|
844
755
|
foundMappedChild,
|
|
845
756
|
};
|
|
846
757
|
};
|
|
847
|
-
/**
|
|
848
|
-
* @param {Y.Text} ytext
|
|
849
|
-
*/
|
|
850
758
|
const ytextTrans = (ytext) => {
|
|
851
759
|
let str = '';
|
|
852
760
|
/**
|
|
@@ -872,10 +780,6 @@ const ytextTrans = (ytext) => {
|
|
|
872
780
|
};
|
|
873
781
|
/**
|
|
874
782
|
* @todo test this more
|
|
875
|
-
*
|
|
876
|
-
* @param {Y.Text} ytext
|
|
877
|
-
* @param {Array<any>} ptexts
|
|
878
|
-
* @param {BindingMetadata} meta
|
|
879
783
|
*/
|
|
880
784
|
const updateYText = (ytext, ptexts, meta) => {
|
|
881
785
|
meta.mapping.set(ytext, ptexts);
|
|
@@ -890,20 +794,11 @@ const updateYText = (ytext, ptexts, meta) => {
|
|
|
890
794
|
ytext.applyDelta(content.map((c) => ({ retain: c.insert.length, attributes: c.attributes })));
|
|
891
795
|
};
|
|
892
796
|
const hashedMarkNameRegex = /(.*)(--[a-zA-Z0-9+/=]{8})$/;
|
|
893
|
-
/**
|
|
894
|
-
* @param {string} attrName
|
|
895
|
-
*/
|
|
896
797
|
export const yattr2markname = (attrName) => hashedMarkNameRegex.exec(attrName)?.[1] ?? attrName;
|
|
897
798
|
/**
|
|
898
799
|
* @todo move this to markstoattributes
|
|
899
|
-
*
|
|
900
|
-
* @param {Object<string, any>} attrs
|
|
901
|
-
* @param {import('prosemirror-model').Schema} schema
|
|
902
800
|
*/
|
|
903
801
|
export const attributesToMarks = (attrs, schema) => {
|
|
904
|
-
/**
|
|
905
|
-
* @type {Array<import('prosemirror-model').Mark>}
|
|
906
|
-
*/
|
|
907
802
|
const marks = [];
|
|
908
803
|
for (const markName in attrs) {
|
|
909
804
|
// remove hashes if necessary
|
|
@@ -911,10 +806,6 @@ export const attributesToMarks = (attrs, schema) => {
|
|
|
911
806
|
}
|
|
912
807
|
return marks;
|
|
913
808
|
};
|
|
914
|
-
/**
|
|
915
|
-
* @param {Array<import('prosemirror-model').Mark>} marks
|
|
916
|
-
* @param {BindingMetadata} meta
|
|
917
|
-
*/
|
|
918
809
|
const marksToAttributes = (marks, meta) => {
|
|
919
810
|
const pattrs = {};
|
|
920
811
|
marks.forEach((mark) => {
|
|
@@ -934,11 +825,6 @@ const marksToAttributes = (marks, meta) => {
|
|
|
934
825
|
*
|
|
935
826
|
* @private
|
|
936
827
|
* @unstable
|
|
937
|
-
*
|
|
938
|
-
* @param {{transact: Function}} y
|
|
939
|
-
* @param {Y.XmlFragment} yDomFragment
|
|
940
|
-
* @param {any} pNode
|
|
941
|
-
* @param {BindingMetadata} meta
|
|
942
828
|
*/
|
|
943
829
|
export const updateYFragment = (y, yDomFragment, pNode, meta) => {
|
|
944
830
|
if (yDomFragment instanceof Y.XmlElement &&
|
|
@@ -1022,13 +908,9 @@ export const updateYFragment = (y, yDomFragment, pNode, meta) => {
|
|
|
1022
908
|
let updateRight = rightY instanceof Y.XmlElement &&
|
|
1023
909
|
matchNodeName(rightY, rightP);
|
|
1024
910
|
if (updateLeft && updateRight) {
|
|
1025
|
-
// decide which
|
|
1026
|
-
const equalityLeft = computeChildEqualityFactor(
|
|
1027
|
-
|
|
1028
|
-
/** @type {PModel.Node} */ (leftP), meta);
|
|
1029
|
-
const equalityRight = computeChildEqualityFactor(
|
|
1030
|
-
/** @type {Y.XmlElement} */ (rightY),
|
|
1031
|
-
/** @type {PModel.Node} */ (rightP), meta);
|
|
911
|
+
// decide which element to update
|
|
912
|
+
const equalityLeft = computeChildEqualityFactor(leftY, leftP, meta);
|
|
913
|
+
const equalityRight = computeChildEqualityFactor(rightY, rightP, meta);
|
|
1032
914
|
if (equalityLeft.foundMappedChild && !equalityRight.foundMappedChild) {
|
|
1033
915
|
updateRight = false;
|
|
1034
916
|
}
|
|
@@ -1084,9 +966,4 @@ export const updateYFragment = (y, yDomFragment, pNode, meta) => {
|
|
|
1084
966
|
}
|
|
1085
967
|
}, ySyncPluginKey);
|
|
1086
968
|
};
|
|
1087
|
-
/**
|
|
1088
|
-
* @function
|
|
1089
|
-
* @param {Y.XmlElement} yElement
|
|
1090
|
-
* @param {any} pNode Prosemirror Node
|
|
1091
|
-
*/
|
|
1092
969
|
const matchNodeName = (yElement, pNode) => !(pNode instanceof Array) && yElement.nodeName === pNode.type.name;
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { Command, EditorState, Plugin } from 'prosemirror-state';
|
|
2
|
+
import { UndoManager } from 'yjs';
|
|
3
|
+
import { getRelativeSelection } from './ySyncPlugin.js';
|
|
4
|
+
export interface UndoPluginState {
|
|
5
|
+
undoManager: UndoManager;
|
|
6
|
+
prevSel: ReturnType<typeof getRelativeSelection> | null;
|
|
7
|
+
hasUndoOps: boolean;
|
|
8
|
+
hasRedoOps: boolean;
|
|
9
|
+
}
|
|
10
|
+
export declare const undo: (state: EditorState) => boolean;
|
|
11
|
+
export declare const redo: (state: EditorState) => boolean;
|
|
12
|
+
export declare const undoCommand: Command;
|
|
13
|
+
export declare const redoCommand: Command;
|
|
14
|
+
export declare const defaultProtectedNodes: Set<string>;
|
|
15
|
+
export declare const defaultDeleteFilter: (item: import("yjs").Item, protectedNodes: Set<string>) => boolean;
|
|
16
|
+
export declare const yUndoPlugin: ({ protectedNodes, trackedOrigins, undoManager, }?: {
|
|
17
|
+
protectedNodes?: Set<string>;
|
|
18
|
+
trackedOrigins?: any[];
|
|
19
|
+
undoManager?: import("yjs").UndoManager | null;
|
|
20
|
+
}) => Plugin<any>;
|
|
21
|
+
//# sourceMappingURL=yUndoPlugin.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"yUndoPlugin.d.ts","sourceRoot":"","sources":["../../../src/extension-yjs/src/yUndoPlugin.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,WAAW,EAAE,MAAM,EAAE,MAAM,mBAAmB,CAAC;AACjE,OAAO,EAA2B,WAAW,EAAc,MAAM,KAAK,CAAC;AAEvE,OAAO,EAAE,oBAAoB,EAAE,MAAM,kBAAkB,CAAC;AAGxD,MAAM,WAAW,eAAe;IAC9B,WAAW,EAAE,WAAW,CAAC;IACzB,OAAO,EAAE,UAAU,CAAC,OAAO,oBAAoB,CAAC,GAAG,IAAI,CAAC;IACxD,UAAU,EAAE,OAAO,CAAC;IACpB,UAAU,EAAE,OAAO,CAAC;CACrB;AAED,eAAO,MAAM,IAAI,GAAI,OAAO,WAAW,KAAG,OACmB,CAAC;AAE9D,eAAO,MAAM,IAAI,GAAI,OAAO,WAAW,KAAG,OACmB,CAAC;AAE9D,eAAO,MAAM,WAAW,EAAE,OAGT,CAAC;AAElB,eAAO,MAAM,WAAW,EAAE,OAGT,CAAC;AAElB,eAAO,MAAM,qBAAqB,aAAyB,CAAC;AAE5D,eAAO,MAAM,mBAAmB,GAC9B,MAAM,OAAO,KAAK,EAAE,IAAI,EACxB,gBAAgB,GAAG,CAAC,MAAM,CAAC,KAC1B,OAM8B,CAAC;AAElC,eAAO,MAAM,WAAW,GAAI,mDAIzB;IACD,cAAc,CAAC,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC;IAC7B,cAAc,CAAC,EAAE,GAAG,EAAE,CAAC;IACvB,WAAW,CAAC,EAAE,OAAO,KAAK,EAAE,WAAW,GAAG,IAAI,CAAC;CAC3C,gBAoEF,CAAC"}
|