@atlaskit/editor-plugin-show-diff 15.1.8 → 15.1.9

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,18 @@
1
1
  # @atlaskit/editor-plugin-show-diff
2
2
 
3
+ ## 15.1.9
4
+
5
+ ### Patch Changes
6
+
7
+ - [`e3e068dfdea34`](https://bitbucket.org/atlassian/atlassian-frontend-monorepo/commits/e3e068dfdea34) -
8
+ [ux] Behind `platform_editor_ai_show_diff_patch_1`, fix scrolling to a diff landing below the top
9
+ of the change when `confluence_ncs_step_diffing_version_history` is enabled. Deleted content shown
10
+ above the content that replaced it is a zero-width widget on a more negative `side` at the same
11
+ position, and grouping collapsed the pair into one entry represented by the added content — so
12
+ scrolling resolved that range and left the deleted block above the viewport. Decorations sharing a
13
+ position are now ranked by the side they paint on, and a group that starts with deleted content is
14
+ represented by that widget, so scrolling reaches the visual start of the edit.
15
+
3
16
  ## 15.1.8
4
17
 
5
18
  ### Patch Changes
@@ -6,12 +6,15 @@ Object.defineProperty(exports, "__esModule", {
6
6
  });
7
7
  exports.getScrollableDecorations = void 0;
8
8
  exports.isInlineDiffDecorationRenderableInDoc = isInlineDiffDecorationRenderableInDoc;
9
+ var _defineProperty2 = _interopRequireDefault(require("@babel/runtime/helpers/defineProperty"));
9
10
  var _toConsumableArray2 = _interopRequireDefault(require("@babel/runtime/helpers/toConsumableArray"));
10
11
  var _typeof2 = _interopRequireDefault(require("@babel/runtime/helpers/typeof"));
11
12
  var _view = require("@atlaskit/editor-prosemirror/view");
12
13
  var _fg = require("@atlaskit/platform-feature-flags/fg");
13
14
  var _decorationKeys = require("./decorations/decorationKeys");
14
15
  var _isExtendedEnabled = require("./isExtendedEnabled");
16
+ 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; }
17
+ function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? ownKeys(Object(t), !0).forEach(function (r) { (0, _defineProperty2.default)(e, r, t[r]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function (r) { Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r)); }); } return e; }
15
18
  /**
16
19
  * True if `fragment` contains at least one inline node (text, hardBreak, emoji, mention, etc.).
17
20
  * Block-only subtrees (e.g. empty paragraphs, block cards with no inline children) return false.
@@ -49,6 +52,15 @@ function specHasDiffKeyPrefix(spec, keyPrefix) {
49
52
  return Boolean(spec && (0, _typeof2.default)(spec) === 'object' && 'key' in spec && typeof spec.key === 'string' && spec.key.startsWith(keyPrefix));
50
53
  }
51
54
 
55
+ /**
56
+ * Where a decoration paints relative to others at the same position: negative before the node at
57
+ * that position, positive after it. Only deleted-content widgets carry one — everything else is
58
+ * ranged, and so paints where its range starts.
59
+ */
60
+ function decorationSide(decoration) {
61
+ return (0, _decorationKeys.isDiffDecoration)(decoration) && decoration.spec.side || 0;
62
+ }
63
+
52
64
  /**
53
65
  * Collapses decorations that represent one continuous customer-facing edit.
54
66
  *
@@ -94,13 +106,29 @@ function groupTouchingDecorations(decorations, isInlineDecoration) {
94
106
  var representative = (_ref2 = (_group$find = group.find(isInlineDecoration)) !== null && _group$find !== void 0 ? _group$find : group.find(function (decoration) {
95
107
  return decoration.from !== decoration.to;
96
108
  })) !== null && _ref2 !== void 0 ? _ref2 : group[0];
97
- if (!representative || representative.from === from && representative.to === to) {
109
+ if (!representative) {
110
+ return representative;
111
+ }
112
+
113
+ // Deleted content is a widget at the start of the added content that replaced it, on a more
114
+ // negative side so it paints above. That makes it the visual start of the edit, reachable
115
+ // only through its own DOM — resolving the group's start position lands on the added content
116
+ // painted after it. The group keeps its range, which navigation and the active-range
117
+ // calculation both need, and reports the widget as what to scroll to.
118
+ var scrollTarget = (0, _fg.fg)('platform_editor_ai_show_diff_patch_1') ? group.find(function (decoration) {
119
+ return decoration.from === from && decorationSide(decoration) < decorationSide(representative);
120
+ }) : undefined;
121
+ if (!scrollTarget && representative.from === from && representative.to === to) {
98
122
  return representative;
99
123
  }
100
124
 
101
125
  // This decoration is only used for navigation and active-range calculation. The actual
102
- // visual decorations remain in the DecorationSet with their original ranges and styles.
103
- return isInlineDecoration(representative) ? _view.Decoration.inline(from, to, {}, representative.spec) : _view.Decoration.node(from, to, {}, representative.spec);
126
+ // visual decorations remain in the DecorationSet with their original ranges and styles —
127
+ // including their spec, hence a copy to add `scrollTarget` for `scrollToDiff` to read.
128
+ var spec = _objectSpread(_objectSpread({}, representative.spec), {}, {
129
+ scrollTarget: scrollTarget
130
+ });
131
+ return isInlineDecoration(representative) ? _view.Decoration.inline(from, to, {}, spec) : _view.Decoration.node(from, to, {}, spec);
104
132
  });
105
133
  }
106
134
 
@@ -117,7 +145,10 @@ function groupTouchingDecorations(decorations, isInlineDecoration) {
117
145
  * 5. When `confluence_ncs_step_diffing_version_history` is enabled, groups overlapping or
118
146
  * directly touching ranges across decoration types into one result, using the union of all
119
147
  * grouped ranges
120
- * (zero-width widgets can join a group without extending it)
148
+ * (zero-width widgets can join a group without extending it). Under
149
+ * `platform_editor_ai_show_diff_patch_1`, a group that starts with content painting above
150
+ * the content that replaced it — reports that widget as its `scrollTarget` spec,
151
+ * so scrolling reaches the visual start of the edit
121
152
  * 6. Results are sorted by from position, then by to position
122
153
  *
123
154
  * @param set - The DecorationSet to extract scrollable decorations from
@@ -51,17 +51,22 @@ var scrollToDecoration = exports.scrollToDecoration = function scrollToDecoratio
51
51
  if (!decoration) {
52
52
  return function () {};
53
53
  }
54
+
55
+ // A grouped decoration spans a whole customer-facing edit, which can visually start with a
56
+ // deleted-content widget rather than at the group's `from` position — see `scrollTarget` in
57
+ // `getScrollableDecorations`. Only set under `platform_editor_ai_show_diff_patch_1`.
58
+ var target = (0, _decorationKeys.isDiffDecoration)(decoration) && decoration.spec.scrollTarget || decoration;
54
59
  var rafId = requestAnimationFrame(function () {
55
60
  rafId = null;
56
- if ((0, _decorationKeys.isDiffDecoration)(decoration) && decoration.spec.decorationType === 'widget') {
57
- var _decoration$type;
61
+ if ((0, _decorationKeys.isDiffDecoration)(target) && target.spec.decorationType === 'widget') {
62
+ var _target$type;
58
63
  // @ts-expect-error - decoration.type is not typed public API
59
- var widgetDom = decoration === null || decoration === void 0 || (_decoration$type = decoration.type) === null || _decoration$type === void 0 ? void 0 : _decoration$type.toDOM;
64
+ var widgetDom = target === null || target === void 0 || (_target$type = target.type) === null || _target$type === void 0 ? void 0 : _target$type.toDOM;
60
65
  scrollToSelection(widgetDom);
61
66
  } else {
62
67
  var _view$domAtPos;
63
- var targetNode = view.nodeDOM(decoration === null || decoration === void 0 ? void 0 : decoration.from);
64
- 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;
68
+ var targetNode = view.nodeDOM(target === null || target === void 0 ? void 0 : target.from);
69
+ var node = targetNode instanceof Element ? targetNode : (_view$domAtPos = view.domAtPos(target === null || target === void 0 ? void 0 : target.from)) === null || _view$domAtPos === void 0 ? void 0 : _view$domAtPos.node;
65
70
  scrollToSelection(node);
66
71
  }
67
72
  });
@@ -40,6 +40,15 @@ function specHasDiffKeyPrefix(spec, keyPrefix) {
40
40
  return Boolean(spec && typeof spec === 'object' && 'key' in spec && typeof spec.key === 'string' && spec.key.startsWith(keyPrefix));
41
41
  }
42
42
 
43
+ /**
44
+ * Where a decoration paints relative to others at the same position: negative before the node at
45
+ * that position, positive after it. Only deleted-content widgets carry one — everything else is
46
+ * ranged, and so paints where its range starts.
47
+ */
48
+ function decorationSide(decoration) {
49
+ return isDiffDecoration(decoration) && decoration.spec.side || 0;
50
+ }
51
+
43
52
  /**
44
53
  * Collapses decorations that represent one continuous customer-facing edit.
45
54
  *
@@ -82,13 +91,28 @@ function groupTouchingDecorations(decorations, isInlineDecoration) {
82
91
  }) => {
83
92
  var _ref, _group$find;
84
93
  const representative = (_ref = (_group$find = group.find(isInlineDecoration)) !== null && _group$find !== void 0 ? _group$find : group.find(decoration => decoration.from !== decoration.to)) !== null && _ref !== void 0 ? _ref : group[0];
85
- if (!representative || representative.from === from && representative.to === to) {
94
+ if (!representative) {
95
+ return representative;
96
+ }
97
+
98
+ // Deleted content is a widget at the start of the added content that replaced it, on a more
99
+ // negative side so it paints above. That makes it the visual start of the edit, reachable
100
+ // only through its own DOM — resolving the group's start position lands on the added content
101
+ // painted after it. The group keeps its range, which navigation and the active-range
102
+ // calculation both need, and reports the widget as what to scroll to.
103
+ const scrollTarget = fg('platform_editor_ai_show_diff_patch_1') ? group.find(decoration => decoration.from === from && decorationSide(decoration) < decorationSide(representative)) : undefined;
104
+ if (!scrollTarget && representative.from === from && representative.to === to) {
86
105
  return representative;
87
106
  }
88
107
 
89
108
  // This decoration is only used for navigation and active-range calculation. The actual
90
- // visual decorations remain in the DecorationSet with their original ranges and styles.
91
- return isInlineDecoration(representative) ? Decoration.inline(from, to, {}, representative.spec) : Decoration.node(from, to, {}, representative.spec);
109
+ // visual decorations remain in the DecorationSet with their original ranges and styles —
110
+ // including their spec, hence a copy to add `scrollTarget` for `scrollToDiff` to read.
111
+ const spec = {
112
+ ...representative.spec,
113
+ scrollTarget
114
+ };
115
+ return isInlineDecoration(representative) ? Decoration.inline(from, to, {}, spec) : Decoration.node(from, to, {}, spec);
92
116
  });
93
117
  }
94
118
 
@@ -105,7 +129,10 @@ function groupTouchingDecorations(decorations, isInlineDecoration) {
105
129
  * 5. When `confluence_ncs_step_diffing_version_history` is enabled, groups overlapping or
106
130
  * directly touching ranges across decoration types into one result, using the union of all
107
131
  * grouped ranges
108
- * (zero-width widgets can join a group without extending it)
132
+ * (zero-width widgets can join a group without extending it). Under
133
+ * `platform_editor_ai_show_diff_patch_1`, a group that starts with content painting above
134
+ * the content that replaced it — reports that widget as its `scrollTarget` spec,
135
+ * so scrolling reaches the visual start of the edit
109
136
  * 6. Results are sorted by from position, then by to position
110
137
  *
111
138
  * @param set - The DecorationSet to extract scrollable decorations from
@@ -45,17 +45,22 @@ export const scrollToDecoration = (view, decorations, activeIndex = 0) => {
45
45
  if (!decoration) {
46
46
  return () => {};
47
47
  }
48
+
49
+ // A grouped decoration spans a whole customer-facing edit, which can visually start with a
50
+ // deleted-content widget rather than at the group's `from` position — see `scrollTarget` in
51
+ // `getScrollableDecorations`. Only set under `platform_editor_ai_show_diff_patch_1`.
52
+ const target = isDiffDecoration(decoration) && decoration.spec.scrollTarget || decoration;
48
53
  let rafId = requestAnimationFrame(() => {
49
54
  rafId = null;
50
- if (isDiffDecoration(decoration) && decoration.spec.decorationType === 'widget') {
51
- var _decoration$type;
55
+ if (isDiffDecoration(target) && target.spec.decorationType === 'widget') {
56
+ var _target$type;
52
57
  // @ts-expect-error - decoration.type is not typed public API
53
- const widgetDom = decoration === null || decoration === void 0 ? void 0 : (_decoration$type = decoration.type) === null || _decoration$type === void 0 ? void 0 : _decoration$type.toDOM;
58
+ const widgetDom = target === null || target === void 0 ? void 0 : (_target$type = target.type) === null || _target$type === void 0 ? void 0 : _target$type.toDOM;
54
59
  scrollToSelection(widgetDom);
55
60
  } else {
56
61
  var _view$domAtPos;
57
- const targetNode = view.nodeDOM(decoration === null || decoration === void 0 ? void 0 : decoration.from);
58
- 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
+ const targetNode = view.nodeDOM(target === null || target === void 0 ? void 0 : target.from);
63
+ const node = targetNode instanceof Element ? targetNode : (_view$domAtPos = view.domAtPos(target === null || target === void 0 ? void 0 : target.from)) === null || _view$domAtPos === void 0 ? void 0 : _view$domAtPos.node;
59
64
  scrollToSelection(node);
60
65
  }
61
66
  });
@@ -1,5 +1,8 @@
1
+ import _defineProperty from "@babel/runtime/helpers/defineProperty";
1
2
  import _toConsumableArray from "@babel/runtime/helpers/toConsumableArray";
2
3
  import _typeof from "@babel/runtime/helpers/typeof";
4
+ 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; }
5
+ 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; }
3
6
  import { Decoration } from '@atlaskit/editor-prosemirror/view';
4
7
  import { fg } from '@atlaskit/platform-feature-flags/fg';
5
8
  import { DiffDecorationKey, isDiffDecoration, isDiffDecorationSpec } from './decorations/decorationKeys';
@@ -42,6 +45,15 @@ function specHasDiffKeyPrefix(spec, keyPrefix) {
42
45
  return Boolean(spec && _typeof(spec) === 'object' && 'key' in spec && typeof spec.key === 'string' && spec.key.startsWith(keyPrefix));
43
46
  }
44
47
 
48
+ /**
49
+ * Where a decoration paints relative to others at the same position: negative before the node at
50
+ * that position, positive after it. Only deleted-content widgets carry one — everything else is
51
+ * ranged, and so paints where its range starts.
52
+ */
53
+ function decorationSide(decoration) {
54
+ return isDiffDecoration(decoration) && decoration.spec.side || 0;
55
+ }
56
+
45
57
  /**
46
58
  * Collapses decorations that represent one continuous customer-facing edit.
47
59
  *
@@ -87,13 +99,29 @@ function groupTouchingDecorations(decorations, isInlineDecoration) {
87
99
  var representative = (_ref2 = (_group$find = group.find(isInlineDecoration)) !== null && _group$find !== void 0 ? _group$find : group.find(function (decoration) {
88
100
  return decoration.from !== decoration.to;
89
101
  })) !== null && _ref2 !== void 0 ? _ref2 : group[0];
90
- if (!representative || representative.from === from && representative.to === to) {
102
+ if (!representative) {
103
+ return representative;
104
+ }
105
+
106
+ // Deleted content is a widget at the start of the added content that replaced it, on a more
107
+ // negative side so it paints above. That makes it the visual start of the edit, reachable
108
+ // only through its own DOM — resolving the group's start position lands on the added content
109
+ // painted after it. The group keeps its range, which navigation and the active-range
110
+ // calculation both need, and reports the widget as what to scroll to.
111
+ var scrollTarget = fg('platform_editor_ai_show_diff_patch_1') ? group.find(function (decoration) {
112
+ return decoration.from === from && decorationSide(decoration) < decorationSide(representative);
113
+ }) : undefined;
114
+ if (!scrollTarget && representative.from === from && representative.to === to) {
91
115
  return representative;
92
116
  }
93
117
 
94
118
  // This decoration is only used for navigation and active-range calculation. The actual
95
- // visual decorations remain in the DecorationSet with their original ranges and styles.
96
- return isInlineDecoration(representative) ? Decoration.inline(from, to, {}, representative.spec) : Decoration.node(from, to, {}, representative.spec);
119
+ // visual decorations remain in the DecorationSet with their original ranges and styles —
120
+ // including their spec, hence a copy to add `scrollTarget` for `scrollToDiff` to read.
121
+ var spec = _objectSpread(_objectSpread({}, representative.spec), {}, {
122
+ scrollTarget: scrollTarget
123
+ });
124
+ return isInlineDecoration(representative) ? Decoration.inline(from, to, {}, spec) : Decoration.node(from, to, {}, spec);
97
125
  });
98
126
  }
99
127
 
@@ -110,7 +138,10 @@ function groupTouchingDecorations(decorations, isInlineDecoration) {
110
138
  * 5. When `confluence_ncs_step_diffing_version_history` is enabled, groups overlapping or
111
139
  * directly touching ranges across decoration types into one result, using the union of all
112
140
  * grouped ranges
113
- * (zero-width widgets can join a group without extending it)
141
+ * (zero-width widgets can join a group without extending it). Under
142
+ * `platform_editor_ai_show_diff_patch_1`, a group that starts with content painting above
143
+ * the content that replaced it — reports that widget as its `scrollTarget` spec,
144
+ * so scrolling reaches the visual start of the edit
114
145
  * 6. Results are sorted by from position, then by to position
115
146
  *
116
147
  * @param set - The DecorationSet to extract scrollable decorations from
@@ -46,17 +46,22 @@ export var scrollToDecoration = function scrollToDecoration(view, decorations) {
46
46
  if (!decoration) {
47
47
  return function () {};
48
48
  }
49
+
50
+ // A grouped decoration spans a whole customer-facing edit, which can visually start with a
51
+ // deleted-content widget rather than at the group's `from` position — see `scrollTarget` in
52
+ // `getScrollableDecorations`. Only set under `platform_editor_ai_show_diff_patch_1`.
53
+ var target = isDiffDecoration(decoration) && decoration.spec.scrollTarget || decoration;
49
54
  var rafId = requestAnimationFrame(function () {
50
55
  rafId = null;
51
- if (isDiffDecoration(decoration) && decoration.spec.decorationType === 'widget') {
52
- var _decoration$type;
56
+ if (isDiffDecoration(target) && target.spec.decorationType === 'widget') {
57
+ var _target$type;
53
58
  // @ts-expect-error - decoration.type is not typed public API
54
- var widgetDom = decoration === null || decoration === void 0 || (_decoration$type = decoration.type) === null || _decoration$type === void 0 ? void 0 : _decoration$type.toDOM;
59
+ var widgetDom = target === null || target === void 0 || (_target$type = target.type) === null || _target$type === void 0 ? void 0 : _target$type.toDOM;
55
60
  scrollToSelection(widgetDom);
56
61
  } else {
57
62
  var _view$domAtPos;
58
- var targetNode = view.nodeDOM(decoration === null || decoration === void 0 ? void 0 : decoration.from);
59
- 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;
63
+ var targetNode = view.nodeDOM(target === null || target === void 0 ? void 0 : target.from);
64
+ var node = targetNode instanceof Element ? targetNode : (_view$domAtPos = view.domAtPos(target === null || target === void 0 ? void 0 : target.from)) === null || _view$domAtPos === void 0 ? void 0 : _view$domAtPos.node;
60
65
  scrollToSelection(node);
61
66
  }
62
67
  });
@@ -32,6 +32,14 @@ export type DiffDecorationSpec = BaseDecorationSpec<typeof DecorationFamily.diff
32
32
  */
33
33
  isInserted?: boolean;
34
34
  nodeName?: string;
35
+ /**
36
+ * The decoration to scroll to, when it is not the one carrying this spec: the member of a merged
37
+ * navigation group that paints first. Deleted content is a widget shown above the added content
38
+ * it replaces, and only that widget's own DOM can be scrolled to — resolving the group's `from`
39
+ * position lands on the added content painted after it. Set by `getScrollableDecorations`,
40
+ * consumed by `scrollToDiff`; never present on a decoration in the plugin's `DecorationSet`.
41
+ */
42
+ scrollTarget?: Decoration;
35
43
  };
36
44
  export declare const AnchorTypeKey: {
37
45
  readonly from: 'from';
@@ -20,7 +20,10 @@ export declare function isInlineDiffDecorationRenderableInDoc(doc: PMNode, from:
20
20
  * 5. When `confluence_ncs_step_diffing_version_history` is enabled, groups overlapping or
21
21
  * directly touching ranges across decoration types into one result, using the union of all
22
22
  * grouped ranges
23
- * (zero-width widgets can join a group without extending it)
23
+ * (zero-width widgets can join a group without extending it). Under
24
+ * `platform_editor_ai_show_diff_patch_1`, a group that starts with content painting above
25
+ * the content that replaced it — reports that widget as its `scrollTarget` spec,
26
+ * so scrolling reaches the visual start of the edit
24
27
  * 6. Results are sorted by from position, then by to position
25
28
  *
26
29
  * @param set - The DecorationSet to extract scrollable decorations from
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@atlaskit/editor-plugin-show-diff",
3
- "version": "15.1.8",
3
+ "version": "15.1.9",
4
4
  "description": "ShowDiff plugin for @atlaskit/editor-core",
5
5
  "author": "Atlassian Pty Ltd",
6
6
  "license": "Apache-2.0",