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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (65) hide show
  1. package/CHANGELOG.md +37 -0
  2. package/dist/cjs/pm-plugins/calculateDiff/calculateDiffDecorations.js +132 -31
  3. package/dist/cjs/pm-plugins/calculateDiff/groupChangesByBlock.js +4 -2
  4. package/dist/cjs/pm-plugins/calculateDiff/smart/classifySmartChanges.js +841 -0
  5. package/dist/cjs/pm-plugins/calculateDiff/smart/helpers.js +135 -0
  6. package/dist/cjs/pm-plugins/calculateDiff/smart/segmentText.js +331 -0
  7. package/dist/cjs/pm-plugins/calculateDiff/smart/thresholds.js +46 -0
  8. package/dist/cjs/pm-plugins/decorations/createBlockChangedDecoration.js +24 -18
  9. package/dist/cjs/pm-plugins/decorations/createChangedRowDecorationWidgets.js +18 -12
  10. package/dist/cjs/pm-plugins/decorations/createInlineChangedDecoration.js +6 -5
  11. package/dist/cjs/pm-plugins/decorations/createNodeChangedDecorationWidget.js +30 -17
  12. package/dist/cjs/pm-plugins/decorations/decorationKeys.js +8 -5
  13. package/dist/cjs/pm-plugins/decorations/utils/wrapBlockNodeView.js +52 -30
  14. package/dist/cjs/pm-plugins/getScrollableDecorations.js +6 -6
  15. package/dist/cjs/pm-plugins/isExtendedEnabled.js +20 -0
  16. package/dist/cjs/pm-plugins/main.js +21 -16
  17. package/dist/es2019/pm-plugins/calculateDiff/calculateDiffDecorations.js +119 -29
  18. package/dist/es2019/pm-plugins/calculateDiff/groupChangesByBlock.js +4 -3
  19. package/dist/es2019/pm-plugins/calculateDiff/smart/classifySmartChanges.js +689 -0
  20. package/dist/es2019/pm-plugins/calculateDiff/smart/helpers.js +106 -0
  21. package/dist/es2019/pm-plugins/calculateDiff/smart/segmentText.js +283 -0
  22. package/dist/es2019/pm-plugins/calculateDiff/smart/thresholds.js +45 -0
  23. package/dist/es2019/pm-plugins/decorations/createBlockChangedDecoration.js +24 -18
  24. package/dist/es2019/pm-plugins/decorations/createChangedRowDecorationWidgets.js +18 -12
  25. package/dist/es2019/pm-plugins/decorations/createInlineChangedDecoration.js +6 -5
  26. package/dist/es2019/pm-plugins/decorations/createNodeChangedDecorationWidget.js +31 -16
  27. package/dist/es2019/pm-plugins/decorations/decorationKeys.js +9 -5
  28. package/dist/es2019/pm-plugins/decorations/utils/wrapBlockNodeView.js +52 -34
  29. package/dist/es2019/pm-plugins/getScrollableDecorations.js +6 -6
  30. package/dist/es2019/pm-plugins/isExtendedEnabled.js +12 -0
  31. package/dist/es2019/pm-plugins/main.js +21 -16
  32. package/dist/esm/pm-plugins/calculateDiff/calculateDiffDecorations.js +132 -31
  33. package/dist/esm/pm-plugins/calculateDiff/groupChangesByBlock.js +4 -3
  34. package/dist/esm/pm-plugins/calculateDiff/smart/classifySmartChanges.js +834 -0
  35. package/dist/esm/pm-plugins/calculateDiff/smart/helpers.js +128 -0
  36. package/dist/esm/pm-plugins/calculateDiff/smart/segmentText.js +325 -0
  37. package/dist/esm/pm-plugins/calculateDiff/smart/thresholds.js +39 -0
  38. package/dist/esm/pm-plugins/decorations/createBlockChangedDecoration.js +24 -18
  39. package/dist/esm/pm-plugins/decorations/createChangedRowDecorationWidgets.js +18 -12
  40. package/dist/esm/pm-plugins/decorations/createInlineChangedDecoration.js +6 -5
  41. package/dist/esm/pm-plugins/decorations/createNodeChangedDecorationWidget.js +30 -17
  42. package/dist/esm/pm-plugins/decorations/decorationKeys.js +9 -5
  43. package/dist/esm/pm-plugins/decorations/utils/wrapBlockNodeView.js +52 -30
  44. package/dist/esm/pm-plugins/getScrollableDecorations.js +6 -6
  45. package/dist/esm/pm-plugins/isExtendedEnabled.js +14 -0
  46. package/dist/esm/pm-plugins/main.js +21 -16
  47. package/dist/types/entry-points/show-diff-plugin-type.d.ts +1 -1
  48. package/dist/types/pm-plugins/calculateDiff/calculateDiffDecorations.d.ts +3 -1
  49. package/dist/types/pm-plugins/calculateDiff/groupChangesByBlock.d.ts +9 -0
  50. package/dist/types/pm-plugins/calculateDiff/smart/classifySmartChanges.d.ts +26 -0
  51. package/dist/types/pm-plugins/calculateDiff/smart/helpers.d.ts +44 -0
  52. package/dist/types/pm-plugins/calculateDiff/smart/segmentText.d.ts +32 -0
  53. package/dist/types/pm-plugins/calculateDiff/smart/thresholds.d.ts +44 -0
  54. package/dist/types/pm-plugins/decorations/createBlockChangedDecoration.d.ts +3 -2
  55. package/dist/types/pm-plugins/decorations/createChangedRowDecorationWidgets.d.ts +3 -2
  56. package/dist/types/pm-plugins/decorations/createInlineChangedDecoration.d.ts +3 -2
  57. package/dist/types/pm-plugins/decorations/createNodeChangedDecorationWidget.d.ts +4 -2
  58. package/dist/types/pm-plugins/decorations/decorationKeys.d.ts +5 -3
  59. package/dist/types/pm-plugins/decorations/utils/wrapBlockNodeView.d.ts +6 -4
  60. package/dist/types/pm-plugins/getScrollableDecorations.d.ts +2 -1
  61. package/dist/types/pm-plugins/isExtendedEnabled.d.ts +11 -0
  62. package/dist/types/pm-plugins/main.d.ts +11 -1
  63. package/dist/types/showDiffPluginType.d.ts +20 -1
  64. package/docs/smart-diff-design.md +240 -0
  65. package/package.json +7 -2
@@ -11,10 +11,10 @@ var _safePlugin = require("@atlaskit/editor-common/safe-plugin");
11
11
  var _state2 = require("@atlaskit/editor-prosemirror/state");
12
12
  var _transform = require("@atlaskit/editor-prosemirror/transform");
13
13
  var _view = require("@atlaskit/editor-prosemirror/view");
14
- var _expValEquals = require("@atlaskit/tmp-editor-statsig/exp-val-equals");
15
14
  var _calculateDiffDecorations = require("./calculateDiff/calculateDiffDecorations");
16
15
  var _enforceCustomStepRegisters = require("./enforceCustomStepRegisters");
17
16
  var _getScrollableDecorations = require("./getScrollableDecorations");
17
+ var _isExtendedEnabled = require("./isExtendedEnabled");
18
18
  var _NodeViewSerializer = require("./NodeViewSerializer");
19
19
  var _scrollToDiff = require("./scrollToDiff");
20
20
  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; }
@@ -38,7 +38,7 @@ var createPlugin = exports.createPlugin = function createPlugin(config, getIntl,
38
38
  originalDoc: undefined,
39
39
  decorations: _view.DecorationSet.empty,
40
40
  isDisplayingChanges: false
41
- }, (0, _expValEquals.expValEquals)('platform_editor_diff_plugin_extended', 'isEnabled', true) ? {
41
+ }, (0, _isExtendedEnabled.isExtendedEnabled)() ? {
42
42
  isInverted: false,
43
43
  diffType: 'inline',
44
44
  hideDeletedDiffs: false,
@@ -51,7 +51,7 @@ var createPlugin = exports.createPlugin = function createPlugin(config, getIntl,
51
51
  var newPluginState = currentPluginState;
52
52
  if (meta) {
53
53
  if ((meta === null || meta === void 0 ? void 0 : meta.action) === 'SHOW_DIFF') {
54
- var _newPluginState, _newPluginState2, _newPluginState3, _newPluginState4;
54
+ var _newPluginState, _newPluginState2, _newPluginState3, _newPluginState4, _newPluginState5, _newPluginState6, _newPluginState7, _newPluginState8;
55
55
  // Update the plugin state with the new metadata
56
56
  newPluginState = _objectSpread(_objectSpread(_objectSpread({}, currentPluginState), meta), {}, {
57
57
  isDisplayingChanges: true,
@@ -66,17 +66,19 @@ var createPlugin = exports.createPlugin = function createPlugin(config, getIntl,
66
66
  intl: getIntl(),
67
67
  activeIndexPos: newPluginState.activeIndexPos,
68
68
  api: api
69
- }, (0, _expValEquals.expValEquals)('platform_editor_diff_plugin_extended', 'isEnabled', true) ? {
70
- isInverted: (_newPluginState = newPluginState) === null || _newPluginState === void 0 ? void 0 : _newPluginState.isInverted,
71
- diffType: (_newPluginState2 = newPluginState) === null || _newPluginState2 === void 0 ? void 0 : _newPluginState2.diffType,
72
- hideDeletedDiffs: (_newPluginState3 = newPluginState) === null || _newPluginState3 === void 0 ? void 0 : _newPluginState3.hideDeletedDiffs,
73
- showIndicators: (_newPluginState4 = newPluginState) === null || _newPluginState4 === void 0 ? void 0 : _newPluginState4.showIndicators
69
+ }, (0, _isExtendedEnabled.isExtendedEnabled)((_newPluginState = newPluginState) === null || _newPluginState === void 0 ? void 0 : _newPluginState.diffType) ? {
70
+ isInverted: (_newPluginState2 = newPluginState) === null || _newPluginState2 === void 0 ? void 0 : _newPluginState2.isInverted,
71
+ diffType: (_newPluginState3 = newPluginState) === null || _newPluginState3 === void 0 ? void 0 : _newPluginState3.diffType,
72
+ hideDeletedDiffs: (_newPluginState4 = newPluginState) === null || _newPluginState4 === void 0 ? void 0 : _newPluginState4.hideDeletedDiffs,
73
+ showIndicators: (_newPluginState5 = newPluginState) === null || _newPluginState5 === void 0 ? void 0 : _newPluginState5.showIndicators,
74
+ smartThresholds: (_newPluginState6 = newPluginState) === null || _newPluginState6 === void 0 ? void 0 : _newPluginState6.smartThresholds,
75
+ deletedDiffPlacement: (_newPluginState7 = newPluginState) === null || _newPluginState7 === void 0 ? void 0 : _newPluginState7.deletedDiffPlacement
74
76
  } : {})),
75
77
  decorations = _calculateDiffDecorat.decorations,
76
78
  diffDescriptors = _calculateDiffDecorat.diffDescriptors;
77
79
  // Update the decorations and their ids
78
80
  newPluginState.decorations = decorations;
79
- if ((0, _expValEquals.expValEquals)('platform_editor_diff_plugin_extended', 'isEnabled', true)) {
81
+ if ((0, _isExtendedEnabled.isExtendedEnabled)((_newPluginState8 = newPluginState) === null || _newPluginState8 === void 0 ? void 0 : _newPluginState8.diffType)) {
80
82
  newPluginState.diffDescriptors = diffDescriptors;
81
83
  }
82
84
  } else if ((meta === null || meta === void 0 ? void 0 : meta.action) === 'HIDE_DIFF') {
@@ -84,15 +86,16 @@ var createPlugin = exports.createPlugin = function createPlugin(config, getIntl,
84
86
  decorations: _view.DecorationSet.empty,
85
87
  isDisplayingChanges: false,
86
88
  activeIndex: undefined
87
- }, (0, _expValEquals.expValEquals)('platform_editor_diff_plugin_extended', 'isEnabled', true) ? {
89
+ }, (0, _isExtendedEnabled.isExtendedEnabled)(currentPluginState.diffType) ? {
88
90
  isInverted: false,
89
91
  diffType: 'inline',
90
92
  hideDeletedDiffs: false,
91
93
  diffDescriptors: []
92
94
  } : {});
93
95
  } else if ((meta === null || meta === void 0 ? void 0 : meta.action) === 'SCROLL_TO_NEXT' || (meta === null || meta === void 0 ? void 0 : meta.action) === 'SCROLL_TO_PREVIOUS') {
96
+ var _newPluginState9;
94
97
  // Update the active index in plugin state and recalculate decorations
95
- var _decorations = (0, _getScrollableDecorations.getScrollableDecorations)(currentPluginState.decorations, newState.doc);
98
+ var _decorations = (0, _getScrollableDecorations.getScrollableDecorations)(currentPluginState.decorations, newState.doc, (_newPluginState9 = newPluginState) === null || _newPluginState9 === void 0 ? void 0 : _newPluginState9.diffType);
96
99
  if (_decorations.length > 0) {
97
100
  var _currentPluginState$a;
98
101
  // Initialize to -1 if undefined so that the first "next" scroll takes us to index 0 (first change).
@@ -126,16 +129,18 @@ var createPlugin = exports.createPlugin = function createPlugin(config, getIntl,
126
129
  intl: getIntl(),
127
130
  activeIndexPos: newPluginState.activeIndexPos,
128
131
  api: api
129
- }, (0, _expValEquals.expValEquals)('platform_editor_diff_plugin_extended', 'isEnabled', true) ? {
132
+ }, (0, _isExtendedEnabled.isExtendedEnabled)(newPluginState.diffType) ? {
130
133
  isInverted: newPluginState.isInverted,
131
134
  diffType: newPluginState.diffType,
132
135
  hideDeletedDiffs: newPluginState.hideDeletedDiffs,
133
- showIndicators: newPluginState.showIndicators
136
+ showIndicators: newPluginState.showIndicators,
137
+ smartThresholds: newPluginState.smartThresholds,
138
+ deletedDiffPlacement: newPluginState.deletedDiffPlacement
134
139
  } : {})),
135
140
  updatedDecorations = _calculateDiffDecorat2.decorations,
136
141
  updatedDiffDescriptors = _calculateDiffDecorat2.diffDescriptors;
137
142
  newPluginState.decorations = updatedDecorations;
138
- if ((0, _expValEquals.expValEquals)('platform_editor_diff_plugin_extended', 'isEnabled', true)) {
143
+ if ((0, _isExtendedEnabled.isExtendedEnabled)(newPluginState.diffType)) {
139
144
  newPluginState.diffDescriptors = updatedDiffDescriptors;
140
145
  }
141
146
  }
@@ -169,7 +174,7 @@ var createPlugin = exports.createPlugin = function createPlugin(config, getIntl,
169
174
  var pluginState = showDiffPluginKey.getState(view.state);
170
175
 
171
176
  // Scroll to the first decoration when scrollIntoView was requested
172
- if (pluginState !== null && pluginState !== void 0 && pluginState.scrollIntoView && (0, _expValEquals.expValEquals)('platform_editor_diff_plugin_extended', 'isEnabled', true)) {
177
+ if (pluginState !== null && pluginState !== void 0 && pluginState.scrollIntoView && (0, _isExtendedEnabled.isExtendedEnabled)(pluginState === null || pluginState === void 0 ? void 0 : pluginState.diffType)) {
173
178
  var _cancelPendingScrollT;
174
179
  (_cancelPendingScrollT = cancelPendingScrollToDecoration) === null || _cancelPendingScrollT === void 0 || _cancelPendingScrollT();
175
180
  cancelPendingScrollToDecoration = (0, _scrollToDiff.scrollToFirstDecoration)(view, pluginState.decorations);
@@ -186,7 +191,7 @@ var createPlugin = exports.createPlugin = function createPlugin(config, getIntl,
186
191
  if ((pluginState === null || pluginState === void 0 ? void 0 : pluginState.activeIndex) !== undefined && activeIndexChanged) {
187
192
  var _cancelPendingScrollT2, _api$expand;
188
193
  (_cancelPendingScrollT2 = cancelPendingScrollToDecoration) === null || _cancelPendingScrollT2 === void 0 || _cancelPendingScrollT2();
189
- var scrollableDecorations = (0, _getScrollableDecorations.getScrollableDecorations)(pluginState.decorations, view.state.doc);
194
+ var scrollableDecorations = (0, _getScrollableDecorations.getScrollableDecorations)(pluginState.decorations, view.state.doc, pluginState === null || pluginState === void 0 ? void 0 : pluginState.diffType);
190
195
  var activeDecoration = scrollableDecorations[pluginState.activeIndex];
191
196
  if (activeDecoration && api !== null && api !== void 0 && (_api$expand = api.expand) !== null && _api$expand !== void 0 && (_api$expand = _api$expand.commands) !== null && _api$expand !== void 0 && _api$expand.toggleExpandRange) {
192
197
  api === null || api === void 0 || api.core.actions.execute(api.expand.commands.toggleExpandRange(activeDecoration.from, activeDecoration.to, true));
@@ -4,6 +4,7 @@ import memoizeOne from 'memoize-one';
4
4
  import { ChangeSet, simplifyChanges } from 'prosemirror-changeset';
5
5
  import { areNodesEqualIgnoreAttrs } from '@atlaskit/editor-common/utils/document';
6
6
  import { DecorationSet } from '@atlaskit/editor-prosemirror/view';
7
+ import { fg } from '@atlaskit/platform-feature-flags';
7
8
  import { expValEquals } from '@atlaskit/tmp-editor-statsig/exp-val-equals';
8
9
  import { areDocsEqualByBlockStructureAndText } from '../areDocsEqualByBlockStructureAndText';
9
10
  import { createDocMarginAnchorWidget } from '../decorations/createAnchorDecorationWidgets';
@@ -14,19 +15,37 @@ import { createNodeChangedDecorationWidget } from '../decorations/createNodeChan
14
15
  import { extractDiffDescriptors } from '../decorations/decorationKeys';
15
16
  import { getAttrChangeRanges, stepIsValidAttrChange } from '../decorations/utils/getAttrChangeRanges';
16
17
  import { getMarkChangeRanges } from '../decorations/utils/getMarkChangeRanges';
18
+ import { isExtendedEnabled } from '../isExtendedEnabled';
17
19
  import { diffBySteps, getStepChanges } from './diffBySteps';
18
20
  import { groupChangesByBlock } from './groupChangesByBlock';
19
21
  import { optimizeChanges } from './optimizeChanges';
20
22
  import { simplifySteps } from './simplifySteps';
23
+ import { classifySmartChanges } from './smart/classifySmartChanges';
24
+ import { smartChangeLevel } from './smart/helpers';
21
25
  const getChanges = ({
22
26
  changeset,
23
27
  originalDoc,
24
28
  steppedDoc,
25
29
  diffType,
26
30
  tr,
27
- steps
31
+ steps,
32
+ intl,
33
+ smartThresholds
28
34
  }) => {
29
- if (expValEquals('platform_editor_diff_plugin_extended', 'isEnabled', true)) {
35
+ if (isExtendedEnabled(diffType)) {
36
+ // The `smart` diff type is gated behind `platform_editor_ai_smart_diff`. When the gate is
37
+ // off, `smart` falls through to the default (`inline`) path below so behaviour degrades
38
+ // gracefully (see docs/smart-diff-design.md §3).
39
+ if (diffType === 'smart' && fg('platform_editor_ai_smart_diff')) {
40
+ const changes = simplifyChanges(changeset.changes, tr.doc);
41
+ return classifySmartChanges({
42
+ changes,
43
+ originalDoc,
44
+ newDoc: tr.doc,
45
+ locale: intl.locale,
46
+ thresholds: smartThresholds
47
+ });
48
+ }
30
49
  if (diffType === 'step') {
31
50
  return diffBySteps(originalDoc, steps);
32
51
  }
@@ -39,6 +58,29 @@ const getChanges = ({
39
58
  const changes = simplifyChanges(changeset.changes, tr.doc);
40
59
  return optimizeChanges(changes);
41
60
  };
61
+
62
+ /**
63
+ * Collect the inline-content ranges of every leaf text-bearing block (paragraph, heading, …)
64
+ * whose content overlaps `[from, to)`. Used to clip a node-level `smart` insertion highlight to
65
+ * the actual added text, so the inserted background/underline never spans structural gaps
66
+ * (list markers, empty item slots, cell/column boundaries) which would render as phantom rows.
67
+ */
68
+ const leafTextblockRanges = (doc, from, to) => {
69
+ const ranges = [];
70
+ doc.nodesBetween(from, to, (node, pos) => {
71
+ if (node.isTextblock && node.content.size > 0) {
72
+ const contentFrom = Math.max(pos + 1, from);
73
+ const contentTo = Math.min(pos + 1 + node.content.size, to);
74
+ if (contentTo > contentFrom) {
75
+ ranges.push([contentFrom, contentTo]);
76
+ }
77
+ // Textblocks have no block children to descend into.
78
+ return false;
79
+ }
80
+ return true;
81
+ });
82
+ return ranges;
83
+ };
42
84
  const calculateNodesForBlockDecoration = ({
43
85
  doc,
44
86
  from,
@@ -47,12 +89,13 @@ const calculateNodesForBlockDecoration = ({
47
89
  isInserted = true,
48
90
  activeIndexPos,
49
91
  shouldHideDeleted = false,
50
- showIndicators = false
92
+ showIndicators = false,
93
+ diffType
51
94
  }) => {
52
95
  const decorations = [];
53
96
  // Iterate over the document nodes within the range
54
97
  doc.nodesBetween(from, to, (node, pos) => {
55
- if (node.isBlock && (!expValEquals('platform_editor_diff_plugin_extended', 'isEnabled', true) || pos + node.nodeSize <= to)) {
98
+ if (node.isBlock && (!isExtendedEnabled(diffType) || pos + node.nodeSize <= to)) {
56
99
  const nodeEnd = pos + node.nodeSize;
57
100
  const isActive = activeIndexPos && pos === activeIndexPos.from && nodeEnd === activeIndexPos.to;
58
101
  decorations.push(...createBlockChangedDecoration({
@@ -66,7 +109,8 @@ const calculateNodesForBlockDecoration = ({
66
109
  isActive,
67
110
  shouldHideDeleted,
68
111
  showIndicators,
69
- doc
112
+ doc,
113
+ diffType
70
114
  }));
71
115
  }
72
116
  });
@@ -83,7 +127,9 @@ const calculateDiffDecorationsInner = ({
83
127
  isInverted = false,
84
128
  diffType = 'inline',
85
129
  hideDeletedDiffs = false,
86
- showIndicators = false
130
+ showIndicators = false,
131
+ smartThresholds,
132
+ deletedDiffPlacement = 'top'
87
133
  }) => {
88
134
  const {
89
135
  originalDoc,
@@ -143,14 +189,16 @@ const calculateDiffDecorationsInner = ({
143
189
  steppedDoc,
144
190
  diffType,
145
191
  tr,
146
- steps
192
+ steps,
193
+ intl,
194
+ smartThresholds
147
195
  });
148
196
  const decorations = [];
149
197
 
150
198
  /**
151
199
  * If showIndicators is on, we create an anchor widget here to mark the doc margin.
152
200
  */
153
- if (showIndicators && expValEquals('platform_editor_diff_plugin_extended', 'isEnabled', true)) {
201
+ if (showIndicators && isExtendedEnabled(diffType)) {
154
202
  decorations.push(createDocMarginAnchorWidget());
155
203
  }
156
204
 
@@ -162,34 +210,68 @@ const calculateDiffDecorationsInner = ({
162
210
  // shouldHideDeleted for block/node decorations: suppressed when isInverted + hideDeletedDiffs,
163
211
  // or when showGranularWithBlock (block reference widget is shown instead).
164
212
  // isInverted gates both — on an inverted diff the inserted side is visually the deleted side.
165
- const shouldHideDeleted = expValEquals('platform_editor_diff_plugin_extended', 'isEnabled', true) ? isInverted && (hideDeletedDiffs || showGranularWithBlock) && change.deleted.length > 0 : false;
166
- decorations.push(...createInlineChangedDecoration({
167
- change,
168
- doc: tr.doc,
169
- colorScheme,
170
- isActive,
171
- ...(expValEquals('platform_editor_diff_plugin_extended', 'isEnabled', true) && {
172
- isInserted,
173
- shouldHideDeleted,
174
- showIndicators
175
- })
176
- }));
213
+ const shouldHideDeleted = isExtendedEnabled(diffType) ? isInverted && (hideDeletedDiffs || showGranularWithBlock) && change.deleted.length > 0 : false;
214
+
215
+ // For `smart` NODE-level promotions the change range spans a whole container
216
+ // (e.g. an entire list/table/layout, using outer node bounds). Applying a SINGLE
217
+ // inline decoration across that whole range would paint the inserted style across
218
+ // block boundaries and structural gaps (list markers, empty item slots), producing
219
+ // phantom "empty" rows above the real content. But skipping the inline highlight
220
+ // entirely leaves added text-bearing blocks (paragraphs/headings inside the added
221
+ // container) without the inserted background+underline, because block decorations
222
+ // return no style for paragraph/heading. So for node-level smart changes we instead
223
+ // emit ONE inline decoration per leaf text-bearing block within the range — the text
224
+ // gets highlighted, and structural gaps never do.
225
+ const isSmartNodeLevel = diffType === 'smart' && fg('platform_editor_ai_smart_diff') && smartChangeLevel(change) === 'node';
226
+ if (isSmartNodeLevel) {
227
+ for (const [from, to] of leafTextblockRanges(tr.doc, change.fromB, change.toB)) {
228
+ decorations.push(...createInlineChangedDecoration({
229
+ change: {
230
+ fromB: from,
231
+ toB: to
232
+ },
233
+ doc: tr.doc,
234
+ colorScheme,
235
+ isActive,
236
+ diffType,
237
+ ...(isExtendedEnabled(diffType) && {
238
+ isInserted,
239
+ shouldHideDeleted,
240
+ showIndicators
241
+ })
242
+ }));
243
+ }
244
+ } else {
245
+ decorations.push(...createInlineChangedDecoration({
246
+ change,
247
+ doc: tr.doc,
248
+ colorScheme,
249
+ isActive,
250
+ diffType,
251
+ ...(isExtendedEnabled(diffType) && {
252
+ isInserted,
253
+ shouldHideDeleted,
254
+ showIndicators
255
+ })
256
+ }));
257
+ }
177
258
  decorations.push(...calculateNodesForBlockDecoration({
178
259
  doc: tr.doc,
179
260
  from: change.fromB,
180
261
  to: change.toB,
181
262
  colorScheme,
182
- ...(expValEquals('platform_editor_diff_plugin_extended', 'isEnabled', true) && {
263
+ ...(isExtendedEnabled(diffType) && {
183
264
  isInserted,
184
265
  shouldHideDeleted,
185
266
  showIndicators
186
267
  }),
187
268
  activeIndexPos,
188
- intl
269
+ intl,
270
+ diffType
189
271
  }));
190
272
  }
191
273
  if (change.deleted.length > 0) {
192
- const shouldHideDeleted = expValEquals('platform_editor_diff_plugin_extended', 'isEnabled', true) ? !isInverted && (hideDeletedDiffs || showGranularWithBlock) && change.inserted.length > 0 : false;
274
+ const shouldHideDeleted = isExtendedEnabled(diffType) ? !isInverted && (hideDeletedDiffs || showGranularWithBlock) && change.inserted.length > 0 : false;
193
275
  if (!shouldHideDeleted) {
194
276
  decorations.push(...createNodeChangedDecorationWidget({
195
277
  change,
@@ -199,9 +281,13 @@ const calculateDiffDecorationsInner = ({
199
281
  newDoc: tr.doc,
200
282
  intl,
201
283
  activeIndexPos,
202
- ...(expValEquals('platform_editor_diff_plugin_extended', 'isEnabled', true) && {
284
+ ...(isExtendedEnabled(diffType) && {
203
285
  isInserted: !isInserted,
204
- diffType
286
+ diffType,
287
+ // For `smart` node- and paragraph-level changes, optionally render the
288
+ // deleted content below the new content (gray + strikethrough) instead
289
+ // of above it. Controlled by `deletedDiffPlacement` (default `'top'`).
290
+ placeBelow: deletedDiffPlacement === 'bottom' && diffType === 'smart' && fg('platform_editor_ai_smart_diff') && (smartChangeLevel(change) === 'node' || smartChangeLevel(change) === 'paragraph')
205
291
  }),
206
292
  showIndicators
207
293
  }));
@@ -336,7 +422,9 @@ export const calculateDiffDecorations = memoizeOne(calculateDiffDecorationsInner
336
422
  isInverted,
337
423
  diffType,
338
424
  hideDeletedDiffs,
339
- showIndicators
425
+ showIndicators,
426
+ smartThresholds,
427
+ deletedDiffPlacement
340
428
  }], [{
341
429
  pluginState: lastPluginState,
342
430
  state: lastState,
@@ -346,13 +434,15 @@ export const calculateDiffDecorations = memoizeOne(calculateDiffDecorationsInner
346
434
  isInverted: lastIsInverted,
347
435
  diffType: lastDiffType,
348
436
  hideDeletedDiffs: lastHideDeletedDiffs,
349
- showIndicators: lastShowIndicators
437
+ showIndicators: lastShowIndicators,
438
+ smartThresholds: lastSmartThresholds,
439
+ deletedDiffPlacement: lastDeletedDiffPlacement
350
440
  }]) => {
351
441
  var _ref2;
352
442
  const originalDocIsSame = lastPluginState.originalDoc && pluginState.originalDoc && pluginState.originalDoc.eq(lastPluginState.originalDoc);
353
- if (expValEquals('platform_editor_diff_plugin_extended', 'isEnabled', true)) {
443
+ if (isExtendedEnabled(diffType)) {
354
444
  var _ref;
355
- return (_ref = colorScheme === lastColorScheme && intl.locale === lastIntl.locale && isInverted === lastIsInverted && diffType === lastDiffType && isEqual(activeIndexPos, lastActiveIndexPos) && originalDocIsSame && isEqual(pluginState.steps, lastPluginState.steps) && state.doc.eq(lastState.doc) && hideDeletedDiffs === lastHideDeletedDiffs && showIndicators === lastShowIndicators) !== null && _ref !== void 0 ? _ref : false;
445
+ return (_ref = colorScheme === lastColorScheme && intl.locale === lastIntl.locale && isInverted === lastIsInverted && diffType === lastDiffType && isEqual(activeIndexPos, lastActiveIndexPos) && originalDocIsSame && isEqual(pluginState.steps, lastPluginState.steps) && state.doc.eq(lastState.doc) && hideDeletedDiffs === lastHideDeletedDiffs && showIndicators === lastShowIndicators && isEqual(smartThresholds, lastSmartThresholds) && deletedDiffPlacement === lastDeletedDiffPlacement) !== null && _ref !== void 0 ? _ref : false;
356
446
  }
357
447
  return (_ref2 = originalDocIsSame && isEqual(pluginState.steps, lastPluginState.steps) && state.doc.eq(lastState.doc) && colorScheme === lastColorScheme && intl.locale === lastIntl.locale && isEqual(activeIndexPos, lastActiveIndexPos) && hideDeletedDiffs === lastHideDeletedDiffs) !== null && _ref2 !== void 0 ? _ref2 : false;
358
448
  });
@@ -1,8 +1,9 @@
1
1
  /**
2
- * Finds the position range of the top-level block (direct child of doc) that contains `pos`.
3
- * Returns null if `pos` is at the doc boundary or outside the doc content.
2
+ * Finds the position range (outer bounds) of the top-level block (direct child of doc) that
3
+ * contains the range [from, to]. Shared with the `smart` diff type so its block grouping is
4
+ * identical to the `block` diff type by construction.
4
5
  */
5
- function getTopLevelBlockAt(doc, from, to) {
6
+ export function getTopLevelBlockAt(doc, from, to) {
6
7
  return {
7
8
  from: doc.resolve(from).before(1),
8
9
  to: doc.resolve(to).after(1)