@kerebron/extension-yjs 0.3.1 → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (80) hide show
  1. package/LICENSE +23 -0
  2. package/README.md +82 -0
  3. package/esm/editor/src/CoreEditor.d.ts +13 -4
  4. package/esm/editor/src/CoreEditor.d.ts.map +1 -1
  5. package/esm/editor/src/CoreEditor.js +64 -12
  6. package/esm/editor/src/Extension.d.ts +6 -1
  7. package/esm/editor/src/Extension.d.ts.map +1 -1
  8. package/esm/editor/src/Extension.js +21 -1
  9. package/esm/editor/src/ExtensionManager.d.ts +5 -6
  10. package/esm/editor/src/ExtensionManager.d.ts.map +1 -1
  11. package/esm/editor/src/ExtensionManager.js +43 -55
  12. package/esm/editor/src/Mark.d.ts +3 -0
  13. package/esm/editor/src/Mark.d.ts.map +1 -1
  14. package/esm/editor/src/Mark.js +11 -0
  15. package/esm/editor/src/Node.d.ts +5 -2
  16. package/esm/editor/src/Node.d.ts.map +1 -1
  17. package/esm/editor/src/Node.js +13 -2
  18. package/esm/editor/src/commands/CommandManager.d.ts +13 -6
  19. package/esm/editor/src/commands/CommandManager.d.ts.map +1 -1
  20. package/esm/editor/src/commands/CommandManager.js +59 -2
  21. package/esm/editor/src/commands/baseCommandFactories.d.ts +3 -0
  22. package/esm/editor/src/commands/baseCommandFactories.d.ts.map +1 -0
  23. package/esm/editor/src/commands/baseCommandFactories.js +836 -0
  24. package/esm/editor/src/commands/keyCommandFactories.d.ts +3 -0
  25. package/esm/editor/src/commands/keyCommandFactories.d.ts.map +1 -0
  26. package/esm/editor/src/commands/keyCommandFactories.js +10 -0
  27. package/esm/editor/src/commands/mod.d.ts +5 -53
  28. package/esm/editor/src/commands/mod.d.ts.map +1 -1
  29. package/esm/editor/src/commands/mod.js +14 -821
  30. package/esm/editor/src/commands/replaceCommandFactories.d.ts +3 -0
  31. package/esm/editor/src/commands/replaceCommandFactories.d.ts.map +1 -0
  32. package/esm/editor/src/commands/replaceCommandFactories.js +94 -0
  33. package/esm/editor/src/commands/types.d.ts +18 -0
  34. package/esm/editor/src/commands/types.d.ts.map +1 -0
  35. package/esm/editor/src/commands/types.js +1 -0
  36. package/esm/editor/src/mod.d.ts +2 -0
  37. package/esm/editor/src/mod.d.ts.map +1 -1
  38. package/esm/editor/src/mod.js +2 -0
  39. package/esm/editor/src/plugins/TrackSelecionPlugin.d.ts +6 -0
  40. package/esm/editor/src/plugins/TrackSelecionPlugin.d.ts.map +1 -0
  41. package/esm/editor/src/plugins/TrackSelecionPlugin.js +24 -0
  42. package/esm/editor/src/types.d.ts +20 -2
  43. package/esm/editor/src/types.d.ts.map +1 -1
  44. package/esm/editor/src/ui.d.ts +15 -0
  45. package/esm/editor/src/ui.d.ts.map +1 -0
  46. package/esm/editor/src/ui.js +16 -0
  47. package/esm/editor/src/utilities/SmartOutput.d.ts +9 -7
  48. package/esm/editor/src/utilities/SmartOutput.d.ts.map +1 -1
  49. package/esm/editor/src/utilities/SmartOutput.js +35 -20
  50. package/esm/extension-basic-editor/src/remote-selection/ExtensionRemoteSelection.d.ts +24 -0
  51. package/esm/extension-basic-editor/src/remote-selection/ExtensionRemoteSelection.d.ts.map +1 -0
  52. package/esm/extension-basic-editor/src/remote-selection/ExtensionRemoteSelection.js +35 -0
  53. package/esm/extension-basic-editor/src/remote-selection/remoteSelectionPlugin.d.ts +25 -0
  54. package/esm/extension-basic-editor/src/remote-selection/remoteSelectionPlugin.d.ts.map +1 -0
  55. package/esm/extension-basic-editor/src/remote-selection/remoteSelectionPlugin.js +96 -0
  56. package/esm/extension-yjs/src/ExtensionYjs.d.ts +9 -4
  57. package/esm/extension-yjs/src/ExtensionYjs.d.ts.map +1 -1
  58. package/esm/extension-yjs/src/ExtensionYjs.js +13 -7
  59. package/esm/extension-yjs/src/convertUtils.d.ts +59 -0
  60. package/esm/extension-yjs/src/convertUtils.d.ts.map +1 -0
  61. package/esm/extension-yjs/src/convertUtils.js +88 -0
  62. package/esm/extension-yjs/src/keys.d.ts +0 -4
  63. package/esm/extension-yjs/src/keys.d.ts.map +1 -1
  64. package/esm/extension-yjs/src/keys.js +0 -4
  65. package/esm/extension-yjs/src/lib.d.ts +15 -0
  66. package/esm/extension-yjs/src/lib.d.ts.map +1 -0
  67. package/esm/extension-yjs/src/lib.js +24 -103
  68. package/esm/extension-yjs/src/yPositionPlugin.d.ts +14 -0
  69. package/esm/extension-yjs/src/yPositionPlugin.d.ts.map +1 -0
  70. package/esm/extension-yjs/src/yPositionPlugin.js +121 -0
  71. package/esm/extension-yjs/src/ySyncPlugin.d.ts +37 -30
  72. package/esm/extension-yjs/src/ySyncPlugin.d.ts.map +1 -1
  73. package/esm/extension-yjs/src/ySyncPlugin.js +99 -77
  74. package/esm/extension-yjs/src/yUndoPlugin.d.ts.map +1 -1
  75. package/esm/extension-yjs/src/yUndoPlugin.js +18 -15
  76. package/esm/package.json +3 -0
  77. package/package.json +2 -2
  78. package/esm/extension-yjs/src/yCursorPlugin.d.ts +0 -29
  79. package/esm/extension-yjs/src/yCursorPlugin.d.ts.map +0 -1
  80. 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 { type CoreEditor, Extension } from '../../editor/src/mod.js';
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
- doc: any;
11
+ conflicts: string[];
12
+ requires: string[];
8
13
  getCommandFactories(): Partial<CommandFactories>;
9
14
  getKeyboardShortcuts(): Partial<CommandShortcuts>;
10
- getProseMirrorPlugins(editor: CoreEditor, schema: Schema): Plugin[];
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;AAChD,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,mBAAmB,CAAC;AAChD,OAAO,EAAE,KAAK,UAAU,EAAE,SAAS,EAAE,MAAM,yBAAyB,CAAC;AAErE,OAAO,KAAK,EACV,gBAAgB,EAChB,gBAAgB,EACjB,MAAM,kCAAkC,CAAC;AAM1C,qBAAa,YAAa,SAAQ,SAAS;IACzC,IAAI,SAAS;IACb,GAAG,EAAE,GAAG,CAAC;IAGA,mBAAmB,IAAI,OAAO,CAAC,gBAAgB,CAAC;IAOhD,oBAAoB,IAAI,OAAO,CAAC,gBAAgB,CAAC;IAOjD,qBAAqB,CAAC,MAAM,EAAE,UAAU,EAAE,MAAM,EAAE,MAAM,GAAG,MAAM,EAAE;CAY7E"}
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,8 +1,8 @@
1
1
  import { Extension } from '../../editor/src/mod.js';
2
2
  import { ySyncPlugin } from './ySyncPlugin.js';
3
- import { yCursorPlugin } from './yCursorPlugin.js';
3
+ import { yPositionPlugin } from './yPositionPlugin.js';
4
4
  import { redo, undo, yUndoPlugin } from './yUndoPlugin.js';
5
- import { initProseMirrorDoc } from './lib.js';
5
+ import { initProseMirrorDoc } from './convertUtils.js';
6
6
  export class ExtensionYjs extends Extension {
7
7
  constructor() {
8
8
  super(...arguments);
@@ -12,11 +12,17 @@ export class ExtensionYjs extends Extension {
12
12
  writable: true,
13
13
  value: 'yjs'
14
14
  });
15
- Object.defineProperty(this, "doc", {
15
+ Object.defineProperty(this, "conflicts", {
16
16
  enumerable: true,
17
17
  configurable: true,
18
18
  writable: true,
19
- value: void 0
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(editor, schema) {
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
- yCursorPlugin(this.config.provider.awareness),
47
+ yPositionPlugin(this.config.provider.awareness, this.editor),
42
48
  yUndoPlugin(),
43
49
  ];
44
50
  }
@@ -0,0 +1,59 @@
1
+ import * as Y from 'yjs';
2
+ import { Fragment, Node, type Schema } from 'prosemirror-model';
3
+ import { BindingMetadata } from './ySyncPlugin.js';
4
+ export declare const createEmptyMeta: () => BindingMetadata;
5
+ /**
6
+ * Utility function for converting an Y.Fragment to a ProseMirror fragment.
7
+ */
8
+ export declare const yXmlFragmentToProseMirrorFragment: (yXmlFragment: Y.XmlFragment, schema: Schema) => Fragment;
9
+ /**
10
+ * Utility function for converting an Y.Fragment to a ProseMirror node.
11
+ */
12
+ export declare const yXmlFragmentToProseMirrorRootNode: (yXmlFragment: Y.XmlFragment, schema: Schema) => Node;
13
+ /**
14
+ * The initial ProseMirror content should be supplied by Yjs. This function transforms a Y.Fragment
15
+ * to a ProseMirror Doc node and creates a mapping that is used by the sync plugin.
16
+ *
17
+ * @todo deprecate mapping property
18
+ */
19
+ export declare const initProseMirrorDoc: (yXmlFragment: Y.XmlFragment, schema: Schema) => {
20
+ doc: Node;
21
+ meta: BindingMetadata;
22
+ mapping: Map<Y.AbstractType<any>, Node | Node[]>;
23
+ };
24
+ /**
25
+ * Utility method to convert a Prosemirror Doc Node into a Y.Doc.
26
+ *
27
+ * This can be used when importing existing content to Y.Doc for the first time,
28
+ * note that this should not be used to rehydrate a Y.Doc from a database once
29
+ * collaboration has begun as all history will be lost
30
+ */
31
+ export declare function prosemirrorToYDoc(doc: Node, xmlFragment?: string): Y.Doc;
32
+ /**
33
+ * Utility method to update an empty Y.XmlFragment with content from a Prosemirror Doc Node.
34
+ *
35
+ * This can be used when importing existing content to Y.Doc for the first time,
36
+ * note that this should not be used to rehydrate a Y.Doc from a database once
37
+ * collaboration has begun as all history will be lost
38
+ *
39
+ * Note: The Y.XmlFragment does not need to be part of a Y.Doc document at the time that this
40
+ * method is called, but it must be added before any other operations are performed on it.
41
+ */
42
+ export declare function prosemirrorToYXmlFragment(doc: Node, xmlFragment: Y.XmlFragment): Y.XmlFragment;
43
+ /**
44
+ * Utility method to convert Prosemirror compatible JSON into a Y.Doc.
45
+ *
46
+ * This can be used when importing existing content to Y.Doc for the first time,
47
+ * note that this should not be used to rehydrate a Y.Doc from a database once
48
+ * collaboration has begun as all history will be lost
49
+ */
50
+ export declare function prosemirrorJSONToYDoc(schema: Schema, state: any, xmlFragment?: string): Y.Doc;
51
+ /**
52
+ * Utility method to convert Prosemirror compatible JSON to a Y.XmlFragment
53
+ *
54
+ * This can be used when importing existing content to Y.Doc for the first time,
55
+ * note that this should not be used to rehydrate a Y.Doc from a database once
56
+ * collaboration has begun as all history will be lost
57
+ */
58
+ export declare function prosemirrorJSONToYXmlFragment(schema: Schema, state: any, xmlFragment: Y.XmlFragment): Y.XmlFragment;
59
+ //# sourceMappingURL=convertUtils.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"convertUtils.d.ts","sourceRoot":"","sources":["../../../src/extension-yjs/src/convertUtils.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,CAAC,MAAM,KAAK,CAAC;AACzB,OAAO,EAAE,QAAQ,EAAE,IAAI,EAAE,KAAK,MAAM,EAAE,MAAM,mBAAmB,CAAC;AAChE,OAAO,EACL,eAAe,EAIhB,MAAM,kBAAkB,CAAC;AAE1B,eAAO,MAAM,eAAe,QAAO,eAGjC,CAAC;AAEH;;GAEG;AACH,eAAO,MAAM,iCAAiC,GAC5C,cAAc,CAAC,CAAC,WAAW,EAC3B,QAAQ,MAAM,aAUf,CAAC;AAEF;;GAEG;AACH,eAAO,MAAM,iCAAiC,GAC5C,cAAc,CAAC,CAAC,WAAW,EAC3B,QAAQ,MAAM,SAKb,CAAC;AAEJ;;;;;GAKG;AACH,eAAO,MAAM,kBAAkB,GAC7B,cAAc,CAAC,CAAC,WAAW,EAC3B,QAAQ,MAAM;;;;CAef,CAAC;AAEF;;;;;;GAMG;AACH,wBAAgB,iBAAiB,CAC/B,GAAG,EAAE,IAAI,EACT,WAAW,GAAE,MAAsB,GAClC,CAAC,CAAC,GAAG,CAUP;AAED;;;;;;;;;GASG;AACH,wBAAgB,yBAAyB,CACvC,GAAG,EAAE,IAAI,EACT,WAAW,EAAE,CAAC,CAAC,WAAW,GACzB,CAAC,CAAC,WAAW,CAOf;AAED;;;;;;GAMG;AACH,wBAAgB,qBAAqB,CACnC,MAAM,EAAE,MAAM,EACd,KAAK,EAAE,GAAG,EACV,WAAW,GAAE,MAAsB,GAClC,CAAC,CAAC,GAAG,CAGP;AAED;;;;;;GAMG;AACH,wBAAgB,6BAA6B,CAC3C,MAAM,EAAE,MAAM,EACd,KAAK,EAAE,GAAG,EACV,WAAW,EAAE,CAAC,CAAC,WAAW,GACzB,CAAC,CAAC,WAAW,CAGf"}
@@ -0,0 +1,88 @@
1
+ import * as Y from 'yjs';
2
+ import { Fragment, Node } from 'prosemirror-model';
3
+ import { createNodeFromYElement, updateYFragment, } from './ySyncPlugin.js';
4
+ export const createEmptyMeta = () => ({
5
+ mapping: new Map(),
6
+ isOMark: new Map(),
7
+ });
8
+ /**
9
+ * Utility function for converting an Y.Fragment to a ProseMirror fragment.
10
+ */
11
+ export const yXmlFragmentToProseMirrorFragment = (yXmlFragment, schema) => {
12
+ const fragmentContent = yXmlFragment.toArray().map((t) => createNodeFromYElement(
13
+ /** @type {Y.XmlElement} */ (t), schema, createEmptyMeta())).filter((n) => n !== null);
14
+ return Fragment.fromArray(fragmentContent);
15
+ };
16
+ /**
17
+ * Utility function for converting an Y.Fragment to a ProseMirror node.
18
+ */
19
+ export const yXmlFragmentToProseMirrorRootNode = (yXmlFragment, schema) => schema.topNodeType.create(null, yXmlFragmentToProseMirrorFragment(yXmlFragment, schema));
20
+ /**
21
+ * The initial ProseMirror content should be supplied by Yjs. This function transforms a Y.Fragment
22
+ * to a ProseMirror Doc node and creates a mapping that is used by the sync plugin.
23
+ *
24
+ * @todo deprecate mapping property
25
+ */
26
+ export const initProseMirrorDoc = (yXmlFragment, schema) => {
27
+ const meta = createEmptyMeta();
28
+ const fragmentContent = yXmlFragment.toArray().map((t) => createNodeFromYElement(t, schema, meta)).filter((n) => n !== null);
29
+ const doc = schema.topNodeType.create(null, Fragment.fromArray(fragmentContent));
30
+ return { doc, meta, mapping: meta.mapping };
31
+ };
32
+ /**
33
+ * Utility method to convert a Prosemirror Doc Node into a Y.Doc.
34
+ *
35
+ * This can be used when importing existing content to Y.Doc for the first time,
36
+ * note that this should not be used to rehydrate a Y.Doc from a database once
37
+ * collaboration has begun as all history will be lost
38
+ */
39
+ export function prosemirrorToYDoc(doc, xmlFragment = 'prosemirror') {
40
+ const ydoc = new Y.Doc();
41
+ const type =
42
+ /** @type {Y.XmlFragment} */ (ydoc.get(xmlFragment, Y.XmlFragment));
43
+ if (!type.doc) {
44
+ return ydoc;
45
+ }
46
+ prosemirrorToYXmlFragment(doc, type);
47
+ return type.doc;
48
+ }
49
+ /**
50
+ * Utility method to update an empty Y.XmlFragment with content from a Prosemirror Doc Node.
51
+ *
52
+ * This can be used when importing existing content to Y.Doc for the first time,
53
+ * note that this should not be used to rehydrate a Y.Doc from a database once
54
+ * collaboration has begun as all history will be lost
55
+ *
56
+ * Note: The Y.XmlFragment does not need to be part of a Y.Doc document at the time that this
57
+ * method is called, but it must be added before any other operations are performed on it.
58
+ */
59
+ export function prosemirrorToYXmlFragment(doc, xmlFragment) {
60
+ const type = xmlFragment || new Y.XmlFragment();
61
+ const ydoc = type.doc
62
+ ? type.doc
63
+ : { transact: (transaction) => transaction(undefined) };
64
+ updateYFragment(ydoc, type, doc, { mapping: new Map(), isOMark: new Map() });
65
+ return type;
66
+ }
67
+ /**
68
+ * Utility method to convert Prosemirror compatible JSON into a Y.Doc.
69
+ *
70
+ * This can be used when importing existing content to Y.Doc for the first time,
71
+ * note that this should not be used to rehydrate a Y.Doc from a database once
72
+ * collaboration has begun as all history will be lost
73
+ */
74
+ export function prosemirrorJSONToYDoc(schema, state, xmlFragment = 'prosemirror') {
75
+ const doc = Node.fromJSON(schema, state);
76
+ return prosemirrorToYDoc(doc, xmlFragment);
77
+ }
78
+ /**
79
+ * Utility method to convert Prosemirror compatible JSON to a Y.XmlFragment
80
+ *
81
+ * This can be used when importing existing content to Y.Doc for the first time,
82
+ * note that this should not be used to rehydrate a Y.Doc from a database once
83
+ * collaboration has begun as all history will be lost
84
+ */
85
+ export function prosemirrorJSONToYXmlFragment(schema, state, xmlFragment) {
86
+ const doc = Node.fromJSON(schema, state);
87
+ return prosemirrorToYXmlFragment(doc, xmlFragment);
88
+ }
@@ -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;AAEF;;GAEG;AACH,eAAO,MAAM,gBAAgB,gBAA8B,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');
@@ -0,0 +1,15 @@
1
+ import * as Y from 'yjs';
2
+ import { type EditorView } from 'prosemirror-view';
3
+ import { type Node } from 'prosemirror-model';
4
+ /**
5
+ * Either a node if type is YXmlElement or an Array of text nodes if YXmlText
6
+ */
7
+ type ProsemirrorMapping = Map<Y.AbstractType<any>, Node>;
8
+ export declare const setMeta: (view: EditorView, key: any, value: any) => void;
9
+ /**
10
+ * Transforms a Prosemirror based absolute position to a Yjs Cursor (relative position in the Yjs model).
11
+ */
12
+ export declare const absolutePositionToRelativePosition: (pos: number, type: Y.XmlFragment, mapping: ProsemirrorMapping) => any;
13
+ export declare const relativePositionToAbsolutePosition: (yDoc: Y.Doc, documentType: Y.XmlFragment, relPos: any, mapping: ProsemirrorMapping) => null | number;
14
+ export {};
15
+ //# sourceMappingURL=lib.d.ts.map
@@ -0,0 +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,MA2DT,CAAC"}
@@ -1,9 +1,4 @@
1
1
  import * as Y from 'yjs';
2
- import { Fragment, Node } from 'prosemirror-model';
3
- import * as error from 'lib0/error';
4
- import * as map from 'lib0/map';
5
- import * as eventloop from 'lib0/eventloop';
6
- import { createEmptyMeta, createNodeFromYElement, updateYFragment, } from './ySyncPlugin.js';
7
2
  import { ySyncPluginKey } from './keys.js';
8
3
  /**
9
4
  * Is null if no timeout is in progress.
@@ -14,6 +9,9 @@ let viewsToUpdate = null;
14
9
  const updateMetas = () => {
15
10
  const ups = viewsToUpdate;
16
11
  viewsToUpdate = null;
12
+ if (!ups) {
13
+ return;
14
+ }
17
15
  ups.forEach((metas, view) => {
18
16
  const tr = view.state.tr;
19
17
  const syncState = ySyncPluginKey.getState(view.state);
@@ -28,9 +26,14 @@ const updateMetas = () => {
28
26
  export const setMeta = (view, key, value) => {
29
27
  if (!viewsToUpdate) {
30
28
  viewsToUpdate = new Map();
31
- eventloop.timeout(0, updateMetas);
29
+ setTimeout(updateMetas, 0);
30
+ }
31
+ let subMap = viewsToUpdate.get(view);
32
+ if (subMap === undefined) {
33
+ subMap = new Map();
34
+ viewsToUpdate.set(view, subMap);
32
35
  }
33
- map.setIfUndefined(viewsToUpdate, view, map.create).set(key, value);
36
+ subMap.set(key, value);
34
37
  };
35
38
  /**
36
39
  * Transforms a Prosemirror based absolute position to a Yjs Cursor (relative position in the Yjs model).
@@ -40,9 +43,6 @@ export const absolutePositionToRelativePosition = (pos, type, mapping) => {
40
43
  // if the type is later populated, we want to retain the 0 position (hence assoc=-1)
41
44
  return Y.createRelativePositionFromTypeIndex(type, 0, type.length === 0 ? -1 : 0);
42
45
  }
43
- /**
44
- * @type {any}
45
- */
46
46
  let n = type._first === null
47
47
  ? null
48
48
  : /** @type {Y.ContentType} */ (type._first.content).type;
@@ -94,7 +94,7 @@ export const absolutePositionToRelativePosition = (pos, type, mapping) => {
94
94
  return new Y.RelativePosition(n._item === null ? null : n._item.id, n._item === null ? Y.findRootTypeKey(n) : null, null);
95
95
  }
96
96
  do {
97
- n = /** @type {Y.Item} */ (n._item).parent;
97
+ n = n._item.parent;
98
98
  pos--;
99
99
  } while (n !== type && /** @type {Y.Item} */ (n._item).next === null);
100
100
  // if n is null at this point, we have an unexpected case
@@ -108,7 +108,7 @@ export const absolutePositionToRelativePosition = (pos, type, mapping) => {
108
108
  }
109
109
  }
110
110
  if (n === null) {
111
- throw error.unexpectedCase();
111
+ throw new Error('Unexpected case');
112
112
  }
113
113
  if (pos === 0 && n.constructor !== Y.XmlText && n !== type) { // TODO: set to <= 0
114
114
  return createRelativePosition(n._item.parent, n._item);
@@ -127,8 +127,8 @@ const createRelativePosition = (type, item) => {
127
127
  }
128
128
  return new Y.RelativePosition(typeid, tname, item.id);
129
129
  };
130
- export const relativePositionToAbsolutePosition = (y, documentType, relPos, mapping) => {
131
- const decodedPos = Y.createAbsolutePositionFromRelativePosition(relPos, y);
130
+ export const relativePositionToAbsolutePosition = (yDoc, documentType, relPos, mapping) => {
131
+ const decodedPos = Y.createAbsolutePositionFromRelativePosition(relPos, yDoc);
132
132
  if (decodedPos === null ||
133
133
  (decodedPos.type !== documentType &&
134
134
  !Y.isParentOf(documentType, decodedPos.type._item))) {
@@ -136,7 +136,7 @@ export const relativePositionToAbsolutePosition = (y, documentType, relPos, mapp
136
136
  }
137
137
  let type = decodedPos.type;
138
138
  let pos = 0;
139
- if (type.constructor === Y.XmlText) {
139
+ if (type instanceof Y.XmlText) {
140
140
  pos = decodedPos.index;
141
141
  }
142
142
  else if (type._item === null || !type._item.deleted) {
@@ -150,7 +150,8 @@ export const relativePositionToAbsolutePosition = (y, documentType, relPos, mapp
150
150
  pos += t._length;
151
151
  }
152
152
  else {
153
- pos += /** @type {any} */ (mapping.get(t)).nodeSize;
153
+ const node = mapping.get(t);
154
+ pos += node?.nodeSize || 0;
154
155
  }
155
156
  }
156
157
  n = n.right;
@@ -158,15 +159,16 @@ export const relativePositionToAbsolutePosition = (y, documentType, relPos, mapp
158
159
  pos += 1; // increase because we go out of n
159
160
  }
160
161
  while (type !== documentType && type._item !== null) {
161
- // @ts-ignore
162
162
  const parent = type._item.parent;
163
- // @ts-ignore
163
+ if (parent instanceof Y.ID || parent === null) {
164
+ continue;
165
+ }
164
166
  if (parent._item === null || !parent._item.deleted) {
165
167
  pos += 1; // the start tag
166
168
  let n = /** @type {Y.AbstractType} */ (parent)._first;
167
169
  // now iterate until we found type
168
170
  while (n !== null) {
169
- const contentType = /** @type {Y.ContentType} */ (n.content).type;
171
+ const contentType = n.content.type;
170
172
  if (contentType === type) {
171
173
  break;
172
174
  }
@@ -175,95 +177,14 @@ export const relativePositionToAbsolutePosition = (y, documentType, relPos, mapp
175
177
  pos += contentType._length;
176
178
  }
177
179
  else {
178
- pos += /** @type {any} */ (mapping.get(contentType)).nodeSize;
180
+ const node = mapping.get(contentType);
181
+ pos += node?.nodeSize || 0;
179
182
  }
180
183
  }
181
184
  n = n.right;
182
185
  }
183
186
  }
184
- type = /** @type {Y.AbstractType} */ (parent);
187
+ type = parent;
185
188
  }
186
189
  return pos - 1; // we don't count the most outer tag, because it is a fragment
187
190
  };
188
- /**
189
- * Utility function for converting an Y.Fragment to a ProseMirror fragment.
190
- */
191
- export const yXmlFragmentToProseMirrorFragment = (yXmlFragment, schema) => {
192
- const fragmentContent = yXmlFragment.toArray().map((t) => createNodeFromYElement(
193
- /** @type {Y.XmlElement} */ (t), schema, createEmptyMeta())).filter((n) => n !== null);
194
- return Fragment.fromArray(fragmentContent);
195
- };
196
- /**
197
- * Utility function for converting an Y.Fragment to a ProseMirror node.
198
- */
199
- export const yXmlFragmentToProseMirrorRootNode = (yXmlFragment, schema) => schema.topNodeType.create(null, yXmlFragmentToProseMirrorFragment(yXmlFragment, schema));
200
- /**
201
- * The initial ProseMirror content should be supplied by Yjs. This function transforms a Y.Fragment
202
- * to a ProseMirror Doc node and creates a mapping that is used by the sync plugin.
203
- *
204
- * @todo deprecate mapping property
205
- */
206
- export const initProseMirrorDoc = (yXmlFragment, schema) => {
207
- const meta = createEmptyMeta();
208
- const fragmentContent = yXmlFragment.toArray().map((t) => createNodeFromYElement(
209
- /** @type {Y.XmlElement} */ (t), schema, meta)).filter((n) => n !== null);
210
- const doc = schema.topNodeType.create(null, Fragment.fromArray(fragmentContent));
211
- return { doc, meta, mapping: meta.mapping };
212
- };
213
- /**
214
- * Utility method to convert a Prosemirror Doc Node into a Y.Doc.
215
- *
216
- * This can be used when importing existing content to Y.Doc for the first time,
217
- * note that this should not be used to rehydrate a Y.Doc from a database once
218
- * collaboration has begun as all history will be lost
219
- */
220
- export function prosemirrorToYDoc(doc, xmlFragment = 'prosemirror') {
221
- const ydoc = new Y.Doc();
222
- const type =
223
- /** @type {Y.XmlFragment} */ (ydoc.get(xmlFragment, Y.XmlFragment));
224
- if (!type.doc) {
225
- return ydoc;
226
- }
227
- prosemirrorToYXmlFragment(doc, type);
228
- return type.doc;
229
- }
230
- /**
231
- * Utility method to update an empty Y.XmlFragment with content from a Prosemirror Doc Node.
232
- *
233
- * This can be used when importing existing content to Y.Doc for the first time,
234
- * note that this should not be used to rehydrate a Y.Doc from a database once
235
- * collaboration has begun as all history will be lost
236
- *
237
- * Note: The Y.XmlFragment does not need to be part of a Y.Doc document at the time that this
238
- * method is called, but it must be added before any other operations are performed on it.
239
- */
240
- export function prosemirrorToYXmlFragment(doc, xmlFragment) {
241
- const type = xmlFragment || new Y.XmlFragment();
242
- const ydoc = type.doc
243
- ? type.doc
244
- : { transact: (transaction) => transaction(undefined) };
245
- updateYFragment(ydoc, type, doc, { mapping: new Map(), isOMark: new Map() });
246
- return type;
247
- }
248
- /**
249
- * Utility method to convert Prosemirror compatible JSON into a Y.Doc.
250
- *
251
- * This can be used when importing existing content to Y.Doc for the first time,
252
- * note that this should not be used to rehydrate a Y.Doc from a database once
253
- * collaboration has begun as all history will be lost
254
- */
255
- export function prosemirrorJSONToYDoc(schema, state, xmlFragment = 'prosemirror') {
256
- const doc = Node.fromJSON(schema, state);
257
- return prosemirrorToYDoc(doc, xmlFragment);
258
- }
259
- /**
260
- * Utility method to convert Prosemirror compatible JSON to a Y.XmlFragment
261
- *
262
- * This can be used when importing existing content to Y.Doc for the first time,
263
- * note that this should not be used to rehydrate a Y.Doc from a database once
264
- * collaboration has begun as all history will be lost
265
- */
266
- export function prosemirrorJSONToYXmlFragment(schema, state, xmlFragment) {
267
- const doc = Node.fromJSON(schema, state);
268
- return prosemirrorToYXmlFragment(doc, xmlFragment);
269
- }
@@ -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
+ };