@atlaskit/editor-plugin-show-diff 14.2.10 → 14.2.11
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 +13 -0
- package/dist/cjs/pm-plugins/calculateDiff/attrAwareTokenEncoder.js +2 -22
- package/dist/cjs/pm-plugins/calculateDiff/calculateDiffDecorations.js +18 -7
- package/dist/cjs/pm-plugins/calculateDiff/computeDiffChanges.js +5 -6
- package/dist/cjs/pm-plugins/calculateDiff/encodeCharacterWithMarks.js +44 -0
- package/dist/cjs/pm-plugins/calculateDiff/isMarkOnlyChange.js +50 -0
- package/dist/cjs/pm-plugins/calculateDiff/markAwareTokenEncoder.js +38 -0
- package/dist/cjs/pm-plugins/calculateDiff/selectTokenEncoder.js +36 -0
- package/dist/cjs/pm-plugins/decorations/createNodeChangedDecorationWidget.js +4 -2
- package/dist/es2019/pm-plugins/calculateDiff/attrAwareTokenEncoder.js +1 -21
- package/dist/es2019/pm-plugins/calculateDiff/calculateDiffDecorations.js +19 -7
- package/dist/es2019/pm-plugins/calculateDiff/computeDiffChanges.js +6 -6
- package/dist/es2019/pm-plugins/calculateDiff/encodeCharacterWithMarks.js +36 -0
- package/dist/es2019/pm-plugins/calculateDiff/isMarkOnlyChange.js +47 -0
- package/dist/es2019/pm-plugins/calculateDiff/markAwareTokenEncoder.js +25 -0
- package/dist/es2019/pm-plugins/calculateDiff/selectTokenEncoder.js +30 -0
- package/dist/es2019/pm-plugins/decorations/createNodeChangedDecorationWidget.js +7 -0
- package/dist/esm/pm-plugins/calculateDiff/attrAwareTokenEncoder.js +1 -21
- package/dist/esm/pm-plugins/calculateDiff/calculateDiffDecorations.js +18 -7
- package/dist/esm/pm-plugins/calculateDiff/computeDiffChanges.js +5 -6
- package/dist/esm/pm-plugins/calculateDiff/encodeCharacterWithMarks.js +38 -0
- package/dist/esm/pm-plugins/calculateDiff/isMarkOnlyChange.js +44 -0
- package/dist/esm/pm-plugins/calculateDiff/markAwareTokenEncoder.js +33 -0
- package/dist/esm/pm-plugins/calculateDiff/selectTokenEncoder.js +30 -0
- package/dist/esm/pm-plugins/decorations/createNodeChangedDecorationWidget.js +4 -2
- package/dist/types/pm-plugins/calculateDiff/encodeCharacterWithMarks.d.ts +9 -0
- package/dist/types/pm-plugins/calculateDiff/isMarkOnlyChange.d.ts +15 -0
- package/dist/types/pm-plugins/calculateDiff/markAwareTokenEncoder.d.ts +9 -0
- package/dist/types/pm-plugins/calculateDiff/selectTokenEncoder.d.ts +17 -0
- package/package.json +6 -39
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,18 @@
|
|
|
1
1
|
# @atlaskit/editor-plugin-show-diff
|
|
2
2
|
|
|
3
|
+
## 14.2.11
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- [`15abc159d8096`](https://bitbucket.org/atlassian/atlassian-frontend-monorepo/commits/15abc159d8096) -
|
|
8
|
+
Add mark (formatting) support to the non-smart diff types behind
|
|
9
|
+
`platform_editor_diff_inline_mark_changes`. A mark-only agent edit arrives as a `ReplaceStep`,
|
|
10
|
+
which the default `prosemirror-changeset` token encoder tokenises identically on both sides, so
|
|
11
|
+
bold/italic/code/link changes were silently invisible in the inline diff. A mark-aware token
|
|
12
|
+
encoder now folds marks into the character token, and mark-only changes render as an added-side
|
|
13
|
+
highlight without duplicating the unchanged text. No behaviour change with the gate off.
|
|
14
|
+
- Updated dependencies
|
|
15
|
+
|
|
3
16
|
## 14.2.10
|
|
4
17
|
|
|
5
18
|
### Patch Changes
|
|
@@ -6,6 +6,7 @@ Object.defineProperty(exports, "__esModule", {
|
|
|
6
6
|
exports.attrAwareTokenEncoder = void 0;
|
|
7
7
|
var _nodeTypeUtils = require("@atlaskit/editor-common/utils/node-type-utils");
|
|
8
8
|
var _diffableAttrs = require("../decorations/utils/diffableAttrs");
|
|
9
|
+
var _encodeCharacterWithMarks = require("./encodeCharacterWithMarks");
|
|
9
10
|
/**
|
|
10
11
|
* Attribute-aware token encoder for `prosemirror-changeset`. The default encoder
|
|
11
12
|
* reduces a node to `node.type.name`, so an attribute-only change (e.g. a table
|
|
@@ -25,33 +26,12 @@ var encodeNodeWithAttrs = function encodeNodeWithAttrs(node, attrNames) {
|
|
|
25
26
|
return "".concat(node.type.name, "|").concat(parts.join('|'));
|
|
26
27
|
};
|
|
27
28
|
|
|
28
|
-
// Fold a character's marks (type + attrs) into its token so a formatting-only change
|
|
29
|
-
// (bold/italic/link/colour, or a link whose `href` changes) registers as a real diff.
|
|
30
|
-
// The default encoder returns just the char code, so marked and unmarked text tokenise
|
|
31
|
-
// identically. Marks and their attrs are sorted so the token is order-independent.
|
|
32
|
-
var encodeCharacterWithMarks = function encodeCharacterWithMarks(char, marks) {
|
|
33
|
-
if (marks.length === 0) {
|
|
34
|
-
// Fast path: unmarked text keeps the numeric char code (unchanged behaviour).
|
|
35
|
-
return char;
|
|
36
|
-
}
|
|
37
|
-
var markKey = marks.map(function (mark) {
|
|
38
|
-
var _mark$attrs;
|
|
39
|
-
var attrs = (_mark$attrs = mark.attrs) !== null && _mark$attrs !== void 0 ? _mark$attrs : {};
|
|
40
|
-
var attrParts = Object.keys(attrs).sort().map(function (key) {
|
|
41
|
-
var _attrs$key;
|
|
42
|
-
return "".concat(key, "=").concat(JSON.stringify((_attrs$key = attrs[key]) !== null && _attrs$key !== void 0 ? _attrs$key : null));
|
|
43
|
-
});
|
|
44
|
-
return attrParts.length > 0 ? "".concat(mark.type.name, "(").concat(attrParts.join(','), ")") : mark.type.name;
|
|
45
|
-
}).sort().join('&');
|
|
46
|
-
return "".concat(char, "\0").concat(markKey);
|
|
47
|
-
};
|
|
48
|
-
|
|
49
29
|
// Like the library default, but node types with a `diffableAttrs` rule fold their
|
|
50
30
|
// attrs into the open token, and text characters fold in their marks. Node-end is
|
|
51
31
|
// unchanged, hence `string | number`.
|
|
52
32
|
var attrAwareTokenEncoder = exports.attrAwareTokenEncoder = {
|
|
53
33
|
encodeCharacter: function encodeCharacter(char, marks) {
|
|
54
|
-
return encodeCharacterWithMarks(char, marks);
|
|
34
|
+
return (0, _encodeCharacterWithMarks.encodeCharacterWithMarks)(char, marks);
|
|
55
35
|
},
|
|
56
36
|
encodeNodeStart: function encodeNodeStart(node) {
|
|
57
37
|
var _node$attrs2;
|
|
@@ -27,10 +27,11 @@ var _decorationKeys = require("../decorations/decorationKeys");
|
|
|
27
27
|
var _getAttrChangeRanges = require("../decorations/utils/getAttrChangeRanges");
|
|
28
28
|
var _getMarkChangeRanges = require("../decorations/utils/getMarkChangeRanges");
|
|
29
29
|
var _isExtendedEnabled = require("../isExtendedEnabled");
|
|
30
|
-
var _attrAwareTokenEncoder = require("./attrAwareTokenEncoder");
|
|
31
30
|
var _diffBySteps = require("./diffBySteps");
|
|
32
31
|
var _groupChangesByBlock = require("./groupChangesByBlock");
|
|
32
|
+
var _isMarkOnlyChange = require("./isMarkOnlyChange");
|
|
33
33
|
var _optimizeChanges = require("./optimizeChanges");
|
|
34
|
+
var _selectTokenEncoder2 = require("./selectTokenEncoder");
|
|
34
35
|
var _simplifyChangesWithAttribution = require("./simplifyChangesWithAttribution");
|
|
35
36
|
var _simplifySteps = require("./simplifySteps");
|
|
36
37
|
var _classifySmartChanges = require("./smart/classifySmartChanges");
|
|
@@ -389,10 +390,10 @@ var calculateDiffDecorationsInner = function calculateDiffDecorationsInner(_ref7
|
|
|
389
390
|
};
|
|
390
391
|
}
|
|
391
392
|
}
|
|
392
|
-
// The attribute-aware encoder is only needed by the smart classifier and is
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
393
|
+
// The attribute-aware encoder is only needed by the smart classifier and is gated with it.
|
|
394
|
+
var _selectTokenEncoder = (0, _selectTokenEncoder2.selectTokenEncoder)(diffType === 'smart' && (0, _fg.fg)('platform_editor_ai_smart_diff')),
|
|
395
|
+
tokenEncoder = _selectTokenEncoder.tokenEncoder,
|
|
396
|
+
shouldHideMarkOnlyDeletions = _selectTokenEncoder.shouldHideMarkOnlyDeletions;
|
|
396
397
|
var changes;
|
|
397
398
|
var attributedChanges = [];
|
|
398
399
|
var attributionColors;
|
|
@@ -447,6 +448,16 @@ var calculateDiffDecorationsInner = function calculateDiffDecorationsInner(_ref7
|
|
|
447
448
|
var changeColorScheme = attributionColors ? (0, _attributions.getColorSchemeForChange)(change, attributedChanges, attributionColors, colorScheme) : colorScheme;
|
|
448
449
|
var isActive = isRangeActive(activeIndexPos, change.fromB, change.toB);
|
|
449
450
|
|
|
451
|
+
// The deleted side of a mark-only change is a byte-identical copy of the new text, so it is
|
|
452
|
+
// suppressed in the clean view. `hideDeletedDiffs` is load-bearing: when false the reviewer
|
|
453
|
+
// asked to compare against the original, and the deleted side is the only place the
|
|
454
|
+
// previous formatting is visible.
|
|
455
|
+
var isMarkOnly = shouldHideMarkOnlyDeletions && hideDeletedDiffs && change.deleted.length > 0 && (0, _isMarkOnlyChange.isMarkOnlyChange)({
|
|
456
|
+
change: change,
|
|
457
|
+
originalDoc: originalDoc,
|
|
458
|
+
newDoc: tr.doc
|
|
459
|
+
});
|
|
460
|
+
|
|
450
461
|
// Hoisted because it decides BOTH where the deleted widget is anchored and — since the
|
|
451
462
|
// widget pins whichever end of the range it sits at — how the indicator anchors below are
|
|
452
463
|
// allowed to move.
|
|
@@ -477,7 +488,7 @@ var calculateDiffDecorationsInner = function calculateDiffDecorationsInner(_ref7
|
|
|
477
488
|
// change. Used to decide if indicator anchor positions should be adjusted
|
|
478
489
|
// inward — when the widget is present the anchor must stay at the block
|
|
479
490
|
// boundary to keep the indicator bar continuous with the deleted content.
|
|
480
|
-
var willRenderDeletedWidget = change.deleted.length > 0 && !((0, _isExtendedEnabled.isExtendedEnabled)(diffType) && !isInverted && hideDeletedDiffs && change.inserted.length > 0);
|
|
491
|
+
var willRenderDeletedWidget = change.deleted.length > 0 && !isMarkOnly && !((0, _isExtendedEnabled.isExtendedEnabled)(diffType) && !isInverted && hideDeletedDiffs && change.inserted.length > 0);
|
|
481
492
|
if (isSmartNodeLevel) {
|
|
482
493
|
// For a large inserted table, skip the O(cells) inline decorations inside the
|
|
483
494
|
// table (the cell overlays added below provide the highlight without the
|
|
@@ -548,7 +559,7 @@ var calculateDiffDecorationsInner = function calculateDiffDecorationsInner(_ref7
|
|
|
548
559
|
coarseTableCellsOnly: useCoarseTableDecoration
|
|
549
560
|
}))));
|
|
550
561
|
}
|
|
551
|
-
if (change.deleted.length > 0) {
|
|
562
|
+
if (change.deleted.length > 0 && !isMarkOnly) {
|
|
552
563
|
var _shouldHideDeleted = (0, _isExtendedEnabled.isExtendedEnabled)(diffType) ? !isInverted && hideDeletedDiffs && change.inserted.length > 0 : false;
|
|
553
564
|
if (!_shouldHideDeleted) {
|
|
554
565
|
decorations.push.apply(decorations, (0, _toConsumableArray2.default)((0, _createNodeChangedDecorationWidget.createNodeChangedDecorationWidget)(_objectSpread(_objectSpread({
|
|
@@ -5,10 +5,10 @@ Object.defineProperty(exports, "__esModule", {
|
|
|
5
5
|
});
|
|
6
6
|
exports.computeDiffChanges = void 0;
|
|
7
7
|
var _prosemirrorChangeset = require("prosemirror-changeset");
|
|
8
|
-
var _attrAwareTokenEncoder = require("./attrAwareTokenEncoder");
|
|
9
8
|
var _diffBySteps = require("./diffBySteps");
|
|
10
9
|
var _groupChangesByBlock = require("./groupChangesByBlock");
|
|
11
10
|
var _optimizeChanges = require("./optimizeChanges");
|
|
11
|
+
var _selectTokenEncoder2 = require("./selectTokenEncoder");
|
|
12
12
|
var _simplifySteps = require("./simplifySteps");
|
|
13
13
|
var _classifySmartChanges = require("./smart/classifySmartChanges");
|
|
14
14
|
function _createForOfIteratorHelper(r, e) { var t = "undefined" != typeof Symbol && r[Symbol.iterator] || r["@@iterator"]; if (!t) { if (Array.isArray(r) || (t = _unsupportedIterableToArray(r)) || e && r && "number" == typeof r.length) { t && (r = t); var _n = 0, F = function F() {}; return { s: F, n: function n() { return _n >= r.length ? { done: !0 } : { done: !1, value: r[_n++] }; }, e: function e(r) { throw r; }, f: F }; } throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); } var o, a = !0, u = !1; return { s: function s() { t = t.call(r); }, n: function n() { var r = t.next(); return a = r.done, r; }, e: function e(r) { u = !0, o = r; }, f: function f() { try { a || null == t.return || t.return(); } finally { if (u) throw o; } } }; }
|
|
@@ -82,11 +82,10 @@ var computeDiffChanges = exports.computeDiffChanges = function computeDiffChange
|
|
|
82
82
|
};
|
|
83
83
|
}
|
|
84
84
|
|
|
85
|
-
//
|
|
86
|
-
//
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
var tokenEncoder = diffType === 'smart' ? _attrAwareTokenEncoder.attrAwareTokenEncoder : undefined;
|
|
85
|
+
// This utility does not apply the smart-diff gate (see the file docstring); a caller
|
|
86
|
+
// requesting `smart` is already behind it.
|
|
87
|
+
var _selectTokenEncoder = (0, _selectTokenEncoder2.selectTokenEncoder)(diffType === 'smart'),
|
|
88
|
+
tokenEncoder = _selectTokenEncoder.tokenEncoder;
|
|
90
89
|
var changeset = _prosemirrorChangeset.ChangeSet.create(originalDoc, undefined, tokenEncoder).addSteps(steppedDoc, stepMaps, steppedDoc);
|
|
91
90
|
if (diffType === 'smart') {
|
|
92
91
|
return {
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports.encodeCharacterWithMarks = void 0;
|
|
7
|
+
/** U+0000 cannot occur in ProseMirror text content, so a token can never be ambiguous. */
|
|
8
|
+
var MARK_KEY_SEPARATOR = "\0";
|
|
9
|
+
var buildMarkKey = function buildMarkKey(marks) {
|
|
10
|
+
return marks.map(function (mark) {
|
|
11
|
+
var _mark$attrs;
|
|
12
|
+
var attrs = (_mark$attrs = mark.attrs) !== null && _mark$attrs !== void 0 ? _mark$attrs : {};
|
|
13
|
+
var attrParts = Object.keys(attrs).sort().map(function (key) {
|
|
14
|
+
var _attrs$key;
|
|
15
|
+
return "".concat(key, "=").concat(JSON.stringify((_attrs$key = attrs[key]) !== null && _attrs$key !== void 0 ? _attrs$key : null));
|
|
16
|
+
});
|
|
17
|
+
return attrParts.length > 0 ? "".concat(mark.type.name, "(").concat(attrParts.join(','), ")") : mark.type.name;
|
|
18
|
+
}).sort().join('&');
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Keyed on array identity: `prosemirror-changeset` passes the same `child.marks` reference for
|
|
23
|
+
* every character in a text node, so this collapses the key building to once per node.
|
|
24
|
+
*/
|
|
25
|
+
var markKeyCache = new WeakMap();
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Folds a character's marks into its `prosemirror-changeset` token. The library default returns
|
|
29
|
+
* only the char code, so marked and unmarked text tokenise identically and a formatting-only edit
|
|
30
|
+
* is invisible to the changeset.
|
|
31
|
+
*
|
|
32
|
+
* Marks and attrs are sorted so the token is order-independent.
|
|
33
|
+
*/
|
|
34
|
+
var encodeCharacterWithMarks = exports.encodeCharacterWithMarks = function encodeCharacterWithMarks(char, marks) {
|
|
35
|
+
if (marks.length === 0) {
|
|
36
|
+
return char;
|
|
37
|
+
}
|
|
38
|
+
var markKey = markKeyCache.get(marks);
|
|
39
|
+
if (markKey === undefined) {
|
|
40
|
+
markKey = buildMarkKey(marks);
|
|
41
|
+
markKeyCache.set(marks, markKey);
|
|
42
|
+
}
|
|
43
|
+
return "".concat(char).concat(MARK_KEY_SEPARATOR).concat(markKey);
|
|
44
|
+
};
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports.isMarkOnlyChange = void 0;
|
|
7
|
+
/** U+0000 cannot occur in ProseMirror text content, so this cannot collide with document text. */
|
|
8
|
+
var NODE_MARKER = "\0";
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Node types and text only; marks and attrs excluded.
|
|
12
|
+
*
|
|
13
|
+
* Text is unprefixed so adjacent runs concatenate — a run only splits because its marks differ, so
|
|
14
|
+
* `strong("hello")` and `strong("hel") + em("lo")` must match. Attrs are excluded because a
|
|
15
|
+
* reconstructed agent edit regenerates `localId`s; `getAttrChangeRanges` reports attrs separately.
|
|
16
|
+
*/
|
|
17
|
+
var markFreeSignature = function markFreeSignature(doc, from, to) {
|
|
18
|
+
var parts = [];
|
|
19
|
+
doc.nodesBetween(from, to, function (node, pos) {
|
|
20
|
+
if (node.isText) {
|
|
21
|
+
var _node$text;
|
|
22
|
+
var start = Math.max(pos, from) - pos;
|
|
23
|
+
var end = Math.min(pos + node.nodeSize, to) - pos;
|
|
24
|
+
parts.push(((_node$text = node.text) !== null && _node$text !== void 0 ? _node$text : '').slice(start, end));
|
|
25
|
+
} else {
|
|
26
|
+
parts.push("".concat(NODE_MARKER).concat(node.type.name));
|
|
27
|
+
}
|
|
28
|
+
return true;
|
|
29
|
+
});
|
|
30
|
+
return parts.join('');
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Whether a change differs only in marks, with identical structure and text on both sides. Only
|
|
35
|
+
* produced when a mark-aware token encoder is in use.
|
|
36
|
+
*/
|
|
37
|
+
var isMarkOnlyChange = exports.isMarkOnlyChange = function isMarkOnlyChange(_ref) {
|
|
38
|
+
var change = _ref.change,
|
|
39
|
+
newDoc = _ref.newDoc,
|
|
40
|
+
originalDoc = _ref.originalDoc;
|
|
41
|
+
var fromA = change.fromA,
|
|
42
|
+
toA = change.toA,
|
|
43
|
+
fromB = change.fromB,
|
|
44
|
+
toB = change.toB;
|
|
45
|
+
var isPureInsertionOrDeletion = toA === fromA || toB === fromB;
|
|
46
|
+
if (isPureInsertionOrDeletion || toA - fromA !== toB - fromB) {
|
|
47
|
+
return false;
|
|
48
|
+
}
|
|
49
|
+
return markFreeSignature(originalDoc, fromA, toA) === markFreeSignature(newDoc, fromB, toB);
|
|
50
|
+
};
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports.markAwareTokenEncoder = void 0;
|
|
7
|
+
var _encodeCharacterWithMarks = require("./encodeCharacterWithMarks");
|
|
8
|
+
/** Mirrors the library's private (unexported) `typeID` so node-end tokens match. */
|
|
9
|
+
var typeID = function typeID(type) {
|
|
10
|
+
var cache = type.schema.cached.changeSetIDs || (type.schema.cached.changeSetIDs = Object.create(null));
|
|
11
|
+
var id = cache[type.name];
|
|
12
|
+
if (id == null) {
|
|
13
|
+
cache[type.name] = id = Object.keys(type.schema.nodes).indexOf(type.name) + 1;
|
|
14
|
+
}
|
|
15
|
+
return id;
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* The library default encoder plus mark folding on text characters.
|
|
20
|
+
*
|
|
21
|
+
* Deliberately narrower than `attrAwareTokenEncoder`: node-start tokens keep the library default
|
|
22
|
+
* and do not fold in diffable attributes, which `getAttrChangeRanges` already reports for every
|
|
23
|
+
* diff type.
|
|
24
|
+
*/
|
|
25
|
+
var markAwareTokenEncoder = exports.markAwareTokenEncoder = {
|
|
26
|
+
encodeCharacter: function encodeCharacter(char, marks) {
|
|
27
|
+
return (0, _encodeCharacterWithMarks.encodeCharacterWithMarks)(char, marks);
|
|
28
|
+
},
|
|
29
|
+
encodeNodeStart: function encodeNodeStart(node) {
|
|
30
|
+
return node.type.name;
|
|
31
|
+
},
|
|
32
|
+
encodeNodeEnd: function encodeNodeEnd(node) {
|
|
33
|
+
return -typeID(node.type);
|
|
34
|
+
},
|
|
35
|
+
compareTokens: function compareTokens(a, b) {
|
|
36
|
+
return a === b;
|
|
37
|
+
}
|
|
38
|
+
};
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports.selectTokenEncoder = void 0;
|
|
7
|
+
var _fg = require("@atlaskit/platform-feature-flags/fg");
|
|
8
|
+
var _attrAwareTokenEncoder = require("./attrAwareTokenEncoder");
|
|
9
|
+
var _markAwareTokenEncoder = require("./markAwareTokenEncoder");
|
|
10
|
+
/**
|
|
11
|
+
* Single place `platform_editor_diff_inline_mark_changes` is read, shared by
|
|
12
|
+
* `calculateDiffDecorations` and `computeDiffChanges` so the two cannot drift.
|
|
13
|
+
*
|
|
14
|
+
* With the gate off this returns what both callers computed before it existed.
|
|
15
|
+
*
|
|
16
|
+
* @param useAttrAwareEncoder Caller is running the smart classifier, which needs node attrs folded
|
|
17
|
+
* into the token.
|
|
18
|
+
*/
|
|
19
|
+
var selectTokenEncoder = exports.selectTokenEncoder = function selectTokenEncoder(useAttrAwareEncoder) {
|
|
20
|
+
if (useAttrAwareEncoder) {
|
|
21
|
+
return {
|
|
22
|
+
tokenEncoder: _attrAwareTokenEncoder.attrAwareTokenEncoder,
|
|
23
|
+
shouldHideMarkOnlyDeletions: false
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
if ((0, _fg.fg)('platform_editor_diff_inline_mark_changes')) {
|
|
27
|
+
return {
|
|
28
|
+
tokenEncoder: _markAwareTokenEncoder.markAwareTokenEncoder,
|
|
29
|
+
shouldHideMarkOnlyDeletions: true
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
return {
|
|
33
|
+
tokenEncoder: undefined,
|
|
34
|
+
shouldHideMarkOnlyDeletions: false
|
|
35
|
+
};
|
|
36
|
+
};
|
|
@@ -347,7 +347,7 @@ var createNodeChangedDecorationWidget = exports.createNodeChangedDecorationWidge
|
|
|
347
347
|
decorations.push(leftAnchor);
|
|
348
348
|
}
|
|
349
349
|
}
|
|
350
|
-
decorations.push(_view.Decoration.widget(safeInsertPos, dom, _objectSpread(_objectSpread({}, (0, _decorationKeys.buildDiffDecorationSpec)(_objectSpread({
|
|
350
|
+
decorations.push(_view.Decoration.widget(safeInsertPos, dom, _objectSpread(_objectSpread(_objectSpread({}, (0, _decorationKeys.buildDiffDecorationSpec)(_objectSpread({
|
|
351
351
|
decorationType: 'widget',
|
|
352
352
|
diffId: diffId,
|
|
353
353
|
isActive: isActive,
|
|
@@ -356,7 +356,9 @@ var createNodeChangedDecorationWidget = exports.createNodeChangedDecorationWidge
|
|
|
356
356
|
// placeBelow anchors at the end of the new content, so render on the
|
|
357
357
|
// trailing side (1); otherwise render before the new content (-1).
|
|
358
358
|
side: placeBelow ? 1 : -1
|
|
359
|
-
}))),
|
|
359
|
+
}))), (0, _fg.fg)('platform_editor_diff_inline_mark_changes') && {
|
|
360
|
+
marks: []
|
|
361
|
+
}), {}, {
|
|
360
362
|
destroy: function destroy() {
|
|
361
363
|
var _constrainMediaObserv2;
|
|
362
364
|
return (_constrainMediaObserv2 = constrainMediaObserver) === null || _constrainMediaObserv2 === void 0 ? void 0 : _constrainMediaObserv2.disconnect();
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { getBaseNodeTypeName } from '@atlaskit/editor-common/utils/node-type-utils';
|
|
2
2
|
import { getDiffableAttrNames } from '../decorations/utils/diffableAttrs';
|
|
3
|
+
import { encodeCharacterWithMarks } from './encodeCharacterWithMarks';
|
|
3
4
|
|
|
4
5
|
/**
|
|
5
6
|
* Attribute-aware token encoder for `prosemirror-changeset`. The default encoder
|
|
@@ -20,27 +21,6 @@ const encodeNodeWithAttrs = (node, attrNames) => {
|
|
|
20
21
|
return `${node.type.name}|${parts.join('|')}`;
|
|
21
22
|
};
|
|
22
23
|
|
|
23
|
-
// Fold a character's marks (type + attrs) into its token so a formatting-only change
|
|
24
|
-
// (bold/italic/link/colour, or a link whose `href` changes) registers as a real diff.
|
|
25
|
-
// The default encoder returns just the char code, so marked and unmarked text tokenise
|
|
26
|
-
// identically. Marks and their attrs are sorted so the token is order-independent.
|
|
27
|
-
const encodeCharacterWithMarks = (char, marks) => {
|
|
28
|
-
if (marks.length === 0) {
|
|
29
|
-
// Fast path: unmarked text keeps the numeric char code (unchanged behaviour).
|
|
30
|
-
return char;
|
|
31
|
-
}
|
|
32
|
-
const markKey = marks.map(mark => {
|
|
33
|
-
var _mark$attrs;
|
|
34
|
-
const attrs = (_mark$attrs = mark.attrs) !== null && _mark$attrs !== void 0 ? _mark$attrs : {};
|
|
35
|
-
const attrParts = Object.keys(attrs).sort().map(key => {
|
|
36
|
-
var _attrs$key;
|
|
37
|
-
return `${key}=${JSON.stringify((_attrs$key = attrs[key]) !== null && _attrs$key !== void 0 ? _attrs$key : null)}`;
|
|
38
|
-
});
|
|
39
|
-
return attrParts.length > 0 ? `${mark.type.name}(${attrParts.join(',')})` : mark.type.name;
|
|
40
|
-
}).sort().join('&');
|
|
41
|
-
return `${char}\u0000${markKey}`;
|
|
42
|
-
};
|
|
43
|
-
|
|
44
24
|
// Like the library default, but node types with a `diffableAttrs` rule fold their
|
|
45
25
|
// attrs into the open token, and text characters fold in their marks. Node-end is
|
|
46
26
|
// unchanged, hence `string | number`.
|
|
@@ -17,10 +17,11 @@ import { extractDiffDescriptors } from '../decorations/decorationKeys';
|
|
|
17
17
|
import { getAttrChangeRanges, stepIsValidAttrChange } from '../decorations/utils/getAttrChangeRanges';
|
|
18
18
|
import { getMarkChangeRanges } from '../decorations/utils/getMarkChangeRanges';
|
|
19
19
|
import { getDefaultDiffType, isExtendedEnabled } from '../isExtendedEnabled';
|
|
20
|
-
import { attrAwareTokenEncoder } from './attrAwareTokenEncoder';
|
|
21
20
|
import { diffBySteps } from './diffBySteps';
|
|
22
21
|
import { groupChangesByBlock } from './groupChangesByBlock';
|
|
22
|
+
import { isMarkOnlyChange } from './isMarkOnlyChange';
|
|
23
23
|
import { optimizeChanges } from './optimizeChanges';
|
|
24
|
+
import { selectTokenEncoder } from './selectTokenEncoder';
|
|
24
25
|
import { simplifyChangesWithAttribution } from './simplifyChangesWithAttribution';
|
|
25
26
|
import { simplifySteps, simplifyStepsWithAttribution } from './simplifySteps';
|
|
26
27
|
import { classifySmartChanges } from './smart/classifySmartChanges';
|
|
@@ -354,10 +355,11 @@ const calculateDiffDecorationsInner = ({
|
|
|
354
355
|
};
|
|
355
356
|
}
|
|
356
357
|
}
|
|
357
|
-
// The attribute-aware encoder is only needed by the smart classifier and is
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
358
|
+
// The attribute-aware encoder is only needed by the smart classifier and is gated with it.
|
|
359
|
+
const {
|
|
360
|
+
tokenEncoder,
|
|
361
|
+
shouldHideMarkOnlyDeletions
|
|
362
|
+
} = selectTokenEncoder(diffType === 'smart' && fg('platform_editor_ai_smart_diff'));
|
|
361
363
|
let changes;
|
|
362
364
|
let attributedChanges = [];
|
|
363
365
|
let attributionColors;
|
|
@@ -409,6 +411,16 @@ const calculateDiffDecorationsInner = ({
|
|
|
409
411
|
const changeColorScheme = attributionColors ? getColorSchemeForChange(change, attributedChanges, attributionColors, colorScheme) : colorScheme;
|
|
410
412
|
const isActive = isRangeActive(activeIndexPos, change.fromB, change.toB);
|
|
411
413
|
|
|
414
|
+
// The deleted side of a mark-only change is a byte-identical copy of the new text, so it is
|
|
415
|
+
// suppressed in the clean view. `hideDeletedDiffs` is load-bearing: when false the reviewer
|
|
416
|
+
// asked to compare against the original, and the deleted side is the only place the
|
|
417
|
+
// previous formatting is visible.
|
|
418
|
+
const isMarkOnly = shouldHideMarkOnlyDeletions && hideDeletedDiffs && change.deleted.length > 0 && isMarkOnlyChange({
|
|
419
|
+
change,
|
|
420
|
+
originalDoc,
|
|
421
|
+
newDoc: tr.doc
|
|
422
|
+
});
|
|
423
|
+
|
|
412
424
|
// Hoisted because it decides BOTH where the deleted widget is anchored and — since the
|
|
413
425
|
// widget pins whichever end of the range it sits at — how the indicator anchors below are
|
|
414
426
|
// allowed to move.
|
|
@@ -439,7 +451,7 @@ const calculateDiffDecorationsInner = ({
|
|
|
439
451
|
// change. Used to decide if indicator anchor positions should be adjusted
|
|
440
452
|
// inward — when the widget is present the anchor must stay at the block
|
|
441
453
|
// boundary to keep the indicator bar continuous with the deleted content.
|
|
442
|
-
const willRenderDeletedWidget = change.deleted.length > 0 && !(isExtendedEnabled(diffType) && !isInverted && hideDeletedDiffs && change.inserted.length > 0);
|
|
454
|
+
const willRenderDeletedWidget = change.deleted.length > 0 && !isMarkOnly && !(isExtendedEnabled(diffType) && !isInverted && hideDeletedDiffs && change.inserted.length > 0);
|
|
443
455
|
if (isSmartNodeLevel) {
|
|
444
456
|
// For a large inserted table, skip the O(cells) inline decorations inside the
|
|
445
457
|
// table (the cell overlays added below provide the highlight without the
|
|
@@ -501,7 +513,7 @@ const calculateDiffDecorationsInner = ({
|
|
|
501
513
|
coarseTableCellsOnly: useCoarseTableDecoration
|
|
502
514
|
}));
|
|
503
515
|
}
|
|
504
|
-
if (change.deleted.length > 0) {
|
|
516
|
+
if (change.deleted.length > 0 && !isMarkOnly) {
|
|
505
517
|
const shouldHideDeleted = isExtendedEnabled(diffType) ? !isInverted && hideDeletedDiffs && change.inserted.length > 0 : false;
|
|
506
518
|
if (!shouldHideDeleted) {
|
|
507
519
|
decorations.push(...createNodeChangedDecorationWidget({
|
|
@@ -14,10 +14,10 @@
|
|
|
14
14
|
* reconstructed by applying the (simplified) steps to `originalDoc`.
|
|
15
15
|
*/
|
|
16
16
|
import { ChangeSet, simplifyChanges } from 'prosemirror-changeset';
|
|
17
|
-
import { attrAwareTokenEncoder } from './attrAwareTokenEncoder';
|
|
18
17
|
import { diffBySteps } from './diffBySteps';
|
|
19
18
|
import { groupChangesByBlock } from './groupChangesByBlock';
|
|
20
19
|
import { optimizeChanges } from './optimizeChanges';
|
|
20
|
+
import { selectTokenEncoder } from './selectTokenEncoder';
|
|
21
21
|
import { simplifySteps } from './simplifySteps';
|
|
22
22
|
import { classifySmartChanges } from './smart/classifySmartChanges';
|
|
23
23
|
/**
|
|
@@ -64,11 +64,11 @@ export const computeDiffChanges = ({
|
|
|
64
64
|
};
|
|
65
65
|
}
|
|
66
66
|
|
|
67
|
-
//
|
|
68
|
-
//
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
67
|
+
// This utility does not apply the smart-diff gate (see the file docstring); a caller
|
|
68
|
+
// requesting `smart` is already behind it.
|
|
69
|
+
const {
|
|
70
|
+
tokenEncoder
|
|
71
|
+
} = selectTokenEncoder(diffType === 'smart');
|
|
72
72
|
const changeset = ChangeSet.create(originalDoc, undefined, tokenEncoder).addSteps(steppedDoc, stepMaps, steppedDoc);
|
|
73
73
|
if (diffType === 'smart') {
|
|
74
74
|
return {
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
/** U+0000 cannot occur in ProseMirror text content, so a token can never be ambiguous. */
|
|
2
|
+
const MARK_KEY_SEPARATOR = '\u0000';
|
|
3
|
+
const buildMarkKey = marks => marks.map(mark => {
|
|
4
|
+
var _mark$attrs;
|
|
5
|
+
const attrs = (_mark$attrs = mark.attrs) !== null && _mark$attrs !== void 0 ? _mark$attrs : {};
|
|
6
|
+
const attrParts = Object.keys(attrs).sort().map(key => {
|
|
7
|
+
var _attrs$key;
|
|
8
|
+
return `${key}=${JSON.stringify((_attrs$key = attrs[key]) !== null && _attrs$key !== void 0 ? _attrs$key : null)}`;
|
|
9
|
+
});
|
|
10
|
+
return attrParts.length > 0 ? `${mark.type.name}(${attrParts.join(',')})` : mark.type.name;
|
|
11
|
+
}).sort().join('&');
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Keyed on array identity: `prosemirror-changeset` passes the same `child.marks` reference for
|
|
15
|
+
* every character in a text node, so this collapses the key building to once per node.
|
|
16
|
+
*/
|
|
17
|
+
const markKeyCache = new WeakMap();
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Folds a character's marks into its `prosemirror-changeset` token. The library default returns
|
|
21
|
+
* only the char code, so marked and unmarked text tokenise identically and a formatting-only edit
|
|
22
|
+
* is invisible to the changeset.
|
|
23
|
+
*
|
|
24
|
+
* Marks and attrs are sorted so the token is order-independent.
|
|
25
|
+
*/
|
|
26
|
+
export const encodeCharacterWithMarks = (char, marks) => {
|
|
27
|
+
if (marks.length === 0) {
|
|
28
|
+
return char;
|
|
29
|
+
}
|
|
30
|
+
let markKey = markKeyCache.get(marks);
|
|
31
|
+
if (markKey === undefined) {
|
|
32
|
+
markKey = buildMarkKey(marks);
|
|
33
|
+
markKeyCache.set(marks, markKey);
|
|
34
|
+
}
|
|
35
|
+
return `${char}${MARK_KEY_SEPARATOR}${markKey}`;
|
|
36
|
+
};
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
/** U+0000 cannot occur in ProseMirror text content, so this cannot collide with document text. */
|
|
2
|
+
const NODE_MARKER = '\u0000';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Node types and text only; marks and attrs excluded.
|
|
6
|
+
*
|
|
7
|
+
* Text is unprefixed so adjacent runs concatenate — a run only splits because its marks differ, so
|
|
8
|
+
* `strong("hello")` and `strong("hel") + em("lo")` must match. Attrs are excluded because a
|
|
9
|
+
* reconstructed agent edit regenerates `localId`s; `getAttrChangeRanges` reports attrs separately.
|
|
10
|
+
*/
|
|
11
|
+
const markFreeSignature = (doc, from, to) => {
|
|
12
|
+
const parts = [];
|
|
13
|
+
doc.nodesBetween(from, to, (node, pos) => {
|
|
14
|
+
if (node.isText) {
|
|
15
|
+
var _node$text;
|
|
16
|
+
const start = Math.max(pos, from) - pos;
|
|
17
|
+
const end = Math.min(pos + node.nodeSize, to) - pos;
|
|
18
|
+
parts.push(((_node$text = node.text) !== null && _node$text !== void 0 ? _node$text : '').slice(start, end));
|
|
19
|
+
} else {
|
|
20
|
+
parts.push(`${NODE_MARKER}${node.type.name}`);
|
|
21
|
+
}
|
|
22
|
+
return true;
|
|
23
|
+
});
|
|
24
|
+
return parts.join('');
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Whether a change differs only in marks, with identical structure and text on both sides. Only
|
|
29
|
+
* produced when a mark-aware token encoder is in use.
|
|
30
|
+
*/
|
|
31
|
+
export const isMarkOnlyChange = ({
|
|
32
|
+
change,
|
|
33
|
+
newDoc,
|
|
34
|
+
originalDoc
|
|
35
|
+
}) => {
|
|
36
|
+
const {
|
|
37
|
+
fromA,
|
|
38
|
+
toA,
|
|
39
|
+
fromB,
|
|
40
|
+
toB
|
|
41
|
+
} = change;
|
|
42
|
+
const isPureInsertionOrDeletion = toA === fromA || toB === fromB;
|
|
43
|
+
if (isPureInsertionOrDeletion || toA - fromA !== toB - fromB) {
|
|
44
|
+
return false;
|
|
45
|
+
}
|
|
46
|
+
return markFreeSignature(originalDoc, fromA, toA) === markFreeSignature(newDoc, fromB, toB);
|
|
47
|
+
};
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { encodeCharacterWithMarks } from './encodeCharacterWithMarks';
|
|
2
|
+
|
|
3
|
+
/** Mirrors the library's private (unexported) `typeID` so node-end tokens match. */
|
|
4
|
+
const typeID = type => {
|
|
5
|
+
const cache = type.schema.cached.changeSetIDs || (type.schema.cached.changeSetIDs = Object.create(null));
|
|
6
|
+
let id = cache[type.name];
|
|
7
|
+
if (id == null) {
|
|
8
|
+
cache[type.name] = id = Object.keys(type.schema.nodes).indexOf(type.name) + 1;
|
|
9
|
+
}
|
|
10
|
+
return id;
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* The library default encoder plus mark folding on text characters.
|
|
15
|
+
*
|
|
16
|
+
* Deliberately narrower than `attrAwareTokenEncoder`: node-start tokens keep the library default
|
|
17
|
+
* and do not fold in diffable attributes, which `getAttrChangeRanges` already reports for every
|
|
18
|
+
* diff type.
|
|
19
|
+
*/
|
|
20
|
+
export const markAwareTokenEncoder = {
|
|
21
|
+
encodeCharacter: (char, marks) => encodeCharacterWithMarks(char, marks),
|
|
22
|
+
encodeNodeStart: node => node.type.name,
|
|
23
|
+
encodeNodeEnd: node => -typeID(node.type),
|
|
24
|
+
compareTokens: (a, b) => a === b
|
|
25
|
+
};
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { fg } from '@atlaskit/platform-feature-flags/fg';
|
|
2
|
+
import { attrAwareTokenEncoder } from './attrAwareTokenEncoder';
|
|
3
|
+
import { markAwareTokenEncoder } from './markAwareTokenEncoder';
|
|
4
|
+
/**
|
|
5
|
+
* Single place `platform_editor_diff_inline_mark_changes` is read, shared by
|
|
6
|
+
* `calculateDiffDecorations` and `computeDiffChanges` so the two cannot drift.
|
|
7
|
+
*
|
|
8
|
+
* With the gate off this returns what both callers computed before it existed.
|
|
9
|
+
*
|
|
10
|
+
* @param useAttrAwareEncoder Caller is running the smart classifier, which needs node attrs folded
|
|
11
|
+
* into the token.
|
|
12
|
+
*/
|
|
13
|
+
export const selectTokenEncoder = useAttrAwareEncoder => {
|
|
14
|
+
if (useAttrAwareEncoder) {
|
|
15
|
+
return {
|
|
16
|
+
tokenEncoder: attrAwareTokenEncoder,
|
|
17
|
+
shouldHideMarkOnlyDeletions: false
|
|
18
|
+
};
|
|
19
|
+
}
|
|
20
|
+
if (fg('platform_editor_diff_inline_mark_changes')) {
|
|
21
|
+
return {
|
|
22
|
+
tokenEncoder: markAwareTokenEncoder,
|
|
23
|
+
shouldHideMarkOnlyDeletions: true
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
return {
|
|
27
|
+
tokenEncoder: undefined,
|
|
28
|
+
shouldHideMarkOnlyDeletions: false
|
|
29
|
+
};
|
|
30
|
+
};
|
|
@@ -338,6 +338,13 @@ export const createNodeChangedDecorationWidget = ({
|
|
|
338
338
|
side: placeBelow ? 1 : -1
|
|
339
339
|
})
|
|
340
340
|
}),
|
|
341
|
+
// Without an explicit mark set, prosemirror-view wraps the widget in the marks of the
|
|
342
|
+
// adjacent text in the NEW document. `dom` already carries the original marks, so a
|
|
343
|
+
// formatting change would render the deleted side wrapped in the very mark the edit
|
|
344
|
+
// introduced — unbolded text shown struck through but still bold.
|
|
345
|
+
...(fg('platform_editor_diff_inline_mark_changes') && {
|
|
346
|
+
marks: []
|
|
347
|
+
}),
|
|
341
348
|
destroy: () => {
|
|
342
349
|
var _constrainMediaObserv2;
|
|
343
350
|
return (_constrainMediaObserv2 = constrainMediaObserver) === null || _constrainMediaObserv2 === void 0 ? void 0 : _constrainMediaObserv2.disconnect();
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { getBaseNodeTypeName } from '@atlaskit/editor-common/utils/node-type-utils';
|
|
2
2
|
import { getDiffableAttrNames } from '../decorations/utils/diffableAttrs';
|
|
3
|
+
import { encodeCharacterWithMarks } from './encodeCharacterWithMarks';
|
|
3
4
|
|
|
4
5
|
/**
|
|
5
6
|
* Attribute-aware token encoder for `prosemirror-changeset`. The default encoder
|
|
@@ -20,27 +21,6 @@ var encodeNodeWithAttrs = function encodeNodeWithAttrs(node, attrNames) {
|
|
|
20
21
|
return "".concat(node.type.name, "|").concat(parts.join('|'));
|
|
21
22
|
};
|
|
22
23
|
|
|
23
|
-
// Fold a character's marks (type + attrs) into its token so a formatting-only change
|
|
24
|
-
// (bold/italic/link/colour, or a link whose `href` changes) registers as a real diff.
|
|
25
|
-
// The default encoder returns just the char code, so marked and unmarked text tokenise
|
|
26
|
-
// identically. Marks and their attrs are sorted so the token is order-independent.
|
|
27
|
-
var encodeCharacterWithMarks = function encodeCharacterWithMarks(char, marks) {
|
|
28
|
-
if (marks.length === 0) {
|
|
29
|
-
// Fast path: unmarked text keeps the numeric char code (unchanged behaviour).
|
|
30
|
-
return char;
|
|
31
|
-
}
|
|
32
|
-
var markKey = marks.map(function (mark) {
|
|
33
|
-
var _mark$attrs;
|
|
34
|
-
var attrs = (_mark$attrs = mark.attrs) !== null && _mark$attrs !== void 0 ? _mark$attrs : {};
|
|
35
|
-
var attrParts = Object.keys(attrs).sort().map(function (key) {
|
|
36
|
-
var _attrs$key;
|
|
37
|
-
return "".concat(key, "=").concat(JSON.stringify((_attrs$key = attrs[key]) !== null && _attrs$key !== void 0 ? _attrs$key : null));
|
|
38
|
-
});
|
|
39
|
-
return attrParts.length > 0 ? "".concat(mark.type.name, "(").concat(attrParts.join(','), ")") : mark.type.name;
|
|
40
|
-
}).sort().join('&');
|
|
41
|
-
return "".concat(char, "\0").concat(markKey);
|
|
42
|
-
};
|
|
43
|
-
|
|
44
24
|
// Like the library default, but node types with a `diffableAttrs` rule fold their
|
|
45
25
|
// attrs into the open token, and text characters fold in their marks. Node-end is
|
|
46
26
|
// unchanged, hence `string | number`.
|
|
@@ -25,10 +25,11 @@ import { extractDiffDescriptors } from '../decorations/decorationKeys';
|
|
|
25
25
|
import { getAttrChangeRanges, stepIsValidAttrChange } from '../decorations/utils/getAttrChangeRanges';
|
|
26
26
|
import { getMarkChangeRanges } from '../decorations/utils/getMarkChangeRanges';
|
|
27
27
|
import { getDefaultDiffType, isExtendedEnabled } from '../isExtendedEnabled';
|
|
28
|
-
import { attrAwareTokenEncoder } from './attrAwareTokenEncoder';
|
|
29
28
|
import { diffBySteps } from './diffBySteps';
|
|
30
29
|
import { groupChangesByBlock } from './groupChangesByBlock';
|
|
30
|
+
import { isMarkOnlyChange } from './isMarkOnlyChange';
|
|
31
31
|
import { optimizeChanges } from './optimizeChanges';
|
|
32
|
+
import { selectTokenEncoder } from './selectTokenEncoder';
|
|
32
33
|
import { simplifyChangesWithAttribution } from './simplifyChangesWithAttribution';
|
|
33
34
|
import { simplifySteps, simplifyStepsWithAttribution } from './simplifySteps';
|
|
34
35
|
import { classifySmartChanges } from './smart/classifySmartChanges';
|
|
@@ -382,10 +383,10 @@ var calculateDiffDecorationsInner = function calculateDiffDecorationsInner(_ref7
|
|
|
382
383
|
};
|
|
383
384
|
}
|
|
384
385
|
}
|
|
385
|
-
// The attribute-aware encoder is only needed by the smart classifier and is
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
386
|
+
// The attribute-aware encoder is only needed by the smart classifier and is gated with it.
|
|
387
|
+
var _selectTokenEncoder = selectTokenEncoder(diffType === 'smart' && fg('platform_editor_ai_smart_diff')),
|
|
388
|
+
tokenEncoder = _selectTokenEncoder.tokenEncoder,
|
|
389
|
+
shouldHideMarkOnlyDeletions = _selectTokenEncoder.shouldHideMarkOnlyDeletions;
|
|
389
390
|
var changes;
|
|
390
391
|
var attributedChanges = [];
|
|
391
392
|
var attributionColors;
|
|
@@ -440,6 +441,16 @@ var calculateDiffDecorationsInner = function calculateDiffDecorationsInner(_ref7
|
|
|
440
441
|
var changeColorScheme = attributionColors ? getColorSchemeForChange(change, attributedChanges, attributionColors, colorScheme) : colorScheme;
|
|
441
442
|
var isActive = isRangeActive(activeIndexPos, change.fromB, change.toB);
|
|
442
443
|
|
|
444
|
+
// The deleted side of a mark-only change is a byte-identical copy of the new text, so it is
|
|
445
|
+
// suppressed in the clean view. `hideDeletedDiffs` is load-bearing: when false the reviewer
|
|
446
|
+
// asked to compare against the original, and the deleted side is the only place the
|
|
447
|
+
// previous formatting is visible.
|
|
448
|
+
var isMarkOnly = shouldHideMarkOnlyDeletions && hideDeletedDiffs && change.deleted.length > 0 && isMarkOnlyChange({
|
|
449
|
+
change: change,
|
|
450
|
+
originalDoc: originalDoc,
|
|
451
|
+
newDoc: tr.doc
|
|
452
|
+
});
|
|
453
|
+
|
|
443
454
|
// Hoisted because it decides BOTH where the deleted widget is anchored and — since the
|
|
444
455
|
// widget pins whichever end of the range it sits at — how the indicator anchors below are
|
|
445
456
|
// allowed to move.
|
|
@@ -470,7 +481,7 @@ var calculateDiffDecorationsInner = function calculateDiffDecorationsInner(_ref7
|
|
|
470
481
|
// change. Used to decide if indicator anchor positions should be adjusted
|
|
471
482
|
// inward — when the widget is present the anchor must stay at the block
|
|
472
483
|
// boundary to keep the indicator bar continuous with the deleted content.
|
|
473
|
-
var willRenderDeletedWidget = change.deleted.length > 0 && !(isExtendedEnabled(diffType) && !isInverted && hideDeletedDiffs && change.inserted.length > 0);
|
|
484
|
+
var willRenderDeletedWidget = change.deleted.length > 0 && !isMarkOnly && !(isExtendedEnabled(diffType) && !isInverted && hideDeletedDiffs && change.inserted.length > 0);
|
|
474
485
|
if (isSmartNodeLevel) {
|
|
475
486
|
// For a large inserted table, skip the O(cells) inline decorations inside the
|
|
476
487
|
// table (the cell overlays added below provide the highlight without the
|
|
@@ -541,7 +552,7 @@ var calculateDiffDecorationsInner = function calculateDiffDecorationsInner(_ref7
|
|
|
541
552
|
coarseTableCellsOnly: useCoarseTableDecoration
|
|
542
553
|
}))));
|
|
543
554
|
}
|
|
544
|
-
if (change.deleted.length > 0) {
|
|
555
|
+
if (change.deleted.length > 0 && !isMarkOnly) {
|
|
545
556
|
var _shouldHideDeleted = isExtendedEnabled(diffType) ? !isInverted && hideDeletedDiffs && change.inserted.length > 0 : false;
|
|
546
557
|
if (!_shouldHideDeleted) {
|
|
547
558
|
decorations.push.apply(decorations, _toConsumableArray(createNodeChangedDecorationWidget(_objectSpread(_objectSpread({
|
|
@@ -17,10 +17,10 @@ function _arrayLikeToArray(r, a) { (null == a || a > r.length) && (a = r.length)
|
|
|
17
17
|
* reconstructed by applying the (simplified) steps to `originalDoc`.
|
|
18
18
|
*/
|
|
19
19
|
import { ChangeSet, simplifyChanges } from 'prosemirror-changeset';
|
|
20
|
-
import { attrAwareTokenEncoder } from './attrAwareTokenEncoder';
|
|
21
20
|
import { diffBySteps } from './diffBySteps';
|
|
22
21
|
import { groupChangesByBlock } from './groupChangesByBlock';
|
|
23
22
|
import { optimizeChanges } from './optimizeChanges';
|
|
23
|
+
import { selectTokenEncoder } from './selectTokenEncoder';
|
|
24
24
|
import { simplifySteps } from './simplifySteps';
|
|
25
25
|
import { classifySmartChanges } from './smart/classifySmartChanges';
|
|
26
26
|
/**
|
|
@@ -77,11 +77,10 @@ export var computeDiffChanges = function computeDiffChanges(_ref) {
|
|
|
77
77
|
};
|
|
78
78
|
}
|
|
79
79
|
|
|
80
|
-
//
|
|
81
|
-
//
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
var tokenEncoder = diffType === 'smart' ? attrAwareTokenEncoder : undefined;
|
|
80
|
+
// This utility does not apply the smart-diff gate (see the file docstring); a caller
|
|
81
|
+
// requesting `smart` is already behind it.
|
|
82
|
+
var _selectTokenEncoder = selectTokenEncoder(diffType === 'smart'),
|
|
83
|
+
tokenEncoder = _selectTokenEncoder.tokenEncoder;
|
|
85
84
|
var changeset = ChangeSet.create(originalDoc, undefined, tokenEncoder).addSteps(steppedDoc, stepMaps, steppedDoc);
|
|
86
85
|
if (diffType === 'smart') {
|
|
87
86
|
return {
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
/** U+0000 cannot occur in ProseMirror text content, so a token can never be ambiguous. */
|
|
2
|
+
var MARK_KEY_SEPARATOR = "\0";
|
|
3
|
+
var buildMarkKey = function buildMarkKey(marks) {
|
|
4
|
+
return marks.map(function (mark) {
|
|
5
|
+
var _mark$attrs;
|
|
6
|
+
var attrs = (_mark$attrs = mark.attrs) !== null && _mark$attrs !== void 0 ? _mark$attrs : {};
|
|
7
|
+
var attrParts = Object.keys(attrs).sort().map(function (key) {
|
|
8
|
+
var _attrs$key;
|
|
9
|
+
return "".concat(key, "=").concat(JSON.stringify((_attrs$key = attrs[key]) !== null && _attrs$key !== void 0 ? _attrs$key : null));
|
|
10
|
+
});
|
|
11
|
+
return attrParts.length > 0 ? "".concat(mark.type.name, "(").concat(attrParts.join(','), ")") : mark.type.name;
|
|
12
|
+
}).sort().join('&');
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Keyed on array identity: `prosemirror-changeset` passes the same `child.marks` reference for
|
|
17
|
+
* every character in a text node, so this collapses the key building to once per node.
|
|
18
|
+
*/
|
|
19
|
+
var markKeyCache = new WeakMap();
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Folds a character's marks into its `prosemirror-changeset` token. The library default returns
|
|
23
|
+
* only the char code, so marked and unmarked text tokenise identically and a formatting-only edit
|
|
24
|
+
* is invisible to the changeset.
|
|
25
|
+
*
|
|
26
|
+
* Marks and attrs are sorted so the token is order-independent.
|
|
27
|
+
*/
|
|
28
|
+
export var encodeCharacterWithMarks = function encodeCharacterWithMarks(char, marks) {
|
|
29
|
+
if (marks.length === 0) {
|
|
30
|
+
return char;
|
|
31
|
+
}
|
|
32
|
+
var markKey = markKeyCache.get(marks);
|
|
33
|
+
if (markKey === undefined) {
|
|
34
|
+
markKey = buildMarkKey(marks);
|
|
35
|
+
markKeyCache.set(marks, markKey);
|
|
36
|
+
}
|
|
37
|
+
return "".concat(char).concat(MARK_KEY_SEPARATOR).concat(markKey);
|
|
38
|
+
};
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
/** U+0000 cannot occur in ProseMirror text content, so this cannot collide with document text. */
|
|
2
|
+
var NODE_MARKER = "\0";
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Node types and text only; marks and attrs excluded.
|
|
6
|
+
*
|
|
7
|
+
* Text is unprefixed so adjacent runs concatenate — a run only splits because its marks differ, so
|
|
8
|
+
* `strong("hello")` and `strong("hel") + em("lo")` must match. Attrs are excluded because a
|
|
9
|
+
* reconstructed agent edit regenerates `localId`s; `getAttrChangeRanges` reports attrs separately.
|
|
10
|
+
*/
|
|
11
|
+
var markFreeSignature = function markFreeSignature(doc, from, to) {
|
|
12
|
+
var parts = [];
|
|
13
|
+
doc.nodesBetween(from, to, function (node, pos) {
|
|
14
|
+
if (node.isText) {
|
|
15
|
+
var _node$text;
|
|
16
|
+
var start = Math.max(pos, from) - pos;
|
|
17
|
+
var end = Math.min(pos + node.nodeSize, to) - pos;
|
|
18
|
+
parts.push(((_node$text = node.text) !== null && _node$text !== void 0 ? _node$text : '').slice(start, end));
|
|
19
|
+
} else {
|
|
20
|
+
parts.push("".concat(NODE_MARKER).concat(node.type.name));
|
|
21
|
+
}
|
|
22
|
+
return true;
|
|
23
|
+
});
|
|
24
|
+
return parts.join('');
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Whether a change differs only in marks, with identical structure and text on both sides. Only
|
|
29
|
+
* produced when a mark-aware token encoder is in use.
|
|
30
|
+
*/
|
|
31
|
+
export var isMarkOnlyChange = function isMarkOnlyChange(_ref) {
|
|
32
|
+
var change = _ref.change,
|
|
33
|
+
newDoc = _ref.newDoc,
|
|
34
|
+
originalDoc = _ref.originalDoc;
|
|
35
|
+
var fromA = change.fromA,
|
|
36
|
+
toA = change.toA,
|
|
37
|
+
fromB = change.fromB,
|
|
38
|
+
toB = change.toB;
|
|
39
|
+
var isPureInsertionOrDeletion = toA === fromA || toB === fromB;
|
|
40
|
+
if (isPureInsertionOrDeletion || toA - fromA !== toB - fromB) {
|
|
41
|
+
return false;
|
|
42
|
+
}
|
|
43
|
+
return markFreeSignature(originalDoc, fromA, toA) === markFreeSignature(newDoc, fromB, toB);
|
|
44
|
+
};
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { encodeCharacterWithMarks } from './encodeCharacterWithMarks';
|
|
2
|
+
|
|
3
|
+
/** Mirrors the library's private (unexported) `typeID` so node-end tokens match. */
|
|
4
|
+
var typeID = function typeID(type) {
|
|
5
|
+
var cache = type.schema.cached.changeSetIDs || (type.schema.cached.changeSetIDs = Object.create(null));
|
|
6
|
+
var id = cache[type.name];
|
|
7
|
+
if (id == null) {
|
|
8
|
+
cache[type.name] = id = Object.keys(type.schema.nodes).indexOf(type.name) + 1;
|
|
9
|
+
}
|
|
10
|
+
return id;
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* The library default encoder plus mark folding on text characters.
|
|
15
|
+
*
|
|
16
|
+
* Deliberately narrower than `attrAwareTokenEncoder`: node-start tokens keep the library default
|
|
17
|
+
* and do not fold in diffable attributes, which `getAttrChangeRanges` already reports for every
|
|
18
|
+
* diff type.
|
|
19
|
+
*/
|
|
20
|
+
export var markAwareTokenEncoder = {
|
|
21
|
+
encodeCharacter: function encodeCharacter(char, marks) {
|
|
22
|
+
return encodeCharacterWithMarks(char, marks);
|
|
23
|
+
},
|
|
24
|
+
encodeNodeStart: function encodeNodeStart(node) {
|
|
25
|
+
return node.type.name;
|
|
26
|
+
},
|
|
27
|
+
encodeNodeEnd: function encodeNodeEnd(node) {
|
|
28
|
+
return -typeID(node.type);
|
|
29
|
+
},
|
|
30
|
+
compareTokens: function compareTokens(a, b) {
|
|
31
|
+
return a === b;
|
|
32
|
+
}
|
|
33
|
+
};
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { fg } from '@atlaskit/platform-feature-flags/fg';
|
|
2
|
+
import { attrAwareTokenEncoder } from './attrAwareTokenEncoder';
|
|
3
|
+
import { markAwareTokenEncoder } from './markAwareTokenEncoder';
|
|
4
|
+
/**
|
|
5
|
+
* Single place `platform_editor_diff_inline_mark_changes` is read, shared by
|
|
6
|
+
* `calculateDiffDecorations` and `computeDiffChanges` so the two cannot drift.
|
|
7
|
+
*
|
|
8
|
+
* With the gate off this returns what both callers computed before it existed.
|
|
9
|
+
*
|
|
10
|
+
* @param useAttrAwareEncoder Caller is running the smart classifier, which needs node attrs folded
|
|
11
|
+
* into the token.
|
|
12
|
+
*/
|
|
13
|
+
export var selectTokenEncoder = function selectTokenEncoder(useAttrAwareEncoder) {
|
|
14
|
+
if (useAttrAwareEncoder) {
|
|
15
|
+
return {
|
|
16
|
+
tokenEncoder: attrAwareTokenEncoder,
|
|
17
|
+
shouldHideMarkOnlyDeletions: false
|
|
18
|
+
};
|
|
19
|
+
}
|
|
20
|
+
if (fg('platform_editor_diff_inline_mark_changes')) {
|
|
21
|
+
return {
|
|
22
|
+
tokenEncoder: markAwareTokenEncoder,
|
|
23
|
+
shouldHideMarkOnlyDeletions: true
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
return {
|
|
27
|
+
tokenEncoder: undefined,
|
|
28
|
+
shouldHideMarkOnlyDeletions: false
|
|
29
|
+
};
|
|
30
|
+
};
|
|
@@ -340,7 +340,7 @@ export var createNodeChangedDecorationWidget = function createNodeChangedDecorat
|
|
|
340
340
|
decorations.push(leftAnchor);
|
|
341
341
|
}
|
|
342
342
|
}
|
|
343
|
-
decorations.push(Decoration.widget(safeInsertPos, dom, _objectSpread(_objectSpread({}, buildDiffDecorationSpec(_objectSpread({
|
|
343
|
+
decorations.push(Decoration.widget(safeInsertPos, dom, _objectSpread(_objectSpread(_objectSpread({}, buildDiffDecorationSpec(_objectSpread({
|
|
344
344
|
decorationType: 'widget',
|
|
345
345
|
diffId: diffId,
|
|
346
346
|
isActive: isActive,
|
|
@@ -349,7 +349,9 @@ export var createNodeChangedDecorationWidget = function createNodeChangedDecorat
|
|
|
349
349
|
// placeBelow anchors at the end of the new content, so render on the
|
|
350
350
|
// trailing side (1); otherwise render before the new content (-1).
|
|
351
351
|
side: placeBelow ? 1 : -1
|
|
352
|
-
}))),
|
|
352
|
+
}))), fg('platform_editor_diff_inline_mark_changes') && {
|
|
353
|
+
marks: []
|
|
354
|
+
}), {}, {
|
|
353
355
|
destroy: function destroy() {
|
|
354
356
|
var _constrainMediaObserv2;
|
|
355
357
|
return (_constrainMediaObserv2 = constrainMediaObserver) === null || _constrainMediaObserv2 === void 0 ? void 0 : _constrainMediaObserv2.disconnect();
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { Mark } from '@atlaskit/editor-prosemirror/model';
|
|
2
|
+
/**
|
|
3
|
+
* Folds a character's marks into its `prosemirror-changeset` token. The library default returns
|
|
4
|
+
* only the char code, so marked and unmarked text tokenise identically and a formatting-only edit
|
|
5
|
+
* is invisible to the changeset.
|
|
6
|
+
*
|
|
7
|
+
* Marks and attrs are sorted so the token is order-independent.
|
|
8
|
+
*/
|
|
9
|
+
export declare const encodeCharacterWithMarks: (char: number, marks: readonly Mark[]) => number | string;
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { Node as PMNode } from '@atlaskit/editor-prosemirror/model';
|
|
2
|
+
/**
|
|
3
|
+
* Whether a change differs only in marks, with identical structure and text on both sides. Only
|
|
4
|
+
* produced when a mark-aware token encoder is in use.
|
|
5
|
+
*/
|
|
6
|
+
export declare const isMarkOnlyChange: ({ change, newDoc, originalDoc, }: {
|
|
7
|
+
change: {
|
|
8
|
+
fromA: number;
|
|
9
|
+
fromB: number;
|
|
10
|
+
toA: number;
|
|
11
|
+
toB: number;
|
|
12
|
+
};
|
|
13
|
+
newDoc: PMNode;
|
|
14
|
+
originalDoc: PMNode;
|
|
15
|
+
}) => boolean;
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { TokenEncoder } from 'prosemirror-changeset';
|
|
2
|
+
/**
|
|
3
|
+
* The library default encoder plus mark folding on text characters.
|
|
4
|
+
*
|
|
5
|
+
* Deliberately narrower than `attrAwareTokenEncoder`: node-start tokens keep the library default
|
|
6
|
+
* and do not fold in diffable attributes, which `getAttrChangeRanges` already reports for every
|
|
7
|
+
* diff type.
|
|
8
|
+
*/
|
|
9
|
+
export declare const markAwareTokenEncoder: TokenEncoder<string | number>;
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import type { TokenEncoder } from 'prosemirror-changeset';
|
|
2
|
+
export type TokenEncoderSelection = {
|
|
3
|
+
/** Non-smart mark-aware path only; lets callers suppress a duplicate deleted side. */
|
|
4
|
+
shouldHideMarkOnlyDeletions: boolean;
|
|
5
|
+
/** Encoder to hand to `ChangeSet.create`. `undefined` means the library default. */
|
|
6
|
+
tokenEncoder: TokenEncoder<string | number> | undefined;
|
|
7
|
+
};
|
|
8
|
+
/**
|
|
9
|
+
* Single place `platform_editor_diff_inline_mark_changes` is read, shared by
|
|
10
|
+
* `calculateDiffDecorations` and `computeDiffChanges` so the two cannot drift.
|
|
11
|
+
*
|
|
12
|
+
* With the gate off this returns what both callers computed before it existed.
|
|
13
|
+
*
|
|
14
|
+
* @param useAttrAwareEncoder Caller is running the smart classifier, which needs node attrs folded
|
|
15
|
+
* into the token.
|
|
16
|
+
*/
|
|
17
|
+
export declare const selectTokenEncoder: (useAttrAwareEncoder: boolean) => TokenEncoderSelection;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@atlaskit/editor-plugin-show-diff",
|
|
3
|
-
"version": "14.2.
|
|
3
|
+
"version": "14.2.11",
|
|
4
4
|
"description": "ShowDiff plugin for @atlaskit/editor-core",
|
|
5
5
|
"author": "Atlassian Pty Ltd",
|
|
6
6
|
"license": "Apache-2.0",
|
|
@@ -28,7 +28,7 @@
|
|
|
28
28
|
"@atlaskit/editor-tables": "^3.1.0",
|
|
29
29
|
"@atlaskit/platform-feature-experiments": "^0.3.0",
|
|
30
30
|
"@atlaskit/platform-feature-flags": "^2.2.0",
|
|
31
|
-
"@atlaskit/tmp-editor-statsig": "^
|
|
31
|
+
"@atlaskit/tmp-editor-statsig": "^172.0.0",
|
|
32
32
|
"@atlaskit/tokens": "^16.11.0",
|
|
33
33
|
"@babel/runtime": "^7.0.0",
|
|
34
34
|
"@compiled/react": "^1.0.2",
|
|
@@ -59,45 +59,9 @@
|
|
|
59
59
|
"react-intl": "^7.0.0"
|
|
60
60
|
},
|
|
61
61
|
"peerDependencies": {
|
|
62
|
-
"@atlaskit/editor-common": "^120.
|
|
62
|
+
"@atlaskit/editor-common": "^120.11.0",
|
|
63
63
|
"react": "^18.2.0 || ^19.2.0"
|
|
64
64
|
},
|
|
65
|
-
"techstack": {
|
|
66
|
-
"@atlassian/frontend": {
|
|
67
|
-
"code-structure": [
|
|
68
|
-
"editor-plugin"
|
|
69
|
-
],
|
|
70
|
-
"import-structure": [
|
|
71
|
-
"atlassian-conventions"
|
|
72
|
-
],
|
|
73
|
-
"circular-dependencies": [
|
|
74
|
-
"file-and-folder-level"
|
|
75
|
-
]
|
|
76
|
-
},
|
|
77
|
-
"@repo/internal": {
|
|
78
|
-
"dom-events": "use-bind-event-listener",
|
|
79
|
-
"analytics": [
|
|
80
|
-
"analytics-next"
|
|
81
|
-
],
|
|
82
|
-
"design-tokens": [
|
|
83
|
-
"color"
|
|
84
|
-
],
|
|
85
|
-
"theming": [
|
|
86
|
-
"react-context"
|
|
87
|
-
],
|
|
88
|
-
"ui-components": [
|
|
89
|
-
"lite-mode"
|
|
90
|
-
],
|
|
91
|
-
"deprecation": "no-deprecated-imports",
|
|
92
|
-
"styling": [
|
|
93
|
-
"emotion",
|
|
94
|
-
"emotion"
|
|
95
|
-
],
|
|
96
|
-
"imports": [
|
|
97
|
-
"import-no-extraneous-disable-for-examples-and-docs"
|
|
98
|
-
]
|
|
99
|
-
}
|
|
100
|
-
},
|
|
101
65
|
"platform-feature-flags": {
|
|
102
66
|
"platform_editor_ai_show_diff_patch_1": {
|
|
103
67
|
"type": "boolean"
|
|
@@ -110,6 +74,9 @@
|
|
|
110
74
|
},
|
|
111
75
|
"platform_editor_reduce_diff_attr_sensitivity": {
|
|
112
76
|
"type": "boolean"
|
|
77
|
+
},
|
|
78
|
+
"platform_editor_diff_inline_mark_changes": {
|
|
79
|
+
"type": "boolean"
|
|
113
80
|
}
|
|
114
81
|
}
|
|
115
82
|
}
|