@atlaskit/editor-plugin-show-diff 10.1.23 → 10.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (74) hide show
  1. package/CHANGELOG.md +63 -0
  2. package/calculate-diff/package.json +8 -0
  3. package/dist/cjs/entry-points/calculate-diff.js +12 -0
  4. package/dist/cjs/pm-plugins/calculateDiff/calculateDiffDecorations.js +132 -31
  5. package/dist/cjs/pm-plugins/calculateDiff/computeDiffChanges.js +112 -0
  6. package/dist/cjs/pm-plugins/calculateDiff/groupChangesByBlock.js +4 -2
  7. package/dist/cjs/pm-plugins/calculateDiff/smart/classifySmartChanges.js +841 -0
  8. package/dist/cjs/pm-plugins/calculateDiff/smart/helpers.js +135 -0
  9. package/dist/cjs/pm-plugins/calculateDiff/smart/segmentText.js +331 -0
  10. package/dist/cjs/pm-plugins/calculateDiff/smart/thresholds.js +46 -0
  11. package/dist/cjs/pm-plugins/decorations/createBlockChangedDecoration.js +24 -18
  12. package/dist/cjs/pm-plugins/decorations/createChangedRowDecorationWidgets.js +18 -12
  13. package/dist/cjs/pm-plugins/decorations/createInlineChangedDecoration.js +6 -5
  14. package/dist/cjs/pm-plugins/decorations/createNodeChangedDecorationWidget.js +30 -17
  15. package/dist/cjs/pm-plugins/decorations/decorationKeys.js +8 -5
  16. package/dist/cjs/pm-plugins/decorations/utils/wrapBlockNodeView.js +52 -30
  17. package/dist/cjs/pm-plugins/getScrollableDecorations.js +6 -6
  18. package/dist/cjs/pm-plugins/isExtendedEnabled.js +20 -0
  19. package/dist/cjs/pm-plugins/main.js +21 -16
  20. package/dist/es2019/entry-points/calculate-diff.js +2 -0
  21. package/dist/es2019/pm-plugins/calculateDiff/calculateDiffDecorations.js +119 -29
  22. package/dist/es2019/pm-plugins/calculateDiff/computeDiffChanges.js +94 -0
  23. package/dist/es2019/pm-plugins/calculateDiff/groupChangesByBlock.js +4 -3
  24. package/dist/es2019/pm-plugins/calculateDiff/smart/classifySmartChanges.js +689 -0
  25. package/dist/es2019/pm-plugins/calculateDiff/smart/helpers.js +106 -0
  26. package/dist/es2019/pm-plugins/calculateDiff/smart/segmentText.js +283 -0
  27. package/dist/es2019/pm-plugins/calculateDiff/smart/thresholds.js +45 -0
  28. package/dist/es2019/pm-plugins/decorations/createBlockChangedDecoration.js +24 -18
  29. package/dist/es2019/pm-plugins/decorations/createChangedRowDecorationWidgets.js +18 -12
  30. package/dist/es2019/pm-plugins/decorations/createInlineChangedDecoration.js +6 -5
  31. package/dist/es2019/pm-plugins/decorations/createNodeChangedDecorationWidget.js +31 -16
  32. package/dist/es2019/pm-plugins/decorations/decorationKeys.js +9 -5
  33. package/dist/es2019/pm-plugins/decorations/utils/wrapBlockNodeView.js +52 -34
  34. package/dist/es2019/pm-plugins/getScrollableDecorations.js +6 -6
  35. package/dist/es2019/pm-plugins/isExtendedEnabled.js +12 -0
  36. package/dist/es2019/pm-plugins/main.js +21 -16
  37. package/dist/esm/entry-points/calculate-diff.js +2 -0
  38. package/dist/esm/pm-plugins/calculateDiff/calculateDiffDecorations.js +132 -31
  39. package/dist/esm/pm-plugins/calculateDiff/computeDiffChanges.js +107 -0
  40. package/dist/esm/pm-plugins/calculateDiff/groupChangesByBlock.js +4 -3
  41. package/dist/esm/pm-plugins/calculateDiff/smart/classifySmartChanges.js +834 -0
  42. package/dist/esm/pm-plugins/calculateDiff/smart/helpers.js +128 -0
  43. package/dist/esm/pm-plugins/calculateDiff/smart/segmentText.js +325 -0
  44. package/dist/esm/pm-plugins/calculateDiff/smart/thresholds.js +39 -0
  45. package/dist/esm/pm-plugins/decorations/createBlockChangedDecoration.js +24 -18
  46. package/dist/esm/pm-plugins/decorations/createChangedRowDecorationWidgets.js +18 -12
  47. package/dist/esm/pm-plugins/decorations/createInlineChangedDecoration.js +6 -5
  48. package/dist/esm/pm-plugins/decorations/createNodeChangedDecorationWidget.js +30 -17
  49. package/dist/esm/pm-plugins/decorations/decorationKeys.js +9 -5
  50. package/dist/esm/pm-plugins/decorations/utils/wrapBlockNodeView.js +52 -30
  51. package/dist/esm/pm-plugins/getScrollableDecorations.js +6 -6
  52. package/dist/esm/pm-plugins/isExtendedEnabled.js +14 -0
  53. package/dist/esm/pm-plugins/main.js +21 -16
  54. package/dist/types/entry-points/calculate-diff.d.ts +2 -0
  55. package/dist/types/entry-points/show-diff-plugin-type.d.ts +1 -1
  56. package/dist/types/pm-plugins/calculateDiff/calculateDiffDecorations.d.ts +3 -1
  57. package/dist/types/pm-plugins/calculateDiff/computeDiffChanges.d.ts +46 -0
  58. package/dist/types/pm-plugins/calculateDiff/groupChangesByBlock.d.ts +9 -0
  59. package/dist/types/pm-plugins/calculateDiff/smart/classifySmartChanges.d.ts +26 -0
  60. package/dist/types/pm-plugins/calculateDiff/smart/helpers.d.ts +44 -0
  61. package/dist/types/pm-plugins/calculateDiff/smart/segmentText.d.ts +32 -0
  62. package/dist/types/pm-plugins/calculateDiff/smart/thresholds.d.ts +44 -0
  63. package/dist/types/pm-plugins/decorations/createBlockChangedDecoration.d.ts +3 -2
  64. package/dist/types/pm-plugins/decorations/createChangedRowDecorationWidgets.d.ts +3 -2
  65. package/dist/types/pm-plugins/decorations/createInlineChangedDecoration.d.ts +3 -2
  66. package/dist/types/pm-plugins/decorations/createNodeChangedDecorationWidget.d.ts +4 -2
  67. package/dist/types/pm-plugins/decorations/decorationKeys.d.ts +5 -3
  68. package/dist/types/pm-plugins/decorations/utils/wrapBlockNodeView.d.ts +6 -4
  69. package/dist/types/pm-plugins/getScrollableDecorations.d.ts +2 -1
  70. package/dist/types/pm-plugins/isExtendedEnabled.d.ts +11 -0
  71. package/dist/types/pm-plugins/main.d.ts +11 -1
  72. package/dist/types/showDiffPluginType.d.ts +20 -1
  73. package/docs/smart-diff-design.md +240 -0
  74. package/package.json +8 -3
package/CHANGELOG.md CHANGED
@@ -1,5 +1,68 @@
1
1
  # @atlaskit/editor-plugin-show-diff
2
2
 
3
+ ## 10.3.0
4
+
5
+ ### Minor Changes
6
+
7
+ - [`21147179a56ea`](https://bitbucket.org/atlassian/atlassian-frontend-monorepo/commits/21147179a56ea) -
8
+ [CCI-17809] Overhaul Post Stream Review segmenting.
9
+
10
+ `@atlaskit/editor-plugin-show-diff`: add a generic `computeDiffChanges` utility (new
11
+ `@atlaskit/editor-plugin-show-diff/calculate-diff` entry point) that returns the classified
12
+ `Change[]` and reconstructed new document for a given `originalDoc` + `steps`, without rendering
13
+ decorations. It reuses the same classifier the diff overlay renders from (default
14
+ `diffType: 'smart'`), so consumers can derive their own reviewable segments that line up with what
15
+ the overlay would draw. Exposes the `ComputeDiffChangesParams` type.
16
+
17
+ `@atlassian/editor-plugin-ai`: support two segmenting strategies in the Post Stream Review
18
+ step-through, selected by the new `platform_editor_ai_diff_based_segmenting` gate. Gate ON =
19
+ diff-based (`smart`) segmenting (via the show-diff `computeDiffChanges` utility, shaped into
20
+ reviewable segments in the AI plugin); gate OFF (default) = top-level-node segmenting, which no
21
+ longer splits a list into individual list items. Fine segments are now stored in plugin state and
22
+ remapped on every doc change, so per-segment undo/redo and view-changes stay correct after an
23
+ undo. Aggregate ("all changes") mode is unchanged.
24
+
25
+ ### Patch Changes
26
+
27
+ - Updated dependencies
28
+
29
+ ## 10.2.0
30
+
31
+ ### Minor Changes
32
+
33
+ - [`eb1f11daf16c8`](https://bitbucket.org/atlassian/atlassian-frontend-monorepo/commits/eb1f11daf16c8) -
34
+ Add a new `smart` `DiffType` that classifies changes by density at the sentence, paragraph and
35
+ node level and renders the most readable granularity for each change. Introduces the exported
36
+ `SmartDiffThresholds` type and a `smartThresholds` option on the `showDiff` params for configuring
37
+ the sentence/paragraph/node promotion thresholds.
38
+
39
+ Also adds a new exported `DeletedDiffPlacement` type (`'top' | 'bottom'`) and a
40
+ `deletedDiffPlacement` param on `PMDiffParams` that controls whether node/paragraph-level deleted
41
+ content is rendered above (`'top'`, the default) or below (`'bottom'`) the new content.
42
+
43
+ Usage:
44
+
45
+ ```ts
46
+ import type {
47
+ DeletedDiffPlacement,
48
+ SmartDiffThresholds,
49
+ } from '@atlaskit/editor-plugin-show-diff/show-diff-plugin-type';
50
+
51
+ editorApi.showDiff.commands.showDiff({
52
+ originalDoc,
53
+ steps,
54
+ diffType: 'smart',
55
+ // Optional; controls where deleted content is rendered. Defaults to 'top'.
56
+ deletedDiffPlacement: 'bottom' satisfies DeletedDiffPlacement,
57
+ // All fields optional; defaults applied internally.
58
+ smartThresholds: {
59
+ sentence: { minChanged: 2, ratio: 0.4 },
60
+ paragraph: { minChanged: 2, ratio: 0.4 },
61
+ node: { ratio: 0.6, textBearingRatio: 0.6 },
62
+ } satisfies SmartDiffThresholds,
63
+ });
64
+ ```
65
+
3
66
  ## 10.1.23
4
67
 
5
68
  ### Patch Changes
@@ -0,0 +1,8 @@
1
+ {
2
+ "name": "@atlaskit/editor-plugin-show-diff/calculate-diff",
3
+ "main": "../dist/cjs/entry-points/calculate-diff.js",
4
+ "module": "../dist/esm/entry-points/calculate-diff.js",
5
+ "module:es2019": "../dist/es2019/entry-points/calculate-diff.js",
6
+ "sideEffects": false,
7
+ "types": "../dist/types/entry-points/calculate-diff.d.ts"
8
+ }
@@ -0,0 +1,12 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ Object.defineProperty(exports, "computeDiffChanges", {
7
+ enumerable: true,
8
+ get: function get() {
9
+ return _computeDiffChanges.computeDiffChanges;
10
+ }
11
+ });
12
+ var _computeDiffChanges = require("../pm-plugins/calculateDiff/computeDiffChanges");
@@ -5,14 +5,15 @@ Object.defineProperty(exports, "__esModule", {
5
5
  value: true
6
6
  });
7
7
  exports.calculateDiffDecorations = void 0;
8
- var _slicedToArray2 = _interopRequireDefault(require("@babel/runtime/helpers/slicedToArray"));
9
8
  var _defineProperty2 = _interopRequireDefault(require("@babel/runtime/helpers/defineProperty"));
9
+ var _slicedToArray2 = _interopRequireDefault(require("@babel/runtime/helpers/slicedToArray"));
10
10
  var _toConsumableArray2 = _interopRequireDefault(require("@babel/runtime/helpers/toConsumableArray"));
11
11
  var _isEqual = _interopRequireDefault(require("lodash/isEqual"));
12
12
  var _memoizeOne = _interopRequireDefault(require("memoize-one"));
13
13
  var _prosemirrorChangeset = require("prosemirror-changeset");
14
14
  var _document = require("@atlaskit/editor-common/utils/document");
15
15
  var _view = require("@atlaskit/editor-prosemirror/view");
16
+ var _platformFeatureFlags = require("@atlaskit/platform-feature-flags");
16
17
  var _expValEquals = require("@atlaskit/tmp-editor-statsig/exp-val-equals");
17
18
  var _areDocsEqualByBlockStructureAndText = require("../areDocsEqualByBlockStructureAndText");
18
19
  var _createAnchorDecorationWidgets = require("../decorations/createAnchorDecorationWidgets");
@@ -23,10 +24,13 @@ var _createNodeChangedDecorationWidget = require("../decorations/createNodeChang
23
24
  var _decorationKeys = require("../decorations/decorationKeys");
24
25
  var _getAttrChangeRanges = require("../decorations/utils/getAttrChangeRanges");
25
26
  var _getMarkChangeRanges = require("../decorations/utils/getMarkChangeRanges");
27
+ var _isExtendedEnabled = require("../isExtendedEnabled");
26
28
  var _diffBySteps = require("./diffBySteps");
27
29
  var _groupChangesByBlock = require("./groupChangesByBlock");
28
30
  var _optimizeChanges = require("./optimizeChanges");
29
31
  var _simplifySteps = require("./simplifySteps");
32
+ var _classifySmartChanges = require("./smart/classifySmartChanges");
33
+ var _helpers = require("./smart/helpers");
30
34
  function ownKeys(e, r) { var t = Object.keys(e); if (Object.getOwnPropertySymbols) { var o = Object.getOwnPropertySymbols(e); r && (o = o.filter(function (r) { return Object.getOwnPropertyDescriptor(e, r).enumerable; })), t.push.apply(t, o); } return t; }
31
35
  function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? ownKeys(Object(t), !0).forEach(function (r) { (0, _defineProperty2.default)(e, r, t[r]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function (r) { Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r)); }); } return e; }
32
36
  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; } } }; }
@@ -38,20 +42,58 @@ var getChanges = function getChanges(_ref) {
38
42
  steppedDoc = _ref.steppedDoc,
39
43
  diffType = _ref.diffType,
40
44
  tr = _ref.tr,
41
- steps = _ref.steps;
42
- if ((0, _expValEquals.expValEquals)('platform_editor_diff_plugin_extended', 'isEnabled', true)) {
45
+ steps = _ref.steps,
46
+ intl = _ref.intl,
47
+ smartThresholds = _ref.smartThresholds;
48
+ if ((0, _isExtendedEnabled.isExtendedEnabled)(diffType)) {
49
+ // The `smart` diff type is gated behind `platform_editor_ai_smart_diff`. When the gate is
50
+ // off, `smart` falls through to the default (`inline`) path below so behaviour degrades
51
+ // gracefully (see docs/smart-diff-design.md §3).
52
+ if (diffType === 'smart' && (0, _platformFeatureFlags.fg)('platform_editor_ai_smart_diff')) {
53
+ var _changes = (0, _prosemirrorChangeset.simplifyChanges)(changeset.changes, tr.doc);
54
+ return (0, _classifySmartChanges.classifySmartChanges)({
55
+ changes: _changes,
56
+ originalDoc: originalDoc,
57
+ newDoc: tr.doc,
58
+ locale: intl.locale,
59
+ thresholds: smartThresholds
60
+ });
61
+ }
43
62
  if (diffType === 'step') {
44
63
  return (0, _diffBySteps.diffBySteps)(originalDoc, steps);
45
64
  }
46
65
  if (diffType === 'block') {
47
66
  return (0, _groupChangesByBlock.groupChangesByBlock)(changeset.changes, originalDoc, steppedDoc);
48
67
  }
49
- var _changes = (0, _prosemirrorChangeset.simplifyChanges)(changeset.changes, tr.doc);
50
- return (0, _optimizeChanges.optimizeChanges)(_changes);
68
+ var _changes2 = (0, _prosemirrorChangeset.simplifyChanges)(changeset.changes, tr.doc);
69
+ return (0, _optimizeChanges.optimizeChanges)(_changes2);
51
70
  }
52
71
  var changes = (0, _prosemirrorChangeset.simplifyChanges)(changeset.changes, tr.doc);
53
72
  return (0, _optimizeChanges.optimizeChanges)(changes);
54
73
  };
74
+
75
+ /**
76
+ * Collect the inline-content ranges of every leaf text-bearing block (paragraph, heading, …)
77
+ * whose content overlaps `[from, to)`. Used to clip a node-level `smart` insertion highlight to
78
+ * the actual added text, so the inserted background/underline never spans structural gaps
79
+ * (list markers, empty item slots, cell/column boundaries) which would render as phantom rows.
80
+ */
81
+ var leafTextblockRanges = function leafTextblockRanges(doc, from, to) {
82
+ var ranges = [];
83
+ doc.nodesBetween(from, to, function (node, pos) {
84
+ if (node.isTextblock && node.content.size > 0) {
85
+ var contentFrom = Math.max(pos + 1, from);
86
+ var contentTo = Math.min(pos + 1 + node.content.size, to);
87
+ if (contentTo > contentFrom) {
88
+ ranges.push([contentFrom, contentTo]);
89
+ }
90
+ // Textblocks have no block children to descend into.
91
+ return false;
92
+ }
93
+ return true;
94
+ });
95
+ return ranges;
96
+ };
55
97
  var calculateNodesForBlockDecoration = function calculateNodesForBlockDecoration(_ref2) {
56
98
  var doc = _ref2.doc,
57
99
  from = _ref2.from,
@@ -63,11 +105,12 @@ var calculateNodesForBlockDecoration = function calculateNodesForBlockDecoration
63
105
  _ref2$shouldHideDelet = _ref2.shouldHideDeleted,
64
106
  shouldHideDeleted = _ref2$shouldHideDelet === void 0 ? false : _ref2$shouldHideDelet,
65
107
  _ref2$showIndicators = _ref2.showIndicators,
66
- showIndicators = _ref2$showIndicators === void 0 ? false : _ref2$showIndicators;
108
+ showIndicators = _ref2$showIndicators === void 0 ? false : _ref2$showIndicators,
109
+ diffType = _ref2.diffType;
67
110
  var decorations = [];
68
111
  // Iterate over the document nodes within the range
69
112
  doc.nodesBetween(from, to, function (node, pos) {
70
- if (node.isBlock && (!(0, _expValEquals.expValEquals)('platform_editor_diff_plugin_extended', 'isEnabled', true) || pos + node.nodeSize <= to)) {
113
+ if (node.isBlock && (!(0, _isExtendedEnabled.isExtendedEnabled)(diffType) || pos + node.nodeSize <= to)) {
71
114
  var nodeEnd = pos + node.nodeSize;
72
115
  var isActive = activeIndexPos && pos === activeIndexPos.from && nodeEnd === activeIndexPos.to;
73
116
  decorations.push.apply(decorations, (0, _toConsumableArray2.default)((0, _createBlockChangedDecoration.createBlockChangedDecoration)({
@@ -81,7 +124,8 @@ var calculateNodesForBlockDecoration = function calculateNodesForBlockDecoration
81
124
  isActive: isActive,
82
125
  shouldHideDeleted: shouldHideDeleted,
83
126
  showIndicators: showIndicators,
84
- doc: doc
127
+ doc: doc,
128
+ diffType: diffType
85
129
  })));
86
130
  }
87
131
  });
@@ -102,7 +146,10 @@ var calculateDiffDecorationsInner = function calculateDiffDecorationsInner(_ref3
102
146
  _ref3$hideDeletedDiff = _ref3.hideDeletedDiffs,
103
147
  hideDeletedDiffs = _ref3$hideDeletedDiff === void 0 ? false : _ref3$hideDeletedDiff,
104
148
  _ref3$showIndicators = _ref3.showIndicators,
105
- showIndicators = _ref3$showIndicators === void 0 ? false : _ref3$showIndicators;
149
+ showIndicators = _ref3$showIndicators === void 0 ? false : _ref3$showIndicators,
150
+ smartThresholds = _ref3.smartThresholds,
151
+ _ref3$deletedDiffPlac = _ref3.deletedDiffPlacement,
152
+ deletedDiffPlacement = _ref3$deletedDiffPlac === void 0 ? 'top' : _ref3$deletedDiffPlac;
106
153
  var originalDoc = pluginState.originalDoc,
107
154
  steps = pluginState.steps,
108
155
  isDisplayingChanges = pluginState.isDisplayingChanges;
@@ -166,14 +213,16 @@ var calculateDiffDecorationsInner = function calculateDiffDecorationsInner(_ref3
166
213
  steppedDoc: steppedDoc,
167
214
  diffType: diffType,
168
215
  tr: tr,
169
- steps: steps
216
+ steps: steps,
217
+ intl: intl,
218
+ smartThresholds: smartThresholds
170
219
  });
171
220
  var decorations = [];
172
221
 
173
222
  /**
174
223
  * If showIndicators is on, we create an anchor widget here to mark the doc margin.
175
224
  */
176
- if (showIndicators && (0, _expValEquals.expValEquals)('platform_editor_diff_plugin_extended', 'isEnabled', true)) {
225
+ if (showIndicators && (0, _isExtendedEnabled.isExtendedEnabled)(diffType)) {
177
226
  decorations.push((0, _createAnchorDecorationWidgets.createDocMarginAnchorWidget)());
178
227
  }
179
228
 
@@ -185,33 +234,77 @@ var calculateDiffDecorationsInner = function calculateDiffDecorationsInner(_ref3
185
234
  // shouldHideDeleted for block/node decorations: suppressed when isInverted + hideDeletedDiffs,
186
235
  // or when showGranularWithBlock (block reference widget is shown instead).
187
236
  // isInverted gates both — on an inverted diff the inserted side is visually the deleted side.
188
- var shouldHideDeleted = (0, _expValEquals.expValEquals)('platform_editor_diff_plugin_extended', 'isEnabled', true) ? isInverted && (hideDeletedDiffs || showGranularWithBlock) && change.deleted.length > 0 : false;
189
- decorations.push.apply(decorations, (0, _toConsumableArray2.default)((0, _createInlineChangedDecoration.createInlineChangedDecoration)(_objectSpread({
190
- change: change,
191
- doc: tr.doc,
192
- colorScheme: colorScheme,
193
- isActive: isActive
194
- }, (0, _expValEquals.expValEquals)('platform_editor_diff_plugin_extended', 'isEnabled', true) && {
195
- isInserted: isInserted,
196
- shouldHideDeleted: shouldHideDeleted,
197
- showIndicators: showIndicators
198
- }))));
237
+ var shouldHideDeleted = (0, _isExtendedEnabled.isExtendedEnabled)(diffType) ? isInverted && (hideDeletedDiffs || showGranularWithBlock) && change.deleted.length > 0 : false;
238
+
239
+ // For `smart` NODE-level promotions the change range spans a whole container
240
+ // (e.g. an entire list/table/layout, using outer node bounds). Applying a SINGLE
241
+ // inline decoration across that whole range would paint the inserted style across
242
+ // block boundaries and structural gaps (list markers, empty item slots), producing
243
+ // phantom "empty" rows above the real content. But skipping the inline highlight
244
+ // entirely leaves added text-bearing blocks (paragraphs/headings inside the added
245
+ // container) without the inserted background+underline, because block decorations
246
+ // return no style for paragraph/heading. So for node-level smart changes we instead
247
+ // emit ONE inline decoration per leaf text-bearing block within the range — the text
248
+ // gets highlighted, and structural gaps never do.
249
+ var isSmartNodeLevel = diffType === 'smart' && (0, _platformFeatureFlags.fg)('platform_editor_ai_smart_diff') && (0, _helpers.smartChangeLevel)(change) === 'node';
250
+ if (isSmartNodeLevel) {
251
+ var _iterator2 = _createForOfIteratorHelper(leafTextblockRanges(tr.doc, change.fromB, change.toB)),
252
+ _step2;
253
+ try {
254
+ for (_iterator2.s(); !(_step2 = _iterator2.n()).done;) {
255
+ var _step2$value = (0, _slicedToArray2.default)(_step2.value, 2),
256
+ from = _step2$value[0],
257
+ to = _step2$value[1];
258
+ decorations.push.apply(decorations, (0, _toConsumableArray2.default)((0, _createInlineChangedDecoration.createInlineChangedDecoration)(_objectSpread({
259
+ change: {
260
+ fromB: from,
261
+ toB: to
262
+ },
263
+ doc: tr.doc,
264
+ colorScheme: colorScheme,
265
+ isActive: isActive,
266
+ diffType: diffType
267
+ }, (0, _isExtendedEnabled.isExtendedEnabled)(diffType) && {
268
+ isInserted: isInserted,
269
+ shouldHideDeleted: shouldHideDeleted,
270
+ showIndicators: showIndicators
271
+ }))));
272
+ }
273
+ } catch (err) {
274
+ _iterator2.e(err);
275
+ } finally {
276
+ _iterator2.f();
277
+ }
278
+ } else {
279
+ decorations.push.apply(decorations, (0, _toConsumableArray2.default)((0, _createInlineChangedDecoration.createInlineChangedDecoration)(_objectSpread({
280
+ change: change,
281
+ doc: tr.doc,
282
+ colorScheme: colorScheme,
283
+ isActive: isActive,
284
+ diffType: diffType
285
+ }, (0, _isExtendedEnabled.isExtendedEnabled)(diffType) && {
286
+ isInserted: isInserted,
287
+ shouldHideDeleted: shouldHideDeleted,
288
+ showIndicators: showIndicators
289
+ }))));
290
+ }
199
291
  decorations.push.apply(decorations, (0, _toConsumableArray2.default)(calculateNodesForBlockDecoration(_objectSpread(_objectSpread({
200
292
  doc: tr.doc,
201
293
  from: change.fromB,
202
294
  to: change.toB,
203
295
  colorScheme: colorScheme
204
- }, (0, _expValEquals.expValEquals)('platform_editor_diff_plugin_extended', 'isEnabled', true) && {
296
+ }, (0, _isExtendedEnabled.isExtendedEnabled)(diffType) && {
205
297
  isInserted: isInserted,
206
298
  shouldHideDeleted: shouldHideDeleted,
207
299
  showIndicators: showIndicators
208
300
  }), {}, {
209
301
  activeIndexPos: activeIndexPos,
210
- intl: intl
302
+ intl: intl,
303
+ diffType: diffType
211
304
  }))));
212
305
  }
213
306
  if (change.deleted.length > 0) {
214
- var _shouldHideDeleted = (0, _expValEquals.expValEquals)('platform_editor_diff_plugin_extended', 'isEnabled', true) ? !isInverted && (hideDeletedDiffs || showGranularWithBlock) && change.inserted.length > 0 : false;
307
+ var _shouldHideDeleted = (0, _isExtendedEnabled.isExtendedEnabled)(diffType) ? !isInverted && (hideDeletedDiffs || showGranularWithBlock) && change.inserted.length > 0 : false;
215
308
  if (!_shouldHideDeleted) {
216
309
  decorations.push.apply(decorations, (0, _toConsumableArray2.default)((0, _createNodeChangedDecorationWidget.createNodeChangedDecorationWidget)(_objectSpread(_objectSpread({
217
310
  change: change,
@@ -221,9 +314,13 @@ var calculateDiffDecorationsInner = function calculateDiffDecorationsInner(_ref3
221
314
  newDoc: tr.doc,
222
315
  intl: intl,
223
316
  activeIndexPos: activeIndexPos
224
- }, (0, _expValEquals.expValEquals)('platform_editor_diff_plugin_extended', 'isEnabled', true) && {
317
+ }, (0, _isExtendedEnabled.isExtendedEnabled)(diffType) && {
225
318
  isInserted: !isInserted,
226
- diffType: diffType
319
+ diffType: diffType,
320
+ // For `smart` node- and paragraph-level changes, optionally render the
321
+ // deleted content below the new content (gray + strikethrough) instead
322
+ // of above it. Controlled by `deletedDiffPlacement` (default `'top'`).
323
+ placeBelow: deletedDiffPlacement === 'bottom' && diffType === 'smart' && (0, _platformFeatureFlags.fg)('platform_editor_ai_smart_diff') && ((0, _helpers.smartChangeLevel)(change) === 'node' || (0, _helpers.smartChangeLevel)(change) === 'paragraph')
227
324
  }), {}, {
228
325
  showIndicators: showIndicators
229
326
  }))));
@@ -364,7 +461,9 @@ function (_ref5, _ref6) {
364
461
  isInverted = _ref7$.isInverted,
365
462
  diffType = _ref7$.diffType,
366
463
  hideDeletedDiffs = _ref7$.hideDeletedDiffs,
367
- showIndicators = _ref7$.showIndicators;
464
+ showIndicators = _ref7$.showIndicators,
465
+ smartThresholds = _ref7$.smartThresholds,
466
+ deletedDiffPlacement = _ref7$.deletedDiffPlacement;
368
467
  var _ref8 = (0, _slicedToArray2.default)(_ref6, 1),
369
468
  _ref8$ = _ref8[0],
370
469
  lastPluginState = _ref8$.pluginState,
@@ -375,11 +474,13 @@ function (_ref5, _ref6) {
375
474
  lastIsInverted = _ref8$.isInverted,
376
475
  lastDiffType = _ref8$.diffType,
377
476
  lastHideDeletedDiffs = _ref8$.hideDeletedDiffs,
378
- lastShowIndicators = _ref8$.showIndicators;
477
+ lastShowIndicators = _ref8$.showIndicators,
478
+ lastSmartThresholds = _ref8$.smartThresholds,
479
+ lastDeletedDiffPlacement = _ref8$.deletedDiffPlacement;
379
480
  var originalDocIsSame = lastPluginState.originalDoc && pluginState.originalDoc && pluginState.originalDoc.eq(lastPluginState.originalDoc);
380
- if ((0, _expValEquals.expValEquals)('platform_editor_diff_plugin_extended', 'isEnabled', true)) {
481
+ if ((0, _isExtendedEnabled.isExtendedEnabled)(diffType)) {
381
482
  var _ref9;
382
- return (_ref9 = colorScheme === lastColorScheme && intl.locale === lastIntl.locale && isInverted === lastIsInverted && diffType === lastDiffType && (0, _isEqual.default)(activeIndexPos, lastActiveIndexPos) && originalDocIsSame && (0, _isEqual.default)(pluginState.steps, lastPluginState.steps) && state.doc.eq(lastState.doc) && hideDeletedDiffs === lastHideDeletedDiffs && showIndicators === lastShowIndicators) !== null && _ref9 !== void 0 ? _ref9 : false;
483
+ return (_ref9 = colorScheme === lastColorScheme && intl.locale === lastIntl.locale && isInverted === lastIsInverted && diffType === lastDiffType && (0, _isEqual.default)(activeIndexPos, lastActiveIndexPos) && originalDocIsSame && (0, _isEqual.default)(pluginState.steps, lastPluginState.steps) && state.doc.eq(lastState.doc) && hideDeletedDiffs === lastHideDeletedDiffs && showIndicators === lastShowIndicators && (0, _isEqual.default)(smartThresholds, lastSmartThresholds) && deletedDiffPlacement === lastDeletedDiffPlacement) !== null && _ref9 !== void 0 ? _ref9 : false;
383
484
  }
384
485
  return (_ref0 = originalDocIsSame && (0, _isEqual.default)(pluginState.steps, lastPluginState.steps) && state.doc.eq(lastState.doc) && colorScheme === lastColorScheme && intl.locale === lastIntl.locale && (0, _isEqual.default)(activeIndexPos, lastActiveIndexPos) && hideDeletedDiffs === lastHideDeletedDiffs) !== null && _ref0 !== void 0 ? _ref0 : false;
385
486
  });
@@ -0,0 +1,112 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.computeDiffChanges = void 0;
7
+ var _prosemirrorChangeset = require("prosemirror-changeset");
8
+ var _diffBySteps = require("./diffBySteps");
9
+ var _groupChangesByBlock = require("./groupChangesByBlock");
10
+ var _optimizeChanges = require("./optimizeChanges");
11
+ var _simplifySteps = require("./simplifySteps");
12
+ var _classifySmartChanges = require("./smart/classifySmartChanges");
13
+ 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; } } }; }
14
+ function _unsupportedIterableToArray(r, a) { if (r) { if ("string" == typeof r) return _arrayLikeToArray(r, a); var t = {}.toString.call(r).slice(8, -1); return "Object" === t && r.constructor && (t = r.constructor.name), "Map" === t || "Set" === t ? Array.from(r) : "Arguments" === t || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(t) ? _arrayLikeToArray(r, a) : void 0; } }
15
+ function _arrayLikeToArray(r, a) { (null == a || a > r.length) && (a = r.length); for (var e = 0, n = Array(a); e < a; e++) n[e] = r[e]; return n; } /**
16
+ * Generic, editor-state-free diff-change computation.
17
+ *
18
+ * Turns an `originalDoc` + forward `steps` into the classified `Change[]` for the
19
+ * requested `diffType`, plus the reconstructed new document — the same classifier
20
+ * the diff overlay uses (`calculateDiffDecorations` → `getChanges`), exposed as a
21
+ * reusable utility (e.g. to derive reviewable segments without rendering
22
+ * decorations). It runs the requested `diffType` DIRECTLY and does NOT apply the
23
+ * overlay's `platform_editor_ai_smart_diff` gate, so a `smart` result here can
24
+ * differ from what the overlay draws when that gate is off (the overlay falls back
25
+ * to inline); callers that need overlay parity must account for that gate.
26
+ *
27
+ * Pure: no `EditorState`, no transactions, no React. The new document is
28
+ * reconstructed by applying the (simplified) steps to `originalDoc`.
29
+ */
30
+ /**
31
+ * Apply the (simplified) steps to `originalDoc` to reconstruct the new doc, and
32
+ * compute the classified `Change[]` for the requested `diffType`. Mirrors the
33
+ * body of `calculateDiffDecorations` → `getChanges` so callers stay in lock-step
34
+ * with the rendered diff. Returns an empty change list (and `originalDoc` as the
35
+ * new doc) when there are no steps.
36
+ */
37
+ var computeDiffChanges = exports.computeDiffChanges = function computeDiffChanges(_ref) {
38
+ var originalDoc = _ref.originalDoc,
39
+ steps = _ref.steps,
40
+ _ref$diffType = _ref.diffType,
41
+ diffType = _ref$diffType === void 0 ? 'smart' : _ref$diffType,
42
+ _ref$locale = _ref.locale,
43
+ locale = _ref$locale === void 0 ? 'en' : _ref$locale,
44
+ smartThresholds = _ref.smartThresholds;
45
+ if (!steps || steps.length === 0) {
46
+ return {
47
+ changes: [],
48
+ newDoc: originalDoc
49
+ };
50
+ }
51
+ var simplifiedSteps = (0, _simplifySteps.simplifySteps)(steps, originalDoc);
52
+ var steppedDoc = originalDoc;
53
+ var stepMaps = [];
54
+ try {
55
+ var _iterator = _createForOfIteratorHelper(simplifiedSteps),
56
+ _step;
57
+ try {
58
+ for (_iterator.s(); !(_step = _iterator.n()).done;) {
59
+ var step = _step.value;
60
+ var result = step.apply(steppedDoc);
61
+ if (result.failed === null && result.doc) {
62
+ stepMaps.push(step.getMap());
63
+ steppedDoc = result.doc;
64
+ }
65
+ }
66
+ } catch (err) {
67
+ _iterator.e(err);
68
+ } finally {
69
+ _iterator.f();
70
+ }
71
+ } catch (_unused) {
72
+ // A step's positions fell outside the current doc — `ReplaceStep.apply`
73
+ // throws a RangeError (rather than returning a failed result) when a
74
+ // position exceeds the doc size. This can happen for pathological or
75
+ // inconsistent step sequences. Bail with no changes rather than letting the
76
+ // error propagate to the caller (which, for the PSR, would crash the
77
+ // toolbar); the consumer then falls back to its non-diff segmenting path.
78
+ return {
79
+ changes: [],
80
+ newDoc: originalDoc
81
+ };
82
+ }
83
+ var changeset = _prosemirrorChangeset.ChangeSet.create(originalDoc).addSteps(steppedDoc, stepMaps, steppedDoc);
84
+ if (diffType === 'smart') {
85
+ return {
86
+ changes: (0, _classifySmartChanges.classifySmartChanges)({
87
+ changes: (0, _prosemirrorChangeset.simplifyChanges)(changeset.changes, steppedDoc),
88
+ originalDoc: originalDoc,
89
+ newDoc: steppedDoc,
90
+ locale: locale,
91
+ thresholds: smartThresholds
92
+ }),
93
+ newDoc: steppedDoc
94
+ };
95
+ }
96
+ if (diffType === 'block') {
97
+ return {
98
+ changes: (0, _groupChangesByBlock.groupChangesByBlock)(changeset.changes, originalDoc, steppedDoc),
99
+ newDoc: steppedDoc
100
+ };
101
+ }
102
+ if (diffType === 'step') {
103
+ return {
104
+ changes: (0, _diffBySteps.diffBySteps)(originalDoc, simplifiedSteps),
105
+ newDoc: steppedDoc
106
+ };
107
+ }
108
+ return {
109
+ changes: (0, _optimizeChanges.optimizeChanges)((0, _prosemirrorChangeset.simplifyChanges)(changeset.changes, steppedDoc)),
110
+ newDoc: steppedDoc
111
+ };
112
+ };
@@ -4,14 +4,16 @@ var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefau
4
4
  Object.defineProperty(exports, "__esModule", {
5
5
  value: true
6
6
  });
7
+ exports.getTopLevelBlockAt = getTopLevelBlockAt;
7
8
  exports.groupChangesByBlock = groupChangesByBlock;
8
9
  var _toConsumableArray2 = _interopRequireDefault(require("@babel/runtime/helpers/toConsumableArray"));
9
10
  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; } } }; }
10
11
  function _unsupportedIterableToArray(r, a) { if (r) { if ("string" == typeof r) return _arrayLikeToArray(r, a); var t = {}.toString.call(r).slice(8, -1); return "Object" === t && r.constructor && (t = r.constructor.name), "Map" === t || "Set" === t ? Array.from(r) : "Arguments" === t || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(t) ? _arrayLikeToArray(r, a) : void 0; } }
11
12
  function _arrayLikeToArray(r, a) { (null == a || a > r.length) && (a = r.length); for (var e = 0, n = Array(a); e < a; e++) n[e] = r[e]; return n; }
12
13
  /**
13
- * Finds the position range of the top-level block (direct child of doc) that contains `pos`.
14
- * Returns null if `pos` is at the doc boundary or outside the doc content.
14
+ * Finds the position range (outer bounds) of the top-level block (direct child of doc) that
15
+ * contains the range [from, to]. Shared with the `smart` diff type so its block grouping is
16
+ * identical to the `block` diff type by construction.
15
17
  */
16
18
  function getTopLevelBlockAt(doc, from, to) {
17
19
  return {