@kerebron/extension-yjs 0.3.2 → 0.4.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/README.md +26 -11
- package/esm/editor/src/CoreEditor.d.ts +13 -4
- package/esm/editor/src/CoreEditor.d.ts.map +1 -1
- package/esm/editor/src/CoreEditor.js +64 -12
- package/esm/editor/src/Extension.d.ts +6 -1
- package/esm/editor/src/Extension.d.ts.map +1 -1
- package/esm/editor/src/Extension.js +21 -1
- package/esm/editor/src/ExtensionManager.d.ts +5 -6
- package/esm/editor/src/ExtensionManager.d.ts.map +1 -1
- package/esm/editor/src/ExtensionManager.js +43 -55
- package/esm/editor/src/Mark.d.ts +3 -0
- package/esm/editor/src/Mark.d.ts.map +1 -1
- package/esm/editor/src/Mark.js +11 -0
- package/esm/editor/src/Node.d.ts +5 -2
- package/esm/editor/src/Node.d.ts.map +1 -1
- package/esm/editor/src/Node.js +13 -2
- package/esm/editor/src/commands/CommandManager.d.ts +13 -6
- package/esm/editor/src/commands/CommandManager.d.ts.map +1 -1
- package/esm/editor/src/commands/CommandManager.js +59 -2
- package/esm/editor/src/commands/baseCommandFactories.d.ts +3 -0
- package/esm/editor/src/commands/baseCommandFactories.d.ts.map +1 -0
- package/esm/editor/src/commands/baseCommandFactories.js +836 -0
- package/esm/editor/src/commands/keyCommandFactories.d.ts +3 -0
- package/esm/editor/src/commands/keyCommandFactories.d.ts.map +1 -0
- package/esm/editor/src/commands/keyCommandFactories.js +10 -0
- package/esm/editor/src/commands/mod.d.ts +5 -53
- package/esm/editor/src/commands/mod.d.ts.map +1 -1
- package/esm/editor/src/commands/mod.js +14 -821
- package/esm/editor/src/commands/replaceCommandFactories.d.ts +3 -0
- package/esm/editor/src/commands/replaceCommandFactories.d.ts.map +1 -0
- package/esm/editor/src/commands/replaceCommandFactories.js +94 -0
- package/esm/editor/src/commands/types.d.ts +18 -0
- package/esm/editor/src/commands/types.d.ts.map +1 -0
- package/esm/editor/src/commands/types.js +1 -0
- package/esm/editor/src/mod.d.ts +2 -0
- package/esm/editor/src/mod.d.ts.map +1 -1
- package/esm/editor/src/mod.js +2 -0
- package/esm/editor/src/plugins/TrackSelecionPlugin.d.ts +6 -0
- package/esm/editor/src/plugins/TrackSelecionPlugin.d.ts.map +1 -0
- package/esm/editor/src/plugins/TrackSelecionPlugin.js +24 -0
- package/esm/editor/src/types.d.ts +19 -1
- package/esm/editor/src/types.d.ts.map +1 -1
- package/esm/editor/src/ui.d.ts +15 -0
- package/esm/editor/src/ui.d.ts.map +1 -0
- package/esm/editor/src/ui.js +16 -0
- package/esm/editor/src/utilities/SmartOutput.d.ts +9 -7
- package/esm/editor/src/utilities/SmartOutput.d.ts.map +1 -1
- package/esm/editor/src/utilities/SmartOutput.js +35 -20
- package/esm/extension-basic-editor/src/remote-selection/ExtensionRemoteSelection.d.ts +24 -0
- package/esm/extension-basic-editor/src/remote-selection/ExtensionRemoteSelection.d.ts.map +1 -0
- package/esm/extension-basic-editor/src/remote-selection/ExtensionRemoteSelection.js +35 -0
- package/esm/extension-basic-editor/src/remote-selection/remoteSelectionPlugin.d.ts +25 -0
- package/esm/extension-basic-editor/src/remote-selection/remoteSelectionPlugin.d.ts.map +1 -0
- package/esm/extension-basic-editor/src/remote-selection/remoteSelectionPlugin.js +96 -0
- package/esm/extension-yjs/src/ExtensionYjs.d.ts +9 -4
- package/esm/extension-yjs/src/ExtensionYjs.d.ts.map +1 -1
- package/esm/extension-yjs/src/ExtensionYjs.js +12 -6
- package/esm/extension-yjs/src/keys.d.ts +0 -4
- package/esm/extension-yjs/src/keys.d.ts.map +1 -1
- package/esm/extension-yjs/src/keys.js +0 -4
- package/esm/extension-yjs/src/lib.d.ts.map +1 -1
- package/esm/extension-yjs/src/lib.js +0 -2
- package/esm/extension-yjs/src/yPositionPlugin.d.ts +14 -0
- package/esm/extension-yjs/src/yPositionPlugin.d.ts.map +1 -0
- package/esm/extension-yjs/src/yPositionPlugin.js +121 -0
- package/esm/extension-yjs/src/ySyncPlugin.d.ts +1 -1
- package/esm/extension-yjs/src/ySyncPlugin.d.ts.map +1 -1
- package/esm/extension-yjs/src/ySyncPlugin.js +13 -9
- package/package.json +1 -1
- package/esm/extension-yjs/src/yCursorPlugin.d.ts +0 -29
- package/esm/extension-yjs/src/yCursorPlugin.d.ts.map +0 -1
- package/esm/extension-yjs/src/yCursorPlugin.js +0 -177
|
@@ -1,12 +1,17 @@
|
|
|
1
|
-
import type { Schema } from 'prosemirror-model';
|
|
2
1
|
import type { Plugin } from 'prosemirror-state';
|
|
3
|
-
import
|
|
2
|
+
import * as awarenessProtocol from 'y-protocols/awareness';
|
|
3
|
+
import { Extension } from '../../editor/src/mod.js';
|
|
4
4
|
import type { CommandFactories, CommandShortcuts } from '../../editor/src/commands/mod.js';
|
|
5
|
+
export interface YjsProvider {
|
|
6
|
+
on(eventName: string, callback: (event: any) => void): void;
|
|
7
|
+
awareness: awarenessProtocol.Awareness;
|
|
8
|
+
}
|
|
5
9
|
export declare class ExtensionYjs extends Extension {
|
|
6
10
|
name: string;
|
|
7
|
-
|
|
11
|
+
conflicts: string[];
|
|
12
|
+
requires: string[];
|
|
8
13
|
getCommandFactories(): Partial<CommandFactories>;
|
|
9
14
|
getKeyboardShortcuts(): Partial<CommandShortcuts>;
|
|
10
|
-
getProseMirrorPlugins(
|
|
15
|
+
getProseMirrorPlugins(): Plugin[];
|
|
11
16
|
}
|
|
12
17
|
//# sourceMappingURL=ExtensionYjs.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ExtensionYjs.d.ts","sourceRoot":"","sources":["../../../src/extension-yjs/src/ExtensionYjs.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,mBAAmB,CAAC;
|
|
1
|
+
{"version":3,"file":"ExtensionYjs.d.ts","sourceRoot":"","sources":["../../../src/extension-yjs/src/ExtensionYjs.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,mBAAmB,CAAC;AAGhD,OAAO,KAAK,iBAAiB,MAAM,uBAAuB,CAAC;AAE3D,OAAO,EAAE,SAAS,EAAE,MAAM,yBAAyB,CAAC;AAEpD,OAAO,KAAK,EACV,gBAAgB,EAChB,gBAAgB,EACjB,MAAM,kCAAkC,CAAC;AAM1C,MAAM,WAAW,WAAW;IAC1B,EAAE,CAAC,SAAS,EAAE,MAAM,EAAE,QAAQ,EAAE,CAAC,KAAK,EAAE,GAAG,KAAK,IAAI,GAAG,IAAI,CAAC;IAC5D,SAAS,EAAE,iBAAiB,CAAC,SAAS,CAAC;CACxC;AAED,qBAAa,YAAa,SAAQ,SAAS;IACzC,IAAI,SAAS;IAEJ,SAAS,WAAe;IACjC,QAAQ,WAAwB;IAGvB,mBAAmB,IAAI,OAAO,CAAC,gBAAgB,CAAC;IAOhD,oBAAoB,IAAI,OAAO,CAAC,gBAAgB,CAAC;IAOjD,qBAAqB,IAAI,MAAM,EAAE;CAY3C"}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { Extension } from '../../editor/src/mod.js';
|
|
2
2
|
import { ySyncPlugin } from './ySyncPlugin.js';
|
|
3
|
-
import {
|
|
3
|
+
import { yPositionPlugin } from './yPositionPlugin.js';
|
|
4
4
|
import { redo, undo, yUndoPlugin } from './yUndoPlugin.js';
|
|
5
5
|
import { initProseMirrorDoc } from './convertUtils.js';
|
|
6
6
|
export class ExtensionYjs extends Extension {
|
|
@@ -12,11 +12,17 @@ export class ExtensionYjs extends Extension {
|
|
|
12
12
|
writable: true,
|
|
13
13
|
value: 'yjs'
|
|
14
14
|
});
|
|
15
|
-
Object.defineProperty(this, "
|
|
15
|
+
Object.defineProperty(this, "conflicts", {
|
|
16
16
|
enumerable: true,
|
|
17
17
|
configurable: true,
|
|
18
18
|
writable: true,
|
|
19
|
-
value:
|
|
19
|
+
value: ['history']
|
|
20
|
+
});
|
|
21
|
+
Object.defineProperty(this, "requires", {
|
|
22
|
+
enumerable: true,
|
|
23
|
+
configurable: true,
|
|
24
|
+
writable: true,
|
|
25
|
+
value: ['remote-selection']
|
|
20
26
|
});
|
|
21
27
|
}
|
|
22
28
|
// declare type Command = (state: EditorState, dispatch?: (tr: Transaction) => void, view?: EditorView) => boolean;
|
|
@@ -32,13 +38,13 @@ export class ExtensionYjs extends Extension {
|
|
|
32
38
|
'Mod-y': 'redo',
|
|
33
39
|
};
|
|
34
40
|
}
|
|
35
|
-
getProseMirrorPlugins(
|
|
41
|
+
getProseMirrorPlugins() {
|
|
36
42
|
const ydoc = this.config.ydoc;
|
|
37
43
|
const fragment = ydoc.getXmlFragment('prosemirror');
|
|
38
|
-
const { mapping } = initProseMirrorDoc(fragment, schema);
|
|
44
|
+
const { mapping } = initProseMirrorDoc(fragment, this.editor.schema);
|
|
39
45
|
return [
|
|
40
46
|
ySyncPlugin(fragment, { mapping }),
|
|
41
|
-
|
|
47
|
+
yPositionPlugin(this.config.provider.awareness, this.editor),
|
|
42
48
|
yUndoPlugin(),
|
|
43
49
|
];
|
|
44
50
|
}
|
|
@@ -8,8 +8,4 @@ export declare const ySyncPluginKey: PluginKey<any>;
|
|
|
8
8
|
* The unique prosemirror plugin key for undoPlugin
|
|
9
9
|
*/
|
|
10
10
|
export declare const yUndoPluginKey: PluginKey<UndoPluginState>;
|
|
11
|
-
/**
|
|
12
|
-
* The unique prosemirror plugin key for cursorPlugin
|
|
13
|
-
*/
|
|
14
|
-
export declare const yCursorPluginKey: PluginKey<any>;
|
|
15
11
|
//# sourceMappingURL=keys.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"keys.d.ts","sourceRoot":"","sources":["../../../src/extension-yjs/src/keys.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,mBAAmB,CAAC;AAC9C,OAAO,EAAE,eAAe,EAAE,MAAM,kBAAkB,CAAC;AAEnD;;GAEG;AACH,eAAO,MAAM,cAAc,gBAA0B,CAAC;AAEtD;;GAEG;AACH,eAAO,MAAM,cAAc,EAAE,SAAS,CAAC,eAAe,CAErD,CAAC
|
|
1
|
+
{"version":3,"file":"keys.d.ts","sourceRoot":"","sources":["../../../src/extension-yjs/src/keys.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,mBAAmB,CAAC;AAC9C,OAAO,EAAE,eAAe,EAAE,MAAM,kBAAkB,CAAC;AAEnD;;GAEG;AACH,eAAO,MAAM,cAAc,gBAA0B,CAAC;AAEtD;;GAEG;AACH,eAAO,MAAM,cAAc,EAAE,SAAS,CAAC,eAAe,CAErD,CAAC"}
|
|
@@ -7,7 +7,3 @@ export const ySyncPluginKey = new PluginKey('y-sync');
|
|
|
7
7
|
* The unique prosemirror plugin key for undoPlugin
|
|
8
8
|
*/
|
|
9
9
|
export const yUndoPluginKey = new PluginKey('y-undo');
|
|
10
|
-
/**
|
|
11
|
-
* The unique prosemirror plugin key for cursorPlugin
|
|
12
|
-
*/
|
|
13
|
-
export const yCursorPluginKey = new PluginKey('yjs-cursor');
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"lib.d.ts","sourceRoot":"","sources":["../../../src/extension-yjs/src/lib.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,CAAC,MAAM,KAAK,CAAC;AACzB,OAAO,EAAE,KAAK,UAAU,EAAE,MAAM,kBAAkB,CAAC;AACnD,OAAO,EAAE,KAAK,IAAI,EAAE,MAAM,mBAAmB,CAAC;AAI9C;;GAEG;AACH,KAAK,kBAAkB,GAAG,GAAG,CAAC,CAAC,CAAC,YAAY,CAAC,GAAG,CAAC,EAAE,IAAI,CAAC,CAAC;AA2BzD,eAAO,MAAM,OAAO,GAAI,MAAM,UAAU,EAAE,QAAG,EAAE,UAAK,SAYnD,CAAC;AAEF;;GAEG;AACH,eAAO,MAAM,kCAAkC,GAC7C,KAAK,MAAM,EACX,MAAM,CAAC,CAAC,WAAW,EACnB,SAAS,kBAAkB,KAC1B,GA+FF,CAAC;AAaF,eAAO,MAAM,kCAAkC,GAC7C,MAAM,CAAC,CAAC,GAAG,EACX,cAAc,CAAC,CAAC,WAAW,EAC3B,QAAQ,GAAG,EACX,SAAS,kBAAkB,KAC1B,IAAI,GAAG,
|
|
1
|
+
{"version":3,"file":"lib.d.ts","sourceRoot":"","sources":["../../../src/extension-yjs/src/lib.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,CAAC,MAAM,KAAK,CAAC;AACzB,OAAO,EAAE,KAAK,UAAU,EAAE,MAAM,kBAAkB,CAAC;AACnD,OAAO,EAAE,KAAK,IAAI,EAAE,MAAM,mBAAmB,CAAC;AAI9C;;GAEG;AACH,KAAK,kBAAkB,GAAG,GAAG,CAAC,CAAC,CAAC,YAAY,CAAC,GAAG,CAAC,EAAE,IAAI,CAAC,CAAC;AA2BzD,eAAO,MAAM,OAAO,GAAI,MAAM,UAAU,EAAE,QAAG,EAAE,UAAK,SAYnD,CAAC;AAEF;;GAEG;AACH,eAAO,MAAM,kCAAkC,GAC7C,KAAK,MAAM,EACX,MAAM,CAAC,CAAC,WAAW,EACnB,SAAS,kBAAkB,KAC1B,GA+FF,CAAC;AAaF,eAAO,MAAM,kCAAkC,GAC7C,MAAM,CAAC,CAAC,GAAG,EACX,cAAc,CAAC,CAAC,WAAW,EAC3B,QAAQ,GAAG,EACX,SAAS,kBAAkB,KAC1B,IAAI,GAAG,MA2DT,CAAC"}
|
|
@@ -159,12 +159,10 @@ export const relativePositionToAbsolutePosition = (yDoc, documentType, relPos, m
|
|
|
159
159
|
pos += 1; // increase because we go out of n
|
|
160
160
|
}
|
|
161
161
|
while (type !== documentType && type._item !== null) {
|
|
162
|
-
// @ts-ignore
|
|
163
162
|
const parent = type._item.parent;
|
|
164
163
|
if (parent instanceof Y.ID || parent === null) {
|
|
165
164
|
continue;
|
|
166
165
|
}
|
|
167
|
-
// @ts-ignore
|
|
168
166
|
if (parent._item === null || !parent._item.deleted) {
|
|
169
167
|
pos += 1; // the start tag
|
|
170
168
|
let n = /** @type {Y.AbstractType} */ (parent)._first;
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import * as awarenessProtocol from 'y-protocols/awareness';
|
|
2
|
+
import { Plugin, PluginKey } from 'prosemirror-state';
|
|
3
|
+
import type { CoreEditor } from '../../editor/src/mod.js';
|
|
4
|
+
export declare const yPositionPluginKey: PluginKey<any>;
|
|
5
|
+
interface PositionPluginConfig {
|
|
6
|
+
getSelection?: (arg0: any) => any;
|
|
7
|
+
}
|
|
8
|
+
/**
|
|
9
|
+
* Default awareness state filter
|
|
10
|
+
*/
|
|
11
|
+
export declare const defaultAwarenessStateFilter: (currentClientId: number, userClientId: number, _user: any) => boolean;
|
|
12
|
+
export declare const yPositionPlugin: (awareness: awarenessProtocol.Awareness, editor: CoreEditor, { getSelection, }?: PositionPluginConfig, cursorStateField?: string) => Plugin<any>;
|
|
13
|
+
export {};
|
|
14
|
+
//# sourceMappingURL=yPositionPlugin.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"yPositionPlugin.d.ts","sourceRoot":"","sources":["../../../src/extension-yjs/src/yPositionPlugin.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,iBAAiB,MAAM,uBAAuB,CAAC;AAE3D,OAAO,EAAe,MAAM,EAAE,SAAS,EAAE,MAAM,mBAAmB,CAAC;AAEnE,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,yBAAyB,CAAC;AAc1D,eAAO,MAAM,kBAAkB,gBAAgC,CAAC;AAYhE,UAAU,oBAAoB;IAC5B,YAAY,CAAC,EAAE,CAAC,IAAI,EAAE,GAAG,KAAK,GAAG,CAAC;CACnC;AAED;;GAEG;AACH,eAAO,MAAM,2BAA2B,GACtC,iBAAiB,MAAM,EACvB,cAAc,MAAM,EACpB,OAAO,GAAG,KACT,OAA2C,CAAC;AAE/C,eAAO,MAAM,eAAe,GAC1B,WAAW,iBAAiB,CAAC,SAAS,EACtC,QAAQ,UAAU,EAClB,oBAEG,oBAAyB,EAC5B,mBAAkB,MAAiB,gBAiLpC,CAAC"}
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
import * as Y from 'yjs';
|
|
2
|
+
import { Plugin, PluginKey } from 'prosemirror-state';
|
|
3
|
+
import { remoteSelectionPluginKey } from '../../extension-basic-editor/src/remote-selection/ExtensionRemoteSelection.js';
|
|
4
|
+
import { absolutePositionToRelativePosition, relativePositionToAbsolutePosition, setMeta, } from './lib.js';
|
|
5
|
+
import { ySyncPluginKey } from './keys.js';
|
|
6
|
+
export const yPositionPluginKey = new PluginKey('yjs-position');
|
|
7
|
+
/**
|
|
8
|
+
* Default awareness state filter
|
|
9
|
+
*/
|
|
10
|
+
export const defaultAwarenessStateFilter = (currentClientId, userClientId, _user) => currentClientId !== userClientId;
|
|
11
|
+
export const yPositionPlugin = (awareness, editor, { getSelection = (state) => state.selection, } = {}, cursorStateField = 'cursor') => {
|
|
12
|
+
return new Plugin({
|
|
13
|
+
key: yPositionPluginKey,
|
|
14
|
+
view: (view) => {
|
|
15
|
+
const extension = editor.getExtension('remote-selection');
|
|
16
|
+
const awarenessListener = ({ added, updated, removed }) => {
|
|
17
|
+
const clients = added.concat(updated).concat(removed);
|
|
18
|
+
if (clients.findIndex((id) => id !== awareness.doc.clientID) ===
|
|
19
|
+
-1) {
|
|
20
|
+
return;
|
|
21
|
+
}
|
|
22
|
+
if (view.docView) {
|
|
23
|
+
setMeta(view, remoteSelectionPluginKey, {
|
|
24
|
+
remotePositionUpdated: true,
|
|
25
|
+
});
|
|
26
|
+
}
|
|
27
|
+
const remoteStates = [];
|
|
28
|
+
const ystate = ySyncPluginKey.getState(view.state);
|
|
29
|
+
const y = ystate.doc;
|
|
30
|
+
awareness.getStates().forEach((aw, clientId) => {
|
|
31
|
+
if (!defaultAwarenessStateFilter(y.clientID, clientId, aw)) {
|
|
32
|
+
return;
|
|
33
|
+
}
|
|
34
|
+
if (!aw.cursor) {
|
|
35
|
+
return;
|
|
36
|
+
}
|
|
37
|
+
let anchor = relativePositionToAbsolutePosition(y, ystate.type, Y.createRelativePositionFromJSON(aw.cursor.anchor), ystate.binding.mapping);
|
|
38
|
+
let head = relativePositionToAbsolutePosition(y, ystate.type, Y.createRelativePositionFromJSON(aw.cursor.head), ystate.binding.mapping);
|
|
39
|
+
if (anchor !== null && head !== null) {
|
|
40
|
+
remoteStates.push({
|
|
41
|
+
clientId,
|
|
42
|
+
user: {
|
|
43
|
+
name: aw.user?.name,
|
|
44
|
+
color: aw.user?.color,
|
|
45
|
+
colorLight: aw.user?.colorLight,
|
|
46
|
+
},
|
|
47
|
+
cursor: {
|
|
48
|
+
anchor,
|
|
49
|
+
head,
|
|
50
|
+
},
|
|
51
|
+
});
|
|
52
|
+
}
|
|
53
|
+
});
|
|
54
|
+
extension.setRemoteStates(remoteStates);
|
|
55
|
+
// view.dispatch({ annotations: [yRemoteSelectionsAnnotation.of([])] });
|
|
56
|
+
};
|
|
57
|
+
{
|
|
58
|
+
// if (
|
|
59
|
+
// ystate.snapshot != null || ystate.prevSnapshot != null ||
|
|
60
|
+
// ystate.binding.mapping.size === 0
|
|
61
|
+
// ) {
|
|
62
|
+
// // do not render cursors while snapshot is active
|
|
63
|
+
// return DecorationSet.create(state.doc, []);
|
|
64
|
+
// }
|
|
65
|
+
}
|
|
66
|
+
const updateAwareness = (selectionAnchor, selectionHead) => {
|
|
67
|
+
const ystate = ySyncPluginKey.getState(view.state);
|
|
68
|
+
const current = awareness.getLocalState() || {};
|
|
69
|
+
const anchor = absolutePositionToRelativePosition(selectionAnchor, ystate.type, ystate.binding.mapping);
|
|
70
|
+
const head = absolutePositionToRelativePosition(selectionHead, ystate.type, ystate.binding.mapping);
|
|
71
|
+
if (current.cursor == null ||
|
|
72
|
+
!Y.compareRelativePositions(Y.createRelativePositionFromJSON(current.cursor.anchor), anchor) ||
|
|
73
|
+
!Y.compareRelativePositions(Y.createRelativePositionFromJSON(current.cursor.head), head)) {
|
|
74
|
+
awareness.setLocalStateField(cursorStateField, {
|
|
75
|
+
anchor,
|
|
76
|
+
head,
|
|
77
|
+
});
|
|
78
|
+
}
|
|
79
|
+
};
|
|
80
|
+
const clearAwareness = () => {
|
|
81
|
+
const ystate = ySyncPluginKey.getState(view.state);
|
|
82
|
+
const current = awareness.getLocalState() || {};
|
|
83
|
+
if (current.cursor != null &&
|
|
84
|
+
relativePositionToAbsolutePosition(ystate.doc, ystate.type, Y.createRelativePositionFromJSON(current.cursor.anchor), ystate.binding.mapping) !== null) {
|
|
85
|
+
// delete cursor information if current cursor information is owned by this editor binding
|
|
86
|
+
awareness.setLocalStateField(cursorStateField, null);
|
|
87
|
+
}
|
|
88
|
+
};
|
|
89
|
+
const updateCursorInfo = () => {
|
|
90
|
+
if (view.hasFocus()) {
|
|
91
|
+
const selection = getSelection(view.state);
|
|
92
|
+
updateAwareness(selection.anchor, selection.head);
|
|
93
|
+
}
|
|
94
|
+
else {
|
|
95
|
+
// clearAwareness();
|
|
96
|
+
}
|
|
97
|
+
};
|
|
98
|
+
const localPositionChangedListener = (event) => {
|
|
99
|
+
const { detail } = event;
|
|
100
|
+
updateAwareness(detail.anchor, detail.head);
|
|
101
|
+
};
|
|
102
|
+
editor.addEventListener('localPositionChanged', localPositionChangedListener);
|
|
103
|
+
awareness.on('change', awarenessListener);
|
|
104
|
+
view.dom.addEventListener('focusin', updateCursorInfo);
|
|
105
|
+
view.dom.addEventListener('focusout', updateCursorInfo);
|
|
106
|
+
return {
|
|
107
|
+
update: updateCursorInfo,
|
|
108
|
+
destroy: () => {
|
|
109
|
+
view.dom.removeEventListener('focusin', updateCursorInfo);
|
|
110
|
+
view.dom.removeEventListener('focusout', updateCursorInfo);
|
|
111
|
+
awareness.off('change', awarenessListener);
|
|
112
|
+
awareness.setLocalStateField(cursorStateField, null);
|
|
113
|
+
editor.removeEventListener('localPositionChanged', localPositionChangedListener);
|
|
114
|
+
},
|
|
115
|
+
};
|
|
116
|
+
},
|
|
117
|
+
updateCursorInfo(state) {
|
|
118
|
+
throw new Error('TODO: merge with updateCursorInfo above');
|
|
119
|
+
},
|
|
120
|
+
});
|
|
121
|
+
};
|
|
@@ -19,7 +19,7 @@ interface YSyncOpts {
|
|
|
19
19
|
colorMapping?: Map<string, ColorDef>;
|
|
20
20
|
permanentUserData?: Y.PermanentUserData | null;
|
|
21
21
|
mapping?: ProsemirrorMapping;
|
|
22
|
-
onFirstRender?:
|
|
22
|
+
onFirstRender?: () => void;
|
|
23
23
|
}
|
|
24
24
|
/**
|
|
25
25
|
* This plugin listens to changes in prosemirror view and keeps yXmlState and view in sync.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ySyncPlugin.d.ts","sourceRoot":"","sources":["../../../src/extension-yjs/src/ySyncPlugin.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"ySyncPlugin.d.ts","sourceRoot":"","sources":["../../../src/extension-yjs/src/ySyncPlugin.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
// deno-lint-ignore-file no-window
|
|
1
2
|
import * as Y from 'yjs';
|
|
2
3
|
import { createMutex } from 'lib0/mutex';
|
|
3
4
|
import * as PModel from 'prosemirror-model';
|
|
@@ -12,6 +13,7 @@ import * as environment from 'lib0/environment';
|
|
|
12
13
|
import * as dom from 'lib0/dom';
|
|
13
14
|
import * as eventloop from 'lib0/eventloop';
|
|
14
15
|
import * as map from 'lib0/map';
|
|
16
|
+
import { remoteSelectionPluginKey } from '../../extension-basic-editor/src/remote-selection/ExtensionRemoteSelection.js';
|
|
15
17
|
import { ySyncPluginKey, yUndoPluginKey } from './keys.js';
|
|
16
18
|
import * as utils from './utils.js';
|
|
17
19
|
import { absolutePositionToRelativePosition, relativePositionToAbsolutePosition, } from './lib.js';
|
|
@@ -34,7 +36,7 @@ const getUserColor = (colorMapping, colors, user) => {
|
|
|
34
36
|
}
|
|
35
37
|
colorMapping.set(user, random.oneOf(colors));
|
|
36
38
|
}
|
|
37
|
-
return colorMapping.get(user);
|
|
39
|
+
return colorMapping.get(user) || defaultColors[0];
|
|
38
40
|
};
|
|
39
41
|
/**
|
|
40
42
|
* This plugin listens to changes in prosemirror view and keeps yXmlState and view in sync.
|
|
@@ -350,7 +352,6 @@ export class ProsemirrorBinding {
|
|
|
350
352
|
}
|
|
351
353
|
const state = this.prosemirrorView.state;
|
|
352
354
|
const fragmentContent = this.type.toArray().map((t) => createNodeFromYElement(t, state.schema, this)).filter((n) => n !== null);
|
|
353
|
-
// @ts-ignore
|
|
354
355
|
const _tr = state.tr.setMeta('addToHistory', false);
|
|
355
356
|
const tr = _tr.replace(0, state.doc.content.size, new PModel.Slice(PModel.Fragment.from(fragmentContent), 0, 0));
|
|
356
357
|
tr.setMeta(ySyncPluginKey, { snapshot: null, prevSnapshot: null });
|
|
@@ -372,7 +373,6 @@ export class ProsemirrorBinding {
|
|
|
372
373
|
: state.selection;
|
|
373
374
|
const fragmentContent = this.type.toArray().map((t) => createNodeFromYElement(
|
|
374
375
|
/** @type {Y.XmlElement} */ (t), state.schema, this)).filter((n) => n !== null);
|
|
375
|
-
// @ts-ignore
|
|
376
376
|
const _tr = state.tr.setMeta('addToHistory', false);
|
|
377
377
|
const tr = _tr.replace(0, state.doc.content.size, new PModel.Slice(PModel.Fragment.from(fragmentContent), 0, 0));
|
|
378
378
|
if (sel) {
|
|
@@ -385,7 +385,8 @@ export class ProsemirrorBinding {
|
|
|
385
385
|
const clampedHead = math.min(math.max(sel.head, 0), tr.doc.content.size);
|
|
386
386
|
tr.setSelection(TextSelection.create(tr.doc, clampedAnchor, clampedHead));
|
|
387
387
|
}
|
|
388
|
-
this.prosemirrorView.dispatch(tr.setMeta(ySyncPluginKey, { isChangeOrigin: true, binding: this })
|
|
388
|
+
this.prosemirrorView.dispatch(tr.setMeta(ySyncPluginKey, { isChangeOrigin: true, binding: this })
|
|
389
|
+
.setMeta(remoteSelectionPluginKey, { isChangeOrigin: true }));
|
|
389
390
|
});
|
|
390
391
|
}
|
|
391
392
|
_renderSnapshot(snapshot, prevSnapshot, pluginState) {
|
|
@@ -454,6 +455,9 @@ export class ProsemirrorBinding {
|
|
|
454
455
|
};
|
|
455
456
|
// Create document fragment and render
|
|
456
457
|
const fragmentContent = Y.typeListToArraySnapshot(historyType, new Y.Snapshot(prevSnapshot.ds, snapshot.sv)).map((t) => {
|
|
458
|
+
if (!this.prosemirrorView) {
|
|
459
|
+
return null;
|
|
460
|
+
}
|
|
457
461
|
if (!t._item.deleted || isVisible(t._item, snapshot) ||
|
|
458
462
|
isVisible(t._item, prevSnapshot)) {
|
|
459
463
|
return createNodeFromYElement(t, this.prosemirrorView.state.schema, { mapping: new Map(), isOMark: new Map() }, snapshot, prevSnapshot, computeYChange);
|
|
@@ -464,9 +468,11 @@ export class ProsemirrorBinding {
|
|
|
464
468
|
return null;
|
|
465
469
|
}
|
|
466
470
|
}).filter((n) => n !== null);
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
471
|
+
if (this.prosemirrorView) {
|
|
472
|
+
const _tr = this.prosemirrorView.state.tr.setMeta('addToHistory', false);
|
|
473
|
+
const tr = _tr.replace(0, this.prosemirrorView.state.doc.content.size, new PModel.Slice(PModel.Fragment.from(fragmentContent), 0, 0));
|
|
474
|
+
this.prosemirrorView.dispatch(tr.setMeta(ySyncPluginKey, { isChangeOrigin: true }));
|
|
475
|
+
}
|
|
470
476
|
}, ySyncPluginKey);
|
|
471
477
|
});
|
|
472
478
|
}
|
|
@@ -643,7 +649,6 @@ const createTextNodesFromYText = (text, schema, _meta, snapshot, prevSnapshot, c
|
|
|
643
649
|
}, ySyncPluginKey);
|
|
644
650
|
return null;
|
|
645
651
|
}
|
|
646
|
-
// @ts-ignore
|
|
647
652
|
return nodes;
|
|
648
653
|
};
|
|
649
654
|
/**
|
|
@@ -652,7 +657,6 @@ const createTextNodesFromYText = (text, schema, _meta, snapshot, prevSnapshot, c
|
|
|
652
657
|
const createTypeFromTextNodes = (nodes, meta) => {
|
|
653
658
|
const type = new Y.XmlText();
|
|
654
659
|
const delta = nodes.map((node) => ({
|
|
655
|
-
// @ts-ignore
|
|
656
660
|
insert: node.text,
|
|
657
661
|
attributes: marksToAttributes(node.marks, meta),
|
|
658
662
|
}));
|
package/package.json
CHANGED
|
@@ -1,29 +0,0 @@
|
|
|
1
|
-
import * as awarenessProtocol from 'y-protocols/awareness';
|
|
2
|
-
import { Plugin } from 'prosemirror-state';
|
|
3
|
-
import { type DecorationAttrs } from 'prosemirror-view';
|
|
4
|
-
/**
|
|
5
|
-
* Default awareness state filter
|
|
6
|
-
*/
|
|
7
|
-
export declare const defaultAwarenessStateFilter: (currentClientId: number, userClientId: number, _user: any) => boolean;
|
|
8
|
-
/**
|
|
9
|
-
* Default generator for a cursor element
|
|
10
|
-
*/
|
|
11
|
-
export declare const defaultCursorBuilder: (user: any) => HTMLElement;
|
|
12
|
-
/**
|
|
13
|
-
* Default generator for the selection attributes
|
|
14
|
-
*/
|
|
15
|
-
export declare const defaultSelectionBuilder: (user: any) => DecorationAttrs;
|
|
16
|
-
export declare const createDecorations: (state: any, awareness: awarenessProtocol.Awareness, awarenessFilter: (arg0: number, arg1: number, arg2: any) => boolean, createCursor: (user: {
|
|
17
|
-
name: string;
|
|
18
|
-
color: string;
|
|
19
|
-
}, clientId: number) => Element, createSelection: (user: {
|
|
20
|
-
name: string;
|
|
21
|
-
color: string;
|
|
22
|
-
}, clientId: number) => DecorationAttrs) => any;
|
|
23
|
-
export declare const yCursorPlugin: (awareness: awarenessProtocol.Awareness, { awarenessStateFilter, cursorBuilder, selectionBuilder, getSelection, }?: {
|
|
24
|
-
awarenessStateFilter?: (arg0: any, arg1: any, arg2: any) => boolean;
|
|
25
|
-
cursorBuilder?: (user: any, clientId: number) => HTMLElement;
|
|
26
|
-
selectionBuilder?: (user: any, clientId: number) => DecorationAttrs;
|
|
27
|
-
getSelection?: (arg0: any) => any;
|
|
28
|
-
}, cursorStateField?: string) => Plugin<any>;
|
|
29
|
-
//# sourceMappingURL=yCursorPlugin.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"yCursorPlugin.d.ts","sourceRoot":"","sources":["../../../src/extension-yjs/src/yCursorPlugin.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,iBAAiB,MAAM,uBAAuB,CAAC;AAG3D,OAAO,EAAe,MAAM,EAAE,MAAM,mBAAmB,CAAC;AACxD,OAAO,EAAE,KAAK,eAAe,EAAE,MAAM,kBAAkB,CAAC;AAYxD;;GAEG;AACH,eAAO,MAAM,2BAA2B,GACtC,iBAAiB,MAAM,EACvB,cAAc,MAAM,EACpB,OAAO,GAAG,KACT,OAA2C,CAAC;AAE/C;;GAEG;AACH,eAAO,MAAM,oBAAoB,GAAI,MAAM,GAAG,KAAG,WAahD,CAAC;AAEF;;GAEG;AACH,eAAO,MAAM,uBAAuB,GAAI,MAAM,GAAG,KAAG,eAKnD,CAAC;AAIF,eAAO,MAAM,iBAAiB,GAC5B,OAAO,GAAG,EACV,WAAW,iBAAiB,CAAC,SAAS,EACtC,iBAAiB,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,GAAG,KAAK,OAAO,EACnE,cAAc,CACZ,IAAI,EAAE;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,EACrC,QAAQ,EAAE,MAAM,KACb,OAAO,EACZ,iBAAiB,CACf,IAAI,EAAE;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,EACrC,QAAQ,EAAE,MAAM,KACb,eAAe,KACnB,GA6DF,CAAC;AAEF,eAAO,MAAM,aAAa,GACxB,WAAW,iBAAiB,CAAC,SAAS,EACtC,2EAKG;IACD,oBAAoB,CAAC,EAAE,CAAC,IAAI,EAAE,GAAG,EAAE,IAAI,EAAE,GAAG,EAAE,IAAI,EAAE,GAAG,KAAK,OAAO,CAAC;IACpE,aAAa,CAAC,EAAE,CAAC,IAAI,EAAE,GAAG,EAAE,QAAQ,EAAE,MAAM,KAAK,WAAW,CAAC;IAC7D,gBAAgB,CAAC,EAAE,CAAC,IAAI,EAAE,GAAG,EAAE,QAAQ,EAAE,MAAM,KAAK,eAAe,CAAC;IACpE,YAAY,CAAC,EAAE,CAAC,IAAI,EAAE,GAAG,KAAK,GAAG,CAAC;CAC9B,EACN,mBAAkB,MAAiB,gBAsJpC,CAAC"}
|
|
@@ -1,177 +0,0 @@
|
|
|
1
|
-
import * as Y from 'yjs';
|
|
2
|
-
import { Decoration, DecorationSet } from 'prosemirror-view'; // eslint-disable-line
|
|
3
|
-
import { Plugin } from 'prosemirror-state'; // eslint-disable-line
|
|
4
|
-
import { absolutePositionToRelativePosition, relativePositionToAbsolutePosition, setMeta, } from './lib.js';
|
|
5
|
-
import { yCursorPluginKey, ySyncPluginKey } from './keys.js';
|
|
6
|
-
import * as math from 'lib0/math';
|
|
7
|
-
/**
|
|
8
|
-
* Default awareness state filter
|
|
9
|
-
*/
|
|
10
|
-
export const defaultAwarenessStateFilter = (currentClientId, userClientId, _user) => currentClientId !== userClientId;
|
|
11
|
-
/**
|
|
12
|
-
* Default generator for a cursor element
|
|
13
|
-
*/
|
|
14
|
-
export const defaultCursorBuilder = (user) => {
|
|
15
|
-
const cursor = document.createElement('span');
|
|
16
|
-
cursor.classList.add('kb-yjs__cursor');
|
|
17
|
-
cursor.setAttribute('style', `border-color: ${user.color};`);
|
|
18
|
-
const userDiv = document.createElement('div');
|
|
19
|
-
userDiv.setAttribute('style', `background-color: ${user.color}`);
|
|
20
|
-
userDiv.insertBefore(document.createTextNode(user.name), null);
|
|
21
|
-
const nonbreakingSpace1 = document.createTextNode('\u2060');
|
|
22
|
-
const nonbreakingSpace2 = document.createTextNode('\u2060');
|
|
23
|
-
cursor.insertBefore(nonbreakingSpace1, null);
|
|
24
|
-
cursor.insertBefore(userDiv, null);
|
|
25
|
-
cursor.insertBefore(nonbreakingSpace2, null);
|
|
26
|
-
return cursor;
|
|
27
|
-
};
|
|
28
|
-
/**
|
|
29
|
-
* Default generator for the selection attributes
|
|
30
|
-
*/
|
|
31
|
-
export const defaultSelectionBuilder = (user) => {
|
|
32
|
-
return {
|
|
33
|
-
style: `background-color: ${user.color}70`,
|
|
34
|
-
class: 'kb-yjs__selection',
|
|
35
|
-
};
|
|
36
|
-
};
|
|
37
|
-
const rxValidColor = /^#[0-9a-fA-F]{6}$/;
|
|
38
|
-
export const createDecorations = (state, awareness, awarenessFilter, createCursor, createSelection) => {
|
|
39
|
-
const ystate = ySyncPluginKey.getState(state);
|
|
40
|
-
const y = ystate.doc;
|
|
41
|
-
const decorations = [];
|
|
42
|
-
if (ystate.snapshot != null || ystate.prevSnapshot != null ||
|
|
43
|
-
ystate.binding.mapping.size === 0) {
|
|
44
|
-
// do not render cursors while snapshot is active
|
|
45
|
-
return DecorationSet.create(state.doc, []);
|
|
46
|
-
}
|
|
47
|
-
awareness.getStates().forEach((aw, clientId) => {
|
|
48
|
-
if (!awarenessFilter(y.clientID, clientId, aw)) {
|
|
49
|
-
return;
|
|
50
|
-
}
|
|
51
|
-
if (aw.cursor != null) {
|
|
52
|
-
const user = aw.user || {};
|
|
53
|
-
if (user.color == null) {
|
|
54
|
-
user.color = '#ffa500';
|
|
55
|
-
}
|
|
56
|
-
else if (!rxValidColor.test(user.color)) {
|
|
57
|
-
// We only support 6-digit RGB colors in y-prosemirror
|
|
58
|
-
console.warn('A user uses an unsupported color format', user);
|
|
59
|
-
}
|
|
60
|
-
if (user.name == null) {
|
|
61
|
-
user.name = `User: ${clientId}`;
|
|
62
|
-
}
|
|
63
|
-
let anchor = relativePositionToAbsolutePosition(y, ystate.type, Y.createRelativePositionFromJSON(aw.cursor.anchor), ystate.binding.mapping);
|
|
64
|
-
let head = relativePositionToAbsolutePosition(y, ystate.type, Y.createRelativePositionFromJSON(aw.cursor.head), ystate.binding.mapping);
|
|
65
|
-
if (anchor !== null && head !== null) {
|
|
66
|
-
const maxsize = math.max(state.doc.content.size - 1, 0);
|
|
67
|
-
anchor = math.min(anchor, maxsize);
|
|
68
|
-
head = math.min(head, maxsize);
|
|
69
|
-
decorations.push(Decoration.widget(head, () => createCursor(user, clientId), {
|
|
70
|
-
key: clientId + '',
|
|
71
|
-
side: 10,
|
|
72
|
-
}));
|
|
73
|
-
const from = math.min(anchor, head);
|
|
74
|
-
const to = math.max(anchor, head);
|
|
75
|
-
decorations.push(Decoration.inline(from, to, createSelection(user, clientId), {
|
|
76
|
-
inclusiveEnd: true,
|
|
77
|
-
inclusiveStart: false,
|
|
78
|
-
}));
|
|
79
|
-
}
|
|
80
|
-
}
|
|
81
|
-
});
|
|
82
|
-
return DecorationSet.create(state.doc, decorations);
|
|
83
|
-
};
|
|
84
|
-
export const yCursorPlugin = (awareness, { awarenessStateFilter = defaultAwarenessStateFilter, cursorBuilder = defaultCursorBuilder, selectionBuilder = defaultSelectionBuilder, getSelection = (state) => state.selection, } = {}, cursorStateField = 'cursor') => {
|
|
85
|
-
return new Plugin({
|
|
86
|
-
key: yCursorPluginKey,
|
|
87
|
-
state: {
|
|
88
|
-
init(_, state) {
|
|
89
|
-
return createDecorations(state, awareness, awarenessStateFilter, cursorBuilder, selectionBuilder);
|
|
90
|
-
},
|
|
91
|
-
apply(tr, prevState, _oldState, newState) {
|
|
92
|
-
const ystate = ySyncPluginKey.getState(newState);
|
|
93
|
-
const yCursorState = tr.getMeta(yCursorPluginKey);
|
|
94
|
-
if ((ystate && ystate.isChangeOrigin) ||
|
|
95
|
-
(yCursorState && yCursorState.awarenessUpdated)) {
|
|
96
|
-
return createDecorations(newState, awareness, awarenessStateFilter, cursorBuilder, selectionBuilder);
|
|
97
|
-
}
|
|
98
|
-
return prevState.map(tr.mapping, tr.doc);
|
|
99
|
-
},
|
|
100
|
-
},
|
|
101
|
-
props: {
|
|
102
|
-
decorations: (state) => {
|
|
103
|
-
return yCursorPluginKey.getState(state);
|
|
104
|
-
},
|
|
105
|
-
},
|
|
106
|
-
view: (view) => {
|
|
107
|
-
const awarenessListener = () => {
|
|
108
|
-
// @ts-ignore
|
|
109
|
-
if (view.docView) {
|
|
110
|
-
setMeta(view, yCursorPluginKey, { awarenessUpdated: true });
|
|
111
|
-
}
|
|
112
|
-
};
|
|
113
|
-
const updateCursorInfo = () => {
|
|
114
|
-
const ystate = ySyncPluginKey.getState(view.state);
|
|
115
|
-
// @note We make implicit checks when checking for the cursor property
|
|
116
|
-
const current = awareness.getLocalState() || {};
|
|
117
|
-
const selection = getSelection(view.state);
|
|
118
|
-
if (view.hasFocus()) {
|
|
119
|
-
const selection = getSelection(view.state);
|
|
120
|
-
/**
|
|
121
|
-
* @type {Y.RelativePosition}
|
|
122
|
-
*/
|
|
123
|
-
const anchor = absolutePositionToRelativePosition(selection.anchor, ystate.type, ystate.binding.mapping);
|
|
124
|
-
/**
|
|
125
|
-
* @type {Y.RelativePosition}
|
|
126
|
-
*/
|
|
127
|
-
const head = absolutePositionToRelativePosition(selection.head, ystate.type, ystate.binding.mapping);
|
|
128
|
-
if (current.cursor == null ||
|
|
129
|
-
!Y.compareRelativePositions(Y.createRelativePositionFromJSON(current.cursor.anchor), anchor) ||
|
|
130
|
-
!Y.compareRelativePositions(Y.createRelativePositionFromJSON(current.cursor.head), head)) {
|
|
131
|
-
awareness.setLocalStateField(cursorStateField, {
|
|
132
|
-
anchor,
|
|
133
|
-
head,
|
|
134
|
-
});
|
|
135
|
-
awareness.setLocalStateField('cm-cursor', null);
|
|
136
|
-
}
|
|
137
|
-
}
|
|
138
|
-
else if (current.cursor != null &&
|
|
139
|
-
relativePositionToAbsolutePosition(ystate.doc, ystate.type, Y.createRelativePositionFromJSON(current.cursor.anchor), ystate.binding.mapping) !== null) {
|
|
140
|
-
// delete cursor information if current cursor information is owned by this editor binding
|
|
141
|
-
awareness.setLocalStateField(cursorStateField, null);
|
|
142
|
-
}
|
|
143
|
-
};
|
|
144
|
-
awareness.on('change', awarenessListener);
|
|
145
|
-
view.dom.addEventListener('focusin', updateCursorInfo);
|
|
146
|
-
view.dom.addEventListener('focusout', updateCursorInfo);
|
|
147
|
-
return {
|
|
148
|
-
update: updateCursorInfo,
|
|
149
|
-
destroy: () => {
|
|
150
|
-
view.dom.removeEventListener('focusin', updateCursorInfo);
|
|
151
|
-
view.dom.removeEventListener('focusout', updateCursorInfo);
|
|
152
|
-
awareness.off('change', awarenessListener);
|
|
153
|
-
awareness.setLocalStateField(cursorStateField, null);
|
|
154
|
-
},
|
|
155
|
-
};
|
|
156
|
-
},
|
|
157
|
-
updateCursorInfo(state) {
|
|
158
|
-
const ystate = ySyncPluginKey.getState(state);
|
|
159
|
-
// @note We make implicit checks when checking for the cursor property
|
|
160
|
-
const current = awareness.getLocalState() || {};
|
|
161
|
-
const selection = getSelection(state);
|
|
162
|
-
const anchor = absolutePositionToRelativePosition(selection.anchor, ystate.type, ystate.binding.mapping);
|
|
163
|
-
const head = absolutePositionToRelativePosition(selection.head, ystate.type, ystate.binding.mapping);
|
|
164
|
-
if (current.cursor == null ||
|
|
165
|
-
!Y.compareRelativePositions(Y.createRelativePositionFromJSON(current.cursor.anchor), anchor) ||
|
|
166
|
-
!Y.compareRelativePositions(Y.createRelativePositionFromJSON(current.cursor.head), head)) {
|
|
167
|
-
awareness.setLocalStateField(cursorStateField, {
|
|
168
|
-
anchor,
|
|
169
|
-
head,
|
|
170
|
-
});
|
|
171
|
-
awareness.setLocalStateField('cm-cursor', null);
|
|
172
|
-
}
|
|
173
|
-
// delete cursor information if current cursor information is owned by this editor binding
|
|
174
|
-
// awareness.setLocalStateField(cursorStateField, null);
|
|
175
|
-
},
|
|
176
|
-
});
|
|
177
|
-
};
|