@manuscripts/track-changes-plugin 0.0.3 → 0.2.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/dist/index.js CHANGED
@@ -1,17 +1,9 @@
1
- 'use strict';
1
+ import { PluginKey, Plugin } from 'prosemirror-state';
2
+ import debug from 'debug';
3
+ import { liftTarget, canJoin, Mapping, ReplaceStep, ReplaceAroundStep } from 'prosemirror-transform';
4
+ import { Fragment, Slice } from 'prosemirror-model';
2
5
 
3
- Object.defineProperty(exports, '__esModule', { value: true });
4
-
5
- var prosemirrorState = require('prosemirror-state');
6
- var debug = require('debug');
7
- var prosemirrorTransform = require('prosemirror-transform');
8
- var prosemirrorModel = require('prosemirror-model');
9
-
10
- function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'default' in e ? e : { 'default': e }; }
11
-
12
- var debug__default = /*#__PURE__*/_interopDefaultLegacy(debug);
13
-
14
- exports.TrackChangesAction = void 0;
6
+ var TrackChangesAction;
15
7
  (function (TrackChangesAction) {
16
8
  TrackChangesAction["skipTrack"] = "track-changes-skip-tracking";
17
9
  TrackChangesAction["setUserID"] = "track-changes-set-user-id";
@@ -20,7 +12,7 @@ exports.TrackChangesAction = void 0;
20
12
  TrackChangesAction["updateChanges"] = "track-changes-update-changes";
21
13
  TrackChangesAction["refreshChanges"] = "track-changes-refresh-changes";
22
14
  TrackChangesAction["applyAndRemoveChanges"] = "track-changes-apply-remove-changes";
23
- })(exports.TrackChangesAction || (exports.TrackChangesAction = {}));
15
+ })(TrackChangesAction || (TrackChangesAction = {}));
24
16
  /**
25
17
  * Gets the value of a meta field, action payload, of a defined track-changes action.
26
18
  * @param tr
@@ -83,7 +75,7 @@ function __classPrivateFieldSet(receiver, state, value, kind, f) {
83
75
  * See the License for the specific language governing permissions and
84
76
  * limitations under the License.
85
77
  */
86
- exports.CHANGE_OPERATION = void 0;
78
+ var CHANGE_OPERATION;
87
79
  (function (CHANGE_OPERATION) {
88
80
  CHANGE_OPERATION["insert"] = "insert";
89
81
  CHANGE_OPERATION["delete"] = "delete";
@@ -92,13 +84,13 @@ exports.CHANGE_OPERATION = void 0;
92
84
  CHANGE_OPERATION["unwrap_from_node"] = "unwrap_from_node";
93
85
  CHANGE_OPERATION["add_mark"] = "add_mark";
94
86
  CHANGE_OPERATION["remove_mark"] = "remove_mark";
95
- })(exports.CHANGE_OPERATION || (exports.CHANGE_OPERATION = {}));
96
- exports.CHANGE_STATUS = void 0;
87
+ })(CHANGE_OPERATION || (CHANGE_OPERATION = {}));
88
+ var CHANGE_STATUS;
97
89
  (function (CHANGE_STATUS) {
98
90
  CHANGE_STATUS["accepted"] = "accepted";
99
91
  CHANGE_STATUS["rejected"] = "rejected";
100
92
  CHANGE_STATUS["pending"] = "pending";
101
- })(exports.CHANGE_STATUS || (exports.CHANGE_STATUS = {}));
93
+ })(CHANGE_STATUS || (CHANGE_STATUS = {}));
102
94
 
103
95
  /*!
104
96
  * © 2021 Atypon Systems LLC
@@ -115,7 +107,7 @@ exports.CHANGE_STATUS = void 0;
115
107
  * See the License for the specific language governing permissions and
116
108
  * limitations under the License.
117
109
  */
118
- const logger = debug__default["default"]('track');
110
+ const logger = debug('track');
119
111
  const log = {
120
112
  info(str, obj) {
121
113
  if (obj) {
@@ -148,10 +140,10 @@ const log = {
148
140
  */
149
141
  const enableDebug = (enabled) => {
150
142
  if (enabled) {
151
- debug__default["default"].enable('track');
143
+ debug.enable('track');
152
144
  }
153
145
  else {
154
- debug__default["default"].disable();
146
+ debug.disable();
155
147
  }
156
148
  };
157
149
 
@@ -211,13 +203,13 @@ class ChangeSet {
211
203
  return rootNodes;
212
204
  }
213
205
  get pending() {
214
- return this.changeTree.filter((c) => c.attrs.status === exports.CHANGE_STATUS.pending);
206
+ return this.changeTree.filter((c) => c.attrs.status === CHANGE_STATUS.pending);
215
207
  }
216
208
  get accepted() {
217
- return this.changeTree.filter((c) => c.attrs.status === exports.CHANGE_STATUS.accepted);
209
+ return this.changeTree.filter((c) => c.attrs.status === CHANGE_STATUS.accepted);
218
210
  }
219
211
  get rejected() {
220
- return this.changeTree.filter((c) => c.attrs.status === exports.CHANGE_STATUS.rejected);
212
+ return this.changeTree.filter((c) => c.attrs.status === CHANGE_STATUS.rejected);
221
213
  }
222
214
  get textChanges() {
223
215
  return this.changes.filter((c) => c.type === 'text-change');
@@ -271,8 +263,8 @@ class ChangeSet {
271
263
  */
272
264
  static shouldNotDelete(change) {
273
265
  const { status, operation } = change.attrs;
274
- return ((operation === exports.CHANGE_OPERATION.insert && status === exports.CHANGE_STATUS.accepted) ||
275
- (operation === exports.CHANGE_OPERATION.delete && status === exports.CHANGE_STATUS.rejected));
266
+ return ((operation === CHANGE_OPERATION.insert && status === CHANGE_STATUS.accepted) ||
267
+ (operation === CHANGE_OPERATION.delete && status === CHANGE_STATUS.rejected));
276
268
  }
277
269
  /**
278
270
  * Determines whether a change should be deleted when applying it to the document.
@@ -280,8 +272,8 @@ class ChangeSet {
280
272
  */
281
273
  static shouldDeleteChange(change) {
282
274
  const { status, operation } = change.attrs;
283
- return ((operation === exports.CHANGE_OPERATION.insert && status === exports.CHANGE_STATUS.rejected) ||
284
- (operation === exports.CHANGE_OPERATION.delete && status === exports.CHANGE_STATUS.accepted));
275
+ return ((operation === CHANGE_OPERATION.insert && status === CHANGE_STATUS.rejected) ||
276
+ (operation === CHANGE_OPERATION.delete && status === CHANGE_STATUS.accepted));
285
277
  }
286
278
  /**
287
279
  * Checks whether change attributes contain all TrackedAttrs keys with non-undefined values
@@ -335,14 +327,17 @@ function deleteNode(node, pos, tr) {
335
327
  var _a;
336
328
  const startPos = tr.doc.resolve(pos + 1);
337
329
  const range = startPos.blockRange(tr.doc.resolve(startPos.pos - 2 + node.nodeSize));
338
- const targetDepth = range ? Number(prosemirrorTransform.liftTarget(range)) : NaN;
339
- if (range && !Number.isNaN(targetDepth)) {
330
+ const targetDepth = range && liftTarget(range);
331
+ // Check with typeof since with old prosemirror-transform targetDepth is undefined
332
+ if (range && typeof targetDepth === 'number') {
340
333
  return tr.lift(range, targetDepth);
341
334
  }
342
335
  const resPos = tr.doc.resolve(pos);
343
- const canMergeToNodeAbove = (resPos.parent !== tr.doc || resPos.nodeBefore) && ((_a = node.firstChild) === null || _a === void 0 ? void 0 : _a.isText);
336
+ // Block nodes can be deleted by just removing their start token which should then merge the text
337
+ // content to above node's content (if there is one)
338
+ const canMergeToNodeAbove = (resPos.parent !== tr.doc || resPos.nodeBefore) && node.isBlock && ((_a = node.firstChild) === null || _a === void 0 ? void 0 : _a.isText);
344
339
  if (canMergeToNodeAbove) {
345
- return tr.replaceWith(pos - 1, pos + 1, prosemirrorModel.Fragment.empty);
340
+ return tr.replaceWith(pos - 1, pos + 1, Fragment.empty);
346
341
  }
347
342
  else {
348
343
  // NOTE: there's an edge case where moving content is not possible but because the immediate
@@ -375,17 +370,18 @@ function deleteNode(node, pos, tr) {
375
370
  */
376
371
  function mergeNode(node, pos, tr) {
377
372
  var _a;
378
- if (prosemirrorTransform.canJoin(tr.doc, pos)) {
373
+ if (canJoin(tr.doc, pos)) {
379
374
  return tr.join(pos);
380
375
  }
381
- else if (prosemirrorTransform.canJoin(tr.doc, pos + node.nodeSize)) {
376
+ else if (canJoin(tr.doc, pos + node.nodeSize)) {
377
+ // TODO should copy the attributes from the merged node below
382
378
  return tr.join(pos + node.nodeSize);
383
379
  }
384
- // TODO is this the same thing as join?
380
+ // TODO is this the same thing as join to above?
385
381
  const resPos = tr.doc.resolve(pos);
386
382
  const canMergeToNodeAbove = (resPos.parent !== tr.doc || resPos.nodeBefore) && ((_a = node.firstChild) === null || _a === void 0 ? void 0 : _a.isText);
387
383
  if (canMergeToNodeAbove) {
388
- return tr.replaceWith(pos - 1, pos + 1, prosemirrorModel.Fragment.empty);
384
+ return tr.replaceWith(pos - 1, pos + 1, Fragment.empty);
389
385
  }
390
386
  return undefined;
391
387
  }
@@ -429,8 +425,8 @@ function getInlineNodeTrackedMarkData(node, schema) {
429
425
  node.marks.forEach((mark) => {
430
426
  if (mark.type === schema.marks.tracked_insert || mark.type === schema.marks.tracked_delete) {
431
427
  const operation = mark.type === schema.marks.tracked_insert
432
- ? exports.CHANGE_OPERATION.insert
433
- : exports.CHANGE_OPERATION.delete;
428
+ ? CHANGE_OPERATION.insert
429
+ : CHANGE_OPERATION.delete;
434
430
  marksTrackedData.push({ ...mark.attrs.dataTracked, operation });
435
431
  }
436
432
  });
@@ -504,16 +500,16 @@ function updateChangeChildrenAttributes(changes, tr, mapping) {
504
500
  * @param changes
505
501
  * @param deleteMap
506
502
  */
507
- function applyAcceptedRejectedChanges(tr, schema, changes, deleteMap = new prosemirrorTransform.Mapping()) {
503
+ function applyAcceptedRejectedChanges(tr, schema, changes, deleteMap = new Mapping()) {
508
504
  changes.forEach((change) => {
509
- if (change.attrs.status === exports.CHANGE_STATUS.pending) {
505
+ if (change.attrs.status === CHANGE_STATUS.pending) {
510
506
  return;
511
507
  }
512
508
  // Map change.from and skip those which dont need to be applied
513
509
  // or were already deleted by an applied block delete
514
510
  const { pos: from, deleted } = deleteMap.mapResult(change.from), node = tr.doc.nodeAt(from), noChangeNeeded = deleted || ChangeSet.shouldNotDelete(change);
515
511
  if (!node) {
516
- log.warn('no node found to update for change', change);
512
+ !deleted && log.warn('no node found to update for change', change);
517
513
  return;
518
514
  }
519
515
  if (ChangeSet.isTextChange(change) && noChangeNeeded) {
@@ -628,8 +624,8 @@ function fixInconsistentChanges(changeSet, trackUserID, newTr, schema) {
628
624
  const newAttrs = {
629
625
  ...((!id || iteratedIds.has(id) || id.length === 0) && { id: uuidv4() }),
630
626
  ...(!userID && { userID: trackUserID }),
631
- ...(!operation && { operation: exports.CHANGE_OPERATION.insert }),
632
- ...(!status && { status: exports.CHANGE_STATUS.pending }),
627
+ ...(!operation && { operation: CHANGE_OPERATION.insert }),
628
+ ...(!status && { status: CHANGE_STATUS.pending }),
633
629
  ...(!createdAt && { createdAt: Date.now() }),
634
630
  };
635
631
  if (Object.keys(newAttrs).length > 0) {
@@ -658,7 +654,7 @@ function fixInconsistentChanges(changeSet, trackUserID, newTr, schema) {
658
654
  */
659
655
  function markInlineNodeChange(node, newTrackAttrs, schema) {
660
656
  const filtered = node.marks.filter((m) => m.type !== schema.marks.tracked_insert && m.type !== schema.marks.tracked_delete);
661
- const mark = newTrackAttrs.operation === exports.CHANGE_OPERATION.insert
657
+ const mark = newTrackAttrs.operation === CHANGE_OPERATION.insert
662
658
  ? schema.marks.tracked_insert
663
659
  : schema.marks.tracked_delete;
664
660
  const createdMark = mark.create({
@@ -678,7 +674,7 @@ function recurseNodeContent(node, newTrackAttrs, schema) {
678
674
  return node.type.create({
679
675
  ...node.attrs,
680
676
  dataTracked: addTrackIdIfDoesntExist(newTrackAttrs),
681
- }, prosemirrorModel.Fragment.fromArray(updatedChildren), node.marks);
677
+ }, Fragment.fromArray(updatedChildren), node.marks);
682
678
  }
683
679
  else {
684
680
  log.error(`unhandled node type: "${node.type.name}"`, node);
@@ -691,7 +687,7 @@ function setFragmentAsInserted(inserted, insertAttrs, schema) {
691
687
  inserted.forEach((n) => {
692
688
  updatedInserted.push(recurseNodeContent(n, insertAttrs, schema));
693
689
  });
694
- return updatedInserted.length === 0 ? inserted : prosemirrorModel.Fragment.fromArray(updatedInserted);
690
+ return updatedInserted.length === 0 ? inserted : Fragment.fromArray(updatedInserted);
695
691
  }
696
692
 
697
693
  /*!
@@ -712,13 +708,13 @@ function setFragmentAsInserted(inserted, insertAttrs, schema) {
712
708
  function createNewInsertAttrs(attrs) {
713
709
  return {
714
710
  ...attrs,
715
- operation: exports.CHANGE_OPERATION.insert,
711
+ operation: CHANGE_OPERATION.insert,
716
712
  };
717
713
  }
718
714
  function createNewDeleteAttrs(attrs) {
719
715
  return {
720
716
  ...attrs,
721
- operation: exports.CHANGE_OPERATION.delete,
717
+ operation: CHANGE_OPERATION.delete,
722
718
  };
723
719
  }
724
720
 
@@ -761,7 +757,7 @@ function getMergedNode(node, currentDepth, depth, first) {
761
757
  };
762
758
  }
763
759
  const result = [];
764
- let merged = prosemirrorModel.Fragment.empty;
760
+ let merged = Fragment.empty;
765
761
  node.content.forEach((n, _, i) => {
766
762
  if ((first && i === 0) || (!first && i === node.childCount - 1)) {
767
763
  const { mergedNodeContent, unmergedContent } = getMergedNode(n, currentDepth + 1, depth, first);
@@ -776,7 +772,7 @@ function getMergedNode(node, currentDepth, depth, first) {
776
772
  });
777
773
  return {
778
774
  mergedNodeContent: merged,
779
- unmergedContent: result.length > 0 ? prosemirrorModel.Fragment.fromArray(result) : undefined,
775
+ unmergedContent: result.length > 0 ? Fragment.fromArray(result) : undefined,
780
776
  };
781
777
  }
782
778
  /**
@@ -839,7 +835,7 @@ function deleteTextIfInserted(node, pos, newTr, schema, deleteAttrs, from, to) {
839
835
  // Math.max(pos, from) is for picking always the start of the node,
840
836
  // not the start of the change (which might span multiple nodes).
841
837
  // Pos can be less than from as nodesBetween iterates through all nodes starting from the top block node
842
- newTr.replaceWith(start, end, prosemirrorModel.Fragment.empty);
838
+ newTr.replaceWith(start, end, Fragment.empty);
843
839
  return start;
844
840
  }
845
841
  else {
@@ -869,7 +865,7 @@ function deleteTextIfInserted(node, pos, newTr, schema, deleteAttrs, from, to) {
869
865
  */
870
866
  function deleteOrSetNodeDeleted(node, pos, newTr, deleteAttrs) {
871
867
  const dataTracked = node.attrs.dataTracked;
872
- const wasInsertedBySameUser = (dataTracked === null || dataTracked === void 0 ? void 0 : dataTracked.operation) === exports.CHANGE_OPERATION.insert && dataTracked.userID === deleteAttrs.userID;
868
+ const wasInsertedBySameUser = (dataTracked === null || dataTracked === void 0 ? void 0 : dataTracked.operation) === CHANGE_OPERATION.insert && dataTracked.userID === deleteAttrs.userID;
873
869
  if (wasInsertedBySameUser) {
874
870
  deleteNode(node, pos, newTr);
875
871
  }
@@ -907,7 +903,7 @@ function deleteOrSetNodeDeleted(node, pos, newTr, deleteAttrs) {
907
903
  * @returns mapping adjusted by the applied operations & modified insert slice
908
904
  */
909
905
  function deleteAndMergeSplitNodes(from, to, gap, startDoc, newTr, schema, trackAttrs, insertSlice) {
910
- const deleteMap = new prosemirrorTransform.Mapping();
906
+ const deleteMap = new Mapping();
911
907
  const mergedInsertPos = undefined;
912
908
  // No deletion applied, return default values
913
909
  if (from === to) {
@@ -925,10 +921,22 @@ function deleteAndMergeSplitNodes(from, to, gap, startDoc, newTr, schema, trackA
925
921
  const { pos: offsetPos, deleted: nodeWasDeleted } = deleteMap.mapResult(pos, 1);
926
922
  const offsetFrom = deleteMap.map(from, -1);
927
923
  const offsetTo = deleteMap.map(to, 1);
928
- const wasWithinGap = gap && offsetPos >= deleteMap.map(gap.start, -1);
929
924
  const nodeEnd = offsetPos + node.nodeSize;
925
+ // So this insane boolean checks for ReplaceAroundStep gaps and whether the node should be skipped
926
+ // since the content inside gap should stay unchanged.
927
+ // All other nodes except text nodes consist of one start and end token (or just a single token for atoms).
928
+ // For them we can just check whether the start token is within the gap eg pos is 10 when gap (8, 18) to
929
+ // determine whether it should be skipped.
930
+ // For text nodes though, since they are continous, they might only partially be enclosed in the gap
931
+ // eg. pos 10 when gap is (8, 18) BUT if their nodeEnd goes past the gap's end eg nodeEnd 20 they actually
932
+ // are altered and should not be skipped.
933
+ // @TODO ATM 20.7.2022 there doesn't seem to be tests that capture this.
934
+ const wasWithinGap = gap &&
935
+ ((!node.isText && offsetPos >= deleteMap.map(gap.start, -1)) ||
936
+ (node.isText &&
937
+ offsetPos <= deleteMap.map(gap.start, -1) &&
938
+ nodeEnd >= deleteMap.map(gap.end, -1)));
930
939
  let step = newTr.steps[newTr.steps.length - 1];
931
- // debugger
932
940
  // nodeEnd > offsetFrom -> delete touches this node
933
941
  // eg (del 6 10) <p 5>|<t 6>cdf</t 9></p 10>| -> <p> nodeEnd 10 > from 6
934
942
  //
@@ -937,6 +945,7 @@ function deleteAndMergeSplitNodes(from, to, gap, startDoc, newTr, schema, trackA
937
945
  // But from what I remember what it safeguards against is, when you've already deleted a node
938
946
  // say an inserted blockquote that had all its children deleted, nodesBetween still iterates over those
939
947
  // nodes and therefore we have to make this check to ensure they still exist in the doc.
948
+ //
940
949
  if (nodeEnd > offsetFrom && !nodeWasDeleted && !wasWithinGap) {
941
950
  // |<p>asdf</p>| -> node deleted completely
942
951
  const nodeCompletelyDeleted = offsetPos >= offsetFrom && nodeEnd <= offsetTo;
@@ -1029,7 +1038,7 @@ function deleteAndMergeSplitNodes(from, to, gap, startDoc, newTr, schema, trackA
1029
1038
  deleteMap,
1030
1039
  mergedInsertPos,
1031
1040
  newSliceContent: updatedSliceNodes
1032
- ? prosemirrorModel.Fragment.fromArray(updatedSliceNodes)
1041
+ ? Fragment.fromArray(updatedSliceNodes)
1033
1042
  : insertSlice.content,
1034
1043
  };
1035
1044
  }
@@ -1128,13 +1137,13 @@ function trackReplaceAroundStep(step, oldState, newTr, attrs) {
1128
1137
  // the sides should be equal. TODO can they be other than 0?
1129
1138
  const openStart = slice.openStart !== slice.openEnd || newSliceContent.size === 0 ? 0 : slice.openStart;
1130
1139
  const openEnd = slice.openStart !== slice.openEnd || newSliceContent.size === 0 ? 0 : slice.openEnd;
1131
- let insertedSlice = new prosemirrorModel.Slice(setFragmentAsInserted(newSliceContent, createNewInsertAttrs(attrs), oldState.schema), openStart, openEnd);
1140
+ let insertedSlice = new Slice(setFragmentAsInserted(newSliceContent, createNewInsertAttrs(attrs), oldState.schema), openStart, openEnd);
1132
1141
  if (gap.size > 0) {
1133
1142
  log.info('insertedSlice before inserted gap', insertedSlice);
1134
1143
  insertedSlice = insertedSlice.insertAt(insertedSlice.size === 0 ? 0 : insert, gap.content);
1135
1144
  log.info('insertedSlice after inserted gap', insertedSlice);
1136
1145
  }
1137
- const newStep = new prosemirrorTransform.ReplaceStep(deleteMap.map(gapFrom), deleteMap.map(gapTo), insertedSlice, false);
1146
+ const newStep = new ReplaceStep(deleteMap.map(gapFrom), deleteMap.map(gapTo), insertedSlice, false);
1138
1147
  const stepResult = newTr.maybeStep(newStep);
1139
1148
  if (stepResult.failed) {
1140
1149
  log.error(`insert ReplaceStep failed: "${stepResult.failed}"`, newStep);
@@ -1179,10 +1188,11 @@ function trackReplaceStep(step, oldState, newTr, attrs) {
1179
1188
  log.error(`invert ReplaceStep failed: "${stepResult.failed}"`, newStep);
1180
1189
  return;
1181
1190
  }
1191
+ log.info('TR: steps before applying delete', [...newTr.steps]);
1182
1192
  // First apply the deleted range and update the insert slice to not include content that was deleted,
1183
1193
  // eg partial nodes in an open-ended slice
1184
1194
  const { deleteMap, mergedInsertPos, newSliceContent } = deleteAndMergeSplitNodes(fromA, toA, undefined, oldState.doc, newTr, oldState.schema, attrs, slice);
1185
- log.info('TR: new steps after applying delete', [...newTr.steps]);
1195
+ log.info('TR: steps after applying delete', [...newTr.steps]);
1186
1196
  const adjustedInsertPos = mergedInsertPos !== null && mergedInsertPos !== void 0 ? mergedInsertPos : deleteMap.map(toA);
1187
1197
  if (newSliceContent.size > 0) {
1188
1198
  log.info('newSliceContent', newSliceContent);
@@ -1190,8 +1200,8 @@ function trackReplaceStep(step, oldState, newTr, attrs) {
1190
1200
  // the sides should be equal. TODO can they be other than 0?
1191
1201
  const openStart = slice.openStart !== slice.openEnd ? 0 : slice.openStart;
1192
1202
  const openEnd = slice.openStart !== slice.openEnd ? 0 : slice.openEnd;
1193
- const insertedSlice = new prosemirrorModel.Slice(setFragmentAsInserted(newSliceContent, createNewInsertAttrs(attrs), oldState.schema), openStart, openEnd);
1194
- const newStep = new prosemirrorTransform.ReplaceStep(adjustedInsertPos, adjustedInsertPos, insertedSlice);
1203
+ const insertedSlice = new Slice(setFragmentAsInserted(newSliceContent, createNewInsertAttrs(attrs), oldState.schema), openStart, openEnd);
1204
+ const newStep = new ReplaceStep(adjustedInsertPos, adjustedInsertPos, insertedSlice);
1195
1205
  const stepResult = newTr.maybeStep(newStep);
1196
1206
  if (stepResult.failed) {
1197
1207
  log.error(`insert ReplaceStep failed: "${stepResult.failed}"`, newStep);
@@ -1217,11 +1227,9 @@ function trackReplaceStep(step, oldState, newTr, attrs) {
1217
1227
  * This skips the direct dependency to prosemirror-state where multiple versions might cause conflicts
1218
1228
  * as the created instances might belong to different prosemirror-state import than one used in the editor.
1219
1229
  * @param sel
1220
- * @param doc
1221
- * @param from
1222
1230
  * @returns
1223
1231
  */
1224
- const getSelectionStaticCreate = (sel, doc, from) => Object.getPrototypeOf(sel).constructor.create(doc, from);
1232
+ const getSelectionStaticConstructor = (sel) => Object.getPrototypeOf(sel).constructor;
1225
1233
  /**
1226
1234
  * Inverts transactions to wrap their contents/operations with track data instead
1227
1235
  *
@@ -1238,11 +1246,10 @@ const getSelectionStaticCreate = (sel, doc, from) => Object.getPrototypeOf(sel).
1238
1246
  * @returns newTr that inverts the initial tr and applies track attributes/marks
1239
1247
  */
1240
1248
  function trackTransaction(tr, oldState, newTr, userID) {
1241
- var _a;
1242
1249
  const emptyAttrs = {
1243
1250
  userID,
1244
1251
  createdAt: tr.time,
1245
- status: exports.CHANGE_STATUS.pending,
1252
+ status: CHANGE_STATUS.pending,
1246
1253
  };
1247
1254
  // Must use constructor.name instead of instanceof as aliasing prosemirror-state is a lot more
1248
1255
  // difficult than prosemirror-transform
@@ -1257,18 +1264,23 @@ function trackTransaction(tr, oldState, newTr, userID) {
1257
1264
  'This is probably an error with the library, please report back to maintainers with a reproduction if possible', newTr);
1258
1265
  return;
1259
1266
  }
1260
- else if (!(step instanceof prosemirrorTransform.ReplaceStep) && step.constructor.name === 'ReplaceStep') {
1267
+ else if (!(step instanceof ReplaceStep) && step.constructor.name === 'ReplaceStep') {
1261
1268
  console.error('@manuscripts/track-changes-plugin: Multiple prosemirror-transform packages imported, alias/dedupe them ' +
1262
1269
  'or instanceof checks fail as well as creating new steps');
1263
1270
  return;
1264
1271
  }
1265
- else if (step instanceof prosemirrorTransform.ReplaceStep) {
1272
+ else if (step instanceof ReplaceStep) {
1266
1273
  const selectionPos = trackReplaceStep(step, oldState, newTr, emptyAttrs);
1267
1274
  if (!wasNodeSelection) {
1268
- newTr.setSelection(getSelectionStaticCreate(tr.selection, newTr.doc, selectionPos));
1275
+ const sel = getSelectionStaticConstructor(tr.selection);
1276
+ // Use Selection.near to fix selections that point to a block node instead of inline content
1277
+ // eg when inserting a complete new paragraph. -1 finds the first valid position moving backwards
1278
+ // inside the content
1279
+ const near = sel.near(newTr.doc.resolve(selectionPos), -1);
1280
+ newTr.setSelection(near);
1269
1281
  }
1270
1282
  }
1271
- else if (step instanceof prosemirrorTransform.ReplaceAroundStep) {
1283
+ else if (step instanceof ReplaceAroundStep) {
1272
1284
  trackReplaceAroundStep(step, oldState, newTr, emptyAttrs);
1273
1285
  // } else if (step instanceof AddMarkStep) {
1274
1286
  // } else if (step instanceof RemoveMarkStep) {
@@ -1276,32 +1288,32 @@ function trackTransaction(tr, oldState, newTr, userID) {
1276
1288
  // TODO: here we could check whether adjacent inserts & deletes cancel each other out.
1277
1289
  // However, this should not be done by diffing and only matching node or char by char instead since
1278
1290
  // it's A easier and B more intuitive to user.
1279
- const { meta } = tr;
1280
- // This is quite non-optimal in some sense but to ensure no information is lost
1281
- // we have to re-add all the old meta keys, such as inputType or uiEvent.
1282
- // This should prevent bugs incase other plugins/widgets rely upon them existing (and they
1283
- // are not able to process the transactions before track-changes).
1284
- // TODO: will this cause race-condition if a meta causes another appendTransaction to fire
1285
- Object.keys(meta).forEach((key) => newTr.setMeta(key, tr.getMeta(key)));
1291
+ // The old meta keys are not copied to the new transaction since this will cause race-conditions
1292
+ // when a single meta-field is expected to having been processed / removed. Generic input meta keys,
1293
+ // inputType and uiEvent, are re-added since some plugins might depend on them and process the transaction
1294
+ // after track-changes plugin.
1295
+ tr.getMeta('inputType') && newTr.setMeta('inputType', tr.getMeta('inputType'));
1296
+ tr.getMeta('uiEvent') && newTr.setMeta('uiEvent', tr.getMeta('uiEvent'));
1286
1297
  });
1287
1298
  // This is kinda hacky solution at the moment to maintain NodeSelections over transactions
1288
- // These are required by at least cross-references that need it to activate the selector pop-up
1299
+ // These are required by at least cross-references and links to activate their selector pop-ups
1289
1300
  if (wasNodeSelection) {
1290
- const mappedPos = newTr.mapping.map(tr.selection.from);
1291
- const resPos = newTr.doc.resolve(mappedPos);
1292
- const nodePos = mappedPos - (((_a = resPos.nodeBefore) === null || _a === void 0 ? void 0 : _a.nodeSize) || 0);
1293
- newTr.setSelection(getSelectionStaticCreate(tr.selection, newTr.doc, nodePos));
1301
+ // And -1 here is necessary to keep the selection pointing at the start of the node
1302
+ // (or something, breaks with cross-references otherwise)
1303
+ const mappedPos = newTr.mapping.map(tr.selection.from, -1);
1304
+ const sel = getSelectionStaticConstructor(tr.selection);
1305
+ newTr.setSelection(sel.create(newTr.doc, mappedPos));
1294
1306
  }
1295
1307
  log.info('NEW transaction', newTr);
1296
1308
  return newTr;
1297
1309
  }
1298
1310
 
1299
- exports.TrackChangesStatus = void 0;
1311
+ var TrackChangesStatus;
1300
1312
  (function (TrackChangesStatus) {
1301
1313
  TrackChangesStatus["enabled"] = "enabled";
1302
1314
  TrackChangesStatus["viewSnapshots"] = "view-snapshots";
1303
1315
  TrackChangesStatus["disabled"] = "disabled";
1304
- })(exports.TrackChangesStatus || (exports.TrackChangesStatus = {}));
1316
+ })(TrackChangesStatus || (TrackChangesStatus = {}));
1305
1317
 
1306
1318
  /*!
1307
1319
  * © 2021 Atypon Systems LLC
@@ -1318,12 +1330,7 @@ exports.TrackChangesStatus = void 0;
1318
1330
  * See the License for the specific language governing permissions and
1319
1331
  * limitations under the License.
1320
1332
  */
1321
- const trackChangesPluginKey = new prosemirrorState.PluginKey('track-changes');
1322
- // TODO remove
1323
- const infiniteLoopCounter = {
1324
- start: 0,
1325
- iters: 0,
1326
- };
1333
+ const trackChangesPluginKey = new PluginKey('track-changes');
1327
1334
  /**
1328
1335
  * The ProseMirror plugin needed to enable track-changes.
1329
1336
  *
@@ -1336,24 +1343,25 @@ const trackChangesPlugin = (opts = { userID: 'anonymous:Anonymous' }) => {
1336
1343
  if (debug) {
1337
1344
  enableDebug(true);
1338
1345
  }
1339
- return new prosemirrorState.Plugin({
1346
+ return new Plugin({
1340
1347
  key: trackChangesPluginKey,
1341
1348
  props: {
1342
1349
  editable(state) {
1343
- return this.getState(state).status !== exports.TrackChangesStatus.viewSnapshots;
1350
+ var _a;
1351
+ return ((_a = trackChangesPluginKey.getState(state)) === null || _a === void 0 ? void 0 : _a.status) !== TrackChangesStatus.viewSnapshots;
1344
1352
  },
1345
1353
  },
1346
1354
  state: {
1347
1355
  init(_config, state) {
1348
1356
  return {
1349
- status: exports.TrackChangesStatus.enabled,
1357
+ status: TrackChangesStatus.enabled,
1350
1358
  userID,
1351
1359
  changeSet: findChanges(state),
1352
1360
  };
1353
1361
  },
1354
1362
  apply(tr, pluginState, _oldState, newState) {
1355
- const setUserID = getAction(tr, exports.TrackChangesAction.setUserID);
1356
- const setStatus = getAction(tr, exports.TrackChangesAction.setPluginStatus);
1363
+ const setUserID = getAction(tr, TrackChangesAction.setUserID);
1364
+ const setStatus = getAction(tr, TrackChangesAction.setPluginStatus);
1357
1365
  if (setUserID) {
1358
1366
  return { ...pluginState, userID: setUserID };
1359
1367
  }
@@ -1364,12 +1372,12 @@ const trackChangesPlugin = (opts = { userID: 'anonymous:Anonymous' }) => {
1364
1372
  changeSet: findChanges(newState),
1365
1373
  };
1366
1374
  }
1367
- else if (pluginState.status === exports.TrackChangesStatus.disabled) {
1375
+ else if (pluginState.status === TrackChangesStatus.disabled) {
1368
1376
  return { ...pluginState, changeSet: new ChangeSet() };
1369
1377
  }
1370
1378
  let { changeSet, ...rest } = pluginState;
1371
- const updatedChangeIds = getAction(tr, exports.TrackChangesAction.updateChanges);
1372
- if (updatedChangeIds || getAction(tr, exports.TrackChangesAction.refreshChanges)) {
1379
+ const updatedChangeIds = getAction(tr, TrackChangesAction.updateChanges);
1380
+ if (updatedChangeIds || getAction(tr, TrackChangesAction.refreshChanges)) {
1373
1381
  changeSet = findChanges(newState);
1374
1382
  }
1375
1383
  return {
@@ -1388,47 +1396,37 @@ const trackChangesPlugin = (opts = { userID: 'anonymous:Anonymous' }) => {
1388
1396
  appendTransaction(trs, oldState, newState) {
1389
1397
  const pluginState = trackChangesPluginKey.getState(newState);
1390
1398
  if (!pluginState ||
1391
- pluginState.status === exports.TrackChangesStatus.disabled ||
1399
+ pluginState.status === TrackChangesStatus.disabled ||
1392
1400
  !(editorView === null || editorView === void 0 ? void 0 : editorView.editable)) {
1393
1401
  return null;
1394
1402
  }
1395
- if (infiniteLoopCounter.start < Date.now() - 10000) {
1396
- infiniteLoopCounter.start = Date.now();
1397
- infiniteLoopCounter.iters = 0;
1398
- }
1399
- if (infiniteLoopCounter.iters >= 100) {
1400
- console.error('Detected probable infinite loop in track changes!');
1401
- return null;
1402
- }
1403
1403
  const { userID, changeSet } = pluginState;
1404
1404
  let createdTr = newState.tr, docChanged = false;
1405
1405
  log.info('TRS', trs);
1406
1406
  trs.forEach((tr) => {
1407
1407
  const wasAppended = tr.getMeta('appendedTransaction');
1408
1408
  const skipMetaUsed = skipTrsWithMetas.some((m) => tr.getMeta(m) || (wasAppended === null || wasAppended === void 0 ? void 0 : wasAppended.getMeta(m)));
1409
- const skipTrackUsed = getAction(tr, exports.TrackChangesAction.skipTrack) ||
1410
- (wasAppended && getAction(wasAppended, exports.TrackChangesAction.skipTrack));
1409
+ const skipTrackUsed = getAction(tr, TrackChangesAction.skipTrack) ||
1410
+ (wasAppended && getAction(wasAppended, TrackChangesAction.skipTrack));
1411
1411
  if (tr.docChanged && !skipMetaUsed && !skipTrackUsed && !tr.getMeta('history$')) {
1412
1412
  createdTr = trackTransaction(tr, oldState, createdTr, userID);
1413
- createdTr.setMeta('origin', trackChangesPluginKey);
1414
- infiniteLoopCounter.iters += 1;
1415
1413
  }
1416
1414
  docChanged = docChanged || tr.docChanged;
1417
- const setChangeStatuses = getAction(tr, exports.TrackChangesAction.setChangeStatuses);
1415
+ const setChangeStatuses = getAction(tr, TrackChangesAction.setChangeStatuses);
1418
1416
  if (setChangeStatuses) {
1419
1417
  const { status, ids } = setChangeStatuses;
1420
1418
  ids.forEach((changeId) => {
1421
1419
  const change = changeSet === null || changeSet === void 0 ? void 0 : changeSet.get(changeId);
1422
1420
  if (change) {
1423
1421
  createdTr = updateChangeAttrs(createdTr, change, { status }, oldState.schema);
1424
- setAction(createdTr, exports.TrackChangesAction.updateChanges, [change.id]);
1422
+ setAction(createdTr, TrackChangesAction.updateChanges, [change.id]);
1425
1423
  }
1426
1424
  });
1427
1425
  }
1428
- else if (getAction(tr, exports.TrackChangesAction.applyAndRemoveChanges)) {
1426
+ else if (getAction(tr, TrackChangesAction.applyAndRemoveChanges)) {
1429
1427
  const mapping = applyAcceptedRejectedChanges(createdTr, oldState.schema, changeSet.nodeChanges);
1430
1428
  applyAcceptedRejectedChanges(createdTr, oldState.schema, changeSet.textChanges, mapping);
1431
- setAction(createdTr, exports.TrackChangesAction.refreshChanges, true);
1429
+ setAction(createdTr, TrackChangesAction.refreshChanges, true);
1432
1430
  }
1433
1431
  });
1434
1432
  const changed = pluginState.changeSet.hasInconsistentData &&
@@ -1437,7 +1435,8 @@ const trackChangesPlugin = (opts = { userID: 'anonymous:Anonymous' }) => {
1437
1435
  log.warn('had to fix inconsistent changes in', createdTr);
1438
1436
  }
1439
1437
  if (docChanged || createdTr.docChanged || changed) {
1440
- return setAction(createdTr, exports.TrackChangesAction.refreshChanges, true);
1438
+ createdTr.setMeta('origin', trackChangesPluginKey);
1439
+ return setAction(createdTr, TrackChangesAction.refreshChanges, true);
1441
1440
  }
1442
1441
  return null;
1443
1442
  },
@@ -1475,11 +1474,11 @@ const setTrackingStatus = (status) => (state, dispatch) => {
1475
1474
  let newStatus = status;
1476
1475
  if (newStatus === undefined) {
1477
1476
  newStatus =
1478
- currentStatus === exports.TrackChangesStatus.enabled
1479
- ? exports.TrackChangesStatus.disabled
1480
- : exports.TrackChangesStatus.enabled;
1477
+ currentStatus === TrackChangesStatus.enabled
1478
+ ? TrackChangesStatus.disabled
1479
+ : TrackChangesStatus.enabled;
1481
1480
  }
1482
- dispatch && dispatch(setAction(state.tr, exports.TrackChangesAction.setPluginStatus, newStatus));
1481
+ dispatch && dispatch(setAction(state.tr, TrackChangesAction.setPluginStatus, newStatus));
1483
1482
  return true;
1484
1483
  }
1485
1484
  return false;
@@ -1491,7 +1490,7 @@ const setTrackingStatus = (status) => (state, dispatch) => {
1491
1490
  */
1492
1491
  const setChangeStatuses = (status, ids) => (state, dispatch) => {
1493
1492
  dispatch &&
1494
- dispatch(setAction(state.tr, exports.TrackChangesAction.setChangeStatuses, {
1493
+ dispatch(setAction(state.tr, TrackChangesAction.setChangeStatuses, {
1495
1494
  status,
1496
1495
  ids,
1497
1496
  }));
@@ -1502,21 +1501,21 @@ const setChangeStatuses = (status, ids) => (state, dispatch) => {
1502
1501
  * @param userID
1503
1502
  */
1504
1503
  const setUserID = (userID) => (state, dispatch) => {
1505
- dispatch && dispatch(setAction(state.tr, exports.TrackChangesAction.setUserID, userID));
1504
+ dispatch && dispatch(setAction(state.tr, TrackChangesAction.setUserID, userID));
1506
1505
  return true;
1507
1506
  };
1508
1507
  /**
1509
1508
  * Appends a transaction that applies all 'accepted' and 'rejected' changes to the document.
1510
1509
  */
1511
1510
  const applyAndRemoveChanges = () => (state, dispatch) => {
1512
- dispatch && dispatch(setAction(state.tr, exports.TrackChangesAction.applyAndRemoveChanges, true));
1511
+ dispatch && dispatch(setAction(state.tr, TrackChangesAction.applyAndRemoveChanges, true));
1513
1512
  return true;
1514
1513
  };
1515
1514
  /**
1516
1515
  * Runs `findChanges` to iterate over the document to collect changes into a new ChangeSet.
1517
1516
  */
1518
1517
  const refreshChanges = () => (state, dispatch) => {
1519
- dispatch && dispatch(setAction(state.tr, exports.TrackChangesAction.updateChanges, []));
1518
+ dispatch && dispatch(setAction(state.tr, TrackChangesAction.updateChanges, []));
1520
1519
  return true;
1521
1520
  };
1522
1521
  /**
@@ -1546,10 +1545,4 @@ var commands = /*#__PURE__*/Object.freeze({
1546
1545
  setParagraphTestAttribute: setParagraphTestAttribute
1547
1546
  });
1548
1547
 
1549
- exports.ChangeSet = ChangeSet;
1550
- exports.enableDebug = enableDebug;
1551
- exports.getAction = getAction;
1552
- exports.setAction = setAction;
1553
- exports.trackChangesPlugin = trackChangesPlugin;
1554
- exports.trackChangesPluginKey = trackChangesPluginKey;
1555
- exports.trackCommands = commands;
1548
+ export { CHANGE_OPERATION, CHANGE_STATUS, ChangeSet, TrackChangesAction, TrackChangesStatus, enableDebug, getAction, setAction, trackChangesPlugin, trackChangesPluginKey, commands as trackCommands };