@manuscripts/track-changes-plugin 0.4.2 → 0.4.4-LEAN-1839
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/actions.d.ts +0 -2
- package/dist/change-steps/diffChangeSteps.d.ts +1 -18
- package/dist/change-steps/matchInserted.d.ts +10 -0
- package/dist/changes/findChanges.d.ts +3 -3
- package/dist/commands.d.ts +1 -5
- package/dist/index.cjs +86 -102
- package/dist/index.js +86 -102
- package/dist/mutate/deleteText.d.ts +1 -0
- package/package.json +16 -13
package/dist/actions.d.ts
CHANGED
|
@@ -21,7 +21,6 @@ export declare enum TrackChangesAction {
|
|
|
21
21
|
setUserID = "track-changes-set-user-id",
|
|
22
22
|
setPluginStatus = "track-changes-set-track-status",
|
|
23
23
|
setChangeStatuses = "track-changes-set-change-statuses",
|
|
24
|
-
updateChanges = "track-changes-update-changes",
|
|
25
24
|
refreshChanges = "track-changes-refresh-changes",
|
|
26
25
|
applyAndRemoveChanges = "track-changes-apply-remove-changes"
|
|
27
26
|
}
|
|
@@ -33,7 +32,6 @@ export declare type TrackChangesActionParams = {
|
|
|
33
32
|
status: CHANGE_STATUS;
|
|
34
33
|
ids: string[];
|
|
35
34
|
};
|
|
36
|
-
[TrackChangesAction.updateChanges]: string[];
|
|
37
35
|
[TrackChangesAction.refreshChanges]: boolean;
|
|
38
36
|
[TrackChangesAction.applyAndRemoveChanges]: boolean;
|
|
39
37
|
};
|
|
@@ -1,19 +1,2 @@
|
|
|
1
|
-
/*!
|
|
2
|
-
* © 2021 Atypon Systems LLC
|
|
3
|
-
*
|
|
4
|
-
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
5
|
-
* you may not use this file except in compliance with the License.
|
|
6
|
-
* You may obtain a copy of the License at
|
|
7
|
-
*
|
|
8
|
-
* http://www.apache.org/licenses/LICENSE-2.0
|
|
9
|
-
*
|
|
10
|
-
* Unless required by applicable law or agreed to in writing, software
|
|
11
|
-
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
12
|
-
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
13
|
-
* See the License for the specific language governing permissions and
|
|
14
|
-
* limitations under the License.
|
|
15
|
-
*/
|
|
16
|
-
import { Schema } from 'prosemirror-model';
|
|
17
|
-
import type { Transaction } from 'prosemirror-state';
|
|
18
1
|
import { ChangeStep, InsertSliceStep } from '../types/step';
|
|
19
|
-
export declare function diffChangeSteps(deleted: ChangeStep[], inserted: InsertSliceStep[]
|
|
2
|
+
export declare function diffChangeSteps(deleted: ChangeStep[], inserted: InsertSliceStep[]): ChangeStep[];
|
|
@@ -1,3 +1,13 @@
|
|
|
1
1
|
import { ExposedFragment } from '../types/pm';
|
|
2
2
|
import { ChangeStep } from '../types/step';
|
|
3
|
+
/**
|
|
4
|
+
* Matches deleted to inserted content and returns the first pos they differ and the updated
|
|
5
|
+
* ChangeStep list.
|
|
6
|
+
*
|
|
7
|
+
* Based on https://github.com/ProseMirror/prosemirror-model/blob/master/src/diff.ts
|
|
8
|
+
* @param matchedDeleted
|
|
9
|
+
* @param deleted
|
|
10
|
+
* @param inserted
|
|
11
|
+
* @returns
|
|
12
|
+
*/
|
|
3
13
|
export declare function matchInserted(matchedDeleted: number, deleted: ChangeStep[], inserted: ExposedFragment): [number, ChangeStep[]];
|
|
@@ -18,9 +18,9 @@ import { ChangeSet } from '../ChangeSet';
|
|
|
18
18
|
/**
|
|
19
19
|
* Finds all changes (basically text marks or node attributes) from document
|
|
20
20
|
*
|
|
21
|
-
* This could be possibly made more efficient by only iterating the sections of doc
|
|
22
|
-
*
|
|
23
|
-
*
|
|
21
|
+
* This could be possibly made more efficient by only iterating the sections of doc where changes have
|
|
22
|
+
* been applied. This could attempted with eg findDiffStart but it might be less robust than just using
|
|
23
|
+
* doc.descendants
|
|
24
24
|
* @param state
|
|
25
25
|
* @returns
|
|
26
26
|
*/
|
package/dist/commands.d.ts
CHANGED
|
@@ -25,7 +25,7 @@ import { TrackChangesStatus } from './types/track';
|
|
|
25
25
|
* to the document.
|
|
26
26
|
* @param status
|
|
27
27
|
*/
|
|
28
|
-
export declare const setTrackingStatus: (status?: TrackChangesStatus
|
|
28
|
+
export declare const setTrackingStatus: (status?: TrackChangesStatus) => Command;
|
|
29
29
|
/**
|
|
30
30
|
* Appends a transaction to set change attributes/marks' statuses to any of: 'pending' 'accepted' 'rejected'.
|
|
31
31
|
* @param status
|
|
@@ -45,7 +45,3 @@ export declare const applyAndRemoveChanges: () => Command;
|
|
|
45
45
|
* Runs `findChanges` to iterate over the document to collect changes into a new ChangeSet.
|
|
46
46
|
*/
|
|
47
47
|
export declare const refreshChanges: () => Command;
|
|
48
|
-
/**
|
|
49
|
-
* Adds track attributes for a block node. For testing puroses
|
|
50
|
-
*/
|
|
51
|
-
export declare const setParagraphTestAttribute: (val?: string) => Command;
|
package/dist/index.cjs
CHANGED
|
@@ -17,7 +17,6 @@ var TrackChangesAction;
|
|
|
17
17
|
TrackChangesAction["setUserID"] = "track-changes-set-user-id";
|
|
18
18
|
TrackChangesAction["setPluginStatus"] = "track-changes-set-track-status";
|
|
19
19
|
TrackChangesAction["setChangeStatuses"] = "track-changes-set-change-statuses";
|
|
20
|
-
TrackChangesAction["updateChanges"] = "track-changes-update-changes";
|
|
21
20
|
TrackChangesAction["refreshChanges"] = "track-changes-refresh-changes";
|
|
22
21
|
TrackChangesAction["applyAndRemoveChanges"] = "track-changes-apply-remove-changes";
|
|
23
22
|
})(TrackChangesAction || (TrackChangesAction = {}));
|
|
@@ -373,7 +372,7 @@ function getTextNodeTrackedMarkData(node, schema) {
|
|
|
373
372
|
return marksTrackedData[0] || undefined;
|
|
374
373
|
}
|
|
375
374
|
function getBlockInlineTrackedData(node) {
|
|
376
|
-
|
|
375
|
+
const { dataTracked } = node.attrs;
|
|
377
376
|
if (dataTracked && !Array.isArray(dataTracked)) {
|
|
378
377
|
return [dataTracked];
|
|
379
378
|
}
|
|
@@ -635,9 +634,9 @@ function applyAcceptedRejectedChanges(tr, schema, changes, deleteMap = new prose
|
|
|
635
634
|
/**
|
|
636
635
|
* Finds all changes (basically text marks or node attributes) from document
|
|
637
636
|
*
|
|
638
|
-
* This could be possibly made more efficient by only iterating the sections of doc
|
|
639
|
-
*
|
|
640
|
-
*
|
|
637
|
+
* This could be possibly made more efficient by only iterating the sections of doc where changes have
|
|
638
|
+
* been applied. This could attempted with eg findDiffStart but it might be less robust than just using
|
|
639
|
+
* doc.descendants
|
|
641
640
|
* @param state
|
|
642
641
|
* @returns
|
|
643
642
|
*/
|
|
@@ -1021,7 +1020,7 @@ function deleteAndMergeSplitNodes(from, to, gap, startDoc, newTr, schema, trackA
|
|
|
1021
1020
|
// @TODO ATM 20.7.2022 there doesn't seem to be tests that capture this.
|
|
1022
1021
|
const wasWithinGap = gap &&
|
|
1023
1022
|
((!node.isText && pos >= gap.start) ||
|
|
1024
|
-
(node.isText && pos
|
|
1023
|
+
(node.isText && pos >= gap.start && nodeEnd <= gap.end));
|
|
1025
1024
|
// nodeEnd > offsetFrom -> delete touches this node
|
|
1026
1025
|
// eg (del 6 10) <p 5>|<t 6>cdf</t 9></p 10>| -> <p> nodeEnd 10 > from 6
|
|
1027
1026
|
if (nodeEnd > from && !wasWithinGap) {
|
|
@@ -1122,41 +1121,6 @@ function deleteAndMergeSplitNodes(from, to, gap, startDoc, newTr, schema, trackA
|
|
|
1122
1121
|
};
|
|
1123
1122
|
}
|
|
1124
1123
|
|
|
1125
|
-
/**
|
|
1126
|
-
* Merges tracked marks between text nodes at a position
|
|
1127
|
-
*
|
|
1128
|
-
* Will work for any nodes that use tracked_insert or tracked_delete marks which may not be preferrable
|
|
1129
|
-
* if used for block nodes (since we possibly want to show the individual changed nodes).
|
|
1130
|
-
* Merging is done based on the userID, operation type and status.
|
|
1131
|
-
* @param pos
|
|
1132
|
-
* @param doc
|
|
1133
|
-
* @param newTr
|
|
1134
|
-
* @param schema
|
|
1135
|
-
*/
|
|
1136
|
-
function mergeTrackedMarks(pos, doc, newTr, schema) {
|
|
1137
|
-
const resolved = doc.resolve(pos);
|
|
1138
|
-
const { nodeAfter, nodeBefore } = resolved;
|
|
1139
|
-
const leftMark = nodeBefore === null || nodeBefore === void 0 ? void 0 : nodeBefore.marks.filter((m) => m.type === schema.marks.tracked_insert || m.type === schema.marks.tracked_delete)[0];
|
|
1140
|
-
const rightMark = nodeAfter === null || nodeAfter === void 0 ? void 0 : nodeAfter.marks.filter((m) => m.type === schema.marks.tracked_insert || m.type === schema.marks.tracked_delete)[0];
|
|
1141
|
-
if (!nodeAfter || !nodeBefore || !leftMark || !rightMark || leftMark.type !== rightMark.type) {
|
|
1142
|
-
return;
|
|
1143
|
-
}
|
|
1144
|
-
const leftDataTracked = leftMark.attrs.dataTracked;
|
|
1145
|
-
const rightDataTracked = rightMark.attrs.dataTracked;
|
|
1146
|
-
if (!shouldMergeTrackedAttributes(leftDataTracked, rightDataTracked)) {
|
|
1147
|
-
return;
|
|
1148
|
-
}
|
|
1149
|
-
const isLeftOlder = (leftDataTracked.createdAt || 0) < (rightDataTracked.createdAt || 0);
|
|
1150
|
-
const ancestorAttrs = isLeftOlder ? leftDataTracked : rightDataTracked;
|
|
1151
|
-
const dataTracked = {
|
|
1152
|
-
...ancestorAttrs,
|
|
1153
|
-
updatedAt: Date.now(),
|
|
1154
|
-
};
|
|
1155
|
-
const fromStartOfMark = pos - nodeBefore.nodeSize;
|
|
1156
|
-
const toEndOfMark = pos + nodeAfter.nodeSize;
|
|
1157
|
-
newTr.addMark(fromStartOfMark, toEndOfMark, leftMark.type.create({ ...leftMark.attrs, dataTracked }));
|
|
1158
|
-
}
|
|
1159
|
-
|
|
1160
1124
|
/*!
|
|
1161
1125
|
* © 2021 Atypon Systems LLC
|
|
1162
1126
|
*
|
|
@@ -1176,27 +1140,6 @@ function trackReplaceAroundStep(step, oldState, newTr, attrs) {
|
|
|
1176
1140
|
log.info('###### ReplaceAroundStep ######');
|
|
1177
1141
|
// @ts-ignore
|
|
1178
1142
|
const { from, to, gapFrom, gapTo, insert, slice, structure, } = step;
|
|
1179
|
-
if (from === gapFrom && to === gapTo) {
|
|
1180
|
-
log.info('WRAPPED IN SOMETHING');
|
|
1181
|
-
}
|
|
1182
|
-
else if (!slice.size || slice.content.content.length === 2) {
|
|
1183
|
-
log.info('UNWRAPPED FROM SOMETHING');
|
|
1184
|
-
}
|
|
1185
|
-
else if (slice.size === 2 && gapFrom - from === 1 && to - gapTo === 1) {
|
|
1186
|
-
log.info('REPLACED WRAPPING');
|
|
1187
|
-
}
|
|
1188
|
-
else {
|
|
1189
|
-
log.info('????');
|
|
1190
|
-
}
|
|
1191
|
-
if (gapFrom - from > to - gapTo) {
|
|
1192
|
-
log.info('DELETED BEFORE GAP FROM');
|
|
1193
|
-
}
|
|
1194
|
-
else if (gapFrom - from < to - gapTo) {
|
|
1195
|
-
log.info('DELETED AFTER GAP TO');
|
|
1196
|
-
}
|
|
1197
|
-
else {
|
|
1198
|
-
log.info('EQUAL REPLACE BETWEEN GAPS');
|
|
1199
|
-
}
|
|
1200
1143
|
// Invert the transaction step to prevent it from actually deleting or inserting anything
|
|
1201
1144
|
const newStep = step.invert(oldState.doc);
|
|
1202
1145
|
const stepResult = newTr.maybeStep(newStep);
|
|
@@ -1209,7 +1152,7 @@ function trackReplaceAroundStep(step, oldState, newTr, attrs) {
|
|
|
1209
1152
|
// First apply the deleted range and update the insert slice to not include content that was deleted,
|
|
1210
1153
|
// eg partial nodes in an open-ended slice
|
|
1211
1154
|
const { sliceWasSplit, newSliceContent, steps: deleteSteps, } = deleteAndMergeSplitNodes(from, to, { start: gapFrom, end: gapTo }, newTr.doc, newTr, oldState.schema, attrs, slice);
|
|
1212
|
-
|
|
1155
|
+
const steps = deleteSteps;
|
|
1213
1156
|
log.info('TR: new steps after applying delete', [...newTr.steps]);
|
|
1214
1157
|
log.info('DELETE STEPS: ', deleteSteps);
|
|
1215
1158
|
// We only want to insert when there something inside the gap (actually would this be always true?)
|
|
@@ -1234,11 +1177,6 @@ function trackReplaceAroundStep(step, oldState, newTr, attrs) {
|
|
|
1234
1177
|
sliceWasSplit,
|
|
1235
1178
|
});
|
|
1236
1179
|
}
|
|
1237
|
-
else {
|
|
1238
|
-
// Incase only deletion was applied, check whether tracked marks around deleted content can be merged
|
|
1239
|
-
mergeTrackedMarks(gapFrom, newTr.doc, newTr, oldState.schema);
|
|
1240
|
-
mergeTrackedMarks(gapTo, newTr.doc, newTr, oldState.schema);
|
|
1241
|
-
}
|
|
1242
1180
|
return steps;
|
|
1243
1181
|
}
|
|
1244
1182
|
|
|
@@ -1277,7 +1215,7 @@ function trackReplaceStep(step, oldState, newTr, attrs) {
|
|
|
1277
1215
|
changeSteps.push(...deleteSteps);
|
|
1278
1216
|
log.info('TR: steps after applying delete', [...newTr.steps]);
|
|
1279
1217
|
log.info('DELETE STEPS: ', changeSteps);
|
|
1280
|
-
const adjustedInsertPos = toA;
|
|
1218
|
+
const adjustedInsertPos = toA;
|
|
1281
1219
|
if (newSliceContent.size > 0) {
|
|
1282
1220
|
log.info('newSliceContent', newSliceContent);
|
|
1283
1221
|
// Since deleteAndMergeSplitBlockNodes modified the slice to not to contain any merged nodes,
|
|
@@ -1294,7 +1232,7 @@ function trackReplaceStep(step, oldState, newTr, attrs) {
|
|
|
1294
1232
|
}
|
|
1295
1233
|
else {
|
|
1296
1234
|
// Incase only deletion was applied, check whether tracked marks around deleted content can be merged
|
|
1297
|
-
mergeTrackedMarks(adjustedInsertPos, newTr.doc, newTr, oldState.schema)
|
|
1235
|
+
// mergeTrackedMarks(adjustedInsertPos, newTr.doc, newTr, oldState.schema)
|
|
1298
1236
|
selectionPos = fromA;
|
|
1299
1237
|
}
|
|
1300
1238
|
});
|
|
@@ -1328,6 +1266,7 @@ function trackReplaceStep(step, oldState, newTr, attrs) {
|
|
|
1328
1266
|
* @param deleteAttrs
|
|
1329
1267
|
* @param from
|
|
1330
1268
|
* @param to
|
|
1269
|
+
* @returns position at the end of the possibly deleted text
|
|
1331
1270
|
*/
|
|
1332
1271
|
function deleteTextIfInserted(node, pos, newTr, schema, deleteAttrs, from, to) {
|
|
1333
1272
|
const start = from ? Math.max(pos, from) : pos;
|
|
@@ -1361,6 +1300,41 @@ function deleteTextIfInserted(node, pos, newTr, schema, deleteAttrs, from, to) {
|
|
|
1361
1300
|
}
|
|
1362
1301
|
}
|
|
1363
1302
|
|
|
1303
|
+
/**
|
|
1304
|
+
* Merges tracked marks between text nodes at a position
|
|
1305
|
+
*
|
|
1306
|
+
* Will work for any nodes that use tracked_insert or tracked_delete marks which may not be preferrable
|
|
1307
|
+
* if used for block nodes (since we possibly want to show the individual changed nodes).
|
|
1308
|
+
* Merging is done based on the userID, operation type and status.
|
|
1309
|
+
* @param pos
|
|
1310
|
+
* @param doc
|
|
1311
|
+
* @param newTr
|
|
1312
|
+
* @param schema
|
|
1313
|
+
*/
|
|
1314
|
+
function mergeTrackedMarks(pos, doc, newTr, schema) {
|
|
1315
|
+
const resolved = doc.resolve(pos);
|
|
1316
|
+
const { nodeAfter, nodeBefore } = resolved;
|
|
1317
|
+
const leftMark = nodeBefore === null || nodeBefore === void 0 ? void 0 : nodeBefore.marks.filter((m) => m.type === schema.marks.tracked_insert || m.type === schema.marks.tracked_delete)[0];
|
|
1318
|
+
const rightMark = nodeAfter === null || nodeAfter === void 0 ? void 0 : nodeAfter.marks.filter((m) => m.type === schema.marks.tracked_insert || m.type === schema.marks.tracked_delete)[0];
|
|
1319
|
+
if (!nodeAfter || !nodeBefore || !leftMark || !rightMark || leftMark.type !== rightMark.type) {
|
|
1320
|
+
return;
|
|
1321
|
+
}
|
|
1322
|
+
const leftDataTracked = leftMark.attrs.dataTracked;
|
|
1323
|
+
const rightDataTracked = rightMark.attrs.dataTracked;
|
|
1324
|
+
if (!shouldMergeTrackedAttributes(leftDataTracked, rightDataTracked)) {
|
|
1325
|
+
return;
|
|
1326
|
+
}
|
|
1327
|
+
const isLeftOlder = (leftDataTracked.createdAt || 0) < (rightDataTracked.createdAt || 0);
|
|
1328
|
+
const ancestorAttrs = isLeftOlder ? leftDataTracked : rightDataTracked;
|
|
1329
|
+
const dataTracked = {
|
|
1330
|
+
...ancestorAttrs,
|
|
1331
|
+
updatedAt: Date.now(),
|
|
1332
|
+
};
|
|
1333
|
+
const fromStartOfMark = pos - nodeBefore.nodeSize;
|
|
1334
|
+
const toEndOfMark = pos + nodeAfter.nodeSize;
|
|
1335
|
+
newTr.addMark(fromStartOfMark, toEndOfMark, leftMark.type.create({ ...leftMark.attrs, dataTracked }));
|
|
1336
|
+
}
|
|
1337
|
+
|
|
1364
1338
|
function processChangeSteps(changes, startPos, newTr, emptyAttrs, schema) {
|
|
1365
1339
|
const mapping = new prosemirrorTransform.Mapping();
|
|
1366
1340
|
const deleteAttrs = createNewDeleteAttrs(emptyAttrs);
|
|
@@ -1448,6 +1422,19 @@ function processChangeSteps(changes, startPos, newTr, emptyAttrs, schema) {
|
|
|
1448
1422
|
return [mapping, selectionPos];
|
|
1449
1423
|
}
|
|
1450
1424
|
|
|
1425
|
+
/**
|
|
1426
|
+
* Matches deleted-text recursively to inserted text
|
|
1427
|
+
*
|
|
1428
|
+
* This is needed as text containing various marks is split into multiple parts even though it's
|
|
1429
|
+
* continously deleted. Therefore, we need to find the next part if there is any and keep going until
|
|
1430
|
+
* we've reached the end of the deleted text or inserted content.
|
|
1431
|
+
* @param adjDeleted
|
|
1432
|
+
* @param insNode
|
|
1433
|
+
* @param offset
|
|
1434
|
+
* @param matchedDeleted
|
|
1435
|
+
* @param deleted
|
|
1436
|
+
* @returns
|
|
1437
|
+
*/
|
|
1451
1438
|
function matchText(adjDeleted, insNode, offset, matchedDeleted, deleted) {
|
|
1452
1439
|
const { pos, from, to, node: delNode } = adjDeleted;
|
|
1453
1440
|
let j = offset, d = from - pos, maxSteps = to - Math.max(pos, from);
|
|
@@ -1480,15 +1467,26 @@ function matchText(adjDeleted, insNode, offset, matchedDeleted, deleted) {
|
|
|
1480
1467
|
}
|
|
1481
1468
|
return [matchedDeleted, deleted];
|
|
1482
1469
|
}
|
|
1470
|
+
/**
|
|
1471
|
+
* Matches deleted to inserted content and returns the first pos they differ and the updated
|
|
1472
|
+
* ChangeStep list.
|
|
1473
|
+
*
|
|
1474
|
+
* Based on https://github.com/ProseMirror/prosemirror-model/blob/master/src/diff.ts
|
|
1475
|
+
* @param matchedDeleted
|
|
1476
|
+
* @param deleted
|
|
1477
|
+
* @param inserted
|
|
1478
|
+
* @returns
|
|
1479
|
+
*/
|
|
1483
1480
|
function matchInserted(matchedDeleted, deleted, inserted) {
|
|
1484
1481
|
var _a;
|
|
1485
1482
|
let matched = [matchedDeleted, deleted];
|
|
1486
1483
|
for (let i = 0;; i += 1) {
|
|
1487
|
-
if (inserted.childCount === i)
|
|
1484
|
+
if (inserted.childCount === i) {
|
|
1488
1485
|
return matched;
|
|
1486
|
+
}
|
|
1489
1487
|
const insNode = inserted.child(i);
|
|
1490
1488
|
// @ts-ignore
|
|
1491
|
-
|
|
1489
|
+
const adjDeleted = matched[1].find((d) => (d.type === 'delete-text' && Math.max(d.pos, d.from) === matched[0]) ||
|
|
1492
1490
|
(d.type === 'delete-node' && d.pos === matched[0]));
|
|
1493
1491
|
if (insNode.type !== ((_a = adjDeleted === null || adjDeleted === void 0 ? void 0 : adjDeleted.node) === null || _a === void 0 ? void 0 : _a.type)) {
|
|
1494
1492
|
return matched;
|
|
@@ -1533,16 +1531,13 @@ function matchInserted(matchedDeleted, deleted, inserted) {
|
|
|
1533
1531
|
/**
|
|
1534
1532
|
* Cuts a fragment similar to Fragment.cut but also removes the parent node.
|
|
1535
1533
|
*
|
|
1536
|
-
* @TODO there is however, some silly calculation mistake so that I need to use matched - deleted + 1 > 0
|
|
1537
|
-
* inside it to check whether to actually cut a text node. The offset might be cascading, therefore it should
|
|
1538
|
-
* be fixed at some point.
|
|
1539
1534
|
* @param matched
|
|
1540
1535
|
* @param deleted
|
|
1541
1536
|
* @param content
|
|
1542
1537
|
* @returns
|
|
1543
1538
|
*/
|
|
1544
1539
|
function cutFragment(matched, deleted, content) {
|
|
1545
|
-
|
|
1540
|
+
const newContent = [];
|
|
1546
1541
|
for (let i = 0; matched <= deleted && i < content.childCount; i += 1) {
|
|
1547
1542
|
const child = content.child(i);
|
|
1548
1543
|
if (!child.isText && child.content.size > 0) {
|
|
@@ -1565,7 +1560,7 @@ function cutFragment(matched, deleted, content) {
|
|
|
1565
1560
|
}
|
|
1566
1561
|
return [matched, prosemirrorModel.Fragment.fromArray(newContent)];
|
|
1567
1562
|
}
|
|
1568
|
-
function diffChangeSteps(deleted, inserted
|
|
1563
|
+
function diffChangeSteps(deleted, inserted) {
|
|
1569
1564
|
const updated = [];
|
|
1570
1565
|
let updatedDeleted = [...deleted];
|
|
1571
1566
|
inserted.forEach((ins) => {
|
|
@@ -1623,6 +1618,7 @@ function diffChangeSteps(deleted, inserted, newTr, schema) {
|
|
|
1623
1618
|
* @returns
|
|
1624
1619
|
*/
|
|
1625
1620
|
const getSelectionStaticConstructor = (sel) => Object.getPrototypeOf(sel).constructor;
|
|
1621
|
+
const isHighlightMarkerNode = (node) => node.type === node.type.schema.nodes.highlight_marker;
|
|
1626
1622
|
/**
|
|
1627
1623
|
* Inverts transactions to wrap their contents/operations with track data instead
|
|
1628
1624
|
*
|
|
@@ -1652,6 +1648,7 @@ function trackTransaction(tr, oldState, newTr, authorID) {
|
|
|
1652
1648
|
let iters = 0;
|
|
1653
1649
|
log.info('ORIGINAL transaction', tr);
|
|
1654
1650
|
tr.steps.forEach((step) => {
|
|
1651
|
+
var _a, _b;
|
|
1655
1652
|
log.info('transaction step', step);
|
|
1656
1653
|
iters += 1;
|
|
1657
1654
|
if (iters > 20) {
|
|
@@ -1666,11 +1663,17 @@ function trackTransaction(tr, oldState, newTr, authorID) {
|
|
|
1666
1663
|
}
|
|
1667
1664
|
else if (step instanceof prosemirrorTransform.ReplaceStep) {
|
|
1668
1665
|
let [steps, startPos] = trackReplaceStep(step, oldState, newTr, emptyAttrs);
|
|
1666
|
+
const { slice } = step;
|
|
1667
|
+
if (((_b = (_a = slice === null || slice === void 0 ? void 0 : slice.content) === null || _a === void 0 ? void 0 : _a.content) === null || _b === void 0 ? void 0 : _b.length) === 1 &&
|
|
1668
|
+
isHighlightMarkerNode(slice.content.content[0])) {
|
|
1669
|
+
// don't track highlight marker nodes
|
|
1670
|
+
return;
|
|
1671
|
+
}
|
|
1669
1672
|
log.info('CHANGES: ', steps);
|
|
1670
1673
|
// deleted and merged really...
|
|
1671
1674
|
const deleted = steps.filter((s) => s.type !== 'insert-slice');
|
|
1672
1675
|
const inserted = steps.filter((s) => s.type === 'insert-slice');
|
|
1673
|
-
steps = diffChangeSteps(deleted, inserted
|
|
1676
|
+
steps = diffChangeSteps(deleted, inserted);
|
|
1674
1677
|
log.info('DIFFED STEPS: ', steps);
|
|
1675
1678
|
const [mapping, selectionPos] = processChangeSteps(steps, startPos || tr.selection.head, // Incase startPos is it's default value 0, use the old selection head
|
|
1676
1679
|
newTr, emptyAttrs, oldState.schema);
|
|
@@ -1688,7 +1691,7 @@ function trackTransaction(tr, oldState, newTr, authorID) {
|
|
|
1688
1691
|
const deleted = steps.filter((s) => s.type !== 'insert-slice');
|
|
1689
1692
|
const inserted = steps.filter((s) => s.type === 'insert-slice');
|
|
1690
1693
|
log.info('INSERT STEPS: ', inserted);
|
|
1691
|
-
steps = diffChangeSteps(deleted, inserted
|
|
1694
|
+
steps = diffChangeSteps(deleted, inserted);
|
|
1692
1695
|
log.info('DIFFED STEPS: ', steps);
|
|
1693
1696
|
processChangeSteps(steps, tr.selection.from, newTr, emptyAttrs, oldState.schema);
|
|
1694
1697
|
}
|
|
@@ -1778,15 +1781,14 @@ const trackChangesPlugin = (opts = { userID: 'anonymous:Anonymous' }) => {
|
|
|
1778
1781
|
return {
|
|
1779
1782
|
...pluginState,
|
|
1780
1783
|
status: setStatus,
|
|
1781
|
-
changeSet: findChanges(newState),
|
|
1784
|
+
changeSet: setStatus === exports.TrackChangesStatus.disabled ? new ChangeSet() : findChanges(newState),
|
|
1782
1785
|
};
|
|
1783
1786
|
}
|
|
1784
1787
|
else if (pluginState.status === exports.TrackChangesStatus.disabled) {
|
|
1785
1788
|
return { ...pluginState, changeSet: new ChangeSet() };
|
|
1786
1789
|
}
|
|
1787
1790
|
let { changeSet, ...rest } = pluginState;
|
|
1788
|
-
|
|
1789
|
-
if (updatedChangeIds || getAction(tr, TrackChangesAction.refreshChanges)) {
|
|
1791
|
+
if (getAction(tr, TrackChangesAction.refreshChanges)) {
|
|
1790
1792
|
changeSet = findChanges(newState);
|
|
1791
1793
|
}
|
|
1792
1794
|
return {
|
|
@@ -1830,7 +1832,6 @@ const trackChangesPlugin = (opts = { userID: 'anonymous:Anonymous' }) => {
|
|
|
1830
1832
|
createdTr = updateChangeAttrs(createdTr, change, { ...change.dataTracked, status, reviewedByID: userID }, oldState.schema);
|
|
1831
1833
|
}
|
|
1832
1834
|
});
|
|
1833
|
-
setAction(createdTr, TrackChangesAction.updateChanges, ids);
|
|
1834
1835
|
}
|
|
1835
1836
|
else if (getAction(tr, TrackChangesAction.applyAndRemoveChanges)) {
|
|
1836
1837
|
const mapping = applyAcceptedRejectedChanges(createdTr, oldState.schema, changeSet.bothNodeChanges);
|
|
@@ -1909,24 +1910,8 @@ const applyAndRemoveChanges = () => (state, dispatch) => {
|
|
|
1909
1910
|
* Runs `findChanges` to iterate over the document to collect changes into a new ChangeSet.
|
|
1910
1911
|
*/
|
|
1911
1912
|
const refreshChanges = () => (state, dispatch) => {
|
|
1912
|
-
dispatch && dispatch(setAction(state.tr, TrackChangesAction.
|
|
1913
|
+
dispatch && dispatch(setAction(state.tr, TrackChangesAction.refreshChanges, true));
|
|
1913
1914
|
return true;
|
|
1914
|
-
};
|
|
1915
|
-
/**
|
|
1916
|
-
* Adds track attributes for a block node. For testing puroses
|
|
1917
|
-
*/
|
|
1918
|
-
const setParagraphTestAttribute = (val = 'changed') => (state, dispatch) => {
|
|
1919
|
-
var _a;
|
|
1920
|
-
const cursor = state.selection.head;
|
|
1921
|
-
const blockNodePos = state.doc.resolve(cursor).start(1) - 1;
|
|
1922
|
-
if (((_a = state.doc.resolve(blockNodePos).nodeAfter) === null || _a === void 0 ? void 0 : _a.type) === state.schema.nodes.paragraph &&
|
|
1923
|
-
dispatch) {
|
|
1924
|
-
dispatch(state.tr.setNodeMarkup(blockNodePos, undefined, {
|
|
1925
|
-
testAttribute: val,
|
|
1926
|
-
}));
|
|
1927
|
-
return true;
|
|
1928
|
-
}
|
|
1929
|
-
return false;
|
|
1930
1915
|
};
|
|
1931
1916
|
|
|
1932
1917
|
var commands = /*#__PURE__*/Object.freeze({
|
|
@@ -1935,8 +1920,7 @@ var commands = /*#__PURE__*/Object.freeze({
|
|
|
1935
1920
|
setChangeStatuses: setChangeStatuses,
|
|
1936
1921
|
setUserID: setUserID,
|
|
1937
1922
|
applyAndRemoveChanges: applyAndRemoveChanges,
|
|
1938
|
-
refreshChanges: refreshChanges
|
|
1939
|
-
setParagraphTestAttribute: setParagraphTestAttribute
|
|
1923
|
+
refreshChanges: refreshChanges
|
|
1940
1924
|
});
|
|
1941
1925
|
|
|
1942
1926
|
exports.ChangeSet = ChangeSet;
|
package/dist/index.js
CHANGED
|
@@ -9,7 +9,6 @@ var TrackChangesAction;
|
|
|
9
9
|
TrackChangesAction["setUserID"] = "track-changes-set-user-id";
|
|
10
10
|
TrackChangesAction["setPluginStatus"] = "track-changes-set-track-status";
|
|
11
11
|
TrackChangesAction["setChangeStatuses"] = "track-changes-set-change-statuses";
|
|
12
|
-
TrackChangesAction["updateChanges"] = "track-changes-update-changes";
|
|
13
12
|
TrackChangesAction["refreshChanges"] = "track-changes-refresh-changes";
|
|
14
13
|
TrackChangesAction["applyAndRemoveChanges"] = "track-changes-apply-remove-changes";
|
|
15
14
|
})(TrackChangesAction || (TrackChangesAction = {}));
|
|
@@ -365,7 +364,7 @@ function getTextNodeTrackedMarkData(node, schema) {
|
|
|
365
364
|
return marksTrackedData[0] || undefined;
|
|
366
365
|
}
|
|
367
366
|
function getBlockInlineTrackedData(node) {
|
|
368
|
-
|
|
367
|
+
const { dataTracked } = node.attrs;
|
|
369
368
|
if (dataTracked && !Array.isArray(dataTracked)) {
|
|
370
369
|
return [dataTracked];
|
|
371
370
|
}
|
|
@@ -627,9 +626,9 @@ function applyAcceptedRejectedChanges(tr, schema, changes, deleteMap = new Mappi
|
|
|
627
626
|
/**
|
|
628
627
|
* Finds all changes (basically text marks or node attributes) from document
|
|
629
628
|
*
|
|
630
|
-
* This could be possibly made more efficient by only iterating the sections of doc
|
|
631
|
-
*
|
|
632
|
-
*
|
|
629
|
+
* This could be possibly made more efficient by only iterating the sections of doc where changes have
|
|
630
|
+
* been applied. This could attempted with eg findDiffStart but it might be less robust than just using
|
|
631
|
+
* doc.descendants
|
|
633
632
|
* @param state
|
|
634
633
|
* @returns
|
|
635
634
|
*/
|
|
@@ -1013,7 +1012,7 @@ function deleteAndMergeSplitNodes(from, to, gap, startDoc, newTr, schema, trackA
|
|
|
1013
1012
|
// @TODO ATM 20.7.2022 there doesn't seem to be tests that capture this.
|
|
1014
1013
|
const wasWithinGap = gap &&
|
|
1015
1014
|
((!node.isText && pos >= gap.start) ||
|
|
1016
|
-
(node.isText && pos
|
|
1015
|
+
(node.isText && pos >= gap.start && nodeEnd <= gap.end));
|
|
1017
1016
|
// nodeEnd > offsetFrom -> delete touches this node
|
|
1018
1017
|
// eg (del 6 10) <p 5>|<t 6>cdf</t 9></p 10>| -> <p> nodeEnd 10 > from 6
|
|
1019
1018
|
if (nodeEnd > from && !wasWithinGap) {
|
|
@@ -1114,41 +1113,6 @@ function deleteAndMergeSplitNodes(from, to, gap, startDoc, newTr, schema, trackA
|
|
|
1114
1113
|
};
|
|
1115
1114
|
}
|
|
1116
1115
|
|
|
1117
|
-
/**
|
|
1118
|
-
* Merges tracked marks between text nodes at a position
|
|
1119
|
-
*
|
|
1120
|
-
* Will work for any nodes that use tracked_insert or tracked_delete marks which may not be preferrable
|
|
1121
|
-
* if used for block nodes (since we possibly want to show the individual changed nodes).
|
|
1122
|
-
* Merging is done based on the userID, operation type and status.
|
|
1123
|
-
* @param pos
|
|
1124
|
-
* @param doc
|
|
1125
|
-
* @param newTr
|
|
1126
|
-
* @param schema
|
|
1127
|
-
*/
|
|
1128
|
-
function mergeTrackedMarks(pos, doc, newTr, schema) {
|
|
1129
|
-
const resolved = doc.resolve(pos);
|
|
1130
|
-
const { nodeAfter, nodeBefore } = resolved;
|
|
1131
|
-
const leftMark = nodeBefore === null || nodeBefore === void 0 ? void 0 : nodeBefore.marks.filter((m) => m.type === schema.marks.tracked_insert || m.type === schema.marks.tracked_delete)[0];
|
|
1132
|
-
const rightMark = nodeAfter === null || nodeAfter === void 0 ? void 0 : nodeAfter.marks.filter((m) => m.type === schema.marks.tracked_insert || m.type === schema.marks.tracked_delete)[0];
|
|
1133
|
-
if (!nodeAfter || !nodeBefore || !leftMark || !rightMark || leftMark.type !== rightMark.type) {
|
|
1134
|
-
return;
|
|
1135
|
-
}
|
|
1136
|
-
const leftDataTracked = leftMark.attrs.dataTracked;
|
|
1137
|
-
const rightDataTracked = rightMark.attrs.dataTracked;
|
|
1138
|
-
if (!shouldMergeTrackedAttributes(leftDataTracked, rightDataTracked)) {
|
|
1139
|
-
return;
|
|
1140
|
-
}
|
|
1141
|
-
const isLeftOlder = (leftDataTracked.createdAt || 0) < (rightDataTracked.createdAt || 0);
|
|
1142
|
-
const ancestorAttrs = isLeftOlder ? leftDataTracked : rightDataTracked;
|
|
1143
|
-
const dataTracked = {
|
|
1144
|
-
...ancestorAttrs,
|
|
1145
|
-
updatedAt: Date.now(),
|
|
1146
|
-
};
|
|
1147
|
-
const fromStartOfMark = pos - nodeBefore.nodeSize;
|
|
1148
|
-
const toEndOfMark = pos + nodeAfter.nodeSize;
|
|
1149
|
-
newTr.addMark(fromStartOfMark, toEndOfMark, leftMark.type.create({ ...leftMark.attrs, dataTracked }));
|
|
1150
|
-
}
|
|
1151
|
-
|
|
1152
1116
|
/*!
|
|
1153
1117
|
* © 2021 Atypon Systems LLC
|
|
1154
1118
|
*
|
|
@@ -1168,27 +1132,6 @@ function trackReplaceAroundStep(step, oldState, newTr, attrs) {
|
|
|
1168
1132
|
log.info('###### ReplaceAroundStep ######');
|
|
1169
1133
|
// @ts-ignore
|
|
1170
1134
|
const { from, to, gapFrom, gapTo, insert, slice, structure, } = step;
|
|
1171
|
-
if (from === gapFrom && to === gapTo) {
|
|
1172
|
-
log.info('WRAPPED IN SOMETHING');
|
|
1173
|
-
}
|
|
1174
|
-
else if (!slice.size || slice.content.content.length === 2) {
|
|
1175
|
-
log.info('UNWRAPPED FROM SOMETHING');
|
|
1176
|
-
}
|
|
1177
|
-
else if (slice.size === 2 && gapFrom - from === 1 && to - gapTo === 1) {
|
|
1178
|
-
log.info('REPLACED WRAPPING');
|
|
1179
|
-
}
|
|
1180
|
-
else {
|
|
1181
|
-
log.info('????');
|
|
1182
|
-
}
|
|
1183
|
-
if (gapFrom - from > to - gapTo) {
|
|
1184
|
-
log.info('DELETED BEFORE GAP FROM');
|
|
1185
|
-
}
|
|
1186
|
-
else if (gapFrom - from < to - gapTo) {
|
|
1187
|
-
log.info('DELETED AFTER GAP TO');
|
|
1188
|
-
}
|
|
1189
|
-
else {
|
|
1190
|
-
log.info('EQUAL REPLACE BETWEEN GAPS');
|
|
1191
|
-
}
|
|
1192
1135
|
// Invert the transaction step to prevent it from actually deleting or inserting anything
|
|
1193
1136
|
const newStep = step.invert(oldState.doc);
|
|
1194
1137
|
const stepResult = newTr.maybeStep(newStep);
|
|
@@ -1201,7 +1144,7 @@ function trackReplaceAroundStep(step, oldState, newTr, attrs) {
|
|
|
1201
1144
|
// First apply the deleted range and update the insert slice to not include content that was deleted,
|
|
1202
1145
|
// eg partial nodes in an open-ended slice
|
|
1203
1146
|
const { sliceWasSplit, newSliceContent, steps: deleteSteps, } = deleteAndMergeSplitNodes(from, to, { start: gapFrom, end: gapTo }, newTr.doc, newTr, oldState.schema, attrs, slice);
|
|
1204
|
-
|
|
1147
|
+
const steps = deleteSteps;
|
|
1205
1148
|
log.info('TR: new steps after applying delete', [...newTr.steps]);
|
|
1206
1149
|
log.info('DELETE STEPS: ', deleteSteps);
|
|
1207
1150
|
// We only want to insert when there something inside the gap (actually would this be always true?)
|
|
@@ -1226,11 +1169,6 @@ function trackReplaceAroundStep(step, oldState, newTr, attrs) {
|
|
|
1226
1169
|
sliceWasSplit,
|
|
1227
1170
|
});
|
|
1228
1171
|
}
|
|
1229
|
-
else {
|
|
1230
|
-
// Incase only deletion was applied, check whether tracked marks around deleted content can be merged
|
|
1231
|
-
mergeTrackedMarks(gapFrom, newTr.doc, newTr, oldState.schema);
|
|
1232
|
-
mergeTrackedMarks(gapTo, newTr.doc, newTr, oldState.schema);
|
|
1233
|
-
}
|
|
1234
1172
|
return steps;
|
|
1235
1173
|
}
|
|
1236
1174
|
|
|
@@ -1269,7 +1207,7 @@ function trackReplaceStep(step, oldState, newTr, attrs) {
|
|
|
1269
1207
|
changeSteps.push(...deleteSteps);
|
|
1270
1208
|
log.info('TR: steps after applying delete', [...newTr.steps]);
|
|
1271
1209
|
log.info('DELETE STEPS: ', changeSteps);
|
|
1272
|
-
const adjustedInsertPos = toA;
|
|
1210
|
+
const adjustedInsertPos = toA;
|
|
1273
1211
|
if (newSliceContent.size > 0) {
|
|
1274
1212
|
log.info('newSliceContent', newSliceContent);
|
|
1275
1213
|
// Since deleteAndMergeSplitBlockNodes modified the slice to not to contain any merged nodes,
|
|
@@ -1286,7 +1224,7 @@ function trackReplaceStep(step, oldState, newTr, attrs) {
|
|
|
1286
1224
|
}
|
|
1287
1225
|
else {
|
|
1288
1226
|
// Incase only deletion was applied, check whether tracked marks around deleted content can be merged
|
|
1289
|
-
mergeTrackedMarks(adjustedInsertPos, newTr.doc, newTr, oldState.schema)
|
|
1227
|
+
// mergeTrackedMarks(adjustedInsertPos, newTr.doc, newTr, oldState.schema)
|
|
1290
1228
|
selectionPos = fromA;
|
|
1291
1229
|
}
|
|
1292
1230
|
});
|
|
@@ -1320,6 +1258,7 @@ function trackReplaceStep(step, oldState, newTr, attrs) {
|
|
|
1320
1258
|
* @param deleteAttrs
|
|
1321
1259
|
* @param from
|
|
1322
1260
|
* @param to
|
|
1261
|
+
* @returns position at the end of the possibly deleted text
|
|
1323
1262
|
*/
|
|
1324
1263
|
function deleteTextIfInserted(node, pos, newTr, schema, deleteAttrs, from, to) {
|
|
1325
1264
|
const start = from ? Math.max(pos, from) : pos;
|
|
@@ -1353,6 +1292,41 @@ function deleteTextIfInserted(node, pos, newTr, schema, deleteAttrs, from, to) {
|
|
|
1353
1292
|
}
|
|
1354
1293
|
}
|
|
1355
1294
|
|
|
1295
|
+
/**
|
|
1296
|
+
* Merges tracked marks between text nodes at a position
|
|
1297
|
+
*
|
|
1298
|
+
* Will work for any nodes that use tracked_insert or tracked_delete marks which may not be preferrable
|
|
1299
|
+
* if used for block nodes (since we possibly want to show the individual changed nodes).
|
|
1300
|
+
* Merging is done based on the userID, operation type and status.
|
|
1301
|
+
* @param pos
|
|
1302
|
+
* @param doc
|
|
1303
|
+
* @param newTr
|
|
1304
|
+
* @param schema
|
|
1305
|
+
*/
|
|
1306
|
+
function mergeTrackedMarks(pos, doc, newTr, schema) {
|
|
1307
|
+
const resolved = doc.resolve(pos);
|
|
1308
|
+
const { nodeAfter, nodeBefore } = resolved;
|
|
1309
|
+
const leftMark = nodeBefore === null || nodeBefore === void 0 ? void 0 : nodeBefore.marks.filter((m) => m.type === schema.marks.tracked_insert || m.type === schema.marks.tracked_delete)[0];
|
|
1310
|
+
const rightMark = nodeAfter === null || nodeAfter === void 0 ? void 0 : nodeAfter.marks.filter((m) => m.type === schema.marks.tracked_insert || m.type === schema.marks.tracked_delete)[0];
|
|
1311
|
+
if (!nodeAfter || !nodeBefore || !leftMark || !rightMark || leftMark.type !== rightMark.type) {
|
|
1312
|
+
return;
|
|
1313
|
+
}
|
|
1314
|
+
const leftDataTracked = leftMark.attrs.dataTracked;
|
|
1315
|
+
const rightDataTracked = rightMark.attrs.dataTracked;
|
|
1316
|
+
if (!shouldMergeTrackedAttributes(leftDataTracked, rightDataTracked)) {
|
|
1317
|
+
return;
|
|
1318
|
+
}
|
|
1319
|
+
const isLeftOlder = (leftDataTracked.createdAt || 0) < (rightDataTracked.createdAt || 0);
|
|
1320
|
+
const ancestorAttrs = isLeftOlder ? leftDataTracked : rightDataTracked;
|
|
1321
|
+
const dataTracked = {
|
|
1322
|
+
...ancestorAttrs,
|
|
1323
|
+
updatedAt: Date.now(),
|
|
1324
|
+
};
|
|
1325
|
+
const fromStartOfMark = pos - nodeBefore.nodeSize;
|
|
1326
|
+
const toEndOfMark = pos + nodeAfter.nodeSize;
|
|
1327
|
+
newTr.addMark(fromStartOfMark, toEndOfMark, leftMark.type.create({ ...leftMark.attrs, dataTracked }));
|
|
1328
|
+
}
|
|
1329
|
+
|
|
1356
1330
|
function processChangeSteps(changes, startPos, newTr, emptyAttrs, schema) {
|
|
1357
1331
|
const mapping = new Mapping();
|
|
1358
1332
|
const deleteAttrs = createNewDeleteAttrs(emptyAttrs);
|
|
@@ -1440,6 +1414,19 @@ function processChangeSteps(changes, startPos, newTr, emptyAttrs, schema) {
|
|
|
1440
1414
|
return [mapping, selectionPos];
|
|
1441
1415
|
}
|
|
1442
1416
|
|
|
1417
|
+
/**
|
|
1418
|
+
* Matches deleted-text recursively to inserted text
|
|
1419
|
+
*
|
|
1420
|
+
* This is needed as text containing various marks is split into multiple parts even though it's
|
|
1421
|
+
* continously deleted. Therefore, we need to find the next part if there is any and keep going until
|
|
1422
|
+
* we've reached the end of the deleted text or inserted content.
|
|
1423
|
+
* @param adjDeleted
|
|
1424
|
+
* @param insNode
|
|
1425
|
+
* @param offset
|
|
1426
|
+
* @param matchedDeleted
|
|
1427
|
+
* @param deleted
|
|
1428
|
+
* @returns
|
|
1429
|
+
*/
|
|
1443
1430
|
function matchText(adjDeleted, insNode, offset, matchedDeleted, deleted) {
|
|
1444
1431
|
const { pos, from, to, node: delNode } = adjDeleted;
|
|
1445
1432
|
let j = offset, d = from - pos, maxSteps = to - Math.max(pos, from);
|
|
@@ -1472,15 +1459,26 @@ function matchText(adjDeleted, insNode, offset, matchedDeleted, deleted) {
|
|
|
1472
1459
|
}
|
|
1473
1460
|
return [matchedDeleted, deleted];
|
|
1474
1461
|
}
|
|
1462
|
+
/**
|
|
1463
|
+
* Matches deleted to inserted content and returns the first pos they differ and the updated
|
|
1464
|
+
* ChangeStep list.
|
|
1465
|
+
*
|
|
1466
|
+
* Based on https://github.com/ProseMirror/prosemirror-model/blob/master/src/diff.ts
|
|
1467
|
+
* @param matchedDeleted
|
|
1468
|
+
* @param deleted
|
|
1469
|
+
* @param inserted
|
|
1470
|
+
* @returns
|
|
1471
|
+
*/
|
|
1475
1472
|
function matchInserted(matchedDeleted, deleted, inserted) {
|
|
1476
1473
|
var _a;
|
|
1477
1474
|
let matched = [matchedDeleted, deleted];
|
|
1478
1475
|
for (let i = 0;; i += 1) {
|
|
1479
|
-
if (inserted.childCount === i)
|
|
1476
|
+
if (inserted.childCount === i) {
|
|
1480
1477
|
return matched;
|
|
1478
|
+
}
|
|
1481
1479
|
const insNode = inserted.child(i);
|
|
1482
1480
|
// @ts-ignore
|
|
1483
|
-
|
|
1481
|
+
const adjDeleted = matched[1].find((d) => (d.type === 'delete-text' && Math.max(d.pos, d.from) === matched[0]) ||
|
|
1484
1482
|
(d.type === 'delete-node' && d.pos === matched[0]));
|
|
1485
1483
|
if (insNode.type !== ((_a = adjDeleted === null || adjDeleted === void 0 ? void 0 : adjDeleted.node) === null || _a === void 0 ? void 0 : _a.type)) {
|
|
1486
1484
|
return matched;
|
|
@@ -1525,16 +1523,13 @@ function matchInserted(matchedDeleted, deleted, inserted) {
|
|
|
1525
1523
|
/**
|
|
1526
1524
|
* Cuts a fragment similar to Fragment.cut but also removes the parent node.
|
|
1527
1525
|
*
|
|
1528
|
-
* @TODO there is however, some silly calculation mistake so that I need to use matched - deleted + 1 > 0
|
|
1529
|
-
* inside it to check whether to actually cut a text node. The offset might be cascading, therefore it should
|
|
1530
|
-
* be fixed at some point.
|
|
1531
1526
|
* @param matched
|
|
1532
1527
|
* @param deleted
|
|
1533
1528
|
* @param content
|
|
1534
1529
|
* @returns
|
|
1535
1530
|
*/
|
|
1536
1531
|
function cutFragment(matched, deleted, content) {
|
|
1537
|
-
|
|
1532
|
+
const newContent = [];
|
|
1538
1533
|
for (let i = 0; matched <= deleted && i < content.childCount; i += 1) {
|
|
1539
1534
|
const child = content.child(i);
|
|
1540
1535
|
if (!child.isText && child.content.size > 0) {
|
|
@@ -1557,7 +1552,7 @@ function cutFragment(matched, deleted, content) {
|
|
|
1557
1552
|
}
|
|
1558
1553
|
return [matched, Fragment.fromArray(newContent)];
|
|
1559
1554
|
}
|
|
1560
|
-
function diffChangeSteps(deleted, inserted
|
|
1555
|
+
function diffChangeSteps(deleted, inserted) {
|
|
1561
1556
|
const updated = [];
|
|
1562
1557
|
let updatedDeleted = [...deleted];
|
|
1563
1558
|
inserted.forEach((ins) => {
|
|
@@ -1615,6 +1610,7 @@ function diffChangeSteps(deleted, inserted, newTr, schema) {
|
|
|
1615
1610
|
* @returns
|
|
1616
1611
|
*/
|
|
1617
1612
|
const getSelectionStaticConstructor = (sel) => Object.getPrototypeOf(sel).constructor;
|
|
1613
|
+
const isHighlightMarkerNode = (node) => node.type === node.type.schema.nodes.highlight_marker;
|
|
1618
1614
|
/**
|
|
1619
1615
|
* Inverts transactions to wrap their contents/operations with track data instead
|
|
1620
1616
|
*
|
|
@@ -1644,6 +1640,7 @@ function trackTransaction(tr, oldState, newTr, authorID) {
|
|
|
1644
1640
|
let iters = 0;
|
|
1645
1641
|
log.info('ORIGINAL transaction', tr);
|
|
1646
1642
|
tr.steps.forEach((step) => {
|
|
1643
|
+
var _a, _b;
|
|
1647
1644
|
log.info('transaction step', step);
|
|
1648
1645
|
iters += 1;
|
|
1649
1646
|
if (iters > 20) {
|
|
@@ -1658,11 +1655,17 @@ function trackTransaction(tr, oldState, newTr, authorID) {
|
|
|
1658
1655
|
}
|
|
1659
1656
|
else if (step instanceof ReplaceStep) {
|
|
1660
1657
|
let [steps, startPos] = trackReplaceStep(step, oldState, newTr, emptyAttrs);
|
|
1658
|
+
const { slice } = step;
|
|
1659
|
+
if (((_b = (_a = slice === null || slice === void 0 ? void 0 : slice.content) === null || _a === void 0 ? void 0 : _a.content) === null || _b === void 0 ? void 0 : _b.length) === 1 &&
|
|
1660
|
+
isHighlightMarkerNode(slice.content.content[0])) {
|
|
1661
|
+
// don't track highlight marker nodes
|
|
1662
|
+
return;
|
|
1663
|
+
}
|
|
1661
1664
|
log.info('CHANGES: ', steps);
|
|
1662
1665
|
// deleted and merged really...
|
|
1663
1666
|
const deleted = steps.filter((s) => s.type !== 'insert-slice');
|
|
1664
1667
|
const inserted = steps.filter((s) => s.type === 'insert-slice');
|
|
1665
|
-
steps = diffChangeSteps(deleted, inserted
|
|
1668
|
+
steps = diffChangeSteps(deleted, inserted);
|
|
1666
1669
|
log.info('DIFFED STEPS: ', steps);
|
|
1667
1670
|
const [mapping, selectionPos] = processChangeSteps(steps, startPos || tr.selection.head, // Incase startPos is it's default value 0, use the old selection head
|
|
1668
1671
|
newTr, emptyAttrs, oldState.schema);
|
|
@@ -1680,7 +1683,7 @@ function trackTransaction(tr, oldState, newTr, authorID) {
|
|
|
1680
1683
|
const deleted = steps.filter((s) => s.type !== 'insert-slice');
|
|
1681
1684
|
const inserted = steps.filter((s) => s.type === 'insert-slice');
|
|
1682
1685
|
log.info('INSERT STEPS: ', inserted);
|
|
1683
|
-
steps = diffChangeSteps(deleted, inserted
|
|
1686
|
+
steps = diffChangeSteps(deleted, inserted);
|
|
1684
1687
|
log.info('DIFFED STEPS: ', steps);
|
|
1685
1688
|
processChangeSteps(steps, tr.selection.from, newTr, emptyAttrs, oldState.schema);
|
|
1686
1689
|
}
|
|
@@ -1770,15 +1773,14 @@ const trackChangesPlugin = (opts = { userID: 'anonymous:Anonymous' }) => {
|
|
|
1770
1773
|
return {
|
|
1771
1774
|
...pluginState,
|
|
1772
1775
|
status: setStatus,
|
|
1773
|
-
changeSet: findChanges(newState),
|
|
1776
|
+
changeSet: setStatus === TrackChangesStatus.disabled ? new ChangeSet() : findChanges(newState),
|
|
1774
1777
|
};
|
|
1775
1778
|
}
|
|
1776
1779
|
else if (pluginState.status === TrackChangesStatus.disabled) {
|
|
1777
1780
|
return { ...pluginState, changeSet: new ChangeSet() };
|
|
1778
1781
|
}
|
|
1779
1782
|
let { changeSet, ...rest } = pluginState;
|
|
1780
|
-
|
|
1781
|
-
if (updatedChangeIds || getAction(tr, TrackChangesAction.refreshChanges)) {
|
|
1783
|
+
if (getAction(tr, TrackChangesAction.refreshChanges)) {
|
|
1782
1784
|
changeSet = findChanges(newState);
|
|
1783
1785
|
}
|
|
1784
1786
|
return {
|
|
@@ -1822,7 +1824,6 @@ const trackChangesPlugin = (opts = { userID: 'anonymous:Anonymous' }) => {
|
|
|
1822
1824
|
createdTr = updateChangeAttrs(createdTr, change, { ...change.dataTracked, status, reviewedByID: userID }, oldState.schema);
|
|
1823
1825
|
}
|
|
1824
1826
|
});
|
|
1825
|
-
setAction(createdTr, TrackChangesAction.updateChanges, ids);
|
|
1826
1827
|
}
|
|
1827
1828
|
else if (getAction(tr, TrackChangesAction.applyAndRemoveChanges)) {
|
|
1828
1829
|
const mapping = applyAcceptedRejectedChanges(createdTr, oldState.schema, changeSet.bothNodeChanges);
|
|
@@ -1901,24 +1902,8 @@ const applyAndRemoveChanges = () => (state, dispatch) => {
|
|
|
1901
1902
|
* Runs `findChanges` to iterate over the document to collect changes into a new ChangeSet.
|
|
1902
1903
|
*/
|
|
1903
1904
|
const refreshChanges = () => (state, dispatch) => {
|
|
1904
|
-
dispatch && dispatch(setAction(state.tr, TrackChangesAction.
|
|
1905
|
+
dispatch && dispatch(setAction(state.tr, TrackChangesAction.refreshChanges, true));
|
|
1905
1906
|
return true;
|
|
1906
|
-
};
|
|
1907
|
-
/**
|
|
1908
|
-
* Adds track attributes for a block node. For testing puroses
|
|
1909
|
-
*/
|
|
1910
|
-
const setParagraphTestAttribute = (val = 'changed') => (state, dispatch) => {
|
|
1911
|
-
var _a;
|
|
1912
|
-
const cursor = state.selection.head;
|
|
1913
|
-
const blockNodePos = state.doc.resolve(cursor).start(1) - 1;
|
|
1914
|
-
if (((_a = state.doc.resolve(blockNodePos).nodeAfter) === null || _a === void 0 ? void 0 : _a.type) === state.schema.nodes.paragraph &&
|
|
1915
|
-
dispatch) {
|
|
1916
|
-
dispatch(state.tr.setNodeMarkup(blockNodePos, undefined, {
|
|
1917
|
-
testAttribute: val,
|
|
1918
|
-
}));
|
|
1919
|
-
return true;
|
|
1920
|
-
}
|
|
1921
|
-
return false;
|
|
1922
1907
|
};
|
|
1923
1908
|
|
|
1924
1909
|
var commands = /*#__PURE__*/Object.freeze({
|
|
@@ -1927,8 +1912,7 @@ var commands = /*#__PURE__*/Object.freeze({
|
|
|
1927
1912
|
setChangeStatuses: setChangeStatuses,
|
|
1928
1913
|
setUserID: setUserID,
|
|
1929
1914
|
applyAndRemoveChanges: applyAndRemoveChanges,
|
|
1930
|
-
refreshChanges: refreshChanges
|
|
1931
|
-
setParagraphTestAttribute: setParagraphTestAttribute
|
|
1915
|
+
refreshChanges: refreshChanges
|
|
1932
1916
|
});
|
|
1933
1917
|
|
|
1934
1918
|
export { CHANGE_OPERATION, CHANGE_STATUS, ChangeSet, TrackChangesStatus, enableDebug, skipTracking, trackChangesPlugin, trackChangesPluginKey, commands as trackCommands };
|
|
@@ -28,5 +28,6 @@ import { NewDeleteAttrs } from '../types/track';
|
|
|
28
28
|
* @param deleteAttrs
|
|
29
29
|
* @param from
|
|
30
30
|
* @param to
|
|
31
|
+
* @returns position at the end of the possibly deleted text
|
|
31
32
|
*/
|
|
32
33
|
export declare function deleteTextIfInserted(node: PMNode, pos: number, newTr: Transaction, schema: Schema, deleteAttrs: NewDeleteAttrs, from?: number, to?: number): number;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@manuscripts/track-changes-plugin",
|
|
3
|
-
"version": "0.4.
|
|
3
|
+
"version": "0.4.4-LEAN-1839",
|
|
4
4
|
"author": "Atypon Systems LLC",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"homepage": "https://github.com/Atypon-OpenSource/manuscripts-quarterback/tree/main/quarterback-packages/track-changes-plugin",
|
|
@@ -11,7 +11,10 @@
|
|
|
11
11
|
"exports": {
|
|
12
12
|
"./package.json": "./package.json",
|
|
13
13
|
"./src/styles.css": "./src/styles.css",
|
|
14
|
-
".":
|
|
14
|
+
".": {
|
|
15
|
+
"import": "./dist/index.js",
|
|
16
|
+
"require": "./dist/index.cjs"
|
|
17
|
+
}
|
|
15
18
|
},
|
|
16
19
|
"files": [
|
|
17
20
|
"dist",
|
|
@@ -21,28 +24,28 @@
|
|
|
21
24
|
"access": "public"
|
|
22
25
|
},
|
|
23
26
|
"devDependencies": {
|
|
24
|
-
"@manuscripts/manuscript-transform": "^0.
|
|
25
|
-
"@rollup/plugin-commonjs": "^22.0.
|
|
27
|
+
"@manuscripts/manuscript-transform": "^0.54.0",
|
|
28
|
+
"@rollup/plugin-commonjs": "^22.0.2",
|
|
26
29
|
"@types/debug": "^4.1.7",
|
|
27
30
|
"@types/jest": "27.5.1",
|
|
28
|
-
"@types/node": "^
|
|
31
|
+
"@types/node": "^18.7.18",
|
|
29
32
|
"jest": "27.5.1",
|
|
30
33
|
"jest-environment-jsdom": "27.5.1",
|
|
31
|
-
"jsdom": "^
|
|
32
|
-
"prosemirror-commands": "^1.3.
|
|
34
|
+
"jsdom": "^20.0.0",
|
|
35
|
+
"prosemirror-commands": "^1.3.1",
|
|
33
36
|
"prosemirror-example-setup": "^1.2.1",
|
|
34
37
|
"prosemirror-history": "^1.3.0",
|
|
35
38
|
"prosemirror-keymap": "^1.2.0",
|
|
36
39
|
"prosemirror-model": "^1.18.1",
|
|
37
|
-
"prosemirror-schema-list": "^1.2.
|
|
40
|
+
"prosemirror-schema-list": "^1.2.2",
|
|
38
41
|
"prosemirror-state": "^1.4.1",
|
|
39
|
-
"prosemirror-transform": "^1.
|
|
40
|
-
"prosemirror-view": "^1.
|
|
41
|
-
"rollup": "^2.
|
|
42
|
-
"rollup-plugin-typescript2": "^0.
|
|
42
|
+
"prosemirror-transform": "^1.7.0",
|
|
43
|
+
"prosemirror-view": "^1.28.1",
|
|
44
|
+
"rollup": "^2.79.0",
|
|
45
|
+
"rollup-plugin-typescript2": "^0.34.0",
|
|
43
46
|
"ts-jest": "27.1.4",
|
|
44
47
|
"tslib": "^2.4.0",
|
|
45
|
-
"typescript": "^4.
|
|
48
|
+
"typescript": "^4.8.3"
|
|
46
49
|
},
|
|
47
50
|
"peerDependencies": {
|
|
48
51
|
"prosemirror-model": ">=1.14.0",
|