@atlaskit/editor-plugin-show-diff 11.0.4 → 11.0.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,45 @@
1
1
  # @atlaskit/editor-plugin-show-diff
2
2
 
3
+ ## 11.0.6
4
+
5
+ ### Patch Changes
6
+
7
+ - [`d1b5e85073e48`](https://bitbucket.org/atlassian/atlassian-frontend-monorepo/commits/d1b5e85073e48) -
8
+ [ED-7926] Fix block controls hover jitter when viewing an AI Suggested Edits diff.
9
+
10
+ While a diff is on screen the block controls UI is hidden, but the hover handlers kept running:
11
+ `handleMouseOver` dispatched `showDragHandleAt` (flip-flopping the active node between
12
+ neighbouring blocks) and the right-side hover-side tracker kept dispatching
13
+ `setHoverSide`/`mouseEnter`, both causing visible jitter. Block controls now suppress both hover
14
+ paths (and hide the drag handle) while a diff is displayed, detected via the show-diff plugin's
15
+ `isDisplayingChanges` shared state (plus the `reviewing` user intent for AI suggestions with no
16
+ diff decorations), gated behind `platform_editor_diff_plugin_extended`.
17
+
18
+ To let block controls read that state without forming a circular project reference
19
+ (`block-controls -> show-diff -> expand -> block-controls`), the `toggleExpandRange` command and
20
+ `TOGGLE_EXPAND_RANGE_META_KEY` were moved from `@atlaskit/editor-plugin-expand` into
21
+ `@atlaskit/editor-common` (re-exported from expand for backwards compatibility).
22
+ `@atlaskit/editor-plugin-show-diff` no longer depends on `@atlaskit/editor-plugin-expand`, and
23
+ `@atlaskit/editor-plugin-block-controls` now takes an optional dependency on
24
+ `@atlaskit/editor-plugin-show-diff`.
25
+
26
+ - Updated dependencies
27
+
28
+ ## 11.0.5
29
+
30
+ ### Patch Changes
31
+
32
+ - [`67209770a447f`](https://bitbucket.org/atlassian/atlassian-frontend-monorepo/commits/67209770a447f) -
33
+ [ED-8371] Fix `scrollToDiff` not always scrolling to the first diff decoration. When
34
+ `scrollIntoView` was requested on an extended diff (e.g. AI Suggested Edits),
35
+ `scrollToFirstDecoration` selected its target using `DecorationSet.find()`, which returns
36
+ decorations in the set's internal order rather than document position and applies no scrollability
37
+ filtering. As a result it could scroll to a later decoration (for example a `removeNode` deletion)
38
+ instead of the topmost one. It now scrolls to the first entry of `getScrollableDecorations` — the
39
+ same filtered, position-sorted list used by the active-index navigation path — so "first" reliably
40
+ means the topmost scrollable diff in the document.
41
+ - Updated dependencies
42
+
3
43
  ## 11.0.4
4
44
 
5
45
  ### Patch Changes
@@ -26,9 +26,6 @@
26
26
  {
27
27
  "path": "../../editor-plugin-analytics/afm-cc/tsconfig.json"
28
28
  },
29
- {
30
- "path": "../../editor-plugin-expand/afm-cc/tsconfig.json"
31
- },
32
29
  {
33
30
  "path": "../../editor-plugin-user-intent/afm-cc/tsconfig.json"
34
31
  },
@@ -26,9 +26,6 @@
26
26
  {
27
27
  "path": "../../editor-plugin-analytics/afm-products/tsconfig.json"
28
28
  },
29
- {
30
- "path": "../../editor-plugin-expand/afm-products/tsconfig.json"
31
- },
32
29
  {
33
30
  "path": "../../editor-plugin-user-intent/afm-products/tsconfig.json"
34
31
  },
@@ -6,6 +6,7 @@ Object.defineProperty(exports, "__esModule", {
6
6
  });
7
7
  exports.showDiffPluginKey = exports.createPlugin = void 0;
8
8
  var _defineProperty2 = _interopRequireDefault(require("@babel/runtime/helpers/defineProperty"));
9
+ var _expand = require("@atlaskit/editor-common/expand");
9
10
  var _processRawValue = require("@atlaskit/editor-common/process-raw-value");
10
11
  var _safePlugin = require("@atlaskit/editor-common/safe-plugin");
11
12
  var _state2 = require("@atlaskit/editor-prosemirror/state");
@@ -179,11 +180,13 @@ var createPlugin = exports.createPlugin = function createPlugin(config, getIntl,
179
180
  }
180
181
  var pluginState = showDiffPluginKey.getState(view.state);
181
182
 
182
- // Scroll to the first decoration when scrollIntoView was requested
183
+ // Scroll to the first decoration when scrollIntoView was requested.
184
+ // Use the same filtered/position-sorted list as the active-index path
185
+ // so "first" reliably means the topmost scrollable diff in the document.
183
186
  if (pluginState !== null && pluginState !== void 0 && pluginState.scrollIntoView && (0, _isExtendedEnabled.isExtendedEnabled)(pluginState === null || pluginState === void 0 ? void 0 : pluginState.diffType)) {
184
187
  var _cancelPendingScrollT;
185
188
  (_cancelPendingScrollT = cancelPendingScrollToDecoration) === null || _cancelPendingScrollT === void 0 || _cancelPendingScrollT();
186
- cancelPendingScrollToDecoration = (0, _scrollToDiff.scrollToFirstDecoration)(view, pluginState.decorations);
189
+ cancelPendingScrollToDecoration = (0, _scrollToDiff.scrollToFirstDecoration)(view, (0, _getScrollableDecorations.getScrollableDecorations)(pluginState.decorations, view.state.doc, pluginState === null || pluginState === void 0 ? void 0 : pluginState.diffType));
187
190
 
188
191
  // Reset the flag so we don't scroll again on subsequent updates
189
192
  view.dispatch(view.state.tr.setMeta(showDiffPluginKey, {
@@ -195,12 +198,14 @@ var createPlugin = exports.createPlugin = function createPlugin(config, getIntl,
195
198
  var activeIndexChanged = (pluginState === null || pluginState === void 0 ? void 0 : pluginState.activeIndex) !== undefined && pluginState.activeIndex !== previousActiveIndex;
196
199
  previousActiveIndex = pluginState === null || pluginState === void 0 ? void 0 : pluginState.activeIndex;
197
200
  if ((pluginState === null || pluginState === void 0 ? void 0 : pluginState.activeIndex) !== undefined && activeIndexChanged) {
198
- var _cancelPendingScrollT2, _api$expand;
201
+ var _cancelPendingScrollT2;
199
202
  (_cancelPendingScrollT2 = cancelPendingScrollToDecoration) === null || _cancelPendingScrollT2 === void 0 || _cancelPendingScrollT2();
200
203
  var scrollableDecorations = (0, _getScrollableDecorations.getScrollableDecorations)(pluginState.decorations, view.state.doc, pluginState === null || pluginState === void 0 ? void 0 : pluginState.diffType);
201
204
  var activeDecoration = scrollableDecorations[pluginState.activeIndex];
202
- 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) {
203
- api === null || api === void 0 || api.core.actions.execute(api.expand.commands.toggleExpandRange(activeDecoration.from, activeDecoration.to, true));
205
+ if (activeDecoration) {
206
+ // EDITOR-7926: use editor-common's command instead of the expand plugin API to
207
+ // avoid a circular dependency; expand picks up the meta if loaded (no-op if not).
208
+ api === null || api === void 0 || api.core.actions.execute((0, _expand.toggleExpandRange)(activeDecoration.from, activeDecoration.to, true));
204
209
  }
205
210
  cancelPendingScrollToDecoration = (0, _scrollToDiff.scrollToActiveDecoration)(view, scrollableDecorations, pluginState.activeIndex);
206
211
  }
@@ -44,38 +44,17 @@ function scrollToSelection(node) {
44
44
  * Unlike `scrollToActiveDecoration`, this does not require an active index —
45
45
  * it simply scrolls to bring the first decoration into view.
46
46
  *
47
+ * The caller must pass the list of scrollable decorations as produced by
48
+ * `getScrollableDecorations`, which is filtered (non-scrollable decorations
49
+ * removed) and sorted by document position. This guarantees "first" means the
50
+ * topmost scrollable diff in the document rather than an arbitrary decoration
51
+ * from the raw `DecorationSet` ordering. This is delegated to
52
+ * `scrollToActiveDecoration` at index 0 so both scroll paths behave identically.
53
+ *
47
54
  * @returns A function that cancels the scheduled `requestAnimationFrame` if it has not run yet.
48
55
  */
49
- var scrollToFirstDecoration = exports.scrollToFirstDecoration = function scrollToFirstDecoration(view, set) {
50
- var decoration = set.find(undefined, undefined, _decorationKeys.isDiffDecorationSpec).find(_decorationKeys.isDiffDecoration);
51
- if (!decoration) {
52
- return function () {};
53
- }
54
- var rafId = requestAnimationFrame(function () {
55
- var _decoration$type;
56
- rafId = null;
57
- // @ts-expect-error - decoration.type is not typed public API
58
- if (decoration.spec.decorationType === 'widget' && decoration !== null && decoration !== void 0 && (_decoration$type = decoration.type) !== null && _decoration$type !== void 0 && _decoration$type.toDOM) {
59
- // @ts-expect-error - decoration.type is not typed public API
60
- var widgetDom = decoration.type.toDOM;
61
- // Always scroll to the top of this decoration even if it's in view already
62
- scrollToSelection(widgetDom);
63
- } else {
64
- var _view$domAtPos;
65
- var targetNode = view.nodeDOM(decoration === null || decoration === void 0 ? void 0 : decoration.from);
66
- var node = targetNode instanceof Element ? targetNode : (_view$domAtPos = view.domAtPos(decoration === null || decoration === void 0 ? void 0 : decoration.from)) === null || _view$domAtPos === void 0 ? void 0 : _view$domAtPos.node;
67
- if (node instanceof HTMLElement) {
68
- // Always scroll to the top of this decoration even if it's in view already
69
- scrollToSelection(node);
70
- }
71
- }
72
- });
73
- return function () {
74
- if (rafId !== null) {
75
- cancelAnimationFrame(rafId);
76
- rafId = null;
77
- }
78
- };
56
+ var scrollToFirstDecoration = exports.scrollToFirstDecoration = function scrollToFirstDecoration(view, scrollableDecorations) {
57
+ return scrollToActiveDecoration(view, scrollableDecorations, 0);
79
58
  };
80
59
 
81
60
  /**
@@ -91,14 +70,14 @@ var scrollToActiveDecoration = exports.scrollToActiveDecoration = function scrol
91
70
  var rafId = requestAnimationFrame(function () {
92
71
  rafId = null;
93
72
  if ((0, _decorationKeys.isDiffDecoration)(decoration) && decoration.spec.decorationType === 'widget') {
94
- var _decoration$type2;
73
+ var _decoration$type;
95
74
  // @ts-expect-error - decoration.type is not typed public API
96
- var widgetDom = decoration === null || decoration === void 0 || (_decoration$type2 = decoration.type) === null || _decoration$type2 === void 0 ? void 0 : _decoration$type2.toDOM;
75
+ var widgetDom = decoration === null || decoration === void 0 || (_decoration$type = decoration.type) === null || _decoration$type === void 0 ? void 0 : _decoration$type.toDOM;
97
76
  scrollToSelection(widgetDom);
98
77
  } else {
99
- var _view$domAtPos2;
78
+ var _view$domAtPos;
100
79
  var targetNode = view.nodeDOM(decoration === null || decoration === void 0 ? void 0 : decoration.from);
101
- var node = targetNode instanceof Element ? targetNode : (_view$domAtPos2 = view.domAtPos(decoration === null || decoration === void 0 ? void 0 : decoration.from)) === null || _view$domAtPos2 === void 0 ? void 0 : _view$domAtPos2.node;
80
+ var node = targetNode instanceof Element ? targetNode : (_view$domAtPos = view.domAtPos(decoration === null || decoration === void 0 ? void 0 : decoration.from)) === null || _view$domAtPos === void 0 ? void 0 : _view$domAtPos.node;
102
81
  scrollToSelection(node);
103
82
  }
104
83
  });
@@ -1,3 +1,4 @@
1
+ import { toggleExpandRange } from '@atlaskit/editor-common/expand';
1
2
  import { processRawValue } from '@atlaskit/editor-common/process-raw-value';
2
3
  import { SafePlugin } from '@atlaskit/editor-common/safe-plugin';
3
4
  import { PluginKey } from '@atlaskit/editor-prosemirror/state';
@@ -186,11 +187,13 @@ export const createPlugin = (config, getIntl, api) => {
186
187
  }
187
188
  const pluginState = showDiffPluginKey.getState(view.state);
188
189
 
189
- // Scroll to the first decoration when scrollIntoView was requested
190
+ // Scroll to the first decoration when scrollIntoView was requested.
191
+ // Use the same filtered/position-sorted list as the active-index path
192
+ // so "first" reliably means the topmost scrollable diff in the document.
190
193
  if (pluginState !== null && pluginState !== void 0 && pluginState.scrollIntoView && isExtendedEnabled(pluginState === null || pluginState === void 0 ? void 0 : pluginState.diffType)) {
191
194
  var _cancelPendingScrollT;
192
195
  (_cancelPendingScrollT = cancelPendingScrollToDecoration) === null || _cancelPendingScrollT === void 0 ? void 0 : _cancelPendingScrollT();
193
- cancelPendingScrollToDecoration = scrollToFirstDecoration(view, pluginState.decorations);
196
+ cancelPendingScrollToDecoration = scrollToFirstDecoration(view, getScrollableDecorations(pluginState.decorations, view.state.doc, pluginState === null || pluginState === void 0 ? void 0 : pluginState.diffType));
194
197
 
195
198
  // Reset the flag so we don't scroll again on subsequent updates
196
199
  view.dispatch(view.state.tr.setMeta(showDiffPluginKey, {
@@ -202,12 +205,14 @@ export const createPlugin = (config, getIntl, api) => {
202
205
  const activeIndexChanged = (pluginState === null || pluginState === void 0 ? void 0 : pluginState.activeIndex) !== undefined && pluginState.activeIndex !== previousActiveIndex;
203
206
  previousActiveIndex = pluginState === null || pluginState === void 0 ? void 0 : pluginState.activeIndex;
204
207
  if ((pluginState === null || pluginState === void 0 ? void 0 : pluginState.activeIndex) !== undefined && activeIndexChanged) {
205
- var _cancelPendingScrollT2, _api$expand, _api$expand$commands;
208
+ var _cancelPendingScrollT2;
206
209
  (_cancelPendingScrollT2 = cancelPendingScrollToDecoration) === null || _cancelPendingScrollT2 === void 0 ? void 0 : _cancelPendingScrollT2();
207
210
  const scrollableDecorations = getScrollableDecorations(pluginState.decorations, view.state.doc, pluginState === null || pluginState === void 0 ? void 0 : pluginState.diffType);
208
211
  const activeDecoration = scrollableDecorations[pluginState.activeIndex];
209
- if (activeDecoration && api !== null && api !== void 0 && (_api$expand = api.expand) !== null && _api$expand !== void 0 && (_api$expand$commands = _api$expand.commands) !== null && _api$expand$commands !== void 0 && _api$expand$commands.toggleExpandRange) {
210
- api === null || api === void 0 ? void 0 : api.core.actions.execute(api.expand.commands.toggleExpandRange(activeDecoration.from, activeDecoration.to, true));
212
+ if (activeDecoration) {
213
+ // EDITOR-7926: use editor-common's command instead of the expand plugin API to
214
+ // avoid a circular dependency; expand picks up the meta if loaded (no-op if not).
215
+ api === null || api === void 0 ? void 0 : api.core.actions.execute(toggleExpandRange(activeDecoration.from, activeDecoration.to, true));
211
216
  }
212
217
  cancelPendingScrollToDecoration = scrollToActiveDecoration(view, scrollableDecorations, pluginState.activeIndex);
213
218
  }
@@ -1,4 +1,4 @@
1
- import { isDiffDecoration, isDiffDecorationSpec } from './decorations/decorationKeys';
1
+ import { isDiffDecoration } from './decorations/decorationKeys';
2
2
 
3
3
  /**
4
4
  * Extra space above the scrolled-to element so it does not sit flush under the
@@ -39,39 +39,16 @@ function scrollToSelection(node) {
39
39
  * Unlike `scrollToActiveDecoration`, this does not require an active index —
40
40
  * it simply scrolls to bring the first decoration into view.
41
41
  *
42
+ * The caller must pass the list of scrollable decorations as produced by
43
+ * `getScrollableDecorations`, which is filtered (non-scrollable decorations
44
+ * removed) and sorted by document position. This guarantees "first" means the
45
+ * topmost scrollable diff in the document rather than an arbitrary decoration
46
+ * from the raw `DecorationSet` ordering. This is delegated to
47
+ * `scrollToActiveDecoration` at index 0 so both scroll paths behave identically.
48
+ *
42
49
  * @returns A function that cancels the scheduled `requestAnimationFrame` if it has not run yet.
43
50
  */
44
- export const scrollToFirstDecoration = (view, set) => {
45
- const decoration = set.find(undefined, undefined, isDiffDecorationSpec).find(isDiffDecoration);
46
- if (!decoration) {
47
- return () => {};
48
- }
49
- let rafId = requestAnimationFrame(() => {
50
- var _decoration$type;
51
- rafId = null;
52
- // @ts-expect-error - decoration.type is not typed public API
53
- if (decoration.spec.decorationType === 'widget' && decoration !== null && decoration !== void 0 && (_decoration$type = decoration.type) !== null && _decoration$type !== void 0 && _decoration$type.toDOM) {
54
- // @ts-expect-error - decoration.type is not typed public API
55
- const widgetDom = decoration.type.toDOM;
56
- // Always scroll to the top of this decoration even if it's in view already
57
- scrollToSelection(widgetDom);
58
- } else {
59
- var _view$domAtPos;
60
- const targetNode = view.nodeDOM(decoration === null || decoration === void 0 ? void 0 : decoration.from);
61
- const node = targetNode instanceof Element ? targetNode : (_view$domAtPos = view.domAtPos(decoration === null || decoration === void 0 ? void 0 : decoration.from)) === null || _view$domAtPos === void 0 ? void 0 : _view$domAtPos.node;
62
- if (node instanceof HTMLElement) {
63
- // Always scroll to the top of this decoration even if it's in view already
64
- scrollToSelection(node);
65
- }
66
- }
67
- });
68
- return () => {
69
- if (rafId !== null) {
70
- cancelAnimationFrame(rafId);
71
- rafId = null;
72
- }
73
- };
74
- };
51
+ export const scrollToFirstDecoration = (view, scrollableDecorations) => scrollToActiveDecoration(view, scrollableDecorations, 0);
75
52
 
76
53
  /**
77
54
  * Schedules scrolling to the decoration at the given index after the next frame.
@@ -86,14 +63,14 @@ export const scrollToActiveDecoration = (view, decorations, activeIndex) => {
86
63
  let rafId = requestAnimationFrame(() => {
87
64
  rafId = null;
88
65
  if (isDiffDecoration(decoration) && decoration.spec.decorationType === 'widget') {
89
- var _decoration$type2;
66
+ var _decoration$type;
90
67
  // @ts-expect-error - decoration.type is not typed public API
91
- const widgetDom = decoration === null || decoration === void 0 ? void 0 : (_decoration$type2 = decoration.type) === null || _decoration$type2 === void 0 ? void 0 : _decoration$type2.toDOM;
68
+ const widgetDom = decoration === null || decoration === void 0 ? void 0 : (_decoration$type = decoration.type) === null || _decoration$type === void 0 ? void 0 : _decoration$type.toDOM;
92
69
  scrollToSelection(widgetDom);
93
70
  } else {
94
- var _view$domAtPos2;
71
+ var _view$domAtPos;
95
72
  const targetNode = view.nodeDOM(decoration === null || decoration === void 0 ? void 0 : decoration.from);
96
- const node = targetNode instanceof Element ? targetNode : (_view$domAtPos2 = view.domAtPos(decoration === null || decoration === void 0 ? void 0 : decoration.from)) === null || _view$domAtPos2 === void 0 ? void 0 : _view$domAtPos2.node;
73
+ const node = targetNode instanceof Element ? targetNode : (_view$domAtPos = view.domAtPos(decoration === null || decoration === void 0 ? void 0 : decoration.from)) === null || _view$domAtPos === void 0 ? void 0 : _view$domAtPos.node;
97
74
  scrollToSelection(node);
98
75
  }
99
76
  });
@@ -1,6 +1,7 @@
1
1
  import _defineProperty from "@babel/runtime/helpers/defineProperty";
2
2
  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; }
3
3
  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) { _defineProperty(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; }
4
+ import { toggleExpandRange } from '@atlaskit/editor-common/expand';
4
5
  import { processRawValue } from '@atlaskit/editor-common/process-raw-value';
5
6
  import { SafePlugin } from '@atlaskit/editor-common/safe-plugin';
6
7
  import { PluginKey } from '@atlaskit/editor-prosemirror/state';
@@ -172,11 +173,13 @@ export var createPlugin = function createPlugin(config, getIntl, api) {
172
173
  }
173
174
  var pluginState = showDiffPluginKey.getState(view.state);
174
175
 
175
- // Scroll to the first decoration when scrollIntoView was requested
176
+ // Scroll to the first decoration when scrollIntoView was requested.
177
+ // Use the same filtered/position-sorted list as the active-index path
178
+ // so "first" reliably means the topmost scrollable diff in the document.
176
179
  if (pluginState !== null && pluginState !== void 0 && pluginState.scrollIntoView && isExtendedEnabled(pluginState === null || pluginState === void 0 ? void 0 : pluginState.diffType)) {
177
180
  var _cancelPendingScrollT;
178
181
  (_cancelPendingScrollT = cancelPendingScrollToDecoration) === null || _cancelPendingScrollT === void 0 || _cancelPendingScrollT();
179
- cancelPendingScrollToDecoration = scrollToFirstDecoration(view, pluginState.decorations);
182
+ cancelPendingScrollToDecoration = scrollToFirstDecoration(view, getScrollableDecorations(pluginState.decorations, view.state.doc, pluginState === null || pluginState === void 0 ? void 0 : pluginState.diffType));
180
183
 
181
184
  // Reset the flag so we don't scroll again on subsequent updates
182
185
  view.dispatch(view.state.tr.setMeta(showDiffPluginKey, {
@@ -188,12 +191,14 @@ export var createPlugin = function createPlugin(config, getIntl, api) {
188
191
  var activeIndexChanged = (pluginState === null || pluginState === void 0 ? void 0 : pluginState.activeIndex) !== undefined && pluginState.activeIndex !== previousActiveIndex;
189
192
  previousActiveIndex = pluginState === null || pluginState === void 0 ? void 0 : pluginState.activeIndex;
190
193
  if ((pluginState === null || pluginState === void 0 ? void 0 : pluginState.activeIndex) !== undefined && activeIndexChanged) {
191
- var _cancelPendingScrollT2, _api$expand;
194
+ var _cancelPendingScrollT2;
192
195
  (_cancelPendingScrollT2 = cancelPendingScrollToDecoration) === null || _cancelPendingScrollT2 === void 0 || _cancelPendingScrollT2();
193
196
  var scrollableDecorations = getScrollableDecorations(pluginState.decorations, view.state.doc, pluginState === null || pluginState === void 0 ? void 0 : pluginState.diffType);
194
197
  var activeDecoration = scrollableDecorations[pluginState.activeIndex];
195
- 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) {
196
- api === null || api === void 0 || api.core.actions.execute(api.expand.commands.toggleExpandRange(activeDecoration.from, activeDecoration.to, true));
198
+ if (activeDecoration) {
199
+ // EDITOR-7926: use editor-common's command instead of the expand plugin API to
200
+ // avoid a circular dependency; expand picks up the meta if loaded (no-op if not).
201
+ api === null || api === void 0 || api.core.actions.execute(toggleExpandRange(activeDecoration.from, activeDecoration.to, true));
197
202
  }
198
203
  cancelPendingScrollToDecoration = scrollToActiveDecoration(view, scrollableDecorations, pluginState.activeIndex);
199
204
  }
@@ -1,4 +1,4 @@
1
- import { isDiffDecoration, isDiffDecorationSpec } from './decorations/decorationKeys';
1
+ import { isDiffDecoration } from './decorations/decorationKeys';
2
2
 
3
3
  /**
4
4
  * Extra space above the scrolled-to element so it does not sit flush under the
@@ -39,38 +39,17 @@ function scrollToSelection(node) {
39
39
  * Unlike `scrollToActiveDecoration`, this does not require an active index —
40
40
  * it simply scrolls to bring the first decoration into view.
41
41
  *
42
+ * The caller must pass the list of scrollable decorations as produced by
43
+ * `getScrollableDecorations`, which is filtered (non-scrollable decorations
44
+ * removed) and sorted by document position. This guarantees "first" means the
45
+ * topmost scrollable diff in the document rather than an arbitrary decoration
46
+ * from the raw `DecorationSet` ordering. This is delegated to
47
+ * `scrollToActiveDecoration` at index 0 so both scroll paths behave identically.
48
+ *
42
49
  * @returns A function that cancels the scheduled `requestAnimationFrame` if it has not run yet.
43
50
  */
44
- export var scrollToFirstDecoration = function scrollToFirstDecoration(view, set) {
45
- var decoration = set.find(undefined, undefined, isDiffDecorationSpec).find(isDiffDecoration);
46
- if (!decoration) {
47
- return function () {};
48
- }
49
- var rafId = requestAnimationFrame(function () {
50
- var _decoration$type;
51
- rafId = null;
52
- // @ts-expect-error - decoration.type is not typed public API
53
- if (decoration.spec.decorationType === 'widget' && decoration !== null && decoration !== void 0 && (_decoration$type = decoration.type) !== null && _decoration$type !== void 0 && _decoration$type.toDOM) {
54
- // @ts-expect-error - decoration.type is not typed public API
55
- var widgetDom = decoration.type.toDOM;
56
- // Always scroll to the top of this decoration even if it's in view already
57
- scrollToSelection(widgetDom);
58
- } else {
59
- var _view$domAtPos;
60
- var targetNode = view.nodeDOM(decoration === null || decoration === void 0 ? void 0 : decoration.from);
61
- var node = targetNode instanceof Element ? targetNode : (_view$domAtPos = view.domAtPos(decoration === null || decoration === void 0 ? void 0 : decoration.from)) === null || _view$domAtPos === void 0 ? void 0 : _view$domAtPos.node;
62
- if (node instanceof HTMLElement) {
63
- // Always scroll to the top of this decoration even if it's in view already
64
- scrollToSelection(node);
65
- }
66
- }
67
- });
68
- return function () {
69
- if (rafId !== null) {
70
- cancelAnimationFrame(rafId);
71
- rafId = null;
72
- }
73
- };
51
+ export var scrollToFirstDecoration = function scrollToFirstDecoration(view, scrollableDecorations) {
52
+ return scrollToActiveDecoration(view, scrollableDecorations, 0);
74
53
  };
75
54
 
76
55
  /**
@@ -86,14 +65,14 @@ export var scrollToActiveDecoration = function scrollToActiveDecoration(view, de
86
65
  var rafId = requestAnimationFrame(function () {
87
66
  rafId = null;
88
67
  if (isDiffDecoration(decoration) && decoration.spec.decorationType === 'widget') {
89
- var _decoration$type2;
68
+ var _decoration$type;
90
69
  // @ts-expect-error - decoration.type is not typed public API
91
- var widgetDom = decoration === null || decoration === void 0 || (_decoration$type2 = decoration.type) === null || _decoration$type2 === void 0 ? void 0 : _decoration$type2.toDOM;
70
+ var widgetDom = decoration === null || decoration === void 0 || (_decoration$type = decoration.type) === null || _decoration$type === void 0 ? void 0 : _decoration$type.toDOM;
92
71
  scrollToSelection(widgetDom);
93
72
  } else {
94
- var _view$domAtPos2;
73
+ var _view$domAtPos;
95
74
  var targetNode = view.nodeDOM(decoration === null || decoration === void 0 ? void 0 : decoration.from);
96
- var node = targetNode instanceof Element ? targetNode : (_view$domAtPos2 = view.domAtPos(decoration === null || decoration === void 0 ? void 0 : decoration.from)) === null || _view$domAtPos2 === void 0 ? void 0 : _view$domAtPos2.node;
75
+ var node = targetNode instanceof Element ? targetNode : (_view$domAtPos = view.domAtPos(decoration === null || decoration === void 0 ? void 0 : decoration.from)) === null || _view$domAtPos === void 0 ? void 0 : _view$domAtPos.node;
97
76
  scrollToSelection(node);
98
77
  }
99
78
  });
@@ -1,12 +1,19 @@
1
- import type { EditorView, Decoration, DecorationSet } from '@atlaskit/editor-prosemirror/view';
1
+ import type { EditorView, Decoration } from '@atlaskit/editor-prosemirror/view';
2
2
  /**
3
3
  * Schedules scrolling to the first diff decoration after the next frame.
4
4
  * Unlike `scrollToActiveDecoration`, this does not require an active index —
5
5
  * it simply scrolls to bring the first decoration into view.
6
6
  *
7
+ * The caller must pass the list of scrollable decorations as produced by
8
+ * `getScrollableDecorations`, which is filtered (non-scrollable decorations
9
+ * removed) and sorted by document position. This guarantees "first" means the
10
+ * topmost scrollable diff in the document rather than an arbitrary decoration
11
+ * from the raw `DecorationSet` ordering. This is delegated to
12
+ * `scrollToActiveDecoration` at index 0 so both scroll paths behave identically.
13
+ *
7
14
  * @returns A function that cancels the scheduled `requestAnimationFrame` if it has not run yet.
8
15
  */
9
- export declare const scrollToFirstDecoration: (view: EditorView, set: DecorationSet) => (() => void);
16
+ export declare const scrollToFirstDecoration: (view: EditorView, scrollableDecorations: Decoration[]) => (() => void);
10
17
  /**
11
18
  * Schedules scrolling to the decoration at the given index after the next frame.
12
19
  *
@@ -2,7 +2,6 @@ import type { StepJson } from '@atlaskit/editor-common/collab';
2
2
  import type { NextEditorPlugin, EditorCommand, OptionalPlugin } from '@atlaskit/editor-common/types';
3
3
  import type { JSONDocNode } from '@atlaskit/editor-json-transformer';
4
4
  import type { AnalyticsPlugin } from '@atlaskit/editor-plugin-analytics';
5
- import type { ExpandPlugin } from '@atlaskit/editor-plugin-expand';
6
5
  import type { UserIntentPlugin } from '@atlaskit/editor-plugin-user-intent';
7
6
  import type { Node } from '@atlaskit/editor-prosemirror/model';
8
7
  import type { Step } from '@atlaskit/editor-prosemirror/transform';
@@ -93,11 +92,7 @@ export type ShowDiffPlugin = NextEditorPlugin<'showDiff', {
93
92
  scrollToPrevious: EditorCommand;
94
93
  showDiff: (config: PMDiffParams) => EditorCommand;
95
94
  };
96
- dependencies: [
97
- OptionalPlugin<AnalyticsPlugin>,
98
- OptionalPlugin<ExpandPlugin>,
99
- OptionalPlugin<UserIntentPlugin>
100
- ];
95
+ dependencies: [OptionalPlugin<AnalyticsPlugin>, OptionalPlugin<UserIntentPlugin>];
101
96
  pluginConfiguration: DiffParams | undefined;
102
97
  sharedState: {
103
98
  /**
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@atlaskit/editor-plugin-show-diff",
3
- "version": "11.0.4",
3
+ "version": "11.0.6",
4
4
  "description": "ShowDiff plugin for @atlaskit/editor-core",
5
5
  "author": "Atlassian Pty Ltd",
6
6
  "license": "Apache-2.0",
@@ -23,7 +23,6 @@
23
23
  "@atlaskit/adf-schema": "^56.6.0",
24
24
  "@atlaskit/custom-steps": "^1.0.0",
25
25
  "@atlaskit/editor-plugin-analytics": "^13.0.0",
26
- "@atlaskit/editor-plugin-expand": "^14.0.0",
27
26
  "@atlaskit/editor-plugin-user-intent": "^11.0.0",
28
27
  "@atlaskit/editor-prosemirror": "^8.0.0",
29
28
  "@atlaskit/editor-tables": "^3.0.0",
@@ -54,7 +53,7 @@
54
53
  "react-intl": "^7.0.0"
55
54
  },
56
55
  "peerDependencies": {
57
- "@atlaskit/editor-common": "^117.4.0",
56
+ "@atlaskit/editor-common": "^117.6.0",
58
57
  "react": "^18.2.0 || ^19.2.0"
59
58
  },
60
59
  "techstack": {