@atlaskit/prosemirror-collab 0.1.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.
package/CHANGELOG.md ADDED
@@ -0,0 +1,80 @@
1
+ ## 1.3.0 (2022-05-30)
2
+
3
+ ## 0.1.0
4
+
5
+ ### Minor Changes
6
+
7
+ - [`883590cbe7d`](https://bitbucket.org/atlassian/atlassian-frontend/commits/883590cbe7d) - [ESS-2914] Forked the prosemirror-collab library to create a version that filters out analytics steps
8
+
9
+ ### Patch Changes
10
+
11
+ - Updated dependencies
12
+
13
+ ### New features
14
+
15
+ Include TypeScript type declarations.
16
+
17
+ ## 1.2.2 (2019-11-20)
18
+
19
+ ### Bug fixes
20
+
21
+ Rename ES module files to use a .js extension, since Webpack gets confused by .mjs
22
+
23
+ ## 1.2.1 (2019-11-19)
24
+
25
+ ### Bug fixes
26
+
27
+ The file referred to in the package's `module` field now is compiled down to ES5.
28
+
29
+ ## 1.2.0 (2019-11-08)
30
+
31
+ ### New features
32
+
33
+ Add a `module` field to package json file.
34
+
35
+ ## 1.1.2 (2019-05-29)
36
+
37
+ ### Bug fixes
38
+
39
+ Fix an issue where in `mapSelectionBackward` mode, the plugin flipped the head and anchor of the selection, leading to selection glitches during collaborative editing.
40
+
41
+ ## 1.1.1 (2018-10-09)
42
+
43
+ ### Bug fixes
44
+
45
+ Fix issue where `mapSelectionBackward` didn't work because of a typo.
46
+
47
+ ## 1.1.0 (2018-08-21)
48
+
49
+ ### New features
50
+
51
+ [`receiveTransaction`](https://prosemirror.net/docs/ref/#collab.receiveTransaction) now supports a `mapSelectionBackward` option that makes it so that text selections are mapped to stay in place when remote changes insert content at their position.
52
+
53
+ ## 0.19.0 (2017-03-16)
54
+
55
+ ### New features
56
+
57
+ You can now use strings (as well as numbers) as client IDs (this already worked, but now the documentation reflects this).
58
+
59
+ ## 0.18.0 (2017-02-24)
60
+
61
+ ### New features
62
+
63
+ [`sendableSteps`](https://prosemirror.net/docs/ref/version/0.18.0.html#collab.sendableSteps) now also returns information about the original transactions that produced the steps.
64
+
65
+ ## 0.11.0 (2016-09-21)
66
+
67
+ ### Breaking changes
68
+
69
+ Moved into a separate module.
70
+
71
+ Interface [adjusted](https://prosemirror.net/docs/ref/version/0.11.0.html#collab) to work with the new
72
+ [plugin](https://prosemirror.net/docs/ref/version/0.11.0.html#state.Plugin) system.
73
+
74
+ ### New features
75
+
76
+ When receiving changes, the module now
77
+ [generates](https://prosemirror.net/docs/ref/version/0.11.0.html#collab.receiveAction) a regular
78
+ [transform action](https://prosemirror.net/docs/ref/version/0.11.0.html#state.TransformAction) instead of hard-setting
79
+ the editor's document. This solves problematic corner cases for code
80
+ keeping track of the document by listening to transform actions.
package/LICENSE.md ADDED
@@ -0,0 +1,19 @@
1
+ Copyright (C) 2015-2017 by Marijn Haverbeke <marijnh@gmail.com> and others
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining a copy
4
+ of this software and associated documentation files (the "Software"), to deal
5
+ in the Software without restriction, including without limitation the rights
6
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7
+ copies of the Software, and to permit persons to whom the Software is
8
+ furnished to do so, subject to the following conditions:
9
+
10
+ The above copyright notice and this permission notice shall be included in
11
+ all copies or substantial portions of the Software.
12
+
13
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19
+ THE SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,30 @@
1
+ # prosemirror-collab
2
+
3
+ This is a fork of the ProseMirror Collab module: [GitHub](https://github.com/prosemirror/prosemirror-collab) [NPM](https://www.npmjs.com/package/prosemirror-collab)
4
+
5
+ [ [**WEBSITE**](https://prosemirror.net) | [**ISSUES**](https://github.com/prosemirror/prosemirror/issues) | [**FORUM**](https://discuss.prosemirror.net) | [**CHANGELOG**](https://github.com/ProseMirror/prosemirror-collab/blob/master/CHANGELOG.md) ]
6
+
7
+ This is a [core module](https://prosemirror.net/docs/ref/#collab) of [ProseMirror](https://prosemirror.net).
8
+ ProseMirror is a well-behaved rich semantic content editor based on
9
+ contentEditable, with support for collaborative editing and custom
10
+ document schemas.
11
+
12
+ This [module](https://prosemirror.net/docs/ref/#collab) implements a
13
+ plugin that helps track and merge changes for
14
+ [collaborative editing](https://prosemirror.net/docs/guide/#collab).
15
+
16
+ The [project page](https://prosemirror.net) has more information, a
17
+ number of [examples](https://prosemirror.net/examples/) and the
18
+ [documentation](https://prosemirror.net/docs/).
19
+
20
+ This code is released under an
21
+ [MIT license](https://github.com/prosemirror/prosemirror/tree/master/LICENSE).
22
+ There's a [forum](http://discuss.prosemirror.net) for general
23
+ discussion and support requests, and the
24
+ [Github bug tracker](https://github.com/prosemirror/prosemirror/issues)
25
+ is the place to report issues.
26
+
27
+ We aim to be an inclusive, welcoming community. To make that explicit,
28
+ we have a [code of
29
+ conduct](http://contributor-covenant.org/version/1/1/0/) that applies
30
+ to communication around the project.
@@ -0,0 +1,215 @@
1
+ "use strict";
2
+
3
+ var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
4
+
5
+ Object.defineProperty(exports, "__esModule", {
6
+ value: true
7
+ });
8
+ exports.collab = collab;
9
+ exports.getVersion = getVersion;
10
+ exports.rebaseSteps = rebaseSteps;
11
+ exports.receiveTransaction = receiveTransaction;
12
+ exports.sendableSteps = sendableSteps;
13
+
14
+ var _createClass2 = _interopRequireDefault(require("@babel/runtime/helpers/createClass"));
15
+
16
+ var _classCallCheck2 = _interopRequireDefault(require("@babel/runtime/helpers/classCallCheck"));
17
+
18
+ var _prosemirrorState = require("prosemirror-state");
19
+
20
+ var _steps = require("@atlaskit/adf-schema/steps");
21
+
22
+ var Rebaseable = /*#__PURE__*/(0, _createClass2.default)(function Rebaseable(step, inverted, origin) {
23
+ (0, _classCallCheck2.default)(this, Rebaseable);
24
+ this.step = step;
25
+ this.inverted = inverted;
26
+ this.origin = origin;
27
+ }); /// Undo a given set of steps, apply a set of other steps, and then
28
+ /// redo them @internal
29
+
30
+ function rebaseSteps(steps, over, transform) {
31
+ for (var i = steps.length - 1; i >= 0; i--) {
32
+ transform.step(steps[i].inverted);
33
+ }
34
+
35
+ for (var _i = 0; _i < over.length; _i++) {
36
+ transform.step(over[_i]);
37
+ }
38
+
39
+ var result = [];
40
+
41
+ for (var _i2 = 0, mapFrom = steps.length; _i2 < steps.length; _i2++) {
42
+ var mapped = steps[_i2].step.map(transform.mapping.slice(mapFrom));
43
+
44
+ mapFrom--;
45
+
46
+ if (mapped && !transform.maybeStep(mapped).failed) {
47
+ // Open ticket for setMirror https://github.com/ProseMirror/prosemirror/issues/869
48
+ // @ts-expect-error
49
+ transform.mapping.setMirror(mapFrom, transform.steps.length - 1);
50
+ result.push(new Rebaseable(mapped, mapped.invert(transform.docs[transform.docs.length - 1]), steps[_i2].origin));
51
+ }
52
+ }
53
+
54
+ return result;
55
+ } // This state field accumulates changes that have to be sent to the
56
+ // central authority in the collaborating group and makes it possible
57
+ // to integrate changes made by peers into our local document. It is
58
+ // defined by the plugin, and will be available as the `collab` field
59
+ // in the resulting editor state.
60
+
61
+
62
+ var CollabState = /*#__PURE__*/(0, _createClass2.default)(function CollabState( // The version number of the last update received from the central
63
+ // authority. Starts at 0 or the value of the `version` property
64
+ // in the option object, for the editor's value when the option
65
+ // was enabled.
66
+ version, // The local steps that havent been successfully sent to the
67
+ // server yet.
68
+ unconfirmed) {
69
+ (0, _classCallCheck2.default)(this, CollabState);
70
+ this.version = version;
71
+ this.unconfirmed = unconfirmed;
72
+ });
73
+
74
+ function unconfirmedFrom(transform) {
75
+ var result = [];
76
+
77
+ for (var i = 0; i < transform.steps.length; i++) {
78
+ // Filter out the analytics steps, they don't need to be sent to the collab service
79
+ if (transform.steps[i] instanceof _steps.AnalyticsStep) {
80
+ continue;
81
+ }
82
+
83
+ result.push(new Rebaseable(transform.steps[i], transform.steps[i].invert(transform.docs[i]), transform));
84
+ }
85
+
86
+ return result;
87
+ }
88
+
89
+ var collabKey = new _prosemirrorState.PluginKey('collab');
90
+
91
+ /// Creates a plugin that enables the collaborative editing framework
92
+ /// for the editor.
93
+ function collab() {
94
+ var config = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
95
+ var conf = {
96
+ version: config.version || 0,
97
+ clientID: // eslint-disable-next-line eqeqeq
98
+ config.clientID == null ? Math.floor(Math.random() * 0xffffffff) : config.clientID
99
+ };
100
+ return new _prosemirrorState.Plugin({
101
+ key: collabKey,
102
+ state: {
103
+ init: function init() {
104
+ return new CollabState(conf.version, []);
105
+ },
106
+ apply: function apply(tr, collab) {
107
+ var newState = tr.getMeta(collabKey);
108
+
109
+ if (newState) {
110
+ return newState;
111
+ }
112
+
113
+ if (tr.docChanged) {
114
+ return new CollabState(collab.version, // @ts-expect-error
115
+ collab.unconfirmed.concat(unconfirmedFrom(tr)));
116
+ }
117
+
118
+ return collab;
119
+ }
120
+ },
121
+ // @ts-expect-error
122
+ config: conf,
123
+ // This is used to notify the history plugin to not merge steps,
124
+ // so that the history can be rebased.
125
+ historyPreserveItems: true
126
+ });
127
+ } /// Create a transaction that represents a set of new steps received from
128
+ /// the authority. Applying this transaction moves the state forward to
129
+ /// adjust to the authority's view of the document.
130
+
131
+
132
+ function receiveTransaction(state, steps, clientIDs) {
133
+ var options = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : {};
134
+ // Pushes a set of steps (received from the central authority) into
135
+ // the editor state (which should have the collab plugin enabled).
136
+ // Will recognize its own changes, and confirm unconfirmed steps as
137
+ // appropriate. Remaining unconfirmed steps will be rebased over
138
+ // remote steps.
139
+ var collabState = collabKey.getState(state);
140
+ var version = collabState.version + steps.length;
141
+ var ourID = collabKey.get(state).spec.config.clientID; // Find out which prefix of the steps originated with us
142
+
143
+ var ours = 0; // eslint-disable-next-line eqeqeq
144
+
145
+ while (ours < clientIDs.length && clientIDs[ours] == ourID) {
146
+ ++ours;
147
+ }
148
+
149
+ var unconfirmed = collabState.unconfirmed.slice(ours);
150
+ steps = ours ? steps.slice(ours) : steps; // If all steps originated with us, we're done.
151
+
152
+ if (!steps.length) {
153
+ return state.tr.setMeta(collabKey, new CollabState(version, unconfirmed));
154
+ }
155
+
156
+ var nUnconfirmed = unconfirmed.length;
157
+ var tr = state.tr;
158
+
159
+ if (nUnconfirmed) {
160
+ unconfirmed = rebaseSteps(unconfirmed, steps, tr);
161
+ } else {
162
+ for (var i = 0; i < steps.length; i++) {
163
+ tr.step(steps[i]);
164
+ }
165
+
166
+ unconfirmed = [];
167
+ }
168
+
169
+ var newCollabState = new CollabState(version, unconfirmed);
170
+
171
+ if (options && options.mapSelectionBackward && state.selection instanceof _prosemirrorState.TextSelection) {
172
+ tr.setSelection(_prosemirrorState.TextSelection.between(tr.doc.resolve(tr.mapping.map(state.selection.anchor, -1)), tr.doc.resolve(tr.mapping.map(state.selection.head, -1)), -1));
173
+ tr.updated &= ~1;
174
+ }
175
+
176
+ return tr.setMeta('rebased', nUnconfirmed).setMeta('addToHistory', false).setMeta(collabKey, newCollabState);
177
+ } /// Provides data describing the editor's unconfirmed steps, which need
178
+ /// to be sent to the central authority. Returns null when there is
179
+ /// nothing to send.
180
+ ///
181
+ /// `origins` holds the _original_ transactions that produced each
182
+ /// steps. This can be useful for looking up time stamps and other
183
+ /// metadata for the steps, but note that the steps may have been
184
+ /// rebased, whereas the origin transactions are still the old,
185
+ /// unchanged objects.
186
+
187
+
188
+ function sendableSteps(state) {
189
+ var collabState = collabKey.getState(state); // eslint-disable-next-line eqeqeq
190
+
191
+ if (collabState.unconfirmed.length == 0) {
192
+ return null;
193
+ }
194
+
195
+ return {
196
+ version: collabState.version,
197
+ steps: collabState.unconfirmed.map(function (s) {
198
+ return s.step;
199
+ }),
200
+ clientID: collabKey.get(state).spec.config.clientID,
201
+
202
+ get origins() {
203
+ return this._origins || (this._origins = collabState.unconfirmed.map(function (s) {
204
+ return s.origin;
205
+ }));
206
+ }
207
+
208
+ };
209
+ } /// Get the version up to which the collab plugin has synced with the
210
+ /// central authority.
211
+
212
+
213
+ function getVersion(state) {
214
+ return collabKey.getState(state).version;
215
+ }
@@ -0,0 +1,5 @@
1
+ {
2
+ "name": "@atlaskit/prosemirror-collab",
3
+ "version": "0.1.0",
4
+ "sideEffects": false
5
+ }
@@ -0,0 +1,190 @@
1
+ import { Plugin, PluginKey, TextSelection } from 'prosemirror-state';
2
+ import { AnalyticsStep } from '@atlaskit/adf-schema/steps';
3
+
4
+ class Rebaseable {
5
+ constructor(step, inverted, origin) {
6
+ this.step = step;
7
+ this.inverted = inverted;
8
+ this.origin = origin;
9
+ }
10
+
11
+ } /// Undo a given set of steps, apply a set of other steps, and then
12
+ /// redo them @internal
13
+
14
+
15
+ export function rebaseSteps(steps, over, transform) {
16
+ for (let i = steps.length - 1; i >= 0; i--) {
17
+ transform.step(steps[i].inverted);
18
+ }
19
+
20
+ for (let i = 0; i < over.length; i++) {
21
+ transform.step(over[i]);
22
+ }
23
+
24
+ let result = [];
25
+
26
+ for (let i = 0, mapFrom = steps.length; i < steps.length; i++) {
27
+ let mapped = steps[i].step.map(transform.mapping.slice(mapFrom));
28
+ mapFrom--;
29
+
30
+ if (mapped && !transform.maybeStep(mapped).failed) {
31
+ // Open ticket for setMirror https://github.com/ProseMirror/prosemirror/issues/869
32
+ // @ts-expect-error
33
+ transform.mapping.setMirror(mapFrom, transform.steps.length - 1);
34
+ result.push(new Rebaseable(mapped, mapped.invert(transform.docs[transform.docs.length - 1]), steps[i].origin));
35
+ }
36
+ }
37
+
38
+ return result;
39
+ } // This state field accumulates changes that have to be sent to the
40
+ // central authority in the collaborating group and makes it possible
41
+ // to integrate changes made by peers into our local document. It is
42
+ // defined by the plugin, and will be available as the `collab` field
43
+ // in the resulting editor state.
44
+
45
+ class CollabState {
46
+ constructor( // The version number of the last update received from the central
47
+ // authority. Starts at 0 or the value of the `version` property
48
+ // in the option object, for the editor's value when the option
49
+ // was enabled.
50
+ version, // The local steps that havent been successfully sent to the
51
+ // server yet.
52
+ unconfirmed) {
53
+ this.version = version;
54
+ this.unconfirmed = unconfirmed;
55
+ }
56
+
57
+ }
58
+
59
+ function unconfirmedFrom(transform) {
60
+ let result = [];
61
+
62
+ for (let i = 0; i < transform.steps.length; i++) {
63
+ // Filter out the analytics steps, they don't need to be sent to the collab service
64
+ if (transform.steps[i] instanceof AnalyticsStep) {
65
+ continue;
66
+ }
67
+
68
+ result.push(new Rebaseable(transform.steps[i], transform.steps[i].invert(transform.docs[i]), transform));
69
+ }
70
+
71
+ return result;
72
+ }
73
+
74
+ const collabKey = new PluginKey('collab');
75
+ /// Creates a plugin that enables the collaborative editing framework
76
+ /// for the editor.
77
+ export function collab(config = {}) {
78
+ let conf = {
79
+ version: config.version || 0,
80
+ clientID: // eslint-disable-next-line eqeqeq
81
+ config.clientID == null ? Math.floor(Math.random() * 0xffffffff) : config.clientID
82
+ };
83
+ return new Plugin({
84
+ key: collabKey,
85
+ state: {
86
+ init: () => new CollabState(conf.version, []),
87
+
88
+ apply(tr, collab) {
89
+ let newState = tr.getMeta(collabKey);
90
+
91
+ if (newState) {
92
+ return newState;
93
+ }
94
+
95
+ if (tr.docChanged) {
96
+ return new CollabState(collab.version, // @ts-expect-error
97
+ collab.unconfirmed.concat(unconfirmedFrom(tr)));
98
+ }
99
+
100
+ return collab;
101
+ }
102
+
103
+ },
104
+ // @ts-expect-error
105
+ config: conf,
106
+ // This is used to notify the history plugin to not merge steps,
107
+ // so that the history can be rebased.
108
+ historyPreserveItems: true
109
+ });
110
+ } /// Create a transaction that represents a set of new steps received from
111
+ /// the authority. Applying this transaction moves the state forward to
112
+ /// adjust to the authority's view of the document.
113
+
114
+ export function receiveTransaction(state, steps, clientIDs, options = {}) {
115
+ // Pushes a set of steps (received from the central authority) into
116
+ // the editor state (which should have the collab plugin enabled).
117
+ // Will recognize its own changes, and confirm unconfirmed steps as
118
+ // appropriate. Remaining unconfirmed steps will be rebased over
119
+ // remote steps.
120
+ let collabState = collabKey.getState(state);
121
+ let version = collabState.version + steps.length;
122
+ let ourID = collabKey.get(state).spec.config.clientID; // Find out which prefix of the steps originated with us
123
+
124
+ let ours = 0; // eslint-disable-next-line eqeqeq
125
+
126
+ while (ours < clientIDs.length && clientIDs[ours] == ourID) {
127
+ ++ours;
128
+ }
129
+
130
+ let unconfirmed = collabState.unconfirmed.slice(ours);
131
+ steps = ours ? steps.slice(ours) : steps; // If all steps originated with us, we're done.
132
+
133
+ if (!steps.length) {
134
+ return state.tr.setMeta(collabKey, new CollabState(version, unconfirmed));
135
+ }
136
+
137
+ let nUnconfirmed = unconfirmed.length;
138
+ let tr = state.tr;
139
+
140
+ if (nUnconfirmed) {
141
+ unconfirmed = rebaseSteps(unconfirmed, steps, tr);
142
+ } else {
143
+ for (let i = 0; i < steps.length; i++) {
144
+ tr.step(steps[i]);
145
+ }
146
+
147
+ unconfirmed = [];
148
+ }
149
+
150
+ let newCollabState = new CollabState(version, unconfirmed);
151
+
152
+ if (options && options.mapSelectionBackward && state.selection instanceof TextSelection) {
153
+ tr.setSelection(TextSelection.between(tr.doc.resolve(tr.mapping.map(state.selection.anchor, -1)), tr.doc.resolve(tr.mapping.map(state.selection.head, -1)), -1));
154
+ tr.updated &= ~1;
155
+ }
156
+
157
+ return tr.setMeta('rebased', nUnconfirmed).setMeta('addToHistory', false).setMeta(collabKey, newCollabState);
158
+ } /// Provides data describing the editor's unconfirmed steps, which need
159
+ /// to be sent to the central authority. Returns null when there is
160
+ /// nothing to send.
161
+ ///
162
+ /// `origins` holds the _original_ transactions that produced each
163
+ /// steps. This can be useful for looking up time stamps and other
164
+ /// metadata for the steps, but note that the steps may have been
165
+ /// rebased, whereas the origin transactions are still the old,
166
+ /// unchanged objects.
167
+
168
+ export function sendableSteps(state) {
169
+ let collabState = collabKey.getState(state); // eslint-disable-next-line eqeqeq
170
+
171
+ if (collabState.unconfirmed.length == 0) {
172
+ return null;
173
+ }
174
+
175
+ return {
176
+ version: collabState.version,
177
+ steps: collabState.unconfirmed.map(s => s.step),
178
+ clientID: collabKey.get(state).spec.config.clientID,
179
+
180
+ get origins() {
181
+ return this._origins || (this._origins = collabState.unconfirmed.map(s => s.origin));
182
+ }
183
+
184
+ };
185
+ } /// Get the version up to which the collab plugin has synced with the
186
+ /// central authority.
187
+
188
+ export function getVersion(state) {
189
+ return collabKey.getState(state).version;
190
+ }
@@ -0,0 +1,5 @@
1
+ {
2
+ "name": "@atlaskit/prosemirror-collab",
3
+ "version": "0.1.0",
4
+ "sideEffects": false
5
+ }
@@ -0,0 +1,197 @@
1
+ import _createClass from "@babel/runtime/helpers/createClass";
2
+ import _classCallCheck from "@babel/runtime/helpers/classCallCheck";
3
+ import { Plugin, PluginKey, TextSelection } from 'prosemirror-state';
4
+ import { AnalyticsStep } from '@atlaskit/adf-schema/steps';
5
+
6
+ var Rebaseable = /*#__PURE__*/_createClass(function Rebaseable(step, inverted, origin) {
7
+ _classCallCheck(this, Rebaseable);
8
+
9
+ this.step = step;
10
+ this.inverted = inverted;
11
+ this.origin = origin;
12
+ }); /// Undo a given set of steps, apply a set of other steps, and then
13
+ /// redo them @internal
14
+
15
+
16
+ export function rebaseSteps(steps, over, transform) {
17
+ for (var i = steps.length - 1; i >= 0; i--) {
18
+ transform.step(steps[i].inverted);
19
+ }
20
+
21
+ for (var _i = 0; _i < over.length; _i++) {
22
+ transform.step(over[_i]);
23
+ }
24
+
25
+ var result = [];
26
+
27
+ for (var _i2 = 0, mapFrom = steps.length; _i2 < steps.length; _i2++) {
28
+ var mapped = steps[_i2].step.map(transform.mapping.slice(mapFrom));
29
+
30
+ mapFrom--;
31
+
32
+ if (mapped && !transform.maybeStep(mapped).failed) {
33
+ // Open ticket for setMirror https://github.com/ProseMirror/prosemirror/issues/869
34
+ // @ts-expect-error
35
+ transform.mapping.setMirror(mapFrom, transform.steps.length - 1);
36
+ result.push(new Rebaseable(mapped, mapped.invert(transform.docs[transform.docs.length - 1]), steps[_i2].origin));
37
+ }
38
+ }
39
+
40
+ return result;
41
+ } // This state field accumulates changes that have to be sent to the
42
+ // central authority in the collaborating group and makes it possible
43
+ // to integrate changes made by peers into our local document. It is
44
+ // defined by the plugin, and will be available as the `collab` field
45
+ // in the resulting editor state.
46
+
47
+ var CollabState = /*#__PURE__*/_createClass(function CollabState( // The version number of the last update received from the central
48
+ // authority. Starts at 0 or the value of the `version` property
49
+ // in the option object, for the editor's value when the option
50
+ // was enabled.
51
+ version, // The local steps that havent been successfully sent to the
52
+ // server yet.
53
+ unconfirmed) {
54
+ _classCallCheck(this, CollabState);
55
+
56
+ this.version = version;
57
+ this.unconfirmed = unconfirmed;
58
+ });
59
+
60
+ function unconfirmedFrom(transform) {
61
+ var result = [];
62
+
63
+ for (var i = 0; i < transform.steps.length; i++) {
64
+ // Filter out the analytics steps, they don't need to be sent to the collab service
65
+ if (transform.steps[i] instanceof AnalyticsStep) {
66
+ continue;
67
+ }
68
+
69
+ result.push(new Rebaseable(transform.steps[i], transform.steps[i].invert(transform.docs[i]), transform));
70
+ }
71
+
72
+ return result;
73
+ }
74
+
75
+ var collabKey = new PluginKey('collab');
76
+ /// Creates a plugin that enables the collaborative editing framework
77
+ /// for the editor.
78
+ export function collab() {
79
+ var config = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
80
+ var conf = {
81
+ version: config.version || 0,
82
+ clientID: // eslint-disable-next-line eqeqeq
83
+ config.clientID == null ? Math.floor(Math.random() * 0xffffffff) : config.clientID
84
+ };
85
+ return new Plugin({
86
+ key: collabKey,
87
+ state: {
88
+ init: function init() {
89
+ return new CollabState(conf.version, []);
90
+ },
91
+ apply: function apply(tr, collab) {
92
+ var newState = tr.getMeta(collabKey);
93
+
94
+ if (newState) {
95
+ return newState;
96
+ }
97
+
98
+ if (tr.docChanged) {
99
+ return new CollabState(collab.version, // @ts-expect-error
100
+ collab.unconfirmed.concat(unconfirmedFrom(tr)));
101
+ }
102
+
103
+ return collab;
104
+ }
105
+ },
106
+ // @ts-expect-error
107
+ config: conf,
108
+ // This is used to notify the history plugin to not merge steps,
109
+ // so that the history can be rebased.
110
+ historyPreserveItems: true
111
+ });
112
+ } /// Create a transaction that represents a set of new steps received from
113
+ /// the authority. Applying this transaction moves the state forward to
114
+ /// adjust to the authority's view of the document.
115
+
116
+ export function receiveTransaction(state, steps, clientIDs) {
117
+ var options = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : {};
118
+ // Pushes a set of steps (received from the central authority) into
119
+ // the editor state (which should have the collab plugin enabled).
120
+ // Will recognize its own changes, and confirm unconfirmed steps as
121
+ // appropriate. Remaining unconfirmed steps will be rebased over
122
+ // remote steps.
123
+ var collabState = collabKey.getState(state);
124
+ var version = collabState.version + steps.length;
125
+ var ourID = collabKey.get(state).spec.config.clientID; // Find out which prefix of the steps originated with us
126
+
127
+ var ours = 0; // eslint-disable-next-line eqeqeq
128
+
129
+ while (ours < clientIDs.length && clientIDs[ours] == ourID) {
130
+ ++ours;
131
+ }
132
+
133
+ var unconfirmed = collabState.unconfirmed.slice(ours);
134
+ steps = ours ? steps.slice(ours) : steps; // If all steps originated with us, we're done.
135
+
136
+ if (!steps.length) {
137
+ return state.tr.setMeta(collabKey, new CollabState(version, unconfirmed));
138
+ }
139
+
140
+ var nUnconfirmed = unconfirmed.length;
141
+ var tr = state.tr;
142
+
143
+ if (nUnconfirmed) {
144
+ unconfirmed = rebaseSteps(unconfirmed, steps, tr);
145
+ } else {
146
+ for (var i = 0; i < steps.length; i++) {
147
+ tr.step(steps[i]);
148
+ }
149
+
150
+ unconfirmed = [];
151
+ }
152
+
153
+ var newCollabState = new CollabState(version, unconfirmed);
154
+
155
+ if (options && options.mapSelectionBackward && state.selection instanceof TextSelection) {
156
+ tr.setSelection(TextSelection.between(tr.doc.resolve(tr.mapping.map(state.selection.anchor, -1)), tr.doc.resolve(tr.mapping.map(state.selection.head, -1)), -1));
157
+ tr.updated &= ~1;
158
+ }
159
+
160
+ return tr.setMeta('rebased', nUnconfirmed).setMeta('addToHistory', false).setMeta(collabKey, newCollabState);
161
+ } /// Provides data describing the editor's unconfirmed steps, which need
162
+ /// to be sent to the central authority. Returns null when there is
163
+ /// nothing to send.
164
+ ///
165
+ /// `origins` holds the _original_ transactions that produced each
166
+ /// steps. This can be useful for looking up time stamps and other
167
+ /// metadata for the steps, but note that the steps may have been
168
+ /// rebased, whereas the origin transactions are still the old,
169
+ /// unchanged objects.
170
+
171
+ export function sendableSteps(state) {
172
+ var collabState = collabKey.getState(state); // eslint-disable-next-line eqeqeq
173
+
174
+ if (collabState.unconfirmed.length == 0) {
175
+ return null;
176
+ }
177
+
178
+ return {
179
+ version: collabState.version,
180
+ steps: collabState.unconfirmed.map(function (s) {
181
+ return s.step;
182
+ }),
183
+ clientID: collabKey.get(state).spec.config.clientID,
184
+
185
+ get origins() {
186
+ return this._origins || (this._origins = collabState.unconfirmed.map(function (s) {
187
+ return s.origin;
188
+ }));
189
+ }
190
+
191
+ };
192
+ } /// Get the version up to which the collab plugin has synced with the
193
+ /// central authority.
194
+
195
+ export function getVersion(state) {
196
+ return collabKey.getState(state).version;
197
+ }
@@ -0,0 +1,5 @@
1
+ {
2
+ "name": "@atlaskit/prosemirror-collab",
3
+ "version": "0.1.0",
4
+ "sideEffects": false
5
+ }
@@ -0,0 +1,25 @@
1
+ import { type EditorState, Plugin, type Transaction } from 'prosemirror-state';
2
+ import type { Step as ProseMirrorStep, Transform as ProseMirrorTransform } from 'prosemirror-transform';
3
+ declare class Rebaseable {
4
+ readonly step: ProseMirrorStep;
5
+ readonly inverted: ProseMirrorStep;
6
+ readonly origin: ProseMirrorTransform;
7
+ constructor(step: ProseMirrorStep, inverted: ProseMirrorStep, origin: ProseMirrorTransform);
8
+ }
9
+ export declare function rebaseSteps(steps: readonly Rebaseable[], over: readonly ProseMirrorStep[], transform: ProseMirrorTransform): Rebaseable[];
10
+ declare type CollabConfig = {
11
+ version?: number;
12
+ clientID?: number | string | null;
13
+ };
14
+ export declare function collab(config?: CollabConfig): Plugin;
15
+ export declare function receiveTransaction(state: EditorState, steps: readonly ProseMirrorStep[], clientIDs: readonly (string | number)[], options?: {
16
+ mapSelectionBackward?: boolean;
17
+ }): Transaction<any>;
18
+ export declare function sendableSteps(state: EditorState): {
19
+ version: number;
20
+ steps: readonly ProseMirrorStep[];
21
+ clientID: number | string;
22
+ origins: readonly Transaction[];
23
+ } | null;
24
+ export declare function getVersion(state: EditorState): number;
25
+ export {};
package/package.json ADDED
@@ -0,0 +1,51 @@
1
+ {
2
+ "name": "@atlaskit/prosemirror-collab",
3
+ "version": "0.1.0",
4
+ "description": "Collaborative editing for ProseMirror - Atlassian Fork",
5
+ "publishConfig": {
6
+ "registry": "https://registry.npmjs.org/"
7
+ },
8
+ "repository": "https://bitbucket.org/atlassian/atlassian-frontend",
9
+ "author": "Atlassian Pty Ltd",
10
+ "license": "MIT",
11
+ "main": "dist/cjs/collab.js",
12
+ "module": "dist/esm/collab.js",
13
+ "module:es2019": "dist/es2019/collab.js",
14
+ "types": "dist/types/collab.d.ts",
15
+ "sideEffects": false,
16
+ "atlaskit:src": "src/collab.ts",
17
+ "atlassian": {
18
+ "team": "Editor Services",
19
+ "inPublicMirror": true,
20
+ "releaseModel": "scheduled"
21
+ },
22
+ "af:exports": {
23
+ ".": "./src/collab.ts"
24
+ },
25
+ "dependencies": {
26
+ "@atlaskit/adf-schema": "^25.1.0",
27
+ "@babel/runtime": "^7.0.0",
28
+ "prosemirror-state": "1.3.4"
29
+ },
30
+ "devDependencies": {
31
+ "@atlaskit/editor-test-helpers": "^18.0.0",
32
+ "@atlassian/atlassian-frontend-prettier-config-1.0.0": "npm:@atlassian/atlassian-frontend-prettier-config@1.0.0",
33
+ "@types/prosemirror-history": "^1.0.1",
34
+ "@types/prosemirror-model": "^1.11.0",
35
+ "@types/prosemirror-transform": "^1.1.0",
36
+ "prosemirror-history": "^1.1.3",
37
+ "prosemirror-model": "1.14.3",
38
+ "prosemirror-transform": "1.3.2"
39
+ },
40
+ "techstack": {
41
+ "@atlassian/frontend": {
42
+ "import-structure": [
43
+ "atlassian-conventions"
44
+ ],
45
+ "circular-dependencies": [
46
+ "file-and-folder-level"
47
+ ]
48
+ }
49
+ },
50
+ "prettier": "@atlassian/atlassian-frontend-prettier-config-1.0.0"
51
+ }
package/report.api.md ADDED
@@ -0,0 +1,85 @@
1
+ <!-- API Report Version: 2.3 -->
2
+
3
+ ## API Report File for "@atlaskit/prosemirror-collab"
4
+
5
+ > Do not edit this file. This report is auto-generated using [API Extractor](https://api-extractor.com/).
6
+ > [Learn more about API reports](https://hello.atlassian.net/wiki/spaces/UR/pages/1825484529/Package+API+Reports)
7
+
8
+ ### Table of contents
9
+
10
+ - [Main Entry Types](#main-entry-types)
11
+ - [Peer Dependencies](#peer-dependencies)
12
+
13
+ ### Main Entry Types
14
+
15
+ <!--SECTION START: Main Entry Types-->
16
+
17
+ ```ts
18
+ import { EditorState } from 'prosemirror-state';
19
+ import { Plugin as Plugin_2 } from 'prosemirror-state';
20
+ import type { Step } from 'prosemirror-transform';
21
+ import { Transaction } from 'prosemirror-state';
22
+ import type { Transform } from 'prosemirror-transform';
23
+
24
+ // @public (undocumented)
25
+ export function collab(config?: CollabConfig): Plugin_2;
26
+
27
+ // @public (undocumented)
28
+ type CollabConfig = {
29
+ version?: number;
30
+ clientID?: null | number | string;
31
+ };
32
+
33
+ // @public (undocumented)
34
+ export function getVersion(state: EditorState): number;
35
+
36
+ // @public (undocumented)
37
+ class Rebaseable {
38
+ constructor(step: Step, inverted: Step, origin: Transform);
39
+ // (undocumented)
40
+ readonly inverted: Step;
41
+ // (undocumented)
42
+ readonly origin: Transform;
43
+ // (undocumented)
44
+ readonly step: Step;
45
+ }
46
+
47
+ // @public (undocumented)
48
+ export function rebaseSteps(
49
+ steps: readonly Rebaseable[],
50
+ over: readonly Step[],
51
+ transform: Transform,
52
+ ): Rebaseable[];
53
+
54
+ // @public (undocumented)
55
+ export function receiveTransaction(
56
+ state: EditorState,
57
+ steps: readonly Step[],
58
+ clientIDs: readonly (number | string)[],
59
+ options?: {
60
+ mapSelectionBackward?: boolean;
61
+ },
62
+ ): Transaction<any>;
63
+
64
+ // @public (undocumented)
65
+ export function sendableSteps(state: EditorState): null | {
66
+ version: number;
67
+ steps: readonly Step[];
68
+ clientID: number | string;
69
+ origins: readonly Transaction[];
70
+ };
71
+
72
+ // (No @packageDocumentation comment for this package)
73
+ ```
74
+
75
+ <!--SECTION END: Main Entry Types-->
76
+
77
+ ### Peer Dependencies
78
+
79
+ <!--SECTION START: Peer Dependencies-->
80
+
81
+ ```json
82
+ {}
83
+ ```
84
+
85
+ <!--SECTION END: Peer Dependencies-->