@atlaskit/editor-plugin-show-diff 12.0.3 → 12.1.1

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 (26) hide show
  1. package/CHANGELOG.md +48 -0
  2. package/dist/cjs/pm-plugins/calculateDiff/calculateDiffDecorations.js +104 -77
  3. package/dist/cjs/pm-plugins/decorations/createInlineChangedDecoration.js +14 -3
  4. package/dist/cjs/pm-plugins/decorations/utils/getAttrChangeRanges.js +10 -3
  5. package/dist/cjs/pm-plugins/getDeletedWidgets.js +48 -0
  6. package/dist/cjs/pm-plugins/main.js +3 -1
  7. package/dist/cjs/showDiffPlugin.js +13 -1
  8. package/dist/es2019/pm-plugins/calculateDiff/calculateDiffDecorations.js +41 -17
  9. package/dist/es2019/pm-plugins/decorations/createInlineChangedDecoration.js +13 -3
  10. package/dist/es2019/pm-plugins/decorations/utils/getAttrChangeRanges.js +4 -3
  11. package/dist/es2019/pm-plugins/getDeletedWidgets.js +40 -0
  12. package/dist/es2019/pm-plugins/main.js +3 -1
  13. package/dist/es2019/showDiffPlugin.js +86 -74
  14. package/dist/esm/pm-plugins/calculateDiff/calculateDiffDecorations.js +103 -76
  15. package/dist/esm/pm-plugins/decorations/createInlineChangedDecoration.js +14 -3
  16. package/dist/esm/pm-plugins/decorations/utils/getAttrChangeRanges.js +10 -3
  17. package/dist/esm/pm-plugins/getDeletedWidgets.js +43 -0
  18. package/dist/esm/pm-plugins/main.js +3 -1
  19. package/dist/esm/showDiffPlugin.js +13 -1
  20. package/dist/types/entry-points/show-diff-plugin-type.d.ts +1 -1
  21. package/dist/types/pm-plugins/calculateDiff/calculateDiffDecorations.d.ts +17 -0
  22. package/dist/types/pm-plugins/decorations/createInlineChangedDecoration.d.ts +2 -1
  23. package/dist/types/pm-plugins/getDeletedWidgets.d.ts +14 -0
  24. package/dist/types/pm-plugins/main.d.ts +2 -1
  25. package/dist/types/showDiffPluginType.d.ts +23 -0
  26. package/package.json +7 -7
@@ -37,7 +37,8 @@ export const createInlineChangedDecoration = ({
37
37
  diffType,
38
38
  hideAddedDiffsUnderline = false,
39
39
  inlineNodeName,
40
- hasDeletedWidget = false
40
+ hasDeletedWidget = false,
41
+ isDeletedWidgetBelow = false
41
42
  }) => {
42
43
  const diffId = crypto.randomUUID();
43
44
  if (shouldHideDeleted) {
@@ -94,14 +95,23 @@ export const createInlineChangedDecoration = ({
94
95
  // bar doesn't extend into the preceding block's margin.
95
96
  // Skip when the deleted-content widget is rendered — the anchor must
96
97
  // stay at the boundary to keep the indicator continuous.
98
+ //
99
+ // The widget only pins the end of the range it is anchored to. It sits at `change.fromB`
100
+ // by default, but `deletedDiffPlacement: 'bottom'` / `inlineDeletedDiffPlacement: 'after'`
101
+ // move it to `change.toB` so the deleted content reads after the added content. In that
102
+ // case the `from` anchor has no widget beside it any more and must be adjusted inward
103
+ // again, otherwise the over-long indicator bar that CCI-18224 fixed comes back.
97
104
  let anchorFrom = change.fromB;
98
105
  let anchorTo = change.toB;
99
- if (!hasDeletedWidget) {
100
- var _$from$nodeAfter, _$to$nodeBefore;
106
+ if (!hasDeletedWidget || isDeletedWidgetBelow) {
107
+ var _$from$nodeAfter;
101
108
  const $from = doc.resolve(anchorFrom);
102
109
  if (!$from.parent.inlineContent && (_$from$nodeAfter = $from.nodeAfter) !== null && _$from$nodeAfter !== void 0 && _$from$nodeAfter.isTextblock) {
103
110
  anchorFrom = anchorFrom + 1;
104
111
  }
112
+ }
113
+ if (!hasDeletedWidget) {
114
+ var _$to$nodeBefore;
105
115
  const $to = doc.resolve(anchorTo);
106
116
  if (!$to.parent.inlineContent && (_$to$nodeBefore = $to.nodeBefore) !== null && _$to$nodeBefore !== void 0 && _$to$nodeBefore.isTextblock) {
107
117
  anchorTo = anchorTo - 1;
@@ -1,4 +1,5 @@
1
1
  import { SetAttrsStep } from '@atlaskit/adf-schema/steps';
2
+ import { isExperimentEnabled } from '@atlaskit/editor-common/deprecated-platform-feature-experiments';
2
3
  import { AttrStep } from '@atlaskit/editor-prosemirror/transform';
3
4
  import { expValEquals } from '@atlaskit/tmp-editor-statsig/exp-val-equals';
4
5
  const filterUndefined = x => !!x;
@@ -54,7 +55,7 @@ export const getAttrChangeRanges = (doc, steps, originalDoc) => {
54
55
  const nodeAtPos = doc.nodeAt(step.pos);
55
56
 
56
57
  // date node: timestamp attribute change — highlight the date node itself (inline)
57
- if (stepAttrs.some(v => dateAttrs.includes(v)) && (nodeAtPos === null || nodeAtPos === void 0 ? void 0 : nodeAtPos.type.name) === 'date' && !expValEquals('platform_editor_improve_inline_diffs', 'isEnabled', true)) {
58
+ if (stepAttrs.some(v => dateAttrs.includes(v)) && (nodeAtPos === null || nodeAtPos === void 0 ? void 0 : nodeAtPos.type.name) === 'date' && !isExperimentEnabled('platform_editor_improve_inline_diffs', () => expValEquals('platform_editor_improve_inline_diffs', 'isEnabled', true))) {
58
59
  return {
59
60
  fromB: step.pos,
60
61
  toB: step.pos + nodeAtPos.nodeSize,
@@ -66,7 +67,7 @@ export const getAttrChangeRanges = (doc, steps, originalDoc) => {
66
67
  // The following inline node attr changes are gated behind platform_editor_improve_inline_diffs.
67
68
  // The changeset path (createDecorationsForChange) handles the deletion widget via
68
69
  // prosemirror-changeset; we only need to add the inline insertion highlight here.
69
- if (expValEquals('platform_editor_improve_inline_diffs', 'isEnabled', true) && nodeAtPos) {
70
+ if (isExperimentEnabled('platform_editor_improve_inline_diffs', () => expValEquals('platform_editor_improve_inline_diffs', 'isEnabled', true)) && nodeAtPos) {
70
71
  const nodeName = nodeAtPos.type.name;
71
72
  if (isInlineAttrChangeNodeName(nodeName)) {
72
73
  const watchedAttrs = inlineNodeAttrMap[nodeName];
@@ -119,7 +120,7 @@ export const getAttrChangeRanges = (doc, steps, originalDoc) => {
119
120
  // should produce only one decoration, not one per step.
120
121
  // Gated behind the same experiment as the inline node attr changes that introduced
121
122
  // the possibility of multiple ranges for the same node position.
122
- .filter((range, i, arr) => !expValEquals('platform_editor_improve_inline_diffs', 'isEnabled', true) || arr.findIndex(r => r.fromB === range.fromB) === i);
123
+ .filter((range, i, arr) => !isExperimentEnabled('platform_editor_improve_inline_diffs', () => expValEquals('platform_editor_improve_inline_diffs', 'isEnabled', true)) || arr.findIndex(r => r.fromB === range.fromB) === i);
123
124
  };
124
125
 
125
126
  /**
@@ -0,0 +1,40 @@
1
+ import { isDiffDecorationSpec } from './decorations/decorationKeys';
2
+ import { showDiffPluginKey } from './main';
3
+
4
+ // prosemirror-view does not type `Decoration.type`; a widget's `type.toDOM` is the node passed to
5
+ // `Decoration.widget()` (an element or a factory — the `WidgetConstructor` union).
6
+
7
+ // The element a deleted-content widget renders, or undefined if it renders via a factory.
8
+ const getWidgetElement = decoration => {
9
+ var _type;
10
+ const {
11
+ toDOM
12
+ } = (_type = decoration.type) !== null && _type !== void 0 ? _type : {};
13
+ return toDOM instanceof HTMLElement ? toDOM : undefined;
14
+ };
15
+
16
+ /**
17
+ * The rendered deleted-content widgets in `editorState`, optionally restricted to `[from, to]`,
18
+ * ordered by position. Deleted content is rendered as widget decorations rather than document
19
+ * nodes, so this is how the element and the position it is anchored at are recovered.
20
+ *
21
+ * Kept internal and surfaced only through the `getDeletedWidgets` plugin action, so consumers never
22
+ * receive the plugin key or the decoration set (which would let them mutate plugin behaviour).
23
+ */
24
+ export const getDeletedWidgets = (editorState, range) => {
25
+ var _showDiffPluginKey$ge;
26
+ const decorations = (_showDiffPluginKey$ge = showDiffPluginKey.getState(editorState)) === null || _showDiffPluginKey$ge === void 0 ? void 0 : _showDiffPluginKey$ge.decorations;
27
+ if (!decorations) {
28
+ return [];
29
+ }
30
+ return decorations.find(range === null || range === void 0 ? void 0 : range.from, range === null || range === void 0 ? void 0 : range.to, spec => isDiffDecorationSpec(spec) && spec.decorationType === 'widget').reduce((widgets, decoration) => {
31
+ const element = getWidgetElement(decoration);
32
+ if (element) {
33
+ widgets.push({
34
+ element,
35
+ position: decoration.from
36
+ });
37
+ }
38
+ return widgets;
39
+ }, []).sort((a, b) => a.position - b.position);
40
+ };
@@ -11,7 +11,7 @@ import { isExtendedEnabled } from './isExtendedEnabled';
11
11
  import { NodeViewSerializer } from './NodeViewSerializer';
12
12
  import { scrollToActiveDecoration, scrollToFirstDecoration } from './scrollToDiff';
13
13
  export const showDiffPluginKey = new PluginKey('showDiffPlugin');
14
- export const createPlugin = (config, getIntl, api) => {
14
+ export const createPlugin = (config, getIntl, api, onEditorView) => {
15
15
  enforceCustomStepRegisters();
16
16
  const nodeViewSerializer = new NodeViewSerializer({});
17
17
  const setNodeViewSerializer = editorView => {
@@ -171,6 +171,7 @@ export const createPlugin = (config, getIntl, api) => {
171
171
  },
172
172
  view(editorView) {
173
173
  setNodeViewSerializer(editorView);
174
+ onEditorView === null || onEditorView === void 0 ? void 0 : onEditorView(editorView);
174
175
  let isFirst = true;
175
176
  let previousActiveIndex;
176
177
  let cancelPendingScrollToDecoration = null;
@@ -221,6 +222,7 @@ export const createPlugin = (config, getIntl, api) => {
221
222
  var _cancelPendingScrollT3;
222
223
  (_cancelPendingScrollT3 = cancelPendingScrollToDecoration) === null || _cancelPendingScrollT3 === void 0 ? void 0 : _cancelPendingScrollT3();
223
224
  cancelPendingScrollToDecoration = null;
225
+ onEditorView === null || onEditorView === void 0 ? void 0 : onEditorView(undefined);
224
226
  }
225
227
  };
226
228
  },
@@ -1,93 +1,105 @@
1
1
  import React from 'react';
2
2
  import { expValEquals } from '@atlaskit/tmp-editor-statsig/exp-val-equals';
3
+ import { getDeletedWidgets } from './pm-plugins/getDeletedWidgets';
3
4
  import { getScrollableDecorations } from './pm-plugins/getScrollableDecorations';
4
5
  import { createPlugin, showDiffPluginKey } from './pm-plugins/main';
5
6
  import { IndicatorBarContentComponent } from './ui/IndicatorBar/IndicatorBarContentComponent';
6
7
  export const showDiffPlugin = ({
7
8
  api,
8
9
  config
9
- }) => ({
10
- name: 'showDiff',
11
- commands: {
12
- showDiff: params => ({
13
- tr
14
- }) => {
15
- if (expValEquals('platform_editor_diff_plugin_extended', 'isEnabled', true)) {
16
- var _api$userIntent;
17
- api === null || api === void 0 ? void 0 : (_api$userIntent = api.userIntent) === null || _api$userIntent === void 0 ? void 0 : _api$userIntent.commands.setCurrentUserIntent('viewingDiff')({
18
- tr
19
- });
20
- }
21
- return tr.setMeta(showDiffPluginKey, {
22
- ...params,
23
- action: 'SHOW_DIFF'
24
- });
10
+ }) => {
11
+ // Captured from the plugin's view lifecycle (see `createPlugin`) so read-only actions can access
12
+ // the current state without exposing the plugin key to consumers.
13
+ let editorView;
14
+ const setEditorView = view => {
15
+ editorView = view;
16
+ };
17
+ return {
18
+ name: 'showDiff',
19
+ actions: {
20
+ getDeletedWidgets: range => editorView ? getDeletedWidgets(editorView.state, range) : []
25
21
  },
26
- hideDiff: ({
27
- tr
28
- }) => {
29
- if (expValEquals('platform_editor_diff_plugin_extended', 'isEnabled', true)) {
30
- var _api$userIntent2;
31
- api === null || api === void 0 ? void 0 : (_api$userIntent2 = api.userIntent) === null || _api$userIntent2 === void 0 ? void 0 : _api$userIntent2.commands.setCurrentUserIntent('default')({
32
- tr
22
+ commands: {
23
+ showDiff: params => ({
24
+ tr
25
+ }) => {
26
+ if (expValEquals('platform_editor_diff_plugin_extended', 'isEnabled', true)) {
27
+ var _api$userIntent;
28
+ api === null || api === void 0 ? void 0 : (_api$userIntent = api.userIntent) === null || _api$userIntent === void 0 ? void 0 : _api$userIntent.commands.setCurrentUserIntent('viewingDiff')({
29
+ tr
30
+ });
31
+ }
32
+ return tr.setMeta(showDiffPluginKey, {
33
+ ...params,
34
+ action: 'SHOW_DIFF'
35
+ });
36
+ },
37
+ hideDiff: ({
38
+ tr
39
+ }) => {
40
+ if (expValEquals('platform_editor_diff_plugin_extended', 'isEnabled', true)) {
41
+ var _api$userIntent2;
42
+ api === null || api === void 0 ? void 0 : (_api$userIntent2 = api.userIntent) === null || _api$userIntent2 === void 0 ? void 0 : _api$userIntent2.commands.setCurrentUserIntent('default')({
43
+ tr
44
+ });
45
+ }
46
+ return tr.setMeta(showDiffPluginKey, {
47
+ steps: [],
48
+ action: 'HIDE_DIFF'
49
+ });
50
+ },
51
+ scrollToNext: ({
52
+ tr
53
+ }) => {
54
+ return tr.setMeta(showDiffPluginKey, {
55
+ action: 'SCROLL_TO_NEXT'
56
+ });
57
+ },
58
+ scrollToPrevious: ({
59
+ tr
60
+ }) => {
61
+ return tr.setMeta(showDiffPluginKey, {
62
+ action: 'SCROLL_TO_PREVIOUS'
33
63
  });
34
64
  }
35
- return tr.setMeta(showDiffPluginKey, {
36
- steps: [],
37
- action: 'HIDE_DIFF'
38
- });
39
65
  },
40
- scrollToNext: ({
41
- tr
42
- }) => {
43
- return tr.setMeta(showDiffPluginKey, {
44
- action: 'SCROLL_TO_NEXT'
45
- });
66
+ pmPlugins() {
67
+ return [{
68
+ name: 'showDiffPlugin',
69
+ plugin: ({
70
+ getIntl
71
+ }) => createPlugin(config, getIntl, api, setEditorView)
72
+ }];
46
73
  },
47
- scrollToPrevious: ({
48
- tr
49
- }) => {
50
- return tr.setMeta(showDiffPluginKey, {
51
- action: 'SCROLL_TO_PREVIOUS'
74
+ contentComponent: () => {
75
+ if (!expValEquals('platform_editor_diff_plugin_extended', 'isEnabled', true)) {
76
+ return null;
77
+ }
78
+ return /*#__PURE__*/React.createElement(IndicatorBarContentComponent, {
79
+ api: api
52
80
  });
53
- }
54
- },
55
- pmPlugins() {
56
- return [{
57
- name: 'showDiffPlugin',
58
- plugin: ({
59
- getIntl
60
- }) => createPlugin(config, getIntl, api)
61
- }];
62
- },
63
- contentComponent: () => {
64
- if (!expValEquals('platform_editor_diff_plugin_extended', 'isEnabled', true)) {
65
- return null;
66
- }
67
- return /*#__PURE__*/React.createElement(IndicatorBarContentComponent, {
68
- api: api
69
- });
70
- },
71
- getSharedState: editorState => {
72
- if (!editorState) {
81
+ },
82
+ getSharedState: editorState => {
83
+ if (!editorState) {
84
+ return {
85
+ isDisplayingChanges: false,
86
+ activeIndex: undefined,
87
+ ...(expValEquals('platform_editor_diff_plugin_extended', 'isEnabled', true) ? {
88
+ diffDescriptors: []
89
+ } : {})
90
+ };
91
+ }
92
+ const pluginState = showDiffPluginKey.getState(editorState);
93
+ const decorationCount = getScrollableDecorations(pluginState === null || pluginState === void 0 ? void 0 : pluginState.decorations, editorState.doc);
73
94
  return {
74
- isDisplayingChanges: false,
75
- activeIndex: undefined,
95
+ isDisplayingChanges: decorationCount.length > 0,
96
+ activeIndex: pluginState === null || pluginState === void 0 ? void 0 : pluginState.activeIndex,
97
+ numberOfChanges: decorationCount.length,
76
98
  ...(expValEquals('platform_editor_diff_plugin_extended', 'isEnabled', true) ? {
77
- diffDescriptors: []
99
+ diffDescriptors: pluginState === null || pluginState === void 0 ? void 0 : pluginState.diffDescriptors,
100
+ showIndicators: pluginState === null || pluginState === void 0 ? void 0 : pluginState.showIndicators
78
101
  } : {})
79
102
  };
80
103
  }
81
- const pluginState = showDiffPluginKey.getState(editorState);
82
- const decorationCount = getScrollableDecorations(pluginState === null || pluginState === void 0 ? void 0 : pluginState.decorations, editorState.doc);
83
- return {
84
- isDisplayingChanges: decorationCount.length > 0,
85
- activeIndex: pluginState === null || pluginState === void 0 ? void 0 : pluginState.activeIndex,
86
- numberOfChanges: decorationCount.length,
87
- ...(expValEquals('platform_editor_diff_plugin_extended', 'isEnabled', true) ? {
88
- diffDescriptors: pluginState === null || pluginState === void 0 ? void 0 : pluginState.diffDescriptors,
89
- showIndicators: pluginState === null || pluginState === void 0 ? void 0 : pluginState.showIndicators
90
- } : {})
91
- };
92
- }
93
- });
104
+ };
105
+ };
@@ -10,6 +10,7 @@ function _arrayLikeToArray(r, a) { (null == a || a > r.length) && (a = r.length)
10
10
  import isEqual from 'lodash/isEqual';
11
11
  import memoizeOne from 'memoize-one';
12
12
  import { ChangeSet, simplifyChanges } from 'prosemirror-changeset';
13
+ import { isExperimentEnabled } from '@atlaskit/editor-common/deprecated-platform-feature-experiments';
13
14
  import { areNodesEqualIgnoreAttrs } from '@atlaskit/editor-common/utils/document';
14
15
  import { DecorationSet } from '@atlaskit/editor-prosemirror/view';
15
16
  import { fg } from '@atlaskit/platform-feature-flags';
@@ -125,29 +126,52 @@ var calculateNodesForBlockDecoration = function calculateNodesForBlockDecoration
125
126
  });
126
127
  return decorations;
127
128
  };
128
- var calculateDiffDecorationsInner = function calculateDiffDecorationsInner(_ref3) {
129
- var state = _ref3.state,
130
- pluginState = _ref3.pluginState,
131
- nodeViewSerializer = _ref3.nodeViewSerializer,
132
- colorScheme = _ref3.colorScheme,
133
- intl = _ref3.intl,
134
- activeIndexPos = _ref3.activeIndexPos,
135
- api = _ref3.api,
136
- _ref3$isInverted = _ref3.isInverted,
137
- isInverted = _ref3$isInverted === void 0 ? false : _ref3$isInverted,
138
- _ref3$diffType = _ref3.diffType,
139
- diffType = _ref3$diffType === void 0 ? 'inline' : _ref3$diffType,
140
- _ref3$hideDeletedDiff = _ref3.hideDeletedDiffs,
141
- hideDeletedDiffs = _ref3$hideDeletedDiff === void 0 ? false : _ref3$hideDeletedDiff,
142
- _ref3$hideAddedDiffsU = _ref3.hideAddedDiffsUnderline,
143
- hideAddedDiffsUnderlineParam = _ref3$hideAddedDiffsU === void 0 ? false : _ref3$hideAddedDiffsU,
144
- _ref3$showIndicators = _ref3.showIndicators,
145
- showIndicators = _ref3$showIndicators === void 0 ? false : _ref3$showIndicators,
146
- smartThresholds = _ref3.smartThresholds,
147
- _ref3$deletedDiffPlac = _ref3.deletedDiffPlacement,
148
- deletedDiffPlacement = _ref3$deletedDiffPlac === void 0 ? 'top' : _ref3$deletedDiffPlac,
149
- _ref3$inlineDeletedDi = _ref3.inlineDeletedDiffPlacement,
150
- inlineDeletedDiffPlacement = _ref3$inlineDeletedDi === void 0 ? 'before' : _ref3$inlineDeletedDi;
129
+ /**
130
+ * Whether deleted content for `change` renders after the new content instead of before it.
131
+ *
132
+ * show-diff splits this across two options by change granularity, so the level decides which one
133
+ * applies:
134
+ * - node/paragraph-level: `deletedDiffPlacement` (default `'top'`). Pure deletions are promoted to
135
+ * node level, so they are governed here too.
136
+ * - inline/sentence-level: `inlineDeletedDiffPlacement` (default `'before'`).
137
+ *
138
+ * Callers are responsible for the `smart` diffType and gate checks.
139
+ */
140
+ export var isDeletedContentPlacedBelow = function isDeletedContentPlacedBelow(_ref3) {
141
+ var change = _ref3.change,
142
+ deletedDiffPlacement = _ref3.deletedDiffPlacement,
143
+ inlineDeletedDiffPlacement = _ref3.inlineDeletedDiffPlacement;
144
+ var level = smartChangeLevel(change);
145
+ if (level === 'node' || level === 'paragraph') {
146
+ return deletedDiffPlacement === 'bottom';
147
+ }
148
+
149
+ // Inline-level (undefined) and sentence-level changes.
150
+ return inlineDeletedDiffPlacement === 'after';
151
+ };
152
+ var calculateDiffDecorationsInner = function calculateDiffDecorationsInner(_ref4) {
153
+ var state = _ref4.state,
154
+ pluginState = _ref4.pluginState,
155
+ nodeViewSerializer = _ref4.nodeViewSerializer,
156
+ colorScheme = _ref4.colorScheme,
157
+ intl = _ref4.intl,
158
+ activeIndexPos = _ref4.activeIndexPos,
159
+ api = _ref4.api,
160
+ _ref4$isInverted = _ref4.isInverted,
161
+ isInverted = _ref4$isInverted === void 0 ? false : _ref4$isInverted,
162
+ _ref4$diffType = _ref4.diffType,
163
+ diffType = _ref4$diffType === void 0 ? 'inline' : _ref4$diffType,
164
+ _ref4$hideDeletedDiff = _ref4.hideDeletedDiffs,
165
+ hideDeletedDiffs = _ref4$hideDeletedDiff === void 0 ? false : _ref4$hideDeletedDiff,
166
+ _ref4$hideAddedDiffsU = _ref4.hideAddedDiffsUnderline,
167
+ hideAddedDiffsUnderlineParam = _ref4$hideAddedDiffsU === void 0 ? false : _ref4$hideAddedDiffsU,
168
+ _ref4$showIndicators = _ref4.showIndicators,
169
+ showIndicators = _ref4$showIndicators === void 0 ? false : _ref4$showIndicators,
170
+ smartThresholds = _ref4.smartThresholds,
171
+ _ref4$deletedDiffPlac = _ref4.deletedDiffPlacement,
172
+ deletedDiffPlacement = _ref4$deletedDiffPlac === void 0 ? 'top' : _ref4$deletedDiffPlac,
173
+ _ref4$inlineDeletedDi = _ref4.inlineDeletedDiffPlacement,
174
+ inlineDeletedDiffPlacement = _ref4$inlineDeletedDi === void 0 ? 'before' : _ref4$inlineDeletedDi;
151
175
  var originalDoc = pluginState.originalDoc,
152
176
  steps = pluginState.steps,
153
177
  isDisplayingChanges = pluginState.isDisplayingChanges;
@@ -232,6 +256,15 @@ var calculateDiffDecorationsInner = function calculateDiffDecorationsInner(_ref3
232
256
  var isInserted = !isInverted;
233
257
  var createDecorationsForChange = function createDecorationsForChange(change, showGranularWithBlock) {
234
258
  var isActive = activeIndexPos && change.fromB === activeIndexPos.from && change.toB === activeIndexPos.to;
259
+
260
+ // Hoisted because it decides BOTH where the deleted widget is anchored and — since the
261
+ // widget pins whichever end of the range it sits at — how the indicator anchors below are
262
+ // allowed to move.
263
+ var isDeletedWidgetBelow = diffType === 'smart' && fg('platform_editor_ai_smart_diff') && isDeletedContentPlacedBelow({
264
+ change: change,
265
+ deletedDiffPlacement: deletedDiffPlacement,
266
+ inlineDeletedDiffPlacement: inlineDeletedDiffPlacement
267
+ });
235
268
  if (change.inserted.length > 0) {
236
269
  // shouldHideDeleted for block/node decorations: suppressed when isInverted + hideDeletedDiffs,
237
270
  // or when showGranularWithBlock (block reference widget is shown instead).
@@ -276,7 +309,8 @@ var calculateDiffDecorationsInner = function calculateDiffDecorationsInner(_ref3
276
309
  shouldHideDeleted: shouldHideDeleted,
277
310
  showIndicators: showIndicators,
278
311
  hideAddedDiffsUnderline: hideAddedDiffsUnderline,
279
- hasDeletedWidget: willRenderDeletedWidget
312
+ hasDeletedWidget: willRenderDeletedWidget,
313
+ isDeletedWidgetBelow: isDeletedWidgetBelow
280
314
  }))));
281
315
  }
282
316
  } catch (err) {
@@ -296,7 +330,8 @@ var calculateDiffDecorationsInner = function calculateDiffDecorationsInner(_ref3
296
330
  shouldHideDeleted: shouldHideDeleted,
297
331
  showIndicators: showIndicators,
298
332
  hideAddedDiffsUnderline: hideAddedDiffsUnderline,
299
- hasDeletedWidget: willRenderDeletedWidget
333
+ hasDeletedWidget: willRenderDeletedWidget,
334
+ isDeletedWidgetBelow: isDeletedWidgetBelow
300
335
  }))));
301
336
  }
302
337
  decorations.push.apply(decorations, _toConsumableArray(calculateNodesForBlockDecoration(_objectSpread(_objectSpread({
@@ -329,19 +364,7 @@ var calculateDiffDecorationsInner = function calculateDiffDecorationsInner(_ref3
329
364
  isInserted: !isInserted,
330
365
  diffType: diffType,
331
366
  hideAddedDiffsUnderline: hideAddedDiffsUnderline,
332
- // For `smart` changes, optionally render the deleted content after the new
333
- // content (gray + strikethrough) instead of before it. Two independent
334
- // controls, split by change level:
335
- // - node/paragraph-level: `deletedDiffPlacement` (default `'top'`).
336
- // - inline/sentence-level: `inlineDeletedDiffPlacement` (default `'before'`).
337
- placeBelow: diffType === 'smart' && fg('platform_editor_ai_smart_diff') && function () {
338
- var level = smartChangeLevel(change);
339
- if (level === 'node' || level === 'paragraph') {
340
- return deletedDiffPlacement === 'bottom';
341
- }
342
- // Inline-level (undefined) and sentence-level changes.
343
- return inlineDeletedDiffPlacement === 'after';
344
- }()
367
+ placeBelow: isDeletedWidgetBelow
345
368
  }), {}, {
346
369
  showIndicators: showIndicators
347
370
  }))));
@@ -355,9 +378,9 @@ var calculateDiffDecorationsInner = function calculateDiffDecorationsInner(_ref3
355
378
  // getChanges returns a flat Change[] with no per-step grouping, making this count impossible
356
379
  // to derive after the fact without re-introducing per-change metadata.
357
380
  var stepChanges = getStepChanges(originalDoc, steps);
358
- stepChanges.forEach(function (_ref4) {
359
- var isGranular = _ref4.isGranular,
360
- stepChangeList = _ref4.changes;
381
+ stepChanges.forEach(function (_ref5) {
382
+ var isGranular = _ref5.isGranular,
383
+ stepChangeList = _ref5.changes;
361
384
  var granularCount = isGranular ? stepChangeList.length : 0;
362
385
 
363
386
  // Calculate the average ratio of changed content on both A (original) and B (new)
@@ -443,7 +466,9 @@ var calculateDiffDecorationsInner = function calculateDiffDecorationsInner(_ref3
443
466
  if (change.isInline) {
444
467
  // Inline nodes (e.g. date, emoji, mention, status) need an inline decoration rather than a block decoration
445
468
  var isActive = activeIndexPos && change.fromB === activeIndexPos.from && change.toB === activeIndexPos.to;
446
- var isAtomicInlineNodeFlag = expValEquals('platform_editor_improve_inline_diffs', 'isEnabled', true);
469
+ var isAtomicInlineNodeFlag = isExperimentEnabled('platform_editor_improve_inline_diffs', function () {
470
+ return expValEquals('platform_editor_improve_inline_diffs', 'isEnabled', true);
471
+ });
447
472
  decorations.push.apply(decorations, _toConsumableArray(createInlineChangedDecoration({
448
473
  change: change,
449
474
  colorScheme: colorScheme,
@@ -458,7 +483,9 @@ var calculateDiffDecorationsInner = function calculateDiffDecorationsInner(_ref3
458
483
  })));
459
484
  // If we have the original node position, also render the old node as a "deleted" widget
460
485
  // so the user can see what it looked like before the change.
461
- if (change.fromA !== undefined && change.toA !== undefined && expValEquals('platform_editor_improve_inline_diffs', 'isEnabled', true)) {
486
+ if (change.fromA !== undefined && change.toA !== undefined && isExperimentEnabled('platform_editor_improve_inline_diffs', function () {
487
+ return expValEquals('platform_editor_improve_inline_diffs', 'isEnabled', true);
488
+ })) {
462
489
  decorations.push.apply(decorations, _toConsumableArray(createNodeChangedDecorationWidget({
463
490
  change: {
464
491
  fromA: change.fromA,
@@ -497,42 +524,42 @@ var calculateDiffDecorationsInner = function calculateDiffDecorationsInner(_ref3
497
524
  };
498
525
  export var calculateDiffDecorations = memoizeOne(calculateDiffDecorationsInner,
499
526
  // Cache results unless relevant inputs change
500
- function (_ref5, _ref6) {
501
- var _ref0;
502
- var _ref7 = _slicedToArray(_ref5, 1),
503
- _ref7$ = _ref7[0],
504
- pluginState = _ref7$.pluginState,
505
- state = _ref7$.state,
506
- colorScheme = _ref7$.colorScheme,
507
- intl = _ref7$.intl,
508
- activeIndexPos = _ref7$.activeIndexPos,
509
- isInverted = _ref7$.isInverted,
510
- diffType = _ref7$.diffType,
511
- hideDeletedDiffs = _ref7$.hideDeletedDiffs,
512
- hideAddedDiffsUnderline = _ref7$.hideAddedDiffsUnderline,
513
- showIndicators = _ref7$.showIndicators,
514
- smartThresholds = _ref7$.smartThresholds,
515
- deletedDiffPlacement = _ref7$.deletedDiffPlacement,
516
- inlineDeletedDiffPlacement = _ref7$.inlineDeletedDiffPlacement;
527
+ function (_ref6, _ref7) {
528
+ var _ref1;
517
529
  var _ref8 = _slicedToArray(_ref6, 1),
518
530
  _ref8$ = _ref8[0],
519
- lastPluginState = _ref8$.pluginState,
520
- lastState = _ref8$.state,
521
- lastColorScheme = _ref8$.colorScheme,
522
- lastIntl = _ref8$.intl,
523
- lastActiveIndexPos = _ref8$.activeIndexPos,
524
- lastIsInverted = _ref8$.isInverted,
525
- lastDiffType = _ref8$.diffType,
526
- lastHideDeletedDiffs = _ref8$.hideDeletedDiffs,
527
- lastHideAddedDiffsUnderline = _ref8$.hideAddedDiffsUnderline,
528
- lastShowIndicators = _ref8$.showIndicators,
529
- lastSmartThresholds = _ref8$.smartThresholds,
530
- lastDeletedDiffPlacement = _ref8$.deletedDiffPlacement,
531
- lastInlineDeletedDiffPlacement = _ref8$.inlineDeletedDiffPlacement;
531
+ pluginState = _ref8$.pluginState,
532
+ state = _ref8$.state,
533
+ colorScheme = _ref8$.colorScheme,
534
+ intl = _ref8$.intl,
535
+ activeIndexPos = _ref8$.activeIndexPos,
536
+ isInverted = _ref8$.isInverted,
537
+ diffType = _ref8$.diffType,
538
+ hideDeletedDiffs = _ref8$.hideDeletedDiffs,
539
+ hideAddedDiffsUnderline = _ref8$.hideAddedDiffsUnderline,
540
+ showIndicators = _ref8$.showIndicators,
541
+ smartThresholds = _ref8$.smartThresholds,
542
+ deletedDiffPlacement = _ref8$.deletedDiffPlacement,
543
+ inlineDeletedDiffPlacement = _ref8$.inlineDeletedDiffPlacement;
544
+ var _ref9 = _slicedToArray(_ref7, 1),
545
+ _ref9$ = _ref9[0],
546
+ lastPluginState = _ref9$.pluginState,
547
+ lastState = _ref9$.state,
548
+ lastColorScheme = _ref9$.colorScheme,
549
+ lastIntl = _ref9$.intl,
550
+ lastActiveIndexPos = _ref9$.activeIndexPos,
551
+ lastIsInverted = _ref9$.isInverted,
552
+ lastDiffType = _ref9$.diffType,
553
+ lastHideDeletedDiffs = _ref9$.hideDeletedDiffs,
554
+ lastHideAddedDiffsUnderline = _ref9$.hideAddedDiffsUnderline,
555
+ lastShowIndicators = _ref9$.showIndicators,
556
+ lastSmartThresholds = _ref9$.smartThresholds,
557
+ lastDeletedDiffPlacement = _ref9$.deletedDiffPlacement,
558
+ lastInlineDeletedDiffPlacement = _ref9$.inlineDeletedDiffPlacement;
532
559
  var originalDocIsSame = lastPluginState.originalDoc && pluginState.originalDoc && pluginState.originalDoc.eq(lastPluginState.originalDoc);
533
560
  if (isExtendedEnabled(diffType)) {
534
- var _ref9;
535
- return (_ref9 = 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 && hideAddedDiffsUnderline === lastHideAddedDiffsUnderline && showIndicators === lastShowIndicators && isEqual(smartThresholds, lastSmartThresholds) && deletedDiffPlacement === lastDeletedDiffPlacement && inlineDeletedDiffPlacement === lastInlineDeletedDiffPlacement) !== null && _ref9 !== void 0 ? _ref9 : false;
561
+ var _ref0;
562
+ return (_ref0 = 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 && hideAddedDiffsUnderline === lastHideAddedDiffsUnderline && showIndicators === lastShowIndicators && isEqual(smartThresholds, lastSmartThresholds) && deletedDiffPlacement === lastDeletedDiffPlacement && inlineDeletedDiffPlacement === lastInlineDeletedDiffPlacement) !== null && _ref0 !== void 0 ? _ref0 : false;
536
563
  }
537
- return (_ref0 = originalDocIsSame && isEqual(pluginState.steps, lastPluginState.steps) && state.doc.eq(lastState.doc) && colorScheme === lastColorScheme && intl.locale === lastIntl.locale && isEqual(activeIndexPos, lastActiveIndexPos) && hideDeletedDiffs === lastHideDeletedDiffs) !== null && _ref0 !== void 0 ? _ref0 : false;
564
+ return (_ref1 = originalDocIsSame && isEqual(pluginState.steps, lastPluginState.steps) && state.doc.eq(lastState.doc) && colorScheme === lastColorScheme && intl.locale === lastIntl.locale && isEqual(activeIndexPos, lastActiveIndexPos) && hideDeletedDiffs === lastHideDeletedDiffs) !== null && _ref1 !== void 0 ? _ref1 : false;
538
565
  });
@@ -48,7 +48,9 @@ export var createInlineChangedDecoration = function createInlineChangedDecoratio
48
48
  hideAddedDiffsUnderline = _ref$hideAddedDiffsUn === void 0 ? false : _ref$hideAddedDiffsUn,
49
49
  inlineNodeName = _ref.inlineNodeName,
50
50
  _ref$hasDeletedWidget = _ref.hasDeletedWidget,
51
- hasDeletedWidget = _ref$hasDeletedWidget === void 0 ? false : _ref$hasDeletedWidget;
51
+ hasDeletedWidget = _ref$hasDeletedWidget === void 0 ? false : _ref$hasDeletedWidget,
52
+ _ref$isDeletedWidgetB = _ref.isDeletedWidgetBelow,
53
+ isDeletedWidgetBelow = _ref$isDeletedWidgetB === void 0 ? false : _ref$isDeletedWidgetB;
52
54
  var diffId = crypto.randomUUID();
53
55
  if (shouldHideDeleted) {
54
56
  return [Decoration.inline(change.fromB, change.toB, {
@@ -104,14 +106,23 @@ export var createInlineChangedDecoration = function createInlineChangedDecoratio
104
106
  // bar doesn't extend into the preceding block's margin.
105
107
  // Skip when the deleted-content widget is rendered — the anchor must
106
108
  // stay at the boundary to keep the indicator continuous.
109
+ //
110
+ // The widget only pins the end of the range it is anchored to. It sits at `change.fromB`
111
+ // by default, but `deletedDiffPlacement: 'bottom'` / `inlineDeletedDiffPlacement: 'after'`
112
+ // move it to `change.toB` so the deleted content reads after the added content. In that
113
+ // case the `from` anchor has no widget beside it any more and must be adjusted inward
114
+ // again, otherwise the over-long indicator bar that CCI-18224 fixed comes back.
107
115
  var anchorFrom = change.fromB;
108
116
  var anchorTo = change.toB;
109
- if (!hasDeletedWidget) {
110
- var _$from$nodeAfter, _$to$nodeBefore;
117
+ if (!hasDeletedWidget || isDeletedWidgetBelow) {
118
+ var _$from$nodeAfter;
111
119
  var $from = doc.resolve(anchorFrom);
112
120
  if (!$from.parent.inlineContent && (_$from$nodeAfter = $from.nodeAfter) !== null && _$from$nodeAfter !== void 0 && _$from$nodeAfter.isTextblock) {
113
121
  anchorFrom = anchorFrom + 1;
114
122
  }
123
+ }
124
+ if (!hasDeletedWidget) {
125
+ var _$to$nodeBefore;
115
126
  var $to = doc.resolve(anchorTo);
116
127
  if (!$to.parent.inlineContent && (_$to$nodeBefore = $to.nodeBefore) !== null && _$to$nodeBefore !== void 0 && _$to$nodeBefore.isTextblock) {
117
128
  anchorTo = anchorTo - 1;