@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
@@ -1,3 +1,4 @@
1
+ import _toArray from "@babel/runtime/helpers/toArray";
1
2
  import _defineProperty from "@babel/runtime/helpers/defineProperty";
2
3
  import _toConsumableArray from "@babel/runtime/helpers/toConsumableArray";
3
4
  import _objectWithoutProperties from "@babel/runtime/helpers/objectWithoutProperties";
@@ -50,12 +51,48 @@ var isSameBlockChange = function isSameBlockChange(inner, block) {
50
51
  return inner.spec.attributionKey === block.spec.attributionKey && (block.from <= inner.from && inner.to <= block.to || inner.from <= block.from && block.to <= inner.to);
51
52
  };
52
53
 
54
+ /**
55
+ * Which of two changes in the same range leads, and so is the one a tag captions. Position first,
56
+ * then `side`, since deleted content shares its `from` with the content that replaced it and paints
57
+ * above; then the narrower range, so a change nested in another captions itself.
58
+ */
59
+ var byLeadingPosition = function byLeadingPosition(a, b) {
60
+ var _a$decoration$spec$si, _b$decoration$spec$si;
61
+ 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);
62
+ };
63
+
64
+ /** The targets a navigation range wholly covers, leader first. */
65
+ var containedBy = function containedBy(targets, _ref2) {
66
+ var from = _ref2.from,
67
+ to = _ref2.to;
68
+ return targets.filter(function (_ref3) {
69
+ var decoration = _ref3.decoration;
70
+ return from <= decoration.from && decoration.to <= to;
71
+ }).sort(byLeadingPosition);
72
+ };
73
+
74
+ /**
75
+ * The one tag a navigation step reveals. `spec.isActive` is no use here: it covers everything the
76
+ * group's union range touches, so several tags revealed over each other (EDITOR-8971). Hence
77
+ * containment, and the leading change of what it covers.
78
+ */
79
+ var resolveActiveTarget = function resolveActiveTarget(surviving, activeIndexPos) {
80
+ return containedBy(surviving, activeIndexPos)[0];
81
+ };
82
+
53
83
  /**
54
84
  * One model per diff decoration whose attribution resolves to a supplied contributor; anything
55
85
  * unattributed, untaggable or unresolved is skipped. A replacement's two decorations are collapsed
56
86
  * into a single tag, on whichever half renders first — see `isSameChange` and `leadsReplacement`.
87
+ *
88
+ * `activeIndexPos`, when given, reveals exactly one of the tags — see `resolveActiveTarget`.
89
+ * Absent, each tag keeps the active state its own decoration was drawn with.
90
+ *
91
+ * `stops`, when given, is the navigation stop list the step buttons walk
92
+ * (`getScrollableDecorations`). One contributor cannot hold two tags in one stop, since only the
93
+ * leading one would ever be reachable. Absent, every decoration keeps its own tag.
57
94
  */
58
- export var extractContributorTags = function extractContributorTags(decorations, contributors) {
95
+ export var extractContributorTags = function extractContributorTags(decorations, contributors, activeIndexPos, stops) {
59
96
  if (!contributors) {
60
97
  return [];
61
98
  }
@@ -88,25 +125,30 @@ export var extractContributorTags = function extractContributorTags(decorations,
88
125
  } finally {
89
126
  _iterator.f();
90
127
  }
91
- var inlineTargets = targets.filter(function (_ref2) {
92
- var decoration = _ref2.decoration;
128
+ var inlineTargets = targets.filter(function (_ref4) {
129
+ var decoration = _ref4.decoration;
93
130
  return decoration.spec.decorationType === 'inline';
94
131
  });
95
- var blockTargets = targets.filter(function (_ref3) {
96
- var decoration = _ref3.decoration;
132
+ var blockTargets = targets.filter(function (_ref5) {
133
+ var decoration = _ref5.decoration;
97
134
  return decoration.spec.decorationType === 'block';
98
135
  });
99
- var widgetTargets = targets.filter(function (_ref4) {
100
- var decoration = _ref4.decoration;
136
+ var widgetTargets = targets.filter(function (_ref6) {
137
+ var decoration = _ref6.decoration;
101
138
  return decoration.spec.decorationType === 'widget';
102
139
  });
103
140
  var linkedDiffIds = new Map();
104
141
  var folded = new Set();
105
142
  var fold = function fold(host, target) {
106
- var _linkedDiffIds$get;
143
+ var _linkedDiffIds$get, _linkedDiffIds$get2;
107
144
  folded.add(target);
108
145
  var hostDiffId = host.decoration.spec.diffId;
109
- linkedDiffIds.set(hostDiffId, [].concat(_toConsumableArray((_linkedDiffIds$get = linkedDiffIds.get(hostDiffId)) !== null && _linkedDiffIds$get !== void 0 ? _linkedDiffIds$get : []), [target.decoration.spec.diffId]));
146
+ var targetDiffId = target.decoration.spec.diffId;
147
+ // A target that already hosts folded tags hands them up, so no hover target is orphaned when
148
+ // one host folds into another.
149
+ var inherited = (_linkedDiffIds$get = linkedDiffIds.get(targetDiffId)) !== null && _linkedDiffIds$get !== void 0 ? _linkedDiffIds$get : [];
150
+ linkedDiffIds.delete(targetDiffId);
151
+ linkedDiffIds.set(hostDiffId, [].concat(_toConsumableArray((_linkedDiffIds$get2 = linkedDiffIds.get(hostDiffId)) !== null && _linkedDiffIds$get2 !== void 0 ? _linkedDiffIds$get2 : []), [targetDiffId], _toConsumableArray(inherited)));
110
152
  };
111
153
  var _loop = function _loop() {
112
154
  var target = _targets[_i];
@@ -116,8 +158,8 @@ export var extractContributorTags = function extractContributorTags(decorations,
116
158
  }
117
159
 
118
160
  // A block outranks both other kinds, so it is tried first.
119
- var block = blockTargets.find(function (_ref5) {
120
- var decoration = _ref5.decoration;
161
+ var block = blockTargets.find(function (_ref7) {
162
+ var decoration = _ref7.decoration;
121
163
  return isSameBlockChange(target.decoration, decoration);
122
164
  });
123
165
  if (block) {
@@ -126,8 +168,8 @@ export var extractContributorTags = function extractContributorTags(decorations,
126
168
  }
127
169
  if (decorationType === 'inline') {
128
170
  // The deleted half renders in front of this one, so that half captions the change.
129
- var leadingWidget = widgetTargets.find(function (_ref6) {
130
- var decoration = _ref6.decoration;
171
+ var leadingWidget = widgetTargets.find(function (_ref8) {
172
+ var decoration = _ref8.decoration;
131
173
  return leadsReplacement(decoration) && isSameChange(decoration, target.decoration);
132
174
  });
133
175
  if (leadingWidget) {
@@ -140,8 +182,8 @@ export var extractContributorTags = function extractContributorTags(decorations,
140
182
  if (leadsReplacement(target.decoration)) {
141
183
  return 0; // continue
142
184
  }
143
- var host = inlineTargets.find(function (_ref7) {
144
- var decoration = _ref7.decoration;
185
+ var host = inlineTargets.find(function (_ref9) {
186
+ var decoration = _ref9.decoration;
145
187
  return isSameChange(target.decoration, decoration);
146
188
  });
147
189
  if (host) {
@@ -153,14 +195,72 @@ export var extractContributorTags = function extractContributorTags(decorations,
153
195
  _ret = _loop();
154
196
  if (_ret === 0) continue;
155
197
  }
156
- return targets.filter(function (target) {
198
+
199
+ // One tag per contributor per navigation stop. The folds above only catch a replacement's two
200
+ // halves and a block's own highlights; two of one contributor's changes that merely touch are a
201
+ // single stop (`groupTouchingDecorations`) yet kept a tag each, so the stop's trailing tag was
202
+ // drawn but could never be stepped to (EDITOR-8971). It folds into the leading tag of its
203
+ // contributor, staying a hover target. Contributors are kept apart: a stop spanning two of them
204
+ // still captions each, rather than crediting one for the other's change.
205
+ var _iterator2 = _createForOfIteratorHelper(stops !== null && stops !== void 0 ? stops : []),
206
+ _step2;
207
+ try {
208
+ for (_iterator2.s(); !(_step2 = _iterator2.n()).done;) {
209
+ var stop = _step2.value;
210
+ var byContributor = new Map();
211
+ var _iterator3 = _createForOfIteratorHelper(containedBy(targets.filter(function (target) {
212
+ return !folded.has(target);
213
+ }), stop)),
214
+ _step3;
215
+ try {
216
+ for (_iterator3.s(); !(_step3 = _iterator3.n()).done;) {
217
+ var _target$decoration$sp, _byContributor$get;
218
+ var target = _step3.value;
219
+ var key = (_target$decoration$sp = target.decoration.spec.attributionKey) !== null && _target$decoration$sp !== void 0 ? _target$decoration$sp : '';
220
+ byContributor.set(key, [].concat(_toConsumableArray((_byContributor$get = byContributor.get(key)) !== null && _byContributor$get !== void 0 ? _byContributor$get : []), [target]));
221
+ }
222
+ } catch (err) {
223
+ _iterator3.e(err);
224
+ } finally {
225
+ _iterator3.f();
226
+ }
227
+ var _iterator4 = _createForOfIteratorHelper(byContributor.values()),
228
+ _step4;
229
+ try {
230
+ var _loop2 = function _loop2() {
231
+ var _step4$value = _toArray(_step4.value),
232
+ leader = _step4$value[0],
233
+ trailing = _arrayLikeToArray(_step4$value).slice(1);
234
+ trailing.forEach(function (target) {
235
+ return fold(leader, target);
236
+ });
237
+ };
238
+ for (_iterator4.s(); !(_step4 = _iterator4.n()).done;) {
239
+ _loop2();
240
+ }
241
+ } catch (err) {
242
+ _iterator4.e(err);
243
+ } finally {
244
+ _iterator4.f();
245
+ }
246
+ }
247
+ } catch (err) {
248
+ _iterator2.e(err);
249
+ } finally {
250
+ _iterator2.f();
251
+ }
252
+ var surviving = targets.filter(function (target) {
157
253
  return !folded.has(target);
158
- }).map(function (_ref8) {
159
- var contributor = _ref8.contributor,
160
- spec = _ref8.decoration.spec;
254
+ });
255
+ var activeTarget = activeIndexPos === undefined ? undefined : resolveActiveTarget(surviving, activeIndexPos);
256
+ return surviving.map(function (target) {
257
+ var contributor = target.contributor,
258
+ spec = target.decoration.spec;
161
259
  var connected = contributor.connectedToKey ? contributors[contributor.connectedToKey] : undefined;
162
260
  var connectedContributor = connected ? toTagContributor(connected) : undefined;
163
261
  var linked = linkedDiffIds.get(spec.diffId);
262
+ // Only the navigated tag reveals; with no active range, the decoration's own state stands.
263
+ var isActive = activeIndexPos === undefined ? spec.isActive : target === activeTarget;
164
264
  return _objectSpread(_objectSpread(_objectSpread(_objectSpread(_objectSpread({
165
265
  contributor: toTagContributor(contributor),
166
266
  diffId: spec.diffId
@@ -168,8 +268,8 @@ export var extractContributorTags = function extractContributorTags(decorations,
168
268
  connectedContributor: connectedContributor
169
269
  } : {}), spec.colorScheme ? {
170
270
  colorScheme: spec.colorScheme
171
- } : {}), spec.isActive !== undefined ? {
172
- isActive: spec.isActive
271
+ } : {}), isActive !== undefined ? {
272
+ isActive: isActive
173
273
  } : {}), spec.isInserted !== undefined ? {
174
274
  isInserted: spec.isInserted
175
275
  } : {}), linked ? {
@@ -210,25 +210,25 @@ var applyMultiContainerLikeStyles = function applyMultiContainerLikeStyles(_ref6
210
210
  var targetNodeName = expValEquals('platform_editor_nest_table_in_panel', 'isEnabled', true) ? getBaseNodeTypeName(targetNode.type) : targetNode.type.name;
211
211
  var nodeSpecificStyle = getChangedNodeStyle(targetNodeName, colorScheme, isInserted, isActive, diffType, hideAddedDiffsUnderline) || '';
212
212
  if (targetNode.type.name === 'decisionList') {
213
- var nestedInsertedNodeStyle = resolveNestedInsertedNodeStyle();
213
+ var nestedInsertedNodeStyle = resolveNestedInsertedNodeStyle(colorScheme);
214
214
  element.querySelectorAll('li').forEach(function (listItem) {
215
215
  var currentListItemStyle = listItem.getAttribute('style') || '';
216
- listItem.setAttribute('style', "".concat(currentListItemStyle).concat(nestedInsertedNodeStyle));
216
+ listItem.setAttribute('style', fg('platform_editor_ai_show_diff_patch_1') ? combineStyles(currentListItemStyle, nestedInsertedNodeStyle) : "".concat(currentListItemStyle).concat(nestedInsertedNodeStyle));
217
217
  });
218
218
  } else if (targetNode.type.name === 'layoutSection') {
219
- var _nestedInsertedNodeStyle = resolveNestedInsertedNodeStyle();
219
+ var _nestedInsertedNodeStyle = resolveNestedInsertedNodeStyle(colorScheme);
220
220
  element.querySelectorAll('[data-layout-column="true"]').forEach(function (section) {
221
221
  var currentSectionStyle = section.getAttribute('style') || '';
222
- section.setAttribute('style', "".concat(currentSectionStyle).concat(_nestedInsertedNodeStyle));
222
+ section.setAttribute('style', fg('platform_editor_ai_show_diff_patch_1') ? combineStyles(currentSectionStyle, _nestedInsertedNodeStyle) : "".concat(currentSectionStyle).concat(_nestedInsertedNodeStyle));
223
223
  });
224
224
  } else if (targetNode.type.name === 'taskList') {
225
- var _nestedInsertedNodeStyle2 = resolveNestedInsertedNodeStyle();
225
+ var _nestedInsertedNodeStyle2 = resolveNestedInsertedNodeStyle(colorScheme);
226
226
  element.querySelectorAll('li').forEach(function (listItem) {
227
227
  var currentListItemStyle = listItem.getAttribute('style') || '';
228
228
  listItem.setAttribute('style', "".concat(currentListItemStyle).concat(_nestedInsertedNodeStyle2));
229
229
  });
230
230
  }
231
- element.setAttribute('style', "".concat(currentStyle, ";").concat(nodeSpecificStyle));
231
+ element.setAttribute('style', fg('platform_editor_ai_show_diff_patch_1') ? combineStyles(currentStyle, nodeSpecificStyle) : "".concat(currentStyle, ";").concat(nodeSpecificStyle));
232
232
  };
233
233
  var combineStyles = function combineStyles(currentStyle, appendedStyle) {
234
234
  var separator = currentStyle && appendedStyle && !currentStyle.trimEnd().endsWith(';') ? '; ' : '';
@@ -14,6 +14,7 @@ function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t =
14
14
  * keeping the `*Next` bodies.
15
15
  */
16
16
  import { isExperimentEnabled } from '@atlaskit/platform-feature-experiments/is-experiment-enabled';
17
+ import { fg } from '@atlaskit/platform-feature-flags/fg';
17
18
  import { expValEquals } from '@atlaskit/tmp-editor-statsig/exp-val-equals';
18
19
  import { isExtendedEnabled } from '../../isExtendedEnabled';
19
20
  import { buildAddedCellOverlayRoundedStyle, buildAddedCellOverlayStyle, buildAddedCellOverlayStyleNew, buildDeletedCellOverlayRoundedStyle, buildDeletedCellOverlayStyle, buildDeletedInlineContentStyle, buildDeletedInlineContentStyleExtended, buildDeletedLozengeActiveStyle, buildDeletedLozengeStyle, buildDeletedNodeCSSVariables, buildDeletedQuoteNodeWithLozengeStyle, buildDeletedStrikethroughLine, buildDeletedWrappedBlockOutline, buildInsertedBlockNodeStyle, buildInsertedInlineStyle, buildInsertStyleInBlockExtended, buildInsertStyleInBlockExtendedNoUnderline, buildInsertStyleNode } from '../colorSchemes/factory';
@@ -24,9 +25,7 @@ var getColorScheme = function getColorScheme(colorScheme) {
24
25
  };
25
26
 
26
27
  /**
27
- * Inserted content inside a multi-container or list node always uses the standard scheme, so a
28
- * traditional diff shows purple here. Pre-existing: these call sites predate traditional.
29
- * Preserved as-is; see EDITOR-8281.
28
+ * Preserve the historical purple nested outlines when the layout/decision diff patch is off.
30
29
  */
31
30
  var nestedContentScheme = standardScheme;
32
31
 
@@ -62,7 +61,7 @@ var getChangedNodeStyleNext = function getChangedNodeStyleNext(nodeName, colorSc
62
61
  var colors = getColorScheme(colorScheme);
63
62
  if (isExtendedEnabled(diffType) && isInserted) {
64
63
  if (isMultiContainerBlockNode(nodeName)) {
65
- return hideAddedDiffsUnderline ? buildInsertStyleInBlockExtendedNoUnderline(nestedContentScheme) : buildInsertStyleInBlockExtended(nestedContentScheme);
64
+ return hideAddedDiffsUnderline || fg('platform_editor_ai_show_diff_patch_1') ? buildInsertStyleInBlockExtendedNoUnderline(nestedContentScheme) : buildInsertStyleInBlockExtended(nestedContentScheme);
66
65
  }
67
66
  if (isTextLikeBlockNode(nodeName)) {
68
67
  return undefined;
@@ -123,8 +122,8 @@ var resolveRemovedLozengeStyleNext = function resolveRemovedLozengeStyleNext(col
123
122
  var colors = getColorScheme(colorScheme);
124
123
  return isActive ? buildDeletedLozengeActiveStyle(colors) : buildDeletedLozengeStyle(colors);
125
124
  };
126
- var resolveNestedInsertedNodeStyleNext = function resolveNestedInsertedNodeStyleNext() {
127
- return buildInsertStyleNode(nestedContentScheme);
125
+ var resolveNestedInsertedNodeStyleNext = function resolveNestedInsertedNodeStyleNext(colorScheme) {
126
+ return buildInsertStyleNode(fg('platform_editor_ai_show_diff_patch_1') ? getColorScheme(colorScheme) : nestedContentScheme);
128
127
  };
129
128
 
130
129
  // Only 'stateful' schemes have a resting ring.
@@ -188,11 +187,10 @@ export var resolveRemovedLozengeStyle = function resolveRemovedLozengeStyle(colo
188
187
 
189
188
  /**
190
189
  * Style for inserted content nested inside a multi-container or list node — the `decisionList` and
191
- * `taskList` `li`s and the `layoutSection` columns. Scheme-independent: see
192
- * `nestedContentScheme` above.
190
+ * `taskList` `li`s and the `layoutSection` columns.
193
191
  */
194
- export var resolveNestedInsertedNodeStyle = function resolveNestedInsertedNodeStyle() {
195
- return isExperimentEnabled('platform_editor_show_diff_color_scheme_refactor') ? resolveNestedInsertedNodeStyleNext() : resolveNestedInsertedNodeStyleLegacy();
192
+ export var resolveNestedInsertedNodeStyle = function resolveNestedInsertedNodeStyle(colorScheme) {
193
+ return isExperimentEnabled('platform_editor_show_diff_color_scheme_refactor') ? resolveNestedInsertedNodeStyleNext(colorScheme) : resolveNestedInsertedNodeStyleLegacy();
196
194
  };
197
195
 
198
196
  /**
@@ -9,6 +9,7 @@
9
9
  * `colorSchemes/standard.ts`, `colorSchemes/traditional.ts` (EDITOR-8281).
10
10
  */
11
11
  import { convertToInlineCss } from '@atlaskit/editor-common/lazy-node-view';
12
+ import { fg } from '@atlaskit/platform-feature-flags/fg';
12
13
  import { expValEquals } from '@atlaskit/tmp-editor-statsig/exp-val-equals';
13
14
  import { isExtendedEnabled } from '../../isExtendedEnabled';
14
15
  import { deletedBlockOutline, deletedBlockOutlineActive, deletedBlockOutlineRounded, deletedBlockOutlineRoundedActive, deletedContentStyleUnbounded, deletedInlineContentStyleExtended, deletedStyleQuoteNodeWithLozenge, deletedStyleQuoteNodeWithLozengeActive, editingContentStyleInBlockExtended, editingContentStyleInBlockExtendedNoUnderline, editingStyleExtended, editingStyleExtendedNoUnderline, editingStyleActiveExtended, editingStyleActiveExtendedNoUnderline, editingStyleNode, getStandardDeletedContentStyle, getStandardDeletedContentStyleActive, getStandardDeletedContentStyleNew, addedCellOverlayStyle, addedCellOverlayRoundedStyle, deletedCellOverlayStyle, deletedCellOverlayRoundedStyle } from '../colorSchemes/standard';
@@ -105,7 +106,7 @@ export var getChangedNodeStyleLegacy = function getChangedNodeStyleLegacy(nodeNa
105
106
  var isTraditional = colorScheme === 'traditional';
106
107
  if (isExtendedEnabled(diffType) && isInserted) {
107
108
  if (isMultiContainerBlockNode(nodeName)) {
108
- return hideAddedDiffsUnderline ? editingContentStyleInBlockExtendedNoUnderline : editingContentStyleInBlockExtended;
109
+ return hideAddedDiffsUnderline || fg('platform_editor_ai_show_diff_patch_1') ? editingContentStyleInBlockExtendedNoUnderline : editingContentStyleInBlockExtended;
109
110
  }
110
111
  if (isTextLikeBlockNode(nodeName)) {
111
112
  return undefined;
@@ -42,6 +42,30 @@ export var CONTRIBUTOR_TAG_REVEALED_ATTRIBUTE = 'data-revealed';
42
42
  */
43
43
  export var TAG_EXIT_FALLBACK_MS = 1200;
44
44
 
45
+ /**
46
+ * The tooltip's look, inline rather than through `VANILLA_TOOLTIP_DEFAULT_CLASS`: this tooltip is
47
+ * hoisted out of the tag, and that class's rule is scoped under `.ProseMirror` — see
48
+ * `resolveTooltipContainer`. Otherwise `vanillaTooltipDefaultStyles` in editor-core, which is the
49
+ * look every other vanilla tooltip has; keep the two in step.
50
+ */
51
+ export var CONTRIBUTOR_TAG_TOOLTIP_STYLES = {
52
+ boxSizing: 'border-box',
53
+ maxWidth: '240px',
54
+ backgroundColor: "var(--ds-background-neutral-bold, #292A2E)",
55
+ // A `popover` is given one by the UA stylesheet.
56
+ border: 'none',
57
+ borderRadius: "var(--ds-radius-small, 3px)",
58
+ color: "var(--ds-text-inverse, #FFFFFF)",
59
+ 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)",
60
+ fontFamily: "var(--ds-font-family-body, \"Atlassian Sans\", ui-sans-serif, -apple-system, BlinkMacSystemFont, \"Segoe UI\", Ubuntu, \"Helvetica Neue\", sans-serif)",
61
+ overflowWrap: 'break-word',
62
+ paddingBlock: "var(--ds-space-050, 4px)",
63
+ paddingInline: "var(--ds-space-075, 6px)",
64
+ whiteSpace: 'normal',
65
+ // A tooltip is never a hit target — it hangs over the document's own content.
66
+ pointerEvents: 'none'
67
+ };
68
+
45
69
  // Positioned against the host widget its decoration renders on the change's first character:
46
70
  // `bottom: 100%` lifts the tag onto the line above, `inset-inline-start` starts it on that
47
71
  // character.
@@ -6,7 +6,7 @@ import { bind, bindAll } from 'bind-event-listener';
6
6
  import { VanillaTooltip } from '@atlaskit/editor-common/vanilla-tooltip';
7
7
  import { getAccentTokens } from '../../pm-plugins/decorations/colorSchemes/factory';
8
8
  import { colorSchemeRegistry } from '../../pm-plugins/decorations/colorSchemes/schemes';
9
- import { buildContributorTagDom, CONTRIBUTOR_TAG_REVEALED_ATTRIBUTE, TAG_EXIT_FALLBACK_MS } from './buildContributorTagDom';
9
+ import { buildContributorTagDom, CONTRIBUTOR_TAG_REVEALED_ATTRIBUTE, CONTRIBUTOR_TAG_TOOLTIP_STYLES, TAG_EXIT_FALLBACK_MS } from './buildContributorTagDom';
10
10
  import { contributorAvatarRenderer } from './contributorAvatarRenderer';
11
11
  import { formatContributorLabel } from './contributorLabel';
12
12
  /**
@@ -393,7 +393,7 @@ export var ContributorTagController = /*#__PURE__*/function () {
393
393
  }, {
394
394
  key: "syncTooltip",
395
395
  value: function syncTooltip() {
396
- var _this$tooltip2, _this$dom$tag$querySe;
396
+ var _this$tooltip2;
397
397
  var content = this.dom ? this.fullLabel : undefined;
398
398
  if (content === this.tooltipContent) {
399
399
  return;
@@ -404,17 +404,39 @@ export var ContributorTagController = /*#__PURE__*/function () {
404
404
  if (!content || !this.dom) {
405
405
  return;
406
406
  }
407
- this.tooltip = new VanillaTooltip(this.dom.tag, content, undefined,
408
- // `ak-editor-vanilla-tooltip-default` opts into the shared `VanillaTooltip` look, defined as
409
- // `vanillaTooltipDefaultStyles` in both EditorContentContainer stylesheets — keep in sync when
410
- // renaming. Those styles are scoped under `.ProseMirror`, and the tag renders inside it.
411
- 'ak-editor-vanilla-tooltip-default');
407
+ this.tooltip = new VanillaTooltip(this.dom.tag, content,
408
+ // Generated id.
409
+ undefined,
410
+ // No class: the look is inline, because a hoisted tooltip is outside the `.ProseMirror`
411
+ // scope `VANILLA_TOOLTIP_DEFAULT_CLASS` is keyed on — see `CONTRIBUTOR_TAG_TOOLTIP_STYLES`.
412
+ '',
413
+ // Default delay, no `onShow`, and the default `top` placement.
414
+ undefined, CONTRIBUTOR_TAG_TOOLTIP_STYLES, undefined, undefined, this.resolveTooltipContainer());
412
415
  // `VanillaTooltip` sets `aria-describedby` on its trigger, which would announce the label a
413
416
  // second time — the hidden child already announces it, and names the tag with it.
414
417
  this.dom.tag.removeAttribute('aria-describedby');
415
- // The popover is a child of the trigger, so its text would be a third copy of the same
416
- // sentence. It stays visible for sighted users, who need the part of the name the tag clipped.
417
- (_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');
418
+ // Out of the tag but still in the document, so its text would otherwise be a third copy of the
419
+ // same sentence. It stays visible for sighted users, who need the part of the name the tag
420
+ // clipped.
421
+ this.tooltip.element.setAttribute('aria-hidden', 'true');
422
+ }
423
+
424
+ /**
425
+ * Where the tooltip is appended, rather than inside the tag.
426
+ *
427
+ * Popper and the browser only agree on a top-layer popover's origin when nothing above it is
428
+ * transformed, and the tag hangs under the editor's own transformed nodes — inside a wide image
429
+ * that is `.rich-media-item`, whose `translateX(-50%)` threw the tooltip off screen
430
+ * (EDITOR-8971). The content area is the nearest ancestor with none of that above it, and keeps
431
+ * the tooltip inside the editor it belongs to; the document body covers an appearance that has
432
+ * no content area.
433
+ */
434
+ }, {
435
+ key: "resolveTooltipContainer",
436
+ value: function resolveTooltipContainer() {
437
+ var _editorRoot$closest;
438
+ var editorRoot = this.options.getEditorRoot();
439
+ 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;
418
440
  }
419
441
 
420
442
  /**
@@ -0,0 +1,22 @@
1
+ import type { Node as PMNode } from '@atlaskit/editor-prosemirror/model';
2
+ /**
3
+ * Whether a change spans nothing but a block node's open token, so its deleted side carries no
4
+ * content.
5
+ *
6
+ * The attribute-aware token encoder folds a node's diffable attributes into its open token, so a
7
+ * same-type replacement — an AI suggestion rewriting a code block — is reported as two changes: one
8
+ * over the single position of that token, carrying the attribute change, and one for the content.
9
+ * Slicing the first yields the node with none of its content, which leaves the deleted-content
10
+ * widget nothing to draw but an empty copy of the block (EDITOR-8912). The content change is a
11
+ * change of its own and is decorated in place.
12
+ *
13
+ * A whole node is not matched: its range covers its close token too. Neither is an empty leaf (a
14
+ * `rule`, a `blockCard`), which is one position but slices closed rather than open.
15
+ */
16
+ export declare const isOpenTokenOnlyChange: ({ change, originalDoc, }: {
17
+ change: {
18
+ fromA: number;
19
+ toA: number;
20
+ };
21
+ originalDoc: PMNode;
22
+ }) => boolean;
@@ -5,5 +5,18 @@ import type { ResolvedDiffContributors } from './colorSchemes/attributions';
5
5
  * One model per diff decoration whose attribution resolves to a supplied contributor; anything
6
6
  * unattributed, untaggable or unresolved is skipped. A replacement's two decorations are collapsed
7
7
  * into a single tag, on whichever half renders first — see `isSameChange` and `leadsReplacement`.
8
+ *
9
+ * `activeIndexPos`, when given, reveals exactly one of the tags — see `resolveActiveTarget`.
10
+ * Absent, each tag keeps the active state its own decoration was drawn with.
11
+ *
12
+ * `stops`, when given, is the navigation stop list the step buttons walk
13
+ * (`getScrollableDecorations`). One contributor cannot hold two tags in one stop, since only the
14
+ * leading one would ever be reachable. Absent, every decoration keeps its own tag.
8
15
  */
9
- export declare const extractContributorTags: (decorations: DecorationSet, contributors: ResolvedDiffContributors | undefined) => ContributorTagModel[];
16
+ export declare const extractContributorTags: (decorations: DecorationSet, contributors: ResolvedDiffContributors | undefined, activeIndexPos?: {
17
+ from: number;
18
+ to: number;
19
+ }, stops?: ReadonlyArray<{
20
+ from: number;
21
+ to: number;
22
+ }>) => ContributorTagModel[];
@@ -29,10 +29,9 @@ export declare const resolveCellOverlayStyle: (args: {
29
29
  export declare const resolveRemovedLozengeStyle: (colorScheme: ColorScheme | undefined, isActive: boolean) => string;
30
30
  /**
31
31
  * Style for inserted content nested inside a multi-container or list node — the `decisionList` and
32
- * `taskList` `li`s and the `layoutSection` columns. Scheme-independent: see
33
- * `nestedContentScheme` above.
32
+ * `taskList` `li`s and the `layoutSection` columns.
34
33
  */
35
- export declare const resolveNestedInsertedNodeStyle: () => string;
34
+ export declare const resolveNestedInsertedNodeStyle: (colorScheme?: ColorScheme) => string;
36
35
  /**
37
36
  * Whether the colour scheme draws a resting (non-active) ring on a deleted media/embed node, which
38
37
  * is what the `show-diff-deleted-outline-new` class styles in editor-core's smartCardStyles.
@@ -34,6 +34,13 @@ export declare const CONTRIBUTOR_TAG_REVEALED_ATTRIBUTE = "data-revealed";
34
34
  * number — hence the wide margin. Raise in step with that duration.
35
35
  */
36
36
  export declare const TAG_EXIT_FALLBACK_MS = 1200;
37
+ /**
38
+ * The tooltip's look, inline rather than through `VANILLA_TOOLTIP_DEFAULT_CLASS`: this tooltip is
39
+ * hoisted out of the tag, and that class's rule is scoped under `.ProseMirror` — see
40
+ * `resolveTooltipContainer`. Otherwise `vanillaTooltipDefaultStyles` in editor-core, which is the
41
+ * look every other vanilla tooltip has; keep the two in step.
42
+ */
43
+ export declare const CONTRIBUTOR_TAG_TOOLTIP_STYLES: Readonly<Record<string, string>>;
37
44
  export type ContributorTagDom = {
38
45
  avatars: HTMLSpanElement;
39
46
  name: HTMLSpanElement;
@@ -91,6 +91,17 @@ export declare class ContributorTagController {
91
91
  * shape of `syncVanillaDisabledTooltip` in `mentionNodeView`.
92
92
  */
93
93
  private syncTooltip;
94
+ /**
95
+ * Where the tooltip is appended, rather than inside the tag.
96
+ *
97
+ * Popper and the browser only agree on a top-layer popover's origin when nothing above it is
98
+ * transformed, and the tag hangs under the editor's own transformed nodes — inside a wide image
99
+ * that is `.rich-media-item`, whose `translateX(-50%)` threw the tooltip off screen
100
+ * (EDITOR-8971). The content area is the nearest ancestor with none of that above it, and keeps
101
+ * the tooltip inside the editor it belongs to; the document body covers an appearance that has
102
+ * no content area.
103
+ */
104
+ private resolveTooltipContainer;
94
105
  /**
95
106
  * Observes the highlight this tag describes, for what CSS here cannot see: its hover state. The
96
107
  * highlight is a ProseMirror decoration in a different DOM subtree, so listeners are bound
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@atlaskit/editor-plugin-show-diff",
3
- "version": "16.0.2",
3
+ "version": "16.0.4",
4
4
  "description": "ShowDiff plugin for @atlaskit/editor-core",
5
5
  "author": "Atlassian Pty Ltd",
6
6
  "license": "Apache-2.0",
@@ -32,7 +32,7 @@
32
32
  "@atlaskit/platform-feature-experiments": "^0.3.0",
33
33
  "@atlaskit/platform-feature-flags": "^2.2.0",
34
34
  "@atlaskit/primitives": "^22.5.0",
35
- "@atlaskit/tmp-editor-statsig": "^189.0.0",
35
+ "@atlaskit/tmp-editor-statsig": "^191.0.0",
36
36
  "@atlaskit/tokens": "^16.12.0",
37
37
  "@babel/runtime": "^7.0.0",
38
38
  "@compiled/react": "^1.0.2",
@@ -46,7 +46,7 @@
46
46
  "@atlaskit/button": "^25.3.0",
47
47
  "@atlaskit/css": "^1.1.0",
48
48
  "@atlaskit/dropdown-menu": "^18.3.0",
49
- "@atlaskit/editor-core": "^228.0.0",
49
+ "@atlaskit/editor-core": "^228.1.0",
50
50
  "@atlaskit/editor-json-transformer": "^9.8.0",
51
51
  "@atlaskit/editor-plugins": "^16.1.0",
52
52
  "@atlaskit/form": "^17.2.0",
@@ -57,14 +57,14 @@
57
57
  "@atlassian/confluence-presets": "workspace:^",
58
58
  "@atlassian/content-reconciliation": "^0.1.3506",
59
59
  "@atlassian/editor-ai-injected-editors": "^32.0.0",
60
- "@atlassian/editor-plugin-ai": "^72.0.0",
60
+ "@atlassian/editor-plugin-ai": "^72.1.0",
61
61
  "@atlassian/structured-docs-types": "workspace:^",
62
62
  "react": "^19.2.0",
63
63
  "react-dom": "^19.2.0",
64
64
  "react-intl": "^7.0.0"
65
65
  },
66
66
  "peerDependencies": {
67
- "@atlaskit/editor-common": "^122.2.0",
67
+ "@atlaskit/editor-common": "^122.3.0",
68
68
  "react": "^18.2.0 || ^19.2.0",
69
69
  "react-dom": "^18.2.0 || ^19.2.0",
70
70
  "react-intl": "^5.25.1 || ^6.0.0 || ^7.0.0"