@atlaskit/editor-plugin-show-diff 16.0.2 → 16.0.4

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 (37) hide show
  1. package/CHANGELOG.md +41 -0
  2. package/dist/cjs/pm-plugins/calculateDiff/calculateDiffDecorations.js +17 -2
  3. package/dist/cjs/pm-plugins/calculateDiff/isOpenTokenOnlyChange.js +32 -0
  4. package/dist/cjs/pm-plugins/decorations/createAnchorDecorationWidgets.js +13 -1
  5. package/dist/cjs/pm-plugins/decorations/createContributorTagWidget.js +20 -1
  6. package/dist/cjs/pm-plugins/decorations/extractContributorTags.js +121 -21
  7. package/dist/cjs/pm-plugins/decorations/utils/wrapBlockNodeView.js +6 -6
  8. package/dist/cjs/pm-plugins/decorations/utils/wrapBlockNodeViewStyles.js +8 -10
  9. package/dist/cjs/pm-plugins/decorations/utils/wrapBlockNodeViewStyles.legacy.js +2 -1
  10. package/dist/cjs/ui/ContributorTag/buildContributorTagDom.js +25 -1
  11. package/dist/cjs/ui/ContributorTag/contributorTagController.js +31 -9
  12. package/dist/es2019/pm-plugins/calculateDiff/calculateDiffDecorations.js +17 -2
  13. package/dist/es2019/pm-plugins/calculateDiff/isOpenTokenOnlyChange.js +27 -0
  14. package/dist/es2019/pm-plugins/decorations/createAnchorDecorationWidgets.js +13 -1
  15. package/dist/es2019/pm-plugins/decorations/createContributorTagWidget.js +18 -1
  16. package/dist/es2019/pm-plugins/decorations/extractContributorTags.js +70 -10
  17. package/dist/es2019/pm-plugins/decorations/utils/wrapBlockNodeView.js +6 -6
  18. package/dist/es2019/pm-plugins/decorations/utils/wrapBlockNodeViewStyles.js +6 -8
  19. package/dist/es2019/pm-plugins/decorations/utils/wrapBlockNodeViewStyles.legacy.js +2 -1
  20. package/dist/es2019/ui/ContributorTag/buildContributorTagDom.js +24 -0
  21. package/dist/es2019/ui/ContributorTag/contributorTagController.js +30 -10
  22. package/dist/esm/pm-plugins/calculateDiff/calculateDiffDecorations.js +17 -2
  23. package/dist/esm/pm-plugins/calculateDiff/isOpenTokenOnlyChange.js +26 -0
  24. package/dist/esm/pm-plugins/decorations/createAnchorDecorationWidgets.js +13 -1
  25. package/dist/esm/pm-plugins/decorations/createContributorTagWidget.js +20 -1
  26. package/dist/esm/pm-plugins/decorations/extractContributorTags.js +121 -21
  27. package/dist/esm/pm-plugins/decorations/utils/wrapBlockNodeView.js +6 -6
  28. package/dist/esm/pm-plugins/decorations/utils/wrapBlockNodeViewStyles.js +8 -10
  29. package/dist/esm/pm-plugins/decorations/utils/wrapBlockNodeViewStyles.legacy.js +2 -1
  30. package/dist/esm/ui/ContributorTag/buildContributorTagDom.js +24 -0
  31. package/dist/esm/ui/ContributorTag/contributorTagController.js +32 -10
  32. package/dist/types/pm-plugins/calculateDiff/isOpenTokenOnlyChange.d.ts +22 -0
  33. package/dist/types/pm-plugins/decorations/extractContributorTags.d.ts +14 -1
  34. package/dist/types/pm-plugins/decorations/utils/wrapBlockNodeViewStyles.d.ts +2 -3
  35. package/dist/types/ui/ContributorTag/buildContributorTagDom.d.ts +7 -0
  36. package/dist/types/ui/ContributorTag/contributorTagController.d.ts +11 -0
  37. package/package.json +5 -5
package/CHANGELOG.md CHANGED
@@ -1,5 +1,46 @@
1
1
  # @atlaskit/editor-plugin-show-diff
2
2
 
3
+ ## 16.0.4
4
+
5
+ ### Patch Changes
6
+
7
+ - [`d12940d0a5964`](https://bitbucket.org/atlassian/atlassian-frontend-monorepo/commits/d12940d0a5964) -
8
+ Fix layout and decision diff underlines and attribution colors, preserve column width styles, and
9
+ align the blue indicator outside layout column borders under platform_editor_ai_show_diff_patch_1.
10
+ - [`f044fcdd726df`](https://bitbucket.org/atlassian/atlassian-frontend-monorepo/commits/f044fcdd726df) -
11
+ [EDITOR-8912] Under `platform_editor_ai_show_diff_patch_1`, a change that spans only a block
12
+ node's open token no longer draws a deleted-content widget. A same-type node replacement (an AI
13
+ suggestion rewriting a code block, say) reports the node's attribute change separately from its
14
+ content change, and the attribute half has no deleted content — it rendered as an empty copy of
15
+ the block, e.g. an empty code block above the real diff. Its inserted side, and the node's content
16
+ change, are unaffected.
17
+ - Updated dependencies
18
+
19
+ ## 16.0.3
20
+
21
+ ### Patch Changes
22
+
23
+ - [`0265ca1688db1`](https://bitbucket.org/atlassian/atlassian-frontend-monorepo/commits/0265ca1688db1) -
24
+ [EDITOR-8971] `VanillaTooltip` takes an optional container to append its tooltip to, instead of
25
+ appending it inside the trigger. Opt-in per caller, and used by the diff contributor tag; every
26
+ other tooltip is unchanged.
27
+
28
+ Diff contributor tag fixes, all behind `confluence_ncs_step_diffing_version_history`:
29
+ - The tag's full label no longer renders off screen. Its tooltip is hoisted out of the tag, so
30
+ Popper and the browser measure a top-layer popover from the same origin — inside the trigger
31
+ they disagreed whenever an ancestor was transformed, such as a wide image.
32
+ - Stepping to a change reveals only that change's tag, not every tag the active range touches.
33
+ - A contributor no longer gets two tags inside one navigation stop. Two of their changes that
34
+ merely touched each kept a tag, and the trailing one could be hovered but never stepped to; it
35
+ now folds into the leading tag and stays a hover target. Tags for different contributors sharing
36
+ a stop are still kept apart.
37
+ - A tag on deleted content no longer disappears when the document changes. Any change remaps the
38
+ decoration, which rebuilds its widget and fired the teardown — but the deleted-content widget's
39
+ DOM is reused as-is and runs no mount callback, so the tag was faded out of a change that was
40
+ still the active one. The tag is now kept whenever its host is still in a live editor.
41
+
42
+ - Updated dependencies
43
+
3
44
  ## 16.0.2
4
45
 
5
46
  ### Patch Changes
@@ -27,10 +27,12 @@ var _decorationKeys = require("../decorations/decorationKeys");
27
27
  var _extractContributorTags = require("../decorations/extractContributorTags");
28
28
  var _getAttrChangeRanges = require("../decorations/utils/getAttrChangeRanges");
29
29
  var _getMarkChangeRanges = require("../decorations/utils/getMarkChangeRanges");
30
+ var _getScrollableDecorations = require("../getScrollableDecorations");
30
31
  var _isExtendedEnabled = require("../isExtendedEnabled");
31
32
  var _diffBySteps = require("./diffBySteps");
32
33
  var _groupChangesByBlock = require("./groupChangesByBlock");
33
34
  var _isMarkOnlyChange = require("./isMarkOnlyChange");
35
+ var _isOpenTokenOnlyChange = require("./isOpenTokenOnlyChange");
34
36
  var _optimizeChanges = require("./optimizeChanges");
35
37
  var _selectTokenEncoder2 = require("./selectTokenEncoder");
36
38
  var _simplifyChangesWithAttribution = require("./simplifyChangesWithAttribution");
@@ -504,6 +506,16 @@ var calculateDiffDecorationsInner = function calculateDiffDecorationsInner(_ref8
504
506
  newDoc: tr.doc
505
507
  });
506
508
 
509
+ // The deleted side of a change over a node's open token is an attribute state rather than
510
+ // content, so there is nothing for the deleted-content widget to draw. Decided here, with
511
+ // the other deleted-side suppressions, so the widget is never asked for a slice it can only
512
+ // render as an empty copy of the block (EDITOR-8912). Its inserted side is untouched, and
513
+ // the node's content change is a change of its own.
514
+ var hasNoDeletedContent = (0, _fg.fg)('platform_editor_ai_show_diff_patch_1') && change.deleted.length > 0 && (0, _isOpenTokenOnlyChange.isOpenTokenOnlyChange)({
515
+ change: change,
516
+ originalDoc: originalDoc
517
+ });
518
+
507
519
  // Hoisted because it decides BOTH where the deleted widget is anchored and — since the
508
520
  // widget pins whichever end of the range it sits at — how the indicator anchors below are
509
521
  // allowed to move.
@@ -622,7 +634,7 @@ var calculateDiffDecorationsInner = function calculateDiffDecorationsInner(_ref8
622
634
  coarseTableCellsOnly: useCoarseTableDecoration
623
635
  }))));
624
636
  }
625
- if (change.deleted.length > 0 && !isMarkOnly) {
637
+ if (change.deleted.length > 0 && !isMarkOnly && !hasNoDeletedContent) {
626
638
  var _shouldHideDeleted = shouldHideDeletedSide({
627
639
  change: change,
628
640
  diffType: diffType,
@@ -788,7 +800,10 @@ var calculateDiffDecorationsInner = function calculateDiffDecorationsInner(_ref8
788
800
  });
789
801
  var decorationSet = _view.DecorationSet.empty.add(tr.doc, decorations);
790
802
  return {
791
- contributorTags: showContributorTags ? (0, _extractContributorTags.extractContributorTags)(decorationSet, contributors) : [],
803
+ contributorTags: showContributorTags ? (0, _extractContributorTags.extractContributorTags)(decorationSet, contributors, activeIndexPos,
804
+ // The stops the step buttons walk, so a stop cannot hold a second tag for one
805
+ // contributor that navigation can never reach.
806
+ (0, _getScrollableDecorations.getScrollableDecorations)(decorationSet, tr.doc, diffType)) : [],
792
807
  decorations: decorationSet,
793
808
  diffDescriptors: (0, _decorationKeys.extractDiffDescriptors)(decorationSet)
794
809
  };
@@ -0,0 +1,32 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.isOpenTokenOnlyChange = void 0;
7
+ /**
8
+ * Whether a change spans nothing but a block node's open token, so its deleted side carries no
9
+ * content.
10
+ *
11
+ * The attribute-aware token encoder folds a node's diffable attributes into its open token, so a
12
+ * same-type replacement — an AI suggestion rewriting a code block — is reported as two changes: one
13
+ * over the single position of that token, carrying the attribute change, and one for the content.
14
+ * Slicing the first yields the node with none of its content, which leaves the deleted-content
15
+ * widget nothing to draw but an empty copy of the block (EDITOR-8912). The content change is a
16
+ * change of its own and is decorated in place.
17
+ *
18
+ * A whole node is not matched: its range covers its close token too. Neither is an empty leaf (a
19
+ * `rule`, a `blockCard`), which is one position but slices closed rather than open.
20
+ */
21
+ var isOpenTokenOnlyChange = exports.isOpenTokenOnlyChange = function isOpenTokenOnlyChange(_ref) {
22
+ var change = _ref.change,
23
+ originalDoc = _ref.originalDoc;
24
+ if (change.toA - change.fromA !== 1) {
25
+ return false;
26
+ }
27
+ var slice = originalDoc.slice(change.fromA, change.toA);
28
+ // `childCount === 1` already means `firstChild` is there; the optional chain is for the type,
29
+ // which has it nullable.
30
+ var node = slice.content.firstChild;
31
+ return (slice.openStart > 0 || slice.openEnd > 0) && slice.content.childCount === 1 && (node === null || node === void 0 ? void 0 : node.isBlock) === true && node.content.size === 0;
32
+ };
@@ -51,6 +51,16 @@ var edgeCases = function edgeCases(doc, from) {
51
51
  var node = resolved.node,
52
52
  nodeStart = resolved.nodeStart,
53
53
  beforePos = resolved.beforePos;
54
+ if (node.type.name === 'layoutSection' && (0, _fg.fg)('platform_editor_ai_show_diff_patch_1')) {
55
+ // Columns extend past the node-view wrapper via negative margins (12px or 20px).
56
+ // Measure their container so the indicator stays outside the diff outline, including
57
+ // breakout layouts and after responsive resizing, without changing the column geometry.
58
+ return {
59
+ beforePos: beforePos,
60
+ measurePos: beforePos,
61
+ measureSelector: '[data-layout-section]'
62
+ };
63
+ }
54
64
 
55
65
  /**
56
66
  * All resizable nodes will need dynamic calculations of the block indicator left anchor
@@ -158,10 +168,12 @@ var createLeftAnchorWidget = exports.createLeftAnchorWidget = function createLef
158
168
  anchor.style.setProperty('transform', 'translateX(-50%)');
159
169
  wrapper.appendChild(anchor);
160
170
  var measureWidth = function measureWidth() {
171
+ var _nodeDOM$querySelecto;
161
172
  if (getPos() === undefined || edgeCase.measurePos === undefined) {
162
173
  return;
163
174
  }
164
- var dom = view.nodeDOM(edgeCase.measurePos);
175
+ var nodeDOM = view.nodeDOM(edgeCase.measurePos);
176
+ var dom = edgeCase.measureSelector && nodeDOM instanceof HTMLElement ? (_nodeDOM$querySelecto = nodeDOM.querySelector(edgeCase.measureSelector)) !== null && _nodeDOM$querySelecto !== void 0 ? _nodeDOM$querySelecto : nodeDOM : nodeDOM;
165
177
  if (dom instanceof HTMLElement) {
166
178
  // The left anchor only needs the container width so the
167
179
  // IndicatorBar can align against the block's horizontal extent.
@@ -48,7 +48,26 @@ var mountContributorTag = exports.mountContributorTag = function mountContributo
48
48
  diffId: diffId
49
49
  }));
50
50
  controller.mount(host);
51
- return controller;
51
+
52
+ /**
53
+ * Kept alive when ProseMirror only rebuilt the widget desc around this same host: a document
54
+ * change remaps the decoration and fires `destroy`, but an element `toDOM` is reused verbatim
55
+ * and runs no mount callback, so tearing down here left an active change with no tag
56
+ * (EDITOR-8971).
57
+ *
58
+ * Deferred, because the host is still attached while that update is in flight. A host outside a
59
+ * live editor root is a real removal — including the view being destroyed, which drops the root.
60
+ */
61
+ return {
62
+ destroy: function destroy() {
63
+ return queueMicrotask(function () {
64
+ var _mountContext$getEdit;
65
+ if (!host.isConnected || !((_mountContext$getEdit = mountContext.getEditorRoot()) !== null && _mountContext$getEdit !== void 0 && _mountContext$getEdit.contains(host))) {
66
+ controller.destroy();
67
+ }
68
+ });
69
+ }
70
+ };
52
71
  };
53
72
  var unmountContributorTag = exports.unmountContributorTag = function unmountContributorTag(mount) {
54
73
  mount === null || mount === void 0 || mount.destroy();
@@ -5,6 +5,7 @@ Object.defineProperty(exports, "__esModule", {
5
5
  value: true
6
6
  });
7
7
  exports.extractContributorTags = void 0;
8
+ var _toArray2 = _interopRequireDefault(require("@babel/runtime/helpers/toArray"));
8
9
  var _defineProperty2 = _interopRequireDefault(require("@babel/runtime/helpers/defineProperty"));
9
10
  var _toConsumableArray2 = _interopRequireDefault(require("@babel/runtime/helpers/toConsumableArray"));
10
11
  var _objectWithoutProperties2 = _interopRequireDefault(require("@babel/runtime/helpers/objectWithoutProperties"));
@@ -56,12 +57,48 @@ var isSameBlockChange = function isSameBlockChange(inner, block) {
56
57
  return inner.spec.attributionKey === block.spec.attributionKey && (block.from <= inner.from && inner.to <= block.to || inner.from <= block.from && block.to <= inner.to);
57
58
  };
58
59
 
60
+ /**
61
+ * Which of two changes in the same range leads, and so is the one a tag captions. Position first,
62
+ * then `side`, since deleted content shares its `from` with the content that replaced it and paints
63
+ * above; then the narrower range, so a change nested in another captions itself.
64
+ */
65
+ var byLeadingPosition = function byLeadingPosition(a, b) {
66
+ var _a$decoration$spec$si, _b$decoration$spec$si;
67
+ return a.decoration.from - b.decoration.from || ((_a$decoration$spec$si = a.decoration.spec.side) !== null && _a$decoration$spec$si !== void 0 ? _a$decoration$spec$si : 0) - ((_b$decoration$spec$si = b.decoration.spec.side) !== null && _b$decoration$spec$si !== void 0 ? _b$decoration$spec$si : 0) || a.decoration.to - a.decoration.from - (b.decoration.to - b.decoration.from);
68
+ };
69
+
70
+ /** The targets a navigation range wholly covers, leader first. */
71
+ var containedBy = function containedBy(targets, _ref2) {
72
+ var from = _ref2.from,
73
+ to = _ref2.to;
74
+ return targets.filter(function (_ref3) {
75
+ var decoration = _ref3.decoration;
76
+ return from <= decoration.from && decoration.to <= to;
77
+ }).sort(byLeadingPosition);
78
+ };
79
+
80
+ /**
81
+ * The one tag a navigation step reveals. `spec.isActive` is no use here: it covers everything the
82
+ * group's union range touches, so several tags revealed over each other (EDITOR-8971). Hence
83
+ * containment, and the leading change of what it covers.
84
+ */
85
+ var resolveActiveTarget = function resolveActiveTarget(surviving, activeIndexPos) {
86
+ return containedBy(surviving, activeIndexPos)[0];
87
+ };
88
+
59
89
  /**
60
90
  * One model per diff decoration whose attribution resolves to a supplied contributor; anything
61
91
  * unattributed, untaggable or unresolved is skipped. A replacement's two decorations are collapsed
62
92
  * into a single tag, on whichever half renders first — see `isSameChange` and `leadsReplacement`.
93
+ *
94
+ * `activeIndexPos`, when given, reveals exactly one of the tags — see `resolveActiveTarget`.
95
+ * Absent, each tag keeps the active state its own decoration was drawn with.
96
+ *
97
+ * `stops`, when given, is the navigation stop list the step buttons walk
98
+ * (`getScrollableDecorations`). One contributor cannot hold two tags in one stop, since only the
99
+ * leading one would ever be reachable. Absent, every decoration keeps its own tag.
63
100
  */
64
- var extractContributorTags = exports.extractContributorTags = function extractContributorTags(decorations, contributors) {
101
+ var extractContributorTags = exports.extractContributorTags = function extractContributorTags(decorations, contributors, activeIndexPos, stops) {
65
102
  if (!contributors) {
66
103
  return [];
67
104
  }
@@ -94,25 +131,30 @@ var extractContributorTags = exports.extractContributorTags = function extractCo
94
131
  } finally {
95
132
  _iterator.f();
96
133
  }
97
- var inlineTargets = targets.filter(function (_ref2) {
98
- var decoration = _ref2.decoration;
134
+ var inlineTargets = targets.filter(function (_ref4) {
135
+ var decoration = _ref4.decoration;
99
136
  return decoration.spec.decorationType === 'inline';
100
137
  });
101
- var blockTargets = targets.filter(function (_ref3) {
102
- var decoration = _ref3.decoration;
138
+ var blockTargets = targets.filter(function (_ref5) {
139
+ var decoration = _ref5.decoration;
103
140
  return decoration.spec.decorationType === 'block';
104
141
  });
105
- var widgetTargets = targets.filter(function (_ref4) {
106
- var decoration = _ref4.decoration;
142
+ var widgetTargets = targets.filter(function (_ref6) {
143
+ var decoration = _ref6.decoration;
107
144
  return decoration.spec.decorationType === 'widget';
108
145
  });
109
146
  var linkedDiffIds = new Map();
110
147
  var folded = new Set();
111
148
  var fold = function fold(host, target) {
112
- var _linkedDiffIds$get;
149
+ var _linkedDiffIds$get, _linkedDiffIds$get2;
113
150
  folded.add(target);
114
151
  var hostDiffId = host.decoration.spec.diffId;
115
- linkedDiffIds.set(hostDiffId, [].concat((0, _toConsumableArray2.default)((_linkedDiffIds$get = linkedDiffIds.get(hostDiffId)) !== null && _linkedDiffIds$get !== void 0 ? _linkedDiffIds$get : []), [target.decoration.spec.diffId]));
152
+ var targetDiffId = target.decoration.spec.diffId;
153
+ // A target that already hosts folded tags hands them up, so no hover target is orphaned when
154
+ // one host folds into another.
155
+ var inherited = (_linkedDiffIds$get = linkedDiffIds.get(targetDiffId)) !== null && _linkedDiffIds$get !== void 0 ? _linkedDiffIds$get : [];
156
+ linkedDiffIds.delete(targetDiffId);
157
+ linkedDiffIds.set(hostDiffId, [].concat((0, _toConsumableArray2.default)((_linkedDiffIds$get2 = linkedDiffIds.get(hostDiffId)) !== null && _linkedDiffIds$get2 !== void 0 ? _linkedDiffIds$get2 : []), [targetDiffId], (0, _toConsumableArray2.default)(inherited)));
116
158
  };
117
159
  var _loop = function _loop() {
118
160
  var target = _targets[_i];
@@ -122,8 +164,8 @@ var extractContributorTags = exports.extractContributorTags = function extractCo
122
164
  }
123
165
 
124
166
  // A block outranks both other kinds, so it is tried first.
125
- var block = blockTargets.find(function (_ref5) {
126
- var decoration = _ref5.decoration;
167
+ var block = blockTargets.find(function (_ref7) {
168
+ var decoration = _ref7.decoration;
127
169
  return isSameBlockChange(target.decoration, decoration);
128
170
  });
129
171
  if (block) {
@@ -132,8 +174,8 @@ var extractContributorTags = exports.extractContributorTags = function extractCo
132
174
  }
133
175
  if (decorationType === 'inline') {
134
176
  // The deleted half renders in front of this one, so that half captions the change.
135
- var leadingWidget = widgetTargets.find(function (_ref6) {
136
- var decoration = _ref6.decoration;
177
+ var leadingWidget = widgetTargets.find(function (_ref8) {
178
+ var decoration = _ref8.decoration;
137
179
  return leadsReplacement(decoration) && isSameChange(decoration, target.decoration);
138
180
  });
139
181
  if (leadingWidget) {
@@ -146,8 +188,8 @@ var extractContributorTags = exports.extractContributorTags = function extractCo
146
188
  if (leadsReplacement(target.decoration)) {
147
189
  return 0; // continue
148
190
  }
149
- var host = inlineTargets.find(function (_ref7) {
150
- var decoration = _ref7.decoration;
191
+ var host = inlineTargets.find(function (_ref9) {
192
+ var decoration = _ref9.decoration;
151
193
  return isSameChange(target.decoration, decoration);
152
194
  });
153
195
  if (host) {
@@ -159,14 +201,72 @@ var extractContributorTags = exports.extractContributorTags = function extractCo
159
201
  _ret = _loop();
160
202
  if (_ret === 0) continue;
161
203
  }
162
- return targets.filter(function (target) {
204
+
205
+ // One tag per contributor per navigation stop. The folds above only catch a replacement's two
206
+ // halves and a block's own highlights; two of one contributor's changes that merely touch are a
207
+ // single stop (`groupTouchingDecorations`) yet kept a tag each, so the stop's trailing tag was
208
+ // drawn but could never be stepped to (EDITOR-8971). It folds into the leading tag of its
209
+ // contributor, staying a hover target. Contributors are kept apart: a stop spanning two of them
210
+ // still captions each, rather than crediting one for the other's change.
211
+ var _iterator2 = _createForOfIteratorHelper(stops !== null && stops !== void 0 ? stops : []),
212
+ _step2;
213
+ try {
214
+ for (_iterator2.s(); !(_step2 = _iterator2.n()).done;) {
215
+ var stop = _step2.value;
216
+ var byContributor = new Map();
217
+ var _iterator3 = _createForOfIteratorHelper(containedBy(targets.filter(function (target) {
218
+ return !folded.has(target);
219
+ }), stop)),
220
+ _step3;
221
+ try {
222
+ for (_iterator3.s(); !(_step3 = _iterator3.n()).done;) {
223
+ var _target$decoration$sp, _byContributor$get;
224
+ var target = _step3.value;
225
+ var key = (_target$decoration$sp = target.decoration.spec.attributionKey) !== null && _target$decoration$sp !== void 0 ? _target$decoration$sp : '';
226
+ byContributor.set(key, [].concat((0, _toConsumableArray2.default)((_byContributor$get = byContributor.get(key)) !== null && _byContributor$get !== void 0 ? _byContributor$get : []), [target]));
227
+ }
228
+ } catch (err) {
229
+ _iterator3.e(err);
230
+ } finally {
231
+ _iterator3.f();
232
+ }
233
+ var _iterator4 = _createForOfIteratorHelper(byContributor.values()),
234
+ _step4;
235
+ try {
236
+ var _loop2 = function _loop2() {
237
+ var _step4$value = (0, _toArray2.default)(_step4.value),
238
+ leader = _step4$value[0],
239
+ trailing = _arrayLikeToArray(_step4$value).slice(1);
240
+ trailing.forEach(function (target) {
241
+ return fold(leader, target);
242
+ });
243
+ };
244
+ for (_iterator4.s(); !(_step4 = _iterator4.n()).done;) {
245
+ _loop2();
246
+ }
247
+ } catch (err) {
248
+ _iterator4.e(err);
249
+ } finally {
250
+ _iterator4.f();
251
+ }
252
+ }
253
+ } catch (err) {
254
+ _iterator2.e(err);
255
+ } finally {
256
+ _iterator2.f();
257
+ }
258
+ var surviving = targets.filter(function (target) {
163
259
  return !folded.has(target);
164
- }).map(function (_ref8) {
165
- var contributor = _ref8.contributor,
166
- spec = _ref8.decoration.spec;
260
+ });
261
+ var activeTarget = activeIndexPos === undefined ? undefined : resolveActiveTarget(surviving, activeIndexPos);
262
+ return surviving.map(function (target) {
263
+ var contributor = target.contributor,
264
+ spec = target.decoration.spec;
167
265
  var connected = contributor.connectedToKey ? contributors[contributor.connectedToKey] : undefined;
168
266
  var connectedContributor = connected ? toTagContributor(connected) : undefined;
169
267
  var linked = linkedDiffIds.get(spec.diffId);
268
+ // Only the navigated tag reveals; with no active range, the decoration's own state stands.
269
+ var isActive = activeIndexPos === undefined ? spec.isActive : target === activeTarget;
170
270
  return _objectSpread(_objectSpread(_objectSpread(_objectSpread(_objectSpread({
171
271
  contributor: toTagContributor(contributor),
172
272
  diffId: spec.diffId
@@ -174,8 +274,8 @@ var extractContributorTags = exports.extractContributorTags = function extractCo
174
274
  connectedContributor: connectedContributor
175
275
  } : {}), spec.colorScheme ? {
176
276
  colorScheme: spec.colorScheme
177
- } : {}), spec.isActive !== undefined ? {
178
- isActive: spec.isActive
277
+ } : {}), isActive !== undefined ? {
278
+ isActive: isActive
179
279
  } : {}), spec.isInserted !== undefined ? {
180
280
  isInserted: spec.isInserted
181
281
  } : {}), linked ? {
@@ -217,25 +217,25 @@ var applyMultiContainerLikeStyles = function applyMultiContainerLikeStyles(_ref6
217
217
  var targetNodeName = (0, _expValEquals.expValEquals)('platform_editor_nest_table_in_panel', 'isEnabled', true) ? (0, _nodeTypeUtils.getBaseNodeTypeName)(targetNode.type) : targetNode.type.name;
218
218
  var nodeSpecificStyle = (0, _wrapBlockNodeViewStyles.getChangedNodeStyle)(targetNodeName, colorScheme, isInserted, isActive, diffType, hideAddedDiffsUnderline) || '';
219
219
  if (targetNode.type.name === 'decisionList') {
220
- var nestedInsertedNodeStyle = (0, _wrapBlockNodeViewStyles.resolveNestedInsertedNodeStyle)();
220
+ var nestedInsertedNodeStyle = (0, _wrapBlockNodeViewStyles.resolveNestedInsertedNodeStyle)(colorScheme);
221
221
  element.querySelectorAll('li').forEach(function (listItem) {
222
222
  var currentListItemStyle = listItem.getAttribute('style') || '';
223
- listItem.setAttribute('style', "".concat(currentListItemStyle).concat(nestedInsertedNodeStyle));
223
+ listItem.setAttribute('style', (0, _fg.fg)('platform_editor_ai_show_diff_patch_1') ? combineStyles(currentListItemStyle, nestedInsertedNodeStyle) : "".concat(currentListItemStyle).concat(nestedInsertedNodeStyle));
224
224
  });
225
225
  } else if (targetNode.type.name === 'layoutSection') {
226
- var _nestedInsertedNodeStyle = (0, _wrapBlockNodeViewStyles.resolveNestedInsertedNodeStyle)();
226
+ var _nestedInsertedNodeStyle = (0, _wrapBlockNodeViewStyles.resolveNestedInsertedNodeStyle)(colorScheme);
227
227
  element.querySelectorAll('[data-layout-column="true"]').forEach(function (section) {
228
228
  var currentSectionStyle = section.getAttribute('style') || '';
229
- section.setAttribute('style', "".concat(currentSectionStyle).concat(_nestedInsertedNodeStyle));
229
+ section.setAttribute('style', (0, _fg.fg)('platform_editor_ai_show_diff_patch_1') ? combineStyles(currentSectionStyle, _nestedInsertedNodeStyle) : "".concat(currentSectionStyle).concat(_nestedInsertedNodeStyle));
230
230
  });
231
231
  } else if (targetNode.type.name === 'taskList') {
232
- var _nestedInsertedNodeStyle2 = (0, _wrapBlockNodeViewStyles.resolveNestedInsertedNodeStyle)();
232
+ var _nestedInsertedNodeStyle2 = (0, _wrapBlockNodeViewStyles.resolveNestedInsertedNodeStyle)(colorScheme);
233
233
  element.querySelectorAll('li').forEach(function (listItem) {
234
234
  var currentListItemStyle = listItem.getAttribute('style') || '';
235
235
  listItem.setAttribute('style', "".concat(currentListItemStyle).concat(_nestedInsertedNodeStyle2));
236
236
  });
237
237
  }
238
- element.setAttribute('style', "".concat(currentStyle, ";").concat(nodeSpecificStyle));
238
+ element.setAttribute('style', (0, _fg.fg)('platform_editor_ai_show_diff_patch_1') ? combineStyles(currentStyle, nodeSpecificStyle) : "".concat(currentStyle, ";").concat(nodeSpecificStyle));
239
239
  };
240
240
  var combineStyles = function combineStyles(currentStyle, appendedStyle) {
241
241
  var separator = currentStyle && appendedStyle && !currentStyle.trimEnd().endsWith(';') ? '; ' : '';
@@ -7,6 +7,7 @@ Object.defineProperty(exports, "__esModule", {
7
7
  exports.resolveRemovedLozengeStyle = exports.resolveNestedInsertedNodeStyle = exports.resolveDeletedNodeCSSVariables = exports.resolveCellOverlayStyle = exports.isTextLikeBlockNode = exports.isMultiContainerBlockNode = exports.hasRestingDeletedRing = exports.getInsertedContentStyle = exports.getDeletedContentStyleUnbounded = exports.getDeletedContentStyle = exports.getChangedNodeStyle = exports.getChangedContentStyle = void 0;
8
8
  var _defineProperty2 = _interopRequireDefault(require("@babel/runtime/helpers/defineProperty"));
9
9
  var _isExperimentEnabled = require("@atlaskit/platform-feature-experiments/is-experiment-enabled");
10
+ var _fg = require("@atlaskit/platform-feature-flags/fg");
10
11
  var _expValEquals = require("@atlaskit/tmp-editor-statsig/exp-val-equals");
11
12
  var _isExtendedEnabled = require("../../isExtendedEnabled");
12
13
  var _factory = require("../colorSchemes/factory");
@@ -30,9 +31,7 @@ var getColorScheme = function getColorScheme(colorScheme) {
30
31
  };
31
32
 
32
33
  /**
33
- * Inserted content inside a multi-container or list node always uses the standard scheme, so a
34
- * traditional diff shows purple here. Pre-existing: these call sites predate traditional.
35
- * Preserved as-is; see EDITOR-8281.
34
+ * Preserve the historical purple nested outlines when the layout/decision diff patch is off.
36
35
  */
37
36
  var nestedContentScheme = _schemes.standardScheme;
38
37
 
@@ -68,7 +67,7 @@ var getChangedNodeStyleNext = function getChangedNodeStyleNext(nodeName, colorSc
68
67
  var colors = getColorScheme(colorScheme);
69
68
  if ((0, _isExtendedEnabled.isExtendedEnabled)(diffType) && isInserted) {
70
69
  if (isMultiContainerBlockNode(nodeName)) {
71
- return hideAddedDiffsUnderline ? (0, _factory.buildInsertStyleInBlockExtendedNoUnderline)(nestedContentScheme) : (0, _factory.buildInsertStyleInBlockExtended)(nestedContentScheme);
70
+ return hideAddedDiffsUnderline || (0, _fg.fg)('platform_editor_ai_show_diff_patch_1') ? (0, _factory.buildInsertStyleInBlockExtendedNoUnderline)(nestedContentScheme) : (0, _factory.buildInsertStyleInBlockExtended)(nestedContentScheme);
72
71
  }
73
72
  if (isTextLikeBlockNode(nodeName)) {
74
73
  return undefined;
@@ -129,8 +128,8 @@ var resolveRemovedLozengeStyleNext = function resolveRemovedLozengeStyleNext(col
129
128
  var colors = getColorScheme(colorScheme);
130
129
  return isActive ? (0, _factory.buildDeletedLozengeActiveStyle)(colors) : (0, _factory.buildDeletedLozengeStyle)(colors);
131
130
  };
132
- var resolveNestedInsertedNodeStyleNext = function resolveNestedInsertedNodeStyleNext() {
133
- return (0, _factory.buildInsertStyleNode)(nestedContentScheme);
131
+ var resolveNestedInsertedNodeStyleNext = function resolveNestedInsertedNodeStyleNext(colorScheme) {
132
+ return (0, _factory.buildInsertStyleNode)((0, _fg.fg)('platform_editor_ai_show_diff_patch_1') ? getColorScheme(colorScheme) : nestedContentScheme);
134
133
  };
135
134
 
136
135
  // Only 'stateful' schemes have a resting ring.
@@ -194,11 +193,10 @@ var resolveRemovedLozengeStyle = exports.resolveRemovedLozengeStyle = function r
194
193
 
195
194
  /**
196
195
  * Style for inserted content nested inside a multi-container or list node — the `decisionList` and
197
- * `taskList` `li`s and the `layoutSection` columns. Scheme-independent: see
198
- * `nestedContentScheme` above.
196
+ * `taskList` `li`s and the `layoutSection` columns.
199
197
  */
200
- var resolveNestedInsertedNodeStyle = exports.resolveNestedInsertedNodeStyle = function resolveNestedInsertedNodeStyle() {
201
- return (0, _isExperimentEnabled.isExperimentEnabled)('platform_editor_show_diff_color_scheme_refactor') ? resolveNestedInsertedNodeStyleNext() : (0, _wrapBlockNodeViewStyles.resolveNestedInsertedNodeStyleLegacy)();
198
+ var resolveNestedInsertedNodeStyle = exports.resolveNestedInsertedNodeStyle = function resolveNestedInsertedNodeStyle(colorScheme) {
199
+ return (0, _isExperimentEnabled.isExperimentEnabled)('platform_editor_show_diff_color_scheme_refactor') ? resolveNestedInsertedNodeStyleNext(colorScheme) : (0, _wrapBlockNodeViewStyles.resolveNestedInsertedNodeStyleLegacy)();
202
200
  };
203
201
 
204
202
  /**
@@ -5,6 +5,7 @@ Object.defineProperty(exports, "__esModule", {
5
5
  });
6
6
  exports.resolveRemovedLozengeStyleLegacy = exports.resolveNestedInsertedNodeStyleLegacy = exports.resolveCellOverlayStyleLegacy = exports.hasRestingDeletedRingLegacy = exports.getInsertedContentStyleLegacy = exports.getDeletedContentStyleUnboundedLegacy = exports.getDeletedContentStyleLegacy = exports.getChangedNodeStyleLegacy = exports.getChangedContentStyleLegacy = void 0;
7
7
  var _lazyNodeView = require("@atlaskit/editor-common/lazy-node-view");
8
+ var _fg = require("@atlaskit/platform-feature-flags/fg");
8
9
  var _expValEquals = require("@atlaskit/tmp-editor-statsig/exp-val-equals");
9
10
  var _isExtendedEnabled = require("../../isExtendedEnabled");
10
11
  var _standard = require("../colorSchemes/standard");
@@ -112,7 +113,7 @@ var getChangedNodeStyleLegacy = exports.getChangedNodeStyleLegacy = function get
112
113
  var isTraditional = colorScheme === 'traditional';
113
114
  if ((0, _isExtendedEnabled.isExtendedEnabled)(diffType) && isInserted) {
114
115
  if (isMultiContainerBlockNode(nodeName)) {
115
- return hideAddedDiffsUnderline ? _standard.editingContentStyleInBlockExtendedNoUnderline : _standard.editingContentStyleInBlockExtended;
116
+ return hideAddedDiffsUnderline || (0, _fg.fg)('platform_editor_ai_show_diff_patch_1') ? _standard.editingContentStyleInBlockExtendedNoUnderline : _standard.editingContentStyleInBlockExtended;
116
117
  }
117
118
  if (isTextLikeBlockNode(nodeName)) {
118
119
  return undefined;
@@ -4,7 +4,7 @@ var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefau
4
4
  Object.defineProperty(exports, "__esModule", {
5
5
  value: true
6
6
  });
7
- exports.buildContributorTagDom = exports.TAG_EXIT_FALLBACK_MS = exports.MAX_TAG_WIDTH = exports.CONTRIBUTOR_TAG_Z_INDEX = exports.CONTRIBUTOR_TAG_TESTID = exports.CONTRIBUTOR_TAG_REVEALED_ATTRIBUTE = exports.CONTRIBUTOR_TAG_NAME_TESTID = exports.CONTRIBUTOR_TAG_CLASS = void 0;
7
+ exports.buildContributorTagDom = exports.TAG_EXIT_FALLBACK_MS = exports.MAX_TAG_WIDTH = exports.CONTRIBUTOR_TAG_Z_INDEX = exports.CONTRIBUTOR_TAG_TOOLTIP_STYLES = exports.CONTRIBUTOR_TAG_TESTID = exports.CONTRIBUTOR_TAG_REVEALED_ATTRIBUTE = exports.CONTRIBUTOR_TAG_NAME_TESTID = exports.CONTRIBUTOR_TAG_CLASS = void 0;
8
8
  var _slicedToArray2 = _interopRequireDefault(require("@babel/runtime/helpers/slicedToArray"));
9
9
  var _lazyNodeView = require("@atlaskit/editor-common/lazy-node-view");
10
10
  var CONTRIBUTOR_TAG_TESTID = exports.CONTRIBUTOR_TAG_TESTID = 'diff-contributor-tag';
@@ -49,6 +49,30 @@ var CONTRIBUTOR_TAG_REVEALED_ATTRIBUTE = exports.CONTRIBUTOR_TAG_REVEALED_ATTRIB
49
49
  */
50
50
  var TAG_EXIT_FALLBACK_MS = exports.TAG_EXIT_FALLBACK_MS = 1200;
51
51
 
52
+ /**
53
+ * The tooltip's look, inline rather than through `VANILLA_TOOLTIP_DEFAULT_CLASS`: this tooltip is
54
+ * hoisted out of the tag, and that class's rule is scoped under `.ProseMirror` — see
55
+ * `resolveTooltipContainer`. Otherwise `vanillaTooltipDefaultStyles` in editor-core, which is the
56
+ * look every other vanilla tooltip has; keep the two in step.
57
+ */
58
+ var CONTRIBUTOR_TAG_TOOLTIP_STYLES = exports.CONTRIBUTOR_TAG_TOOLTIP_STYLES = {
59
+ boxSizing: 'border-box',
60
+ maxWidth: '240px',
61
+ backgroundColor: "var(--ds-background-neutral-bold, #292A2E)",
62
+ // A `popover` is given one by the UA stylesheet.
63
+ border: 'none',
64
+ borderRadius: "var(--ds-radius-small, 3px)",
65
+ color: "var(--ds-text-inverse, #FFFFFF)",
66
+ font: "var(--ds-font-body-small, normal 400 12px/16px \"Atlassian Sans\", ui-sans-serif, -apple-system, BlinkMacSystemFont, \"Segoe UI\", Ubuntu, \"Helvetica Neue\", sans-serif)",
67
+ fontFamily: "var(--ds-font-family-body, \"Atlassian Sans\", ui-sans-serif, -apple-system, BlinkMacSystemFont, \"Segoe UI\", Ubuntu, \"Helvetica Neue\", sans-serif)",
68
+ overflowWrap: 'break-word',
69
+ paddingBlock: "var(--ds-space-050, 4px)",
70
+ paddingInline: "var(--ds-space-075, 6px)",
71
+ whiteSpace: 'normal',
72
+ // A tooltip is never a hit target — it hangs over the document's own content.
73
+ pointerEvents: 'none'
74
+ };
75
+
52
76
  // Positioned against the host widget its decoration renders on the change's first character:
53
77
  // `bottom: 100%` lifts the tag onto the line above, `inset-inline-start` starts it on that
54
78
  // character.
@@ -400,7 +400,7 @@ var ContributorTagController = exports.ContributorTagController = /*#__PURE__*/f
400
400
  }, {
401
401
  key: "syncTooltip",
402
402
  value: function syncTooltip() {
403
- var _this$tooltip2, _this$dom$tag$querySe;
403
+ var _this$tooltip2;
404
404
  var content = this.dom ? this.fullLabel : undefined;
405
405
  if (content === this.tooltipContent) {
406
406
  return;
@@ -411,17 +411,39 @@ var ContributorTagController = exports.ContributorTagController = /*#__PURE__*/f
411
411
  if (!content || !this.dom) {
412
412
  return;
413
413
  }
414
- this.tooltip = new _vanillaTooltip.VanillaTooltip(this.dom.tag, content, undefined,
415
- // `ak-editor-vanilla-tooltip-default` opts into the shared `VanillaTooltip` look, defined as
416
- // `vanillaTooltipDefaultStyles` in both EditorContentContainer stylesheets — keep in sync when
417
- // renaming. Those styles are scoped under `.ProseMirror`, and the tag renders inside it.
418
- 'ak-editor-vanilla-tooltip-default');
414
+ this.tooltip = new _vanillaTooltip.VanillaTooltip(this.dom.tag, content,
415
+ // Generated id.
416
+ undefined,
417
+ // No class: the look is inline, because a hoisted tooltip is outside the `.ProseMirror`
418
+ // scope `VANILLA_TOOLTIP_DEFAULT_CLASS` is keyed on — see `CONTRIBUTOR_TAG_TOOLTIP_STYLES`.
419
+ '',
420
+ // Default delay, no `onShow`, and the default `top` placement.
421
+ undefined, _buildContributorTagDom.CONTRIBUTOR_TAG_TOOLTIP_STYLES, undefined, undefined, this.resolveTooltipContainer());
419
422
  // `VanillaTooltip` sets `aria-describedby` on its trigger, which would announce the label a
420
423
  // second time — the hidden child already announces it, and names the tag with it.
421
424
  this.dom.tag.removeAttribute('aria-describedby');
422
- // The popover is a child of the trigger, so its text would be a third copy of the same
423
- // sentence. It stays visible for sighted users, who need the part of the name the tag clipped.
424
- (_this$dom$tag$querySe = this.dom.tag.querySelector(':scope > [role="tooltip"]')) === null || _this$dom$tag$querySe === void 0 || _this$dom$tag$querySe.setAttribute('aria-hidden', 'true');
425
+ // Out of the tag but still in the document, so its text would otherwise be a third copy of the
426
+ // same sentence. It stays visible for sighted users, who need the part of the name the tag
427
+ // clipped.
428
+ this.tooltip.element.setAttribute('aria-hidden', 'true');
429
+ }
430
+
431
+ /**
432
+ * Where the tooltip is appended, rather than inside the tag.
433
+ *
434
+ * Popper and the browser only agree on a top-layer popover's origin when nothing above it is
435
+ * transformed, and the tag hangs under the editor's own transformed nodes — inside a wide image
436
+ * that is `.rich-media-item`, whose `translateX(-50%)` threw the tooltip off screen
437
+ * (EDITOR-8971). The content area is the nearest ancestor with none of that above it, and keeps
438
+ * the tooltip inside the editor it belongs to; the document body covers an appearance that has
439
+ * no content area.
440
+ */
441
+ }, {
442
+ key: "resolveTooltipContainer",
443
+ value: function resolveTooltipContainer() {
444
+ var _editorRoot$closest;
445
+ var editorRoot = this.options.getEditorRoot();
446
+ return (_editorRoot$closest = editorRoot === null || editorRoot === void 0 ? void 0 : editorRoot.closest('.ak-editor-content-area')) !== null && _editorRoot$closest !== void 0 ? _editorRoot$closest : editorRoot === null || editorRoot === void 0 ? void 0 : editorRoot.ownerDocument.body;
425
447
  }
426
448
 
427
449
  /**