@atlaskit/editor-plugin-show-diff 15.0.0 → 15.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (63) hide show
  1. package/CHANGELOG.md +35 -0
  2. package/afm-cc/tsconfig.json +0 -12
  3. package/afm-products/tsconfig.json +0 -12
  4. package/dist/cjs/pm-plugins/calculateDiff/calculateDiffDecorations.js +131 -79
  5. package/dist/cjs/pm-plugins/decorations/colorSchemes/factory.js +32 -0
  6. package/dist/cjs/pm-plugins/decorations/createBlockChangedDecoration.js +87 -25
  7. package/dist/cjs/pm-plugins/decorations/createContributorTagWidget.js +27 -4
  8. package/dist/cjs/pm-plugins/decorations/createInlineChangedDecoration.js +17 -3
  9. package/dist/cjs/pm-plugins/decorations/createNodeChangedDecorationWidget.js +5 -3
  10. package/dist/cjs/pm-plugins/decorations/extractContributorTags.js +43 -14
  11. package/dist/cjs/pm-plugins/decorations/revealStyles.js +156 -0
  12. package/dist/cjs/pm-plugins/decorations/utils/wrapBlockNodeView.js +25 -5
  13. package/dist/cjs/pm-plugins/decorations/utils/wrapBlockNodeViewStyles.js +4 -2
  14. package/dist/cjs/pm-plugins/decorations/utils/wrapBlockNodeViewStyles.legacy.js +2 -1
  15. package/dist/cjs/pm-plugins/main.js +28 -10
  16. package/dist/cjs/pm-plugins/resolveDiffContributors.js +4 -0
  17. package/dist/cjs/pm-plugins/revealAnimation.js +524 -0
  18. package/dist/cjs/showDiffPlugin.js +42 -7
  19. package/dist/es2019/pm-plugins/calculateDiff/calculateDiffDecorations.js +59 -9
  20. package/dist/es2019/pm-plugins/decorations/colorSchemes/factory.js +30 -0
  21. package/dist/es2019/pm-plugins/decorations/createBlockChangedDecoration.js +62 -4
  22. package/dist/es2019/pm-plugins/decorations/createContributorTagWidget.js +25 -3
  23. package/dist/es2019/pm-plugins/decorations/createInlineChangedDecoration.js +18 -1
  24. package/dist/es2019/pm-plugins/decorations/createNodeChangedDecorationWidget.js +5 -3
  25. package/dist/es2019/pm-plugins/decorations/extractContributorTags.js +36 -9
  26. package/dist/es2019/pm-plugins/decorations/revealStyles.js +139 -0
  27. package/dist/es2019/pm-plugins/decorations/utils/wrapBlockNodeView.js +25 -6
  28. package/dist/es2019/pm-plugins/decorations/utils/wrapBlockNodeViewStyles.js +6 -3
  29. package/dist/es2019/pm-plugins/decorations/utils/wrapBlockNodeViewStyles.legacy.js +5 -2
  30. package/dist/es2019/pm-plugins/main.js +28 -9
  31. package/dist/es2019/pm-plugins/resolveDiffContributors.js +4 -0
  32. package/dist/es2019/pm-plugins/revealAnimation.js +495 -0
  33. package/dist/es2019/showDiffPlugin.js +37 -7
  34. package/dist/esm/pm-plugins/calculateDiff/calculateDiffDecorations.js +131 -79
  35. package/dist/esm/pm-plugins/decorations/colorSchemes/factory.js +30 -0
  36. package/dist/esm/pm-plugins/decorations/createBlockChangedDecoration.js +87 -25
  37. package/dist/esm/pm-plugins/decorations/createContributorTagWidget.js +27 -4
  38. package/dist/esm/pm-plugins/decorations/createInlineChangedDecoration.js +17 -3
  39. package/dist/esm/pm-plugins/decorations/createNodeChangedDecorationWidget.js +5 -3
  40. package/dist/esm/pm-plugins/decorations/extractContributorTags.js +43 -14
  41. package/dist/esm/pm-plugins/decorations/revealStyles.js +149 -0
  42. package/dist/esm/pm-plugins/decorations/utils/wrapBlockNodeView.js +25 -5
  43. package/dist/esm/pm-plugins/decorations/utils/wrapBlockNodeViewStyles.js +4 -2
  44. package/dist/esm/pm-plugins/decorations/utils/wrapBlockNodeViewStyles.legacy.js +2 -1
  45. package/dist/esm/pm-plugins/main.js +28 -10
  46. package/dist/esm/pm-plugins/resolveDiffContributors.js +4 -0
  47. package/dist/esm/pm-plugins/revealAnimation.js +518 -0
  48. package/dist/esm/showDiffPlugin.js +42 -7
  49. package/dist/types/entry-points/show-diff-plugin-type.d.ts +1 -1
  50. package/dist/types/pm-plugins/calculateDiff/calculateDiffDecorations.d.ts +2 -1
  51. package/dist/types/pm-plugins/decorations/colorSchemes/factory.d.ts +24 -0
  52. package/dist/types/pm-plugins/decorations/createBlockChangedDecoration.d.ts +5 -1
  53. package/dist/types/pm-plugins/decorations/createContributorTagWidget.d.ts +9 -3
  54. package/dist/types/pm-plugins/decorations/createInlineChangedDecoration.d.ts +3 -2
  55. package/dist/types/pm-plugins/decorations/createNodeChangedDecorationWidget.d.ts +3 -2
  56. package/dist/types/pm-plugins/decorations/revealStyles.d.ts +49 -0
  57. package/dist/types/pm-plugins/decorations/utils/wrapBlockNodeView.d.ts +4 -3
  58. package/dist/types/pm-plugins/decorations/utils/wrapBlockNodeViewStyles.d.ts +1 -1
  59. package/dist/types/pm-plugins/decorations/utils/wrapBlockNodeViewStyles.legacy.d.ts +1 -1
  60. package/dist/types/pm-plugins/main.d.ts +6 -1
  61. package/dist/types/pm-plugins/revealAnimation.d.ts +33 -0
  62. package/dist/types/showDiffPluginType.d.ts +29 -1
  63. package/package.json +10 -4
@@ -111,14 +111,34 @@ var resolveInlineContentStart = function resolveInlineContentStart(doc, from, to
111
111
  };
112
112
 
113
113
  /**
114
- * A widget hosting the contributor tag of one inline diff range, placed on the first character the
115
- * range highlights.
114
+ * The position just before the code block enclosing `pos`, or `undefined` when nothing does.
115
+ *
116
+ * `.code-block-content-wrapper` hides its block-axis overflow, so a host left inside the code has
117
+ * its tag clipped away. Hosting it before the block puts the tag on the block's top-left corner,
118
+ * where a whole-code-block change is tagged too (EDITOR-8766).
119
+ */
120
+ var resolveCodeBlockStart = function resolveCodeBlockStart(doc, pos) {
121
+ var $pos = doc.resolve(pos);
122
+ for (var depth = $pos.depth; depth > 0; depth--) {
123
+ if ($pos.node(depth).type.name === 'codeBlock') {
124
+ return $pos.before(depth);
125
+ }
126
+ }
127
+ return undefined;
128
+ };
129
+
130
+ /**
131
+ * A widget hosting the contributor tag of one diff range, placed on the first character the range
132
+ * highlights — or, with `anchorAtRangeStart`, at `from` itself. Either way a host that would land
133
+ * inside a code block is hoisted out in front of it; see `resolveCodeBlockStart`.
116
134
  *
117
135
  * The tag is mounted in `toDOM` and taken down in `destroy`, so it lives exactly as long as the host
118
136
  * element ProseMirror drew it into — see `mountContributorTag`.
119
137
  */
120
138
  var createContributorTagWidget = exports.createContributorTagWidget = function createContributorTagWidget(_ref2) {
121
- var doc = _ref2.doc,
139
+ var _ref2$anchorAtRangeSt = _ref2.anchorAtRangeStart,
140
+ anchorAtRangeStart = _ref2$anchorAtRangeSt === void 0 ? false : _ref2$anchorAtRangeSt,
141
+ doc = _ref2.doc,
122
142
  from = _ref2.from,
123
143
  to = _ref2.to,
124
144
  diffId = _ref2.diffId,
@@ -130,9 +150,12 @@ var createContributorTagWidget = exports.createContributorTagWidget = function c
130
150
  // Reassigned when ProseMirror redraws this decoration, which it may do more than once for the
131
151
  // same `Decoration` instance.
132
152
  var mount;
153
+ var anchorPos = anchorAtRangeStart ? from : resolveInlineContentStart(doc, from, to);
154
+ // A change inside a code block is hoisted out of it; anything else keeps its own anchor.
155
+ var codeBlockPos = resolveCodeBlockStart(doc, anchorPos);
133
156
  return _view.Decoration.widget(
134
157
  // Keep the host out of the table row's grid (EDITOR-8442).
135
- (0, _createAnchorDecorationWidgets.clampAnchorPosIntoCell)(doc, resolveInlineContentStart(doc, from, to), 1), function () {
158
+ (0, _createAnchorDecorationWidgets.clampAnchorPosIntoCell)(doc, codeBlockPos !== null && codeBlockPos !== void 0 ? codeBlockPos : anchorPos, 1), function () {
136
159
  var host = buildContributorTagHost(diffId);
137
160
  mount = mountContributorTag({
138
161
  diffId: diffId,
@@ -17,6 +17,7 @@ var _createAnchorDecorationWidgets = require("./createAnchorDecorationWidgets");
17
17
  var _createContributorTagWidget = require("./createContributorTagWidget");
18
18
  var _createInlineChangedDecorationStyles = require("./createInlineChangedDecoration.styles.legacy");
19
19
  var _decorationKeys = require("./decorationKeys");
20
+ var _revealStyles = require("./revealStyles");
20
21
  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; }
21
22
  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; }
22
23
  var displayNoneStyle = (0, _lazyNodeView.convertToInlineCss)({
@@ -109,6 +110,7 @@ var getAtomicInlineChangedAttrs = exports.getAtomicInlineChangedAttrs = function
109
110
  */
110
111
 
111
112
  var createInlineChangedDecoration = exports.createInlineChangedDecoration = function createInlineChangedDecoration(_ref2) {
113
+ var _revealed$style;
112
114
  var attributionKey = _ref2.attributionKey,
113
115
  change = _ref2.change,
114
116
  colorScheme = _ref2.colorScheme,
@@ -133,6 +135,7 @@ var createInlineChangedDecoration = exports.createInlineChangedDecoration = func
133
135
  hasDeletedWidget = _ref2$hasDeletedWidge === void 0 ? false : _ref2$hasDeletedWidge,
134
136
  _ref2$isDeletedWidget = _ref2.isDeletedWidgetBelow,
135
137
  isDeletedWidgetBelow = _ref2$isDeletedWidget === void 0 ? false : _ref2$isDeletedWidget,
138
+ reveal = _ref2.reveal,
136
139
  tagMountContext = _ref2.tagMountContext;
137
140
  // Derived from the range so it survives a recalculation: stepping to the next/previous change
138
141
  // rebuilds decorations without touching the doc, and a fresh id would change the React key and the
@@ -152,7 +155,18 @@ var createInlineChangedDecoration = exports.createInlineChangedDecoration = func
152
155
  }))];
153
156
  }
154
157
  var colors = getColorScheme(colorScheme);
155
- var style = resolveInlineChangedStyle({
158
+
159
+ // The reveal withholds the static highlight so the wipe has something to reveal, so it supplies
160
+ // the whole style rather than adding to the usual one. Extended pipeline only — the AI review
161
+ // surface is the only consumer, and the non-extended styles have no background to wipe.
162
+ var revealed = (0, _isExtendedEnabled.isExtendedEnabled)(diffType) ? (0, _revealStyles.resolveRevealStyle)({
163
+ colorScheme: colorScheme,
164
+ hideAddedDiffsUnderline: hideAddedDiffsUnderline,
165
+ isActive: isActive,
166
+ isInserted: isInserted,
167
+ reveal: reveal
168
+ }) : undefined;
169
+ var style = (_revealed$style = revealed === null || revealed === void 0 ? void 0 : revealed.style) !== null && _revealed$style !== void 0 ? _revealed$style : resolveInlineChangedStyle({
156
170
  colors: colors,
157
171
  colorScheme: colorScheme,
158
172
  diffType: diffType,
@@ -162,11 +176,11 @@ var createInlineChangedDecoration = exports.createInlineChangedDecoration = func
162
176
  });
163
177
  var isAtomicInlineInsertion = isAtomicInlineNode && isInserted;
164
178
  var atomicInlineAttrs = isAtomicInlineInsertion ? resolveAtomicInlineAttrs(inlineNodeName, colorScheme, colors) : undefined;
165
- var decorations = [_view.Decoration.inline(change.fromB, change.toB, _objectSpread(_objectSpread({
179
+ var decorations = [_view.Decoration.inline(change.fromB, change.toB, _objectSpread(_objectSpread(_objectSpread({
166
180
  style: atomicInlineAttrs ? "".concat(style).concat(atomicInlineAttrs.styleSuffix) : style
167
181
  }, atomicInlineAttrs && {
168
182
  class: atomicInlineAttrs.className
169
- }), {}, {
183
+ }), revealed && (0, _defineProperty2.default)({}, _revealStyles.REVEAL_ATTR, revealed.role)), {}, {
170
184
  'data-testid': 'show-diff-changed-decoration'
171
185
  }, showContributorTags && {
172
186
  'data-diff-id': diffId
@@ -118,6 +118,7 @@ var createNodeChangedDecorationWidget = exports.createNodeChangedDecorationWidge
118
118
  diffType = _ref2.diffType,
119
119
  _ref2$hideAddedDiffsU = _ref2.hideAddedDiffsUnderline,
120
120
  hideAddedDiffsUnderline = _ref2$hideAddedDiffsU === void 0 ? false : _ref2$hideAddedDiffsU,
121
+ reveal = _ref2.reveal,
121
122
  tagMountContext = _ref2.tagMountContext;
122
123
  var slice = doc.slice(change.fromA, change.toA);
123
124
  var shouldSkipDeletedEmptyParagraphDecoration = !isInserted && (slice === null || slice === void 0 || (_slice$content = slice.content) === null || _slice$content === void 0 ? void 0 : _slice$content.childCount) === 1 && (slice === null || slice === void 0 || (_slice$content2 = slice.content) === null || _slice$content2 === void 0 || (_slice$content2 = _slice$content2.firstChild) === null || _slice$content2 === void 0 ? void 0 : _slice$content2.type.name) === 'paragraph' && (slice === null || slice === void 0 || (_slice$content3 = slice.content) === null || _slice$content3 === void 0 || (_slice$content3 = _slice$content3.firstChild) === null || _slice$content3 === void 0 ? void 0 : _slice$content3.content.size) === 0;
@@ -299,7 +300,7 @@ var createNodeChangedDecorationWidget = exports.createNodeChangedDecorationWidge
299
300
  var nodeView = serializer.tryCreateNodeView(node);
300
301
  if (nodeView) {
301
302
  if (node.isInline) {
302
- var wrapper = (0, _wrapBlockNodeView.createContentWrapper)(colorScheme, isActive, isInserted, diffType);
303
+ var wrapper = (0, _wrapBlockNodeView.createContentWrapper)(colorScheme, isActive, isInserted, diffType, reveal);
303
304
  wrapper.append(nodeView);
304
305
  dom.append(wrapper);
305
306
  } else {
@@ -329,11 +330,12 @@ var createNodeChangedDecorationWidget = exports.createNodeChangedDecorationWidge
329
330
  colorScheme: colorScheme,
330
331
  isActive: isActive,
331
332
  isInserted: isInserted,
332
- diffType: diffType
333
+ diffType: diffType,
334
+ reveal: reveal
333
335
  });
334
336
  dom.append(injectedNode);
335
337
  } else {
336
- var _wrapper2 = (0, _wrapBlockNodeView.createContentWrapper)(colorScheme, isActive, isInserted, diffType);
338
+ var _wrapper2 = (0, _wrapBlockNodeView.createContentWrapper)(colorScheme, isActive, isInserted, diffType, reveal);
337
339
  _wrapper2.append(fallbackNode);
338
340
  dom.append(_wrapper2);
339
341
  }
@@ -17,9 +17,9 @@ function _unsupportedIterableToArray(r, a) { if (r) { if ("string" == typeof r)
17
17
  function _arrayLikeToArray(r, a) { (null == a || a > r.length) && (a = r.length); for (var e = 0, n = Array(a); e < a; e++) n[e] = r[e]; return n; }
18
18
  /**
19
19
  * Diff kinds that expose an anchor a tag can be pinned to: `inline` is added/changed content,
20
- * `widget` is deleted content. Block diffs carry no attribution.
20
+ * `widget` is deleted content, `block` a whole changed block node.
21
21
  */
22
- var TAGGABLE_DECORATION_TYPES = new Set(['inline', 'widget']);
22
+ var TAGGABLE_DECORATION_TYPES = new Set(['block', 'inline', 'widget']);
23
23
 
24
24
  /** The connection is an internal key, so it is dropped on the way to the tag. */
25
25
  var toTagContributor = function toTagContributor(_ref) {
@@ -36,6 +36,15 @@ var isSameChange = function isSameChange(widget, inline) {
36
36
  return widget.spec.attributionKey === inline.spec.attributionKey && widget.from >= inline.from && widget.from <= inline.to;
37
37
  };
38
38
 
39
+ /**
40
+ * Whether `inner` captions the same change as the block decoration `block`, in which case only the
41
+ * block keeps the tag — one tag per block. Containment counts in either direction: a leaf block's
42
+ * highlight spans the wrapper the block sits in. Merely adjacent ranges keep a tag each.
43
+ */
44
+ var isSameBlockChange = function isSameBlockChange(inner, block) {
45
+ return inner.spec.attributionKey === block.spec.attributionKey && (block.from <= inner.from && inner.to <= block.to || inner.from <= block.from && block.to <= inner.to);
46
+ };
47
+
39
48
  /**
40
49
  * One model per diff decoration whose attribution resolves to a supplied contributor; anything
41
50
  * unattributed, untaggable or unresolved is skipped. A replacement's two decorations are collapsed
@@ -78,24 +87,44 @@ var extractContributorTags = exports.extractContributorTags = function extractCo
78
87
  var decoration = _ref2.decoration;
79
88
  return decoration.spec.decorationType === 'inline';
80
89
  });
90
+ var blockTargets = targets.filter(function (_ref3) {
91
+ var decoration = _ref3.decoration;
92
+ return decoration.spec.decorationType === 'block';
93
+ });
81
94
  var linkedDiffIds = new Map();
82
95
  var folded = new Set();
96
+ var fold = function fold(host, target) {
97
+ var _linkedDiffIds$get;
98
+ folded.add(target);
99
+ var hostDiffId = host.decoration.spec.diffId;
100
+ linkedDiffIds.set(hostDiffId, [].concat((0, _toConsumableArray2.default)((_linkedDiffIds$get = linkedDiffIds.get(hostDiffId)) !== null && _linkedDiffIds$get !== void 0 ? _linkedDiffIds$get : []), [target.decoration.spec.diffId]));
101
+ };
83
102
  var _loop = function _loop() {
84
- var _linkedDiffIds$get;
85
103
  var target = _targets[_i];
86
- if (target.decoration.spec.decorationType !== 'widget') {
104
+ var decorationType = target.decoration.spec.decorationType;
105
+ if (decorationType === 'block') {
87
106
  return 0; // continue
88
107
  }
89
- var host = inlineTargets.find(function (_ref3) {
90
- var decoration = _ref3.decoration;
91
- return isSameChange(target.decoration, decoration);
108
+
109
+ // A block outranks both other kinds, so it is tried first.
110
+ var block = blockTargets.find(function (_ref4) {
111
+ var decoration = _ref4.decoration;
112
+ return isSameBlockChange(target.decoration, decoration);
92
113
  });
93
- if (!host) {
114
+ if (block) {
115
+ fold(block, target);
116
+ return 0; // continue
117
+ }
118
+ if (decorationType !== 'widget') {
94
119
  return 0; // continue
95
120
  }
96
- folded.add(target);
97
- var hostDiffId = host.decoration.spec.diffId;
98
- linkedDiffIds.set(hostDiffId, [].concat((0, _toConsumableArray2.default)((_linkedDiffIds$get = linkedDiffIds.get(hostDiffId)) !== null && _linkedDiffIds$get !== void 0 ? _linkedDiffIds$get : []), [target.decoration.spec.diffId]));
121
+ var host = inlineTargets.find(function (_ref5) {
122
+ var decoration = _ref5.decoration;
123
+ return isSameChange(target.decoration, decoration);
124
+ });
125
+ if (host) {
126
+ fold(host, target);
127
+ }
99
128
  },
100
129
  _ret;
101
130
  for (var _i = 0, _targets = targets; _i < _targets.length; _i++) {
@@ -104,9 +133,9 @@ var extractContributorTags = exports.extractContributorTags = function extractCo
104
133
  }
105
134
  return targets.filter(function (target) {
106
135
  return !folded.has(target);
107
- }).map(function (_ref4) {
108
- var contributor = _ref4.contributor,
109
- spec = _ref4.decoration.spec;
136
+ }).map(function (_ref6) {
137
+ var contributor = _ref6.contributor,
138
+ spec = _ref6.decoration.spec;
110
139
  var connected = contributor.connectedToKey ? contributors[contributor.connectedToKey] : undefined;
111
140
  var connectedContributor = connected ? toTagContributor(connected) : undefined;
112
141
  var linked = linkedDiffIds.get(spec.diffId);
@@ -0,0 +1,156 @@
1
+ "use strict";
2
+
3
+ var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
4
+ Object.defineProperty(exports, "__esModule", {
5
+ value: true
6
+ });
7
+ exports.resolveRevealStyle = exports.buildWipeableBackground = exports.applyRevealToElement = exports.REVEAL_BORDER_VAR = exports.REVEAL_BG_VAR = exports.REVEAL_ATTR = void 0;
8
+ var _defineProperty2 = _interopRequireDefault(require("@babel/runtime/helpers/defineProperty"));
9
+ var _lazyNodeView = require("@atlaskit/editor-common/lazy-node-view");
10
+ var _isExperimentEnabled = require("@atlaskit/platform-feature-experiments/is-experiment-enabled");
11
+ var _factory = require("./colorSchemes/factory");
12
+ var _schemes = require("./colorSchemes/schemes");
13
+ var _standard = require("./colorSchemes/standard");
14
+ /**
15
+ * Painting strategy for reveal highlights. Highlights are `linear-gradient`s with animated
16
+ * `background-size` for the wipe effect. Colours live on elements as custom properties so the
17
+ * attribution palette works per-contributor.
18
+ */
19
+
20
+ /** Marks an element as taking part in the reveal, and says which side it represents. */
21
+ var REVEAL_ATTR = exports.REVEAL_ATTR = 'data-show-diff-reveal';
22
+ /** Wipe colour, read by the animation as the gradient's colour stop. */
23
+ var REVEAL_BG_VAR = exports.REVEAL_BG_VAR = '--show-diff-reveal-bg';
24
+
25
+ /** Underline colour, brought in on the same schedule as the highlight. */
26
+ var REVEAL_BORDER_VAR = exports.REVEAL_BORDER_VAR = '--show-diff-reveal-border';
27
+
28
+ /** Only schemes with background highlights can be revealed; 'traditional' uses underline only. */
29
+ var schemeSupportsReveal = function schemeSupportsReveal(colorScheme) {
30
+ return colorScheme !== 'traditional';
31
+ };
32
+
33
+ /** Convert a flat background colour into a wipeable gradient (zero width initially). */
34
+ var buildWipeableBackground = exports.buildWipeableBackground = function buildWipeableBackground(color) {
35
+ return (0, _lazyNodeView.convertToInlineCss)({
36
+ backgroundColor: 'transparent',
37
+ backgroundImage: "linear-gradient(".concat(color, ", ").concat(color, ")"),
38
+ backgroundRepeat: 'no-repeat',
39
+ backgroundSize: '0% 100%',
40
+ backgroundPosition: '0 0',
41
+ // Each line fragment of a wrapped highlight gets its own background box, so they wipe
42
+ // together rather than the gradient stretching across the whole wrapped run.
43
+ WebkitBoxDecorationBreak: 'clone',
44
+ boxDecorationBreak: 'clone'
45
+ });
46
+ };
47
+
48
+ /** Base padding and transparent underline placeholder (no layout shift on reveal). */
49
+ var revealBaseStyle = (0, _lazyNodeView.convertToInlineCss)({
50
+ padding: "1px 0 2px",
51
+ borderBottom: "2px solid transparent"
52
+ });
53
+ var buildRevealVariables = function buildRevealVariables(_ref) {
54
+ var background = _ref.background,
55
+ border = _ref.border;
56
+ return (0, _lazyNodeView.convertToInlineCss)((0, _defineProperty2.default)((0, _defineProperty2.default)({}, REVEAL_BG_VAR, background), REVEAL_BORDER_VAR, border));
57
+ };
58
+
59
+ /** Configuration that affects both resting state and reveal endpoint. */
60
+
61
+ /** Reveal colours from the refactored scheme registry (attribution palette ready). */
62
+ var revealColorsRefactored = function revealColorsRefactored(colors, _ref2) {
63
+ var hideAddedDiffsUnderline = _ref2.hideAddedDiffsUnderline,
64
+ isActive = _ref2.isActive,
65
+ isInserted = _ref2.isInserted;
66
+ return isInserted ? (0, _factory.getInsertedInlineRevealColors)(colors, isActive, hideAddedDiffsUnderline) : (0, _factory.getDeletedInlineRevealColors)(colors);
67
+ };
68
+
69
+ /**
70
+ * Legacy cohort reveal colours. Values MUST match `revealColorsRefactored()` output for
71
+ * `standard` scheme (test: `revealStyles.ts` in test package).
72
+ */
73
+ var revealColorsLegacy = function revealColorsLegacy(_ref3) {
74
+ var hideAddedDiffsUnderline = _ref3.hideAddedDiffsUnderline,
75
+ isActive = _ref3.isActive,
76
+ isInserted = _ref3.isInserted;
77
+ return isInserted ? {
78
+ // Mirrors `editingStyleExtended` / `editingStyleActiveExtended` and their
79
+ // `*NoUnderline` variants in `colorSchemes/standard.ts`.
80
+ background: isActive ? "var(--ds-background-accent-purple-subtler-pressed, #D8A0F7)" : "var(--ds-background-accent-purple-subtlest, #F8EEFE)",
81
+ border: hideAddedDiffsUnderline ? 'transparent' : "var(--ds-border-accent-purple, #AF59E1)"
82
+ } : {
83
+ // Mirrors `deletedInlineContentStyleExtended` in `colorSchemes/standard.ts`.
84
+ background: "var(--ds-background-accent-gray-subtlest, #F0F1F2)",
85
+ border: "var(--ds-border-accent-gray, #7D818A)"
86
+ };
87
+ };
88
+ var resolveRevealColors = function resolveRevealColors(colors, variant) {
89
+ return (0, _isExperimentEnabled.isExperimentEnabled)('platform_editor_show_diff_color_scheme_refactor') ? revealColorsRefactored(colors, variant) : revealColorsLegacy(variant);
90
+ };
91
+ var getColorScheme = function getColorScheme(colorScheme) {
92
+ return _schemes.colorSchemeRegistry[colorScheme !== null && colorScheme !== void 0 ? colorScheme : 'standard'];
93
+ };
94
+
95
+ /** Deleted text appearance (grey and strikethrough) without the highlight. */
96
+ var deletedTextOnlyStyle = function deletedTextOnlyStyle(colors, isActive) {
97
+ return (0, _isExperimentEnabled.isExperimentEnabled)('platform_editor_show_diff_color_scheme_refactor') ? (0, _factory.buildDeletedInlineStyleStandard)(colors, isActive ? 'active' : 'default') : isActive ? _standard.deletedContentStyleActive : _standard.deletedContentStyle;
98
+ };
99
+
100
+ /**
101
+ * Resolve reveal attribute and style for a decoration. Returned style REPLACES the decoration's
102
+ * highlight (not an addition) so the animation has something to wipe. Gate resolved in showDiff
103
+ * command for single shared answer between decorations and animation.
104
+ */
105
+ var resolveRevealStyle = exports.resolveRevealStyle = function resolveRevealStyle(_ref4) {
106
+ var colorScheme = _ref4.colorScheme,
107
+ _ref4$hideAddedDiffsU = _ref4.hideAddedDiffsUnderline,
108
+ hideAddedDiffsUnderline = _ref4$hideAddedDiffsU === void 0 ? false : _ref4$hideAddedDiffsU,
109
+ _ref4$includeDeletedT = _ref4.includeDeletedTextStyle,
110
+ includeDeletedTextStyle = _ref4$includeDeletedT === void 0 ? true : _ref4$includeDeletedT,
111
+ isActive = _ref4.isActive,
112
+ isInserted = _ref4.isInserted,
113
+ reveal = _ref4.reveal;
114
+ if ((reveal === null || reveal === void 0 ? void 0 : reveal.mode) !== 'phased' || !schemeSupportsReveal(colorScheme)) {
115
+ return undefined;
116
+ }
117
+ var colors = getColorScheme(colorScheme);
118
+ var _resolveRevealColors = resolveRevealColors(colors, {
119
+ hideAddedDiffsUnderline: hideAddedDiffsUnderline,
120
+ isActive: isActive,
121
+ isInserted: isInserted
122
+ }),
123
+ background = _resolveRevealColors.background,
124
+ border = _resolveRevealColors.border;
125
+ return {
126
+ role: isInserted ? 'added' : 'deleted',
127
+ style: (isInserted || !includeDeletedTextStyle ? '' : deletedTextOnlyStyle(colors, isActive)) + revealBaseStyle + buildWipeableBackground(background) + buildRevealVariables({
128
+ background: background,
129
+ border: border
130
+ })
131
+ };
132
+ };
133
+
134
+ /** Reveal style for imperatively-built elements (deleted-content widget wrappers). */
135
+ var applyRevealToElement = exports.applyRevealToElement = function applyRevealToElement(_ref5) {
136
+ var colorScheme = _ref5.colorScheme,
137
+ element = _ref5.element,
138
+ hideAddedDiffsUnderline = _ref5.hideAddedDiffsUnderline,
139
+ isActive = _ref5.isActive,
140
+ isInserted = _ref5.isInserted,
141
+ reveal = _ref5.reveal;
142
+ var resolved = resolveRevealStyle({
143
+ colorScheme: colorScheme,
144
+ hideAddedDiffsUnderline: hideAddedDiffsUnderline,
145
+ isActive: isActive,
146
+ isInserted: isInserted,
147
+ reveal: reveal,
148
+ // The widget supplies the deleted text's own grey/strikethrough styling already.
149
+ includeDeletedTextStyle: false
150
+ });
151
+ if (!resolved) {
152
+ return '';
153
+ }
154
+ element.setAttribute(REVEAL_ATTR, resolved.role);
155
+ return resolved.style;
156
+ };
@@ -12,6 +12,7 @@ var _nodeTypeUtils = require("@atlaskit/editor-common/utils/node-type-utils");
12
12
  var _isExperimentEnabled = require("@atlaskit/platform-feature-experiments/is-experiment-enabled");
13
13
  var _expValEquals = require("@atlaskit/tmp-editor-statsig/exp-val-equals");
14
14
  var _isExtendedEnabled = require("../../isExtendedEnabled");
15
+ var _revealStyles = require("../revealStyles");
15
16
  var _createInlineChangedDecoration = require("../createInlineChangedDecoration");
16
17
  var _tableCellEdgeAttrs = require("./tableCellEdgeAttrs");
17
18
  var _getAttrChangeRanges = require("./getAttrChangeRanges");
@@ -614,9 +615,17 @@ var injectInnerWrapper = exports.injectInnerWrapper = function injectInnerWrappe
614
615
  colorScheme = _ref13.colorScheme,
615
616
  isActive = _ref13.isActive,
616
617
  isInserted = _ref13.isInserted,
617
- diffType = _ref13.diffType;
618
+ diffType = _ref13.diffType,
619
+ reveal = _ref13.reveal;
618
620
  var wrapper = document.createElement('span');
619
- wrapper.setAttribute('style', isInserted ? (0, _wrapBlockNodeViewStyles.getInsertedContentStyle)(colorScheme, isActive) : (0, _wrapBlockNodeViewStyles.getDeletedContentStyle)(colorScheme, isActive, diffType));
621
+ var revealStyle = (0, _revealStyles.applyRevealToElement)({
622
+ colorScheme: colorScheme,
623
+ element: wrapper,
624
+ isActive: isActive !== null && isActive !== void 0 ? isActive : false,
625
+ isInserted: isInserted !== null && isInserted !== void 0 ? isInserted : false,
626
+ reveal: reveal
627
+ });
628
+ wrapper.setAttribute('style', (isInserted ? (0, _wrapBlockNodeViewStyles.getInsertedContentStyle)(colorScheme, isActive) : (0, _wrapBlockNodeViewStyles.getDeletedContentStyle)(colorScheme, isActive, diffType, Boolean(revealStyle))) + revealStyle);
620
629
  (0, _toConsumableArray2.default)(node.childNodes).forEach(function (child) {
621
630
  var removedChild = node.removeChild(child);
622
631
  wrapper.append(removedChild);
@@ -632,22 +641,33 @@ var createContentWrapper = exports.createContentWrapper = function createContent
632
641
  var isActive = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : false;
633
642
  var isInserted = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : false;
634
643
  var diffType = arguments.length > 3 ? arguments[3] : undefined;
644
+ var reveal = arguments.length > 4 ? arguments[4] : undefined;
635
645
  var wrapper = document.createElement('span');
636
646
  var baseStyle = (0, _lazyNodeView.convertToInlineCss)({
637
647
  position: 'relative',
638
648
  width: 'fit-content'
639
649
  });
650
+ // Empty unless the reveal is running, in which case the static highlight below is withheld so
651
+ // the animation can wipe it in instead.
652
+ var revealStyle = (0, _revealStyles.applyRevealToElement)({
653
+ colorScheme: colorScheme,
654
+ element: wrapper,
655
+ isActive: isActive,
656
+ isInserted: isInserted,
657
+ reveal: reveal
658
+ });
659
+ var deletedStyle = (0, _wrapBlockNodeViewStyles.getDeletedContentStyle)(colorScheme, isActive, diffType, Boolean(revealStyle));
640
660
  if ((0, _isExtendedEnabled.isExtendedEnabled)(diffType)) {
641
661
  if (isInserted) {
642
- wrapper.setAttribute('style', "".concat(baseStyle).concat((0, _wrapBlockNodeViewStyles.getInsertedContentStyle)(colorScheme, isActive)));
662
+ wrapper.setAttribute('style', "".concat(baseStyle).concat((0, _wrapBlockNodeViewStyles.getInsertedContentStyle)(colorScheme, isActive)).concat(revealStyle));
643
663
  } else {
644
- wrapper.setAttribute('style', "".concat(baseStyle).concat((0, _wrapBlockNodeViewStyles.getDeletedContentStyle)(colorScheme, isActive, diffType)));
664
+ wrapper.setAttribute('style', "".concat(baseStyle).concat(deletedStyle).concat(revealStyle));
645
665
  var strikethrough = document.createElement('span');
646
666
  strikethrough.setAttribute('style', (0, _wrapBlockNodeViewStyles.getDeletedContentStyleUnbounded)(colorScheme, isActive));
647
667
  wrapper.append(strikethrough);
648
668
  }
649
669
  } else {
650
- wrapper.setAttribute('style', "".concat(baseStyle).concat((0, _wrapBlockNodeViewStyles.getDeletedContentStyle)(colorScheme, isActive, diffType)));
670
+ wrapper.setAttribute('style', "".concat(baseStyle).concat(deletedStyle).concat(revealStyle));
651
671
  var _strikethrough = document.createElement('span');
652
672
  _strikethrough.setAttribute('style', (0, _wrapBlockNodeViewStyles.getDeletedContentStyleUnbounded)(colorScheme, isActive));
653
673
  wrapper.append(_strikethrough);
@@ -106,11 +106,12 @@ var getInsertedContentStyleNext = function getInsertedContentStyleNext(colorSche
106
106
  var getDeletedContentStyleNext = function getDeletedContentStyleNext(colorScheme) {
107
107
  var isActive = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : false;
108
108
  var diffType = arguments.length > 2 ? arguments[2] : undefined;
109
+ var omitHighlight = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : false;
109
110
  var colors = getColorScheme(colorScheme);
110
111
  var base = (0, _factory.buildDeletedInlineContentStyle)(colors, isActive ? 'active' : 'new');
111
112
 
112
113
  // glyphTint adds a background highlight and border-bottom over the tint; strikethrough does not.
113
- var needsExtendedHighlight = colors.deletedInlineTreatment === 'glyphTint' && (0, _isExtendedEnabled.isExtendedEnabled)(diffType);
114
+ var needsExtendedHighlight = colors.deletedInlineTreatment === 'glyphTint' && (0, _isExtendedEnabled.isExtendedEnabled)(diffType) && !omitHighlight;
114
115
  return needsExtendedHighlight ? base + (0, _factory.buildDeletedInlineContentStyleExtended)(colors) : base;
115
116
  };
116
117
  var resolveCellOverlayStyleNext = function resolveCellOverlayStyleNext(_ref) {
@@ -172,7 +173,8 @@ var getInsertedContentStyle = exports.getInsertedContentStyle = function getInse
172
173
  var getDeletedContentStyle = exports.getDeletedContentStyle = function getDeletedContentStyle(colorScheme) {
173
174
  var isActive = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : false;
174
175
  var diffType = arguments.length > 2 ? arguments[2] : undefined;
175
- return (0, _isExperimentEnabled.isExperimentEnabled)('platform_editor_show_diff_color_scheme_refactor') ? getDeletedContentStyleNext(colorScheme, isActive, diffType) : (0, _wrapBlockNodeViewStyles.getDeletedContentStyleLegacy)((0, _schemes.getLegacyColorScheme)(colorScheme), isActive, diffType);
176
+ var omitHighlight = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : false;
177
+ return (0, _isExperimentEnabled.isExperimentEnabled)('platform_editor_show_diff_color_scheme_refactor') ? getDeletedContentStyleNext(colorScheme, isActive, diffType, omitHighlight) : (0, _wrapBlockNodeViewStyles.getDeletedContentStyleLegacy)((0, _schemes.getLegacyColorScheme)(colorScheme), isActive, diffType, omitHighlight);
176
178
  };
177
179
 
178
180
  /**
@@ -172,10 +172,11 @@ var getInsertedContentStyleLegacy = exports.getInsertedContentStyleLegacy = func
172
172
  var getDeletedContentStyleLegacy = exports.getDeletedContentStyleLegacy = function getDeletedContentStyleLegacy(colorScheme) {
173
173
  var isActive = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : false;
174
174
  var diffType = arguments.length > 2 ? arguments[2] : undefined;
175
+ var omitHighlight = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : false;
175
176
  if (colorScheme === 'traditional') {
176
177
  return (0, _traditional.getDeletedTraditionalInlineStyle)(isActive);
177
178
  }
178
- if ((0, _isExtendedEnabled.isExtendedEnabled)(diffType)) {
179
+ if ((0, _isExtendedEnabled.isExtendedEnabled)(diffType) && !omitHighlight) {
179
180
  return (isActive ? _standard.deletedContentStyleActive : _standard.deletedContentStyleNew) + _standard.deletedInlineContentStyleExtended;
180
181
  }
181
182
  return isActive ? _standard.deletedContentStyleActive : _standard.deletedContentStyleNew;
@@ -18,6 +18,7 @@ var _enforceCustomStepRegisters = require("./enforceCustomStepRegisters");
18
18
  var _getScrollableDecorations = require("./getScrollableDecorations");
19
19
  var _isExtendedEnabled = require("./isExtendedEnabled");
20
20
  var _NodeViewSerializer = require("./NodeViewSerializer");
21
+ var _revealAnimation = require("./revealAnimation");
21
22
  var _scrollToDiff = require("./scrollToDiff");
22
23
  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; }
23
24
  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; }
@@ -91,12 +92,21 @@ var createPlugin = exports.createPlugin = function createPlugin(config, getIntl,
91
92
  var meta = tr.getMeta(showDiffPluginKey);
92
93
  var newPluginState = currentPluginState;
93
94
  if (meta) {
94
- if ((meta === null || meta === void 0 ? void 0 : meta.action) === 'SHOW_DIFF') {
95
- var _newPluginState, _newPluginState2, _newPluginState3, _newPluginState4, _newPluginState5, _newPluginState6, _newPluginState7, _newPluginState8, _newPluginState9, _newPluginState0;
96
- // Update the plugin state with the new metadata
97
- newPluginState = _objectSpread(_objectSpread(_objectSpread({}, currentPluginState), meta), {}, {
95
+ if ((meta === null || meta === void 0 ? void 0 : meta.action) === 'SHOW_DIFF' || (meta === null || meta === void 0 ? void 0 : meta.action) === 'REVEAL_COMPLETE') {
96
+ var _newPluginState, _newPluginState2, _newPluginState3, _newPluginState4, _newPluginState5, _newPluginState6, _newPluginState7, _newPluginState8, _newPluginState9, _newPluginState0, _newPluginState1;
97
+ // REVEAL_COMPLETE repaints with the reveal dropped, so the decorations render their
98
+ // ordinary resting style. Without it the reveal stays in state indefinitely and the
99
+ // next unrelated repaint re-emits the hidden, zero-width highlight with no animation
100
+ // left to bring it in — the highlight simply vanishes.
101
+ newPluginState = meta.action === 'REVEAL_COMPLETE' ? _objectSpread(_objectSpread({}, currentPluginState), {}, {
102
+ reveal: undefined
103
+ }) : _objectSpread(_objectSpread(_objectSpread({}, currentPluginState), meta), {}, {
98
104
  isDisplayingChanges: true,
99
- activeIndex: undefined
105
+ activeIndex: undefined,
106
+ // Explicit rather than left to the spread: an absent key in `meta` would let
107
+ // the previous paint's choreography leak into this one, so closing the diff
108
+ // would animate too.
109
+ reveal: meta.reveal
100
110
  });
101
111
  // Calculate and store decorations in state
102
112
  var _calculateDiffDecorat = (0, _calculateDiffDecorations.calculateDiffDecorations)(_objectSpread({
@@ -116,7 +126,10 @@ var createPlugin = exports.createPlugin = function createPlugin(config, getIntl,
116
126
  showIndicators: (_newPluginState6 = newPluginState) === null || _newPluginState6 === void 0 ? void 0 : _newPluginState6.showIndicators,
117
127
  smartThresholds: (_newPluginState7 = newPluginState) === null || _newPluginState7 === void 0 ? void 0 : _newPluginState7.smartThresholds,
118
128
  deletedDiffPlacement: (_newPluginState8 = newPluginState) === null || _newPluginState8 === void 0 ? void 0 : _newPluginState8.deletedDiffPlacement,
119
- inlineDeletedDiffPlacement: (_newPluginState9 = newPluginState) === null || _newPluginState9 === void 0 ? void 0 : _newPluginState9.inlineDeletedDiffPlacement
129
+ inlineDeletedDiffPlacement: (_newPluginState9 = newPluginState) === null || _newPluginState9 === void 0 ? void 0 : _newPluginState9.inlineDeletedDiffPlacement,
130
+ // SHOW_DIFF only. The scroll-to-next recalculation further down deliberately
131
+ // omits this so stepping through changes cannot replay the choreography.
132
+ reveal: (_newPluginState0 = newPluginState) === null || _newPluginState0 === void 0 ? void 0 : _newPluginState0.reveal
120
133
  } : {})),
121
134
  contributorTags = _calculateDiffDecorat.contributorTags,
122
135
  decorations = _calculateDiffDecorat.decorations,
@@ -124,7 +137,7 @@ var createPlugin = exports.createPlugin = function createPlugin(config, getIntl,
124
137
  // Update the decorations and their ids
125
138
  newPluginState.decorations = decorations;
126
139
  newPluginState.contributorTags = contributorTags;
127
- if ((0, _isExtendedEnabled.isExtendedEnabled)((_newPluginState0 = newPluginState) === null || _newPluginState0 === void 0 ? void 0 : _newPluginState0.diffType)) {
140
+ if ((0, _isExtendedEnabled.isExtendedEnabled)((_newPluginState1 = newPluginState) === null || _newPluginState1 === void 0 ? void 0 : _newPluginState1.diffType)) {
128
141
  newPluginState.diffDescriptors = diffDescriptors;
129
142
  }
130
143
  } else if ((meta === null || meta === void 0 ? void 0 : meta.action) === 'HIDE_DIFF') {
@@ -132,7 +145,8 @@ var createPlugin = exports.createPlugin = function createPlugin(config, getIntl,
132
145
  decorations: _view.DecorationSet.empty,
133
146
  isDisplayingChanges: false,
134
147
  activeIndex: undefined,
135
- contributorTags: []
148
+ contributorTags: [],
149
+ reveal: undefined
136
150
  }, (0, _isExtendedEnabled.isExtendedEnabled)(currentPluginState.diffType) ? {
137
151
  isInverted: false,
138
152
  diffType: (0, _isExtendedEnabled.getDefaultDiffType)(),
@@ -141,9 +155,9 @@ var createPlugin = exports.createPlugin = function createPlugin(config, getIntl,
141
155
  diffDescriptors: []
142
156
  } : {});
143
157
  } else if ((meta === null || meta === void 0 ? void 0 : meta.action) === 'SCROLL_TO_NEXT' || (meta === null || meta === void 0 ? void 0 : meta.action) === 'SCROLL_TO_PREVIOUS') {
144
- var _newPluginState1;
158
+ var _newPluginState10;
145
159
  // Update the active index in plugin state and recalculate decorations
146
- var _decorations = (0, _getScrollableDecorations.getScrollableDecorations)(currentPluginState.decorations, newState.doc, (_newPluginState1 = newPluginState) === null || _newPluginState1 === void 0 ? void 0 : _newPluginState1.diffType);
160
+ var _decorations = (0, _getScrollableDecorations.getScrollableDecorations)(currentPluginState.decorations, newState.doc, (_newPluginState10 = newPluginState) === null || _newPluginState10 === void 0 ? void 0 : _newPluginState10.diffType);
147
161
  if (_decorations.length > 0) {
148
162
  var _currentPluginState$a;
149
163
  // Initialize to -1 if undefined so that the first "next" scroll takes us to index 0 (first change).
@@ -233,6 +247,10 @@ var createPlugin = exports.createPlugin = function createPlugin(config, getIntl,
233
247
  }
234
248
  var pluginState = showDiffPluginKey.getState(view.state);
235
249
 
250
+ // A repaint rebuilds inline decorations, destroying any animation bound to them, so
251
+ // an in-flight reveal has to be re-attached to the new nodes. No-op when idle.
252
+ (0, _revealAnimation.rebindReveal)(view, view.dom);
253
+
236
254
  // Scroll to the first decoration when scrollIntoView was requested.
237
255
  // Use the same filtered/position-sorted list as the active-index path
238
256
  // so "first" reliably means the topmost scrollable diff in the document.
@@ -74,6 +74,10 @@ var resolveAgent = function resolveAgent(attribution, profilesByAccountId) {
74
74
  * reads as "nobody touched this", so the whole list is dropped and the plain diff renders instead.
75
75
  */
76
76
  var resolveDiffContributors = exports.resolveDiffContributors = function resolveDiffContributors(stepsWithAttribution, profiles) {
77
+ // Profiles opt callers into contributor tags; attribution alone still supplies diff colours.
78
+ if (profiles === undefined) {
79
+ return undefined;
80
+ }
77
81
  var profilesByAccountId = toProfilesByAccountId(profiles);
78
82
  var contributors = [];
79
83
  /** Keyed as the plugin keys changes, so no two entries can collapse onto one another. */