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

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 +45 -0
  2. package/dist/cjs/pm-plugins/calculateDiff/calculateDiffDecorations.js +5 -4
  3. package/dist/cjs/pm-plugins/decorations/colorSchemes/attributions.js +32 -7
  4. package/dist/cjs/pm-plugins/decorations/colorSchemes/factory.js +25 -4
  5. package/dist/cjs/pm-plugins/decorations/createInlineChangedDecoration.js +40 -14
  6. package/dist/cjs/pm-plugins/decorations/utils/wrapBlockNodeView.js +14 -0
  7. package/dist/cjs/pm-plugins/resolveDiffContributors.js +58 -7
  8. package/dist/cjs/ui/ContributorTag/buildContributorTagDom.js +70 -14
  9. package/dist/cjs/ui/ContributorTag/contributorAvatarRenderer.js +8 -2
  10. package/dist/cjs/ui/ContributorTag/contributorTagController.js +169 -150
  11. package/dist/cjs/ui/ContributorTag/contributorTagIcons.js +5 -1
  12. package/dist/es2019/pm-plugins/calculateDiff/calculateDiffDecorations.js +5 -4
  13. package/dist/es2019/pm-plugins/decorations/colorSchemes/attributions.js +17 -0
  14. package/dist/es2019/pm-plugins/decorations/colorSchemes/factory.js +25 -4
  15. package/dist/es2019/pm-plugins/decorations/createInlineChangedDecoration.js +40 -14
  16. package/dist/es2019/pm-plugins/decorations/utils/wrapBlockNodeView.js +14 -0
  17. package/dist/es2019/pm-plugins/resolveDiffContributors.js +57 -4
  18. package/dist/es2019/ui/ContributorTag/buildContributorTagDom.js +69 -13
  19. package/dist/es2019/ui/ContributorTag/contributorAvatarRenderer.js +8 -2
  20. package/dist/es2019/ui/ContributorTag/contributorTagController.js +164 -104
  21. package/dist/es2019/ui/ContributorTag/contributorTagIcons.js +5 -1
  22. package/dist/esm/pm-plugins/calculateDiff/calculateDiffDecorations.js +5 -4
  23. package/dist/esm/pm-plugins/decorations/colorSchemes/attributions.js +32 -7
  24. package/dist/esm/pm-plugins/decorations/colorSchemes/factory.js +25 -4
  25. package/dist/esm/pm-plugins/decorations/createInlineChangedDecoration.js +40 -14
  26. package/dist/esm/pm-plugins/decorations/utils/wrapBlockNodeView.js +14 -0
  27. package/dist/esm/pm-plugins/resolveDiffContributors.js +58 -7
  28. package/dist/esm/ui/ContributorTag/buildContributorTagDom.js +69 -13
  29. package/dist/esm/ui/ContributorTag/contributorAvatarRenderer.js +8 -2
  30. package/dist/esm/ui/ContributorTag/contributorTagController.js +171 -152
  31. package/dist/esm/ui/ContributorTag/contributorTagIcons.js +5 -1
  32. package/dist/types/pm-plugins/decorations/colorSchemes/factory.d.ts +8 -2
  33. package/dist/types/showDiffPluginType.d.ts +2 -2
  34. package/dist/types/ui/ContributorTag/buildContributorTagDom.d.ts +32 -3
  35. package/dist/types/ui/ContributorTag/contributorTagController.d.ts +32 -12
  36. package/dist/types/ui/ContributorTag/contributorTagIcons.d.ts +2 -2
  37. package/package.json +5 -5
@@ -2,14 +2,11 @@ import _toConsumableArray from "@babel/runtime/helpers/toConsumableArray";
2
2
  import _classCallCheck from "@babel/runtime/helpers/classCallCheck";
3
3
  import _createClass from "@babel/runtime/helpers/createClass";
4
4
  import _defineProperty from "@babel/runtime/helpers/defineProperty";
5
- function _createForOfIteratorHelper(r, e) { var t = "undefined" != typeof Symbol && r[Symbol.iterator] || r["@@iterator"]; if (!t) { if (Array.isArray(r) || (t = _unsupportedIterableToArray(r)) || e && r && "number" == typeof r.length) { t && (r = t); var _n = 0, F = function F() {}; return { s: F, n: function n() { return _n >= r.length ? { done: !0 } : { done: !1, value: r[_n++] }; }, e: function e(r) { throw r; }, f: F }; } throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); } var o, a = !0, u = !1; return { s: function s() { t = t.call(r); }, n: function n() { var r = t.next(); return a = r.done, r; }, e: function e(r) { u = !0, o = r; }, f: function f() { try { a || null == t.return || t.return(); } finally { if (u) throw o; } } }; }
6
- function _unsupportedIterableToArray(r, a) { if (r) { if ("string" == typeof r) return _arrayLikeToArray(r, a); var t = {}.toString.call(r).slice(8, -1); return "Object" === t && r.constructor && (t = r.constructor.name), "Map" === t || "Set" === t ? Array.from(r) : "Arguments" === t || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(t) ? _arrayLikeToArray(r, a) : void 0; } }
7
- 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; }
8
- import { bindAll } from 'bind-event-listener';
5
+ import { bind, bindAll } from 'bind-event-listener';
9
6
  import { VanillaTooltip } from '@atlaskit/editor-common/vanilla-tooltip';
10
7
  import { getAccentTokens } from '../../pm-plugins/decorations/colorSchemes/factory';
11
8
  import { colorSchemeRegistry } from '../../pm-plugins/decorations/colorSchemes/schemes';
12
- import { buildContributorTagDom, MIN_TAG_WIDTH } from './buildContributorTagDom';
9
+ import { buildContributorTagDom, CONTRIBUTOR_TAG_REVEALED_ATTRIBUTE, TAG_EXIT_FALLBACK_MS } from './buildContributorTagDom';
13
10
  import { contributorAvatarRenderer } from './contributorAvatarRenderer';
14
11
  import { contributorTagMessages } from './messages';
15
12
  var getContributorName = function getContributorName(contributor, formatMessage) {
@@ -28,8 +25,8 @@ var getContributorName = function getContributorName(contributor, formatMessage)
28
25
  };
29
26
 
30
27
  /**
31
- * Resolves the tag's accent from the same scheme value and token maps the highlight used, so the
32
- * two can never drift.
28
+ * Resolves the tag's accent from the same scheme value the highlight was drawn from, so the colour
29
+ * the two carry can never drift — only the tone does, which is `getAccentTokens`'s to pick.
33
30
  */
34
31
  var getTagAccent = function getTagAccent(_ref) {
35
32
  var colorScheme = _ref.colorScheme,
@@ -40,35 +37,70 @@ var getTagAccent = function getTagAccent(_ref) {
40
37
  return getAccentTokens(accent);
41
38
  };
42
39
 
43
- /** The line the change starts on: topmost of its fragments, leftmost among those. */
44
- var findChangeStartLine = function findChangeStartLine(highlights) {
45
- var start;
46
- var _iterator = _createForOfIteratorHelper(highlights),
47
- _step;
48
- try {
49
- for (_iterator.s(); !(_step = _iterator.n()).done;) {
50
- var highlight = _step.value;
51
- var _iterator2 = _createForOfIteratorHelper(highlight.getClientRects()),
52
- _step2;
53
- try {
54
- for (_iterator2.s(); !(_step2 = _iterator2.n()).done;) {
55
- var rect = _step2.value;
56
- if (!start || rect.top < start.top - 1 || Math.abs(rect.top - start.top) <= 1 && rect.left < start.left) {
57
- start = rect;
58
- }
59
- }
60
- } catch (err) {
61
- _iterator2.e(err);
62
- } finally {
63
- _iterator2.f();
40
+ /**
41
+ * Keyboard focus, as a member of `revealSources` alongside the elements the pointer can be over. It
42
+ * has no element of its own to key on — the tag is already in the set under its own hover.
43
+ */
44
+ var FOCUS_SOURCE = Symbol('contributor-tag-focus');
45
+
46
+ /** Moves the tag between the two ends of the fade `contributorTagStyles` declares. */
47
+ var setRevealed = function setRevealed(tag, isVisible) {
48
+ tag.toggleAttribute(CONTRIBUTOR_TAG_REVEALED_ATTRIBUTE, isVisible);
49
+ };
50
+
51
+ /**
52
+ * Whether flipping the tag's state will actually run a transition on it.
53
+ *
54
+ * Asked of the element rather than of `matchMedia`, because reduced motion and a missing stylesheet
55
+ * both compute to a zero duration — and a transition that never starts fires no `transitionend`, so
56
+ * a tag waiting for one would sit on screen until the backstop timeout.
57
+ *
58
+ * Read only when a tag is being taken down: it resolves style.
59
+ */
60
+ var willTransition = function willTransition(tag) {
61
+ return window.getComputedStyle(tag).transitionDuration.split(',').some(function (duration) {
62
+ return parseFloat(duration) > 0;
63
+ });
64
+ };
65
+
66
+ /**
67
+ * Fades a tag that is still on screen out, and releases it once the fade has finished.
68
+ *
69
+ * No controller owns the element by the time this runs — it is released up front so a republished
70
+ * model's tag can fade in over the top — so everything the removal needs is captured here.
71
+ */
72
+ var fadeTagOut = function fadeTagOut(tag, release) {
73
+ setRevealed(tag, false);
74
+
75
+ // Nothing to wait for: reduced motion, or no stylesheet to fade it with.
76
+ if (!willTransition(tag)) {
77
+ release();
78
+ return;
79
+ }
80
+ var unbind;
81
+ var isFinished = false;
82
+ var finish = function finish() {
83
+ var _unbind;
84
+ // Whichever of the two signals arrives first wins; the other must not release twice.
85
+ if (isFinished) {
86
+ return;
87
+ }
88
+ isFinished = true;
89
+ (_unbind = unbind) === null || _unbind === void 0 || _unbind();
90
+ unbind = undefined;
91
+ clearTimeout(fallbackId);
92
+ release();
93
+ };
94
+ unbind = bind(tag, {
95
+ type: 'transitionend',
96
+ // `transitionend` bubbles, and opacity is not the only property that could ever carry one.
97
+ listener: function listener(event) {
98
+ if (event.target === tag && event.propertyName === 'opacity') {
99
+ finish();
64
100
  }
65
101
  }
66
- } catch (err) {
67
- _iterator.e(err);
68
- } finally {
69
- _iterator.f();
70
- }
71
- return start;
102
+ });
103
+ var fallbackId = setTimeout(finish, TAG_EXIT_FALLBACK_MS);
72
104
  };
73
105
 
74
106
  /** The contributors a tag draws, keyed so a republished model can be recognised as the same pair. */
@@ -97,8 +129,18 @@ export var ContributorTagController = /*#__PURE__*/function () {
97
129
  _defineProperty(this, "bindToken", 0);
98
130
  _defineProperty(this, "fullLabel", '');
99
131
  _defineProperty(this, "isDestroyed", false);
100
- _defineProperty(this, "isHighlightHovered", false);
101
- _defineProperty(this, "isHoveredOrFocused", false);
132
+ /** Last visibility written, so a teardown knows whether there is anything on screen to fade. */
133
+ _defineProperty(this, "isVisible", false);
134
+ /**
135
+ * Everything currently asking for the tag to be up, other than the model: each hovered highlight,
136
+ * the tag itself while the pointer is on it, and `FOCUS_SOURCE` while it holds focus.
137
+ *
138
+ * One set rather than independent booleans, which is what let the exit's trailing delay go: a
139
+ * pointer crossing from the highlight onto the tag removes one member and adds another in the same
140
+ * dispatch, so no style is resolved in between and the browser never starts the exit. Booleans
141
+ * could not see each other, so the crossing wrote the tag out and straight back in.
142
+ */
143
+ _defineProperty(this, "revealSources", new Set());
102
144
  _defineProperty(this, "unbindHighlights", []);
103
145
  this.options = options;
104
146
  }
@@ -160,65 +202,101 @@ export var ContributorTagController = /*#__PURE__*/function () {
160
202
  this.renderLabels(next);
161
203
  this.applyAccent(next);
162
204
  this.syncHighlightBindings(next);
163
- this.applyStyles();
205
+ this.applyVisibility();
164
206
  this.syncTooltip();
165
207
  }
166
208
 
167
- /** Takes the tag's DOM down, keeping the subscription so a republished model redraws it. */
209
+ /**
210
+ * Takes the tag's DOM down, keeping the subscription so a republished model redraws it.
211
+ *
212
+ * A tag that is still on screen is released here and fades out on its own, so a republished
213
+ * model's tag fades in over the top of the outgoing one — see `fadeTagOut`.
214
+ */
168
215
  }, {
169
216
  key: "teardownTag",
170
217
  value: function teardownTag() {
171
- var _this$unbindTag, _this$tooltip, _this$dom;
218
+ var _this$unbindTag, _this$tooltip;
219
+ // Unbound synchronously, ahead of any fade: a tag on its way out must not be revealed again by
220
+ // the pointer, and these listeners are what hold this controller.
172
221
  this.unbindHighlightState();
173
222
  this.boundSelector = undefined;
174
223
  (_this$unbindTag = this.unbindTag) === null || _this$unbindTag === void 0 || _this$unbindTag.call(this);
175
224
  this.unbindTag = undefined;
225
+ // Destroyed rather than carried through the fade: it is a popover in the top layer.
176
226
  (_this$tooltip = this.tooltip) === null || _this$tooltip === void 0 || _this$tooltip.destroy();
177
227
  this.tooltip = undefined;
178
228
  this.tooltipContent = undefined;
179
- this.avatars.forEach(function (avatar) {
180
- return avatar.destroy();
181
- });
229
+ var dom = this.dom,
230
+ isVisible = this.isVisible;
231
+ // Destroying an avatar removes its element, so the avatars go with whoever removes the tag —
232
+ // destroyed here and it would fade out empty.
233
+ var avatars = this.avatars;
182
234
  this.avatars = [];
183
- (_this$dom = this.dom) === null || _this$dom === void 0 || _this$dom.root.remove();
184
235
  this.dom = undefined;
185
- this.maxWidthPx = undefined;
186
- this.isHoveredOrFocused = false;
187
- // Both hover flags belong to the listeners just unbound: a republished model rebinds them, and
188
- // stale `true` would draw the tag as visible with the pointer nowhere near it.
189
- this.isHighlightHovered = false;
236
+ this.isVisible = false;
237
+ // The set belongs to the listeners just unbound: a stale member would draw the tag as visible
238
+ // with the pointer nowhere near it.
239
+ this.revealSources.clear();
240
+ if (!dom) {
241
+ return;
242
+ }
243
+ var release = function release() {
244
+ avatars.forEach(function (avatar) {
245
+ return avatar.destroy();
246
+ });
247
+ dom.root.remove();
248
+ };
249
+
250
+ // Nothing on screen to fade. `isConnected` covers ProseMirror destroying the widget: a fade on
251
+ // a detached element never starts.
252
+ if (!isVisible || !dom.tag.isConnected) {
253
+ release();
254
+ return;
255
+ }
256
+ fadeTagOut(dom.tag, release);
190
257
  }
191
258
  }, {
192
259
  key: "bindTag",
193
260
  value: function bindTag(_ref4) {
194
- var _this3 = this;
195
261
  var tag = _ref4.tag;
196
- var setHoveredOrFocused = function setHoveredOrFocused(isHoveredOrFocused) {
197
- return function () {
198
- _this3.isHoveredOrFocused = isHoveredOrFocused;
199
- _this3.applyStyles();
200
- };
201
- };
262
+ // `mouseover`/`mouseout` rather than the enter/leave pair, because they are what fire as the
263
+ // pointer moves between the tag's own children. Both are keyed on the tag, so those moves add
264
+ // and remove the same member and the set never empties.
202
265
  this.unbindTag = bindAll(tag, [{
203
266
  type: 'mouseover',
204
- listener: setHoveredOrFocused(true)
267
+ listener: this.trackRevealSource(tag, true)
205
268
  }, {
206
269
  type: 'mouseout',
207
- listener: setHoveredOrFocused(false)
270
+ listener: this.trackRevealSource(tag, false)
208
271
  }, {
209
272
  type: 'focus',
210
- listener: setHoveredOrFocused(true)
273
+ listener: this.trackRevealSource(FOCUS_SOURCE, true)
211
274
  }, {
212
275
  type: 'blur',
213
- listener: setHoveredOrFocused(false)
276
+ listener: this.trackRevealSource(FOCUS_SOURCE, false)
214
277
  }]);
215
278
  }
279
+
280
+ /** One listener shape for every reveal source, so all of them land in the same set. */
281
+ }, {
282
+ key: "trackRevealSource",
283
+ value: function trackRevealSource(source, isRevealing) {
284
+ var _this3 = this;
285
+ return function () {
286
+ if (isRevealing) {
287
+ _this3.revealSources.add(source);
288
+ } else {
289
+ _this3.revealSources.delete(source);
290
+ }
291
+ _this3.applyVisibility();
292
+ };
293
+ }
216
294
  }, {
217
295
  key: "renderAvatars",
218
296
  value: function renderAvatars(model) {
219
- var _this$dom2,
297
+ var _this$dom,
220
298
  _this4 = this;
221
- var _ref5 = (_this$dom2 = this.dom) !== null && _this$dom2 !== void 0 ? _this$dom2 : {},
299
+ var _ref5 = (_this$dom = this.dom) !== null && _this$dom !== void 0 ? _this$dom : {},
222
300
  container = _ref5.avatars;
223
301
  if (!container) {
224
302
  return;
@@ -278,8 +356,8 @@ export var ContributorTagController = /*#__PURE__*/function () {
278
356
  var agentName = isPrimaryAgent ? primaryName : secondaryName;
279
357
  var userName = isPrimaryAgent ? secondaryName : primaryName;
280
358
 
281
- // A connected pair only ever shows the agent; the pair is spelled out in the label, because
282
- // the tag is never wider than the highlight.
359
+ // A connected pair only ever shows the agent; the pair is spelled out in the label, because the
360
+ // tag is capped at `MAX_TAG_WIDTH` and two names rarely fit inside it.
283
361
  this.dom.name.textContent = model.connectedContributor ? agentName || primaryName : primaryName;
284
362
  this.fullLabel = model.connectedContributor ? formatMessage(contributorTagMessages.changedByConnected, {
285
363
  agentName: agentName,
@@ -307,34 +385,34 @@ export var ContributorTagController = /*#__PURE__*/function () {
307
385
  }
308
386
  var accent = this.accentOf(model);
309
387
  this.dom.tag.style.setProperty('background-color', accent.background);
310
- this.dom.tag.style.setProperty('border-bottom-color', accent.border);
311
388
  this.dom.name.style.setProperty('color', accent.text);
312
389
  this.avatars.forEach(function (avatar) {
313
390
  return avatar.setRingColor(accent.background);
314
391
  });
315
392
  }
316
393
 
317
- /** Width clamp and visibility — the two things that are not carried by the model alone. */
394
+ /**
395
+ * Visibility — the one thing the model does not carry on its own, because hover is not in it.
396
+ *
397
+ * One idempotent attribute write, with nothing to guard: whether the flip is a real one, whether
398
+ * the tag is attached yet, and whether motion is wanted at all are all the stylesheet's to answer.
399
+ */
318
400
  }, {
319
- key: "applyStyles",
320
- value: function applyStyles() {
401
+ key: "applyVisibility",
402
+ value: function applyVisibility() {
321
403
  var _this$model;
322
404
  if (!this.dom) {
323
405
  return;
324
406
  }
325
- this.dom.root.style.setProperty('max-width',
326
- // Clamped to the highlight's own width, floored at `MIN_TAG_WIDTH` so a one-character
327
- // change still shows an avatar.
328
- this.maxWidthPx === undefined ? '' : "max(".concat(Math.round(this.maxWidthPx), "px, ").concat(MIN_TAG_WIDTH, ")"));
329
- var isVisible = Boolean((_this$model = this.model) === null || _this$model === void 0 ? void 0 : _this$model.isActive) || this.isHighlightHovered || this.isHoveredOrFocused;
330
- this.dom.tag.style.setProperty('opacity', isVisible ? '1' : '0');
331
- this.dom.tag.style.setProperty('pointer-events', isVisible ? 'auto' : 'none');
407
+ var isVisible = Boolean((_this$model = this.model) === null || _this$model === void 0 ? void 0 : _this$model.isActive) || this.revealSources.size > 0;
408
+ this.isVisible = isVisible;
409
+ setRevealed(this.dom.tag, isVisible);
332
410
  }
333
411
 
334
412
  /**
335
- * The full label is always the tooltip, whether or not the name it draws is clipped: the tag is
336
- * never wider than the highlight, so a name that fits at one width is ellipsised at the next, and
337
- * a connected pair spells out a contributor the tag does not show at any width.
413
+ * The full label is always the tooltip, whether or not the name it draws is clipped: a name longer
414
+ * than `MAX_TAG_WIDTH` is ellipsised, and a connected pair spells out a contributor the tag does
415
+ * not show at any width.
338
416
  *
339
417
  * `VanillaTooltip` has no "set new content" call, so it is rebuilt when the label changes — the
340
418
  * shape of `syncVanillaDisabledTooltip` in `mentionNodeView`.
@@ -365,9 +443,9 @@ export var ContributorTagController = /*#__PURE__*/function () {
365
443
  }
366
444
 
367
445
  /**
368
- * Observes the highlight this tag describes, for what CSS here cannot see: its hover state (the
446
+ * Observes the highlight this tag describes, for what CSS here cannot see: its hover state. The
369
447
  * highlight is a ProseMirror decoration in a different DOM subtree, so listeners are bound
370
- * imperatively via `data-diff-id`) and the width the tag is clamped to.
448
+ * imperatively via `data-diff-id`.
371
449
  *
372
450
  * Deliberately not routed through plugin state: a transaction per pointer move would re-run the
373
451
  * memoised decoration calculation.
@@ -387,8 +465,8 @@ export var ContributorTagController = /*#__PURE__*/function () {
387
465
  this.boundSelector = selector;
388
466
  this.unbindHighlightState();
389
467
 
390
- // ProseMirror draws the tag's host in the same pass as the highlights the tag measures, so
391
- // those may not be in the document yet. A microtask lands once that pass has finished.
468
+ // ProseMirror draws the tag's host in the same pass as the highlights it binds to, so those may
469
+ // not be in the document yet. A microtask lands once that pass has finished.
392
470
  var pendingBind = ++this.bindToken;
393
471
  queueMicrotask(function () {
394
472
  if (!_this5.isDestroyed && pendingBind === _this5.bindToken) {
@@ -399,91 +477,32 @@ export var ContributorTagController = /*#__PURE__*/function () {
399
477
  }, {
400
478
  key: "bindHighlightState",
401
479
  value: function bindHighlightState(selector) {
402
- var _this6 = this;
403
- // Both queries are scoped to this editor's content root, so a tag can only ever bind to the
404
- // decorations its own plugin instance rendered — another editor on the page, or one nested
405
- // inside this one, cannot supply the geometry.
406
- var editorRoot = this.options.getEditorRoot();
407
- var highlights = editorRoot === null || editorRoot === void 0 ? void 0 : editorRoot.querySelectorAll(selector);
408
- if (!editorRoot || !(highlights !== null && highlights !== void 0 && highlights.length)) {
480
+ var _this$options$getEdit,
481
+ _this6 = this;
482
+ // Scoped to this editor's content root, so a tag can only ever bind to the decorations its own
483
+ // plugin instance rendered — another editor on the page, or one nested inside this one, cannot
484
+ // reveal this tag.
485
+ var highlights = (_this$options$getEdit = this.options.getEditorRoot()) === null || _this$options$getEdit === void 0 ? void 0 : _this$options$getEdit.querySelectorAll(selector);
486
+ if (!(highlights !== null && highlights !== void 0 && highlights.length)) {
409
487
  return;
410
488
  }
411
489
 
412
- // The width comes from this tag's OWN decoration; sibling halves of the change are here for
413
- // hover only.
414
- var ownElements = Array.from(editorRoot.querySelectorAll("[data-diff-id=\"".concat(this.options.diffId, "\"]")));
415
- var measure = function measure() {
416
- var _findChangeStartLine;
417
- // Counted by distinct line box, not by client rect: an inline node inside the change (a
418
- // mention, emoji, date) splits the highlight into several rects on the same line, and
419
- // treating that as a wrap would drop the width clamp on changes that never wrapped.
420
- var lineTops = new Set();
421
- for (var _i = 0, _ownElements = ownElements; _i < _ownElements.length; _i++) {
422
- var highlight = _ownElements[_i];
423
- var _iterator3 = _createForOfIteratorHelper(highlight.getClientRects()),
424
- _step3;
425
- try {
426
- for (_iterator3.s(); !(_step3 = _iterator3.n()).done;) {
427
- var rect = _step3.value;
428
- lineTops.add(Math.round(rect.top));
429
- }
430
- } catch (err) {
431
- _iterator3.e(err);
432
- } finally {
433
- _iterator3.f();
434
- }
435
- }
436
-
437
- // A wrapped change's first line says nothing useful about how wide the tag may be.
438
- _this6.maxWidthPx = lineTops.size > 1 ? undefined : (_findChangeStartLine = findChangeStartLine(ownElements)) === null || _findChangeStartLine === void 0 ? void 0 : _findChangeStartLine.width;
439
- _this6.applyStyles();
440
- };
441
- measure();
442
-
443
- // An inline element cannot be observed itself (ResizeObserver skips boxes with no principal
444
- // box), so the block it sits in is observed instead.
445
- this.resizeObserver = new ResizeObserver(measure);
446
- var _iterator4 = _createForOfIteratorHelper(new Set(ownElements.map(function (_ref7) {
447
- var parentElement = _ref7.parentElement;
448
- return parentElement;
449
- }))),
450
- _step4;
451
- try {
452
- for (_iterator4.s(); !(_step4 = _iterator4.n()).done;) {
453
- var parent = _step4.value;
454
- if (parent) {
455
- this.resizeObserver.observe(parent);
456
- }
457
- }
458
-
459
- // `bindAll` takes a single target, and a change can render more than one highlight.
460
- } catch (err) {
461
- _iterator4.e(err);
462
- } finally {
463
- _iterator4.f();
464
- }
490
+ // `bindAll` takes a single target, and a change can render more than one highlight. Each is its
491
+ // own member of `revealSources`, so the pointer can cross between them without the set
492
+ // emptying.
465
493
  this.unbindHighlights = Array.from(highlights).map(function (highlight) {
466
494
  return bindAll(highlight, [{
467
495
  type: 'mouseenter',
468
- listener: function listener() {
469
- _this6.isHighlightHovered = true;
470
- _this6.applyStyles();
471
- }
496
+ listener: _this6.trackRevealSource(highlight, true)
472
497
  }, {
473
498
  type: 'mouseleave',
474
- listener: function listener() {
475
- _this6.isHighlightHovered = false;
476
- _this6.applyStyles();
477
- }
499
+ listener: _this6.trackRevealSource(highlight, false)
478
500
  }]);
479
501
  });
480
502
  }
481
503
  }, {
482
504
  key: "unbindHighlightState",
483
505
  value: function unbindHighlightState() {
484
- var _this$resizeObserver;
485
- (_this$resizeObserver = this.resizeObserver) === null || _this$resizeObserver === void 0 || _this$resizeObserver.disconnect();
486
- this.resizeObserver = undefined;
487
506
  this.unbindHighlights.forEach(function (unbind) {
488
507
  return unbind();
489
508
  });
@@ -2,7 +2,7 @@
2
2
  * The icons a contributor tag can draw, as inline SVG.
3
3
  *
4
4
  * Neither `@atlaskit/icon`, `@atlaskit/icon-lab` nor `@atlaskit/logo` exposes a raw-SVG entry
5
- * point, so the markup is copied here rather than kept as a React island for three glyphs. Keep in
5
+ * point, so the markup is copied here rather than kept as a React island for these glyphs. Keep in
6
6
  * sync with the sources named on each constant.
7
7
  *
8
8
  * The Rovo hex is the `brand` appearance, whose `--rovo-*-color` variables resolve to `initial` in
@@ -17,8 +17,12 @@ var AI_BOT_ICON_SVG = "<svg width=\"12\" height=\"12\" fill=\"none\" viewBox=\"0
17
17
 
18
18
  /** `RovoHexIcon` from `@atlaskit/logo/rovo-hex/icon`, at the 16px `xxsmall` renders. */
19
19
  var ROVO_HEX_ICON_SVG = "<svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" role=\"presentation\" style=\"display:block\"><path fill=\"#1868db\" fill-rule=\"evenodd\" d=\"m13.227 16.789-7.154 4.169-3.403-1.96a2.72 2.72 0 0 1-1.362-2.355V7.366c0-.973.518-1.87 1.361-2.354l6.044-3.487-.038.114a3.6 3.6 0 0 0-.172 1.088v9.278c0 1.274.68 2.45 1.786 3.085z\" clip-rule=\"evenodd\"/><path fill=\"#6a9a23\" fill-rule=\"evenodd\" d=\"m11.296 15.671-7.193 4.153 6.607 3.812a2.73 2.73 0 0 0 2.676.026l.048-.033a2.73 2.73 0 0 0 1.232-1.525 2.7 2.7 0 0 0 .127-.822v-3.594z\" clip-rule=\"evenodd\"/><path fill=\"#af59e1\" fill-rule=\"evenodd\" d=\"m21.477 5.003-4.404-2.539-5.904 4.89 2.69 1.556a3.56 3.56 0 0 1 1.785 3.086v9.277a3.6 3.6 0 0 1-.21 1.202l6.044-3.486a2.71 2.71 0 0 0 1.362-2.355V7.357c0-.97-.521-1.87-1.363-2.354\" clip-rule=\"evenodd\"/><path fill=\"#fca700\" fill-rule=\"evenodd\" d=\"M12.74 8.266 9.353 6.312V2.718c0-.283.044-.56.127-.821A2.73 2.73 0 0 1 10.71.372V.37a.4.4 0 0 0 .048-.033 2.73 2.73 0 0 1 2.676.026l6.499 3.75z\" clip-rule=\"evenodd\"/></svg>";
20
+
21
+ /** ClaudeLogo from ai-mate/studio-browse-kit, inset to fit the agent hexagon. */
22
+ var CLAUDE_ICON_SVG = "<svg width=\"12\" height=\"12\" viewBox=\"8 8 32 32\" role=\"presentation\" style=\"display:block\"><path fill=\"#FFFFFF\" d=\"M14.2785 29.274L20.5732 25.7445L20.6785 25.4369L20.5732 25.2669H20.2653L19.2122 25.2021L15.6152 25.105L12.4962 24.9755L9.47443 24.8136L8.71291 24.6517L8 23.7126L8.07291 23.2431L8.71291 22.8141L9.62835 22.895L11.6537 23.0326L14.6916 23.2431L16.8952 23.3726L20.16 23.7126H20.6785L20.7514 23.5022L20.5732 23.3726L20.4354 23.2431L17.2922 21.1141L13.8896 18.8636L12.1073 17.5684L11.1433 16.9127L10.6572 16.2975L10.4466 14.9537L11.3215 13.9904L12.4962 14.0713L12.7959 14.1523L13.9868 15.067L16.5306 17.0342L19.8522 19.4789L20.3382 19.8836L20.5327 19.746L20.557 19.6489L20.3382 19.2846L18.5316 16.0223L16.6035 12.7033L15.7448 11.3271L15.518 10.5014C15.437 10.1614 15.3803 9.87807 15.3803 9.52998L16.3767 8.17809L16.9276 8L18.2562 8.17809L18.8152 8.6638L19.6415 10.55L20.9782 13.5209L23.0522 17.5603L23.6597 18.7584L23.9838 19.8674L24.1053 20.2074H24.3159V20.0132L24.4861 17.7384L24.802 14.9456L25.1099 11.3514L25.2152 10.3395L25.7175 9.12522L26.7139 8.46952L27.4916 8.84189L28.1316 9.75664L28.0425 10.3476L27.6618 12.8166L26.9165 16.6861L26.4304 19.2765H26.7139L27.038 18.9527L28.3504 17.2122L30.5539 14.4599L31.5261 13.3671L32.6603 12.1609L33.3894 11.5861H34.7666L35.7792 13.0918L35.3256 14.6461L33.9079 16.4432L32.7332 17.9651L31.0481 20.2317L29.9949 22.045L30.0922 22.1907L30.3433 22.1665L34.1509 21.3569L36.2086 20.9846L38.6633 20.5636L39.7732 21.0817L39.8947 21.6079L39.4572 22.6845L36.8324 23.3322L33.7539 23.9474L29.1686 25.0321L29.1119 25.0726L29.1767 25.1536L31.2425 25.3478L32.1256 25.3964H34.2886L38.3149 25.6959L39.3681 26.3921L40 27.2421L39.8947 27.8897L38.2744 28.7154L36.0871 28.1973L30.9833 26.9831L29.2334 26.5459H28.9904V26.6916L30.4486 28.1164L33.122 30.5287L36.4678 33.6372L36.638 34.4063L36.2086 35.0134L35.7549 34.9486L32.8142 32.7387L31.68 31.743L29.1119 29.5816H28.9418V29.8082L29.5332 30.6744L32.6603 35.3696L32.8223 36.8105L32.5954 37.28L31.7853 37.5634L30.8942 37.4015L29.0633 34.8353L27.1757 31.9454L25.6527 29.3549L25.4663 29.4602L24.5671 39.1338L24.1458 39.6276L23.1737 40L22.3635 39.3848L21.9342 38.3891L22.3635 36.422L22.882 33.8558L23.3033 31.8158L23.6841 29.2821L23.9109 28.4402L23.8947 28.3835L23.7084 28.4078L21.7965 31.0306L18.8881 34.9567L16.5873 37.4177L16.0365 37.6362L15.0805 37.1424L15.1696 36.2601L15.7043 35.4748L18.8881 31.4273L20.8081 28.9178L22.0476 27.4688L22.0395 27.2583H21.9666L13.5089 32.7468L12.002 32.9411L11.3539 32.3339L11.4349 31.3382L11.7428 31.0144L14.2866 29.2659L14.2785 29.274Z\"/></svg>";
20
23
  var markup = {
21
24
  aiBot: AI_BOT_ICON_SVG,
25
+ claude: CLAUDE_ICON_SVG,
22
26
  person: PERSON_ICON_SVG,
23
27
  rovoHex: ROVO_HEX_ICON_SVG
24
28
  };
@@ -1,8 +1,14 @@
1
1
  import type { AdsAccentColor, DiffColorScheme } from './types';
2
- /** Presentation tokens for one diff colour, shared by highlights and contributor tags. */
2
+ /**
3
+ * Presentation tokens for a contributor tag in one diff colour.
4
+ *
5
+ * A `bolder` fill rather than the `subtlest` tint the highlight uses: the tag is a label on the
6
+ * change, and at tag size a tint reads as a second, weaker highlight sitting above the real one.
7
+ * `inverse` is the only text tone that reads on a bolder fill, and it needs no per-colour map —
8
+ * which is also why the tag carries no border: the fill alone bounds it.
9
+ */
3
10
  export declare function getAccentTokens(color: AdsAccentColor): {
4
11
  background: string;
5
- border: string;
6
12
  text: string;
7
13
  };
8
14
  /** Inline inserted content — default state. */
@@ -35,8 +35,8 @@ export type DiffContributorProfile = {
35
35
  name: string;
36
36
  };
37
37
  type DiffContributorKind = 'user' | 'agent';
38
- /** Agent presentation: `rovo`/`identified` use the agent's identity, `external` is generic. */
39
- type DiffAgentKind = 'rovo' | 'identified' | 'external';
38
+ /** Agent presentation: branded, identified by profile, or a generic external agent. */
39
+ type DiffAgentKind = 'rovo' | 'claude' | 'identified' | 'external';
40
40
  /**
41
41
  * A contributor the plugin has resolved from a step attribution and a supplied profile. Internal:
42
42
  * never re-exported from an entry point and not reachable from any public type. Same for
@@ -1,10 +1,39 @@
1
1
  export declare const CONTRIBUTOR_TAG_TESTID = "diff-contributor-tag";
2
2
  export declare const CONTRIBUTOR_TAG_NAME_TESTID = "diff-contributor-tag-name";
3
3
  /**
4
- * Floor for the tag width (~one 16px avatar plus padding), so the highlight-width clamp cannot
5
- * collapse the box on a very short highlight.
4
+ * Ceiling for the tag width. A constant rather than the width of the change: a change is routinely a
5
+ * word or two, and clamping to it ellipsised the contributor's name — the one thing the tag exists to
6
+ * show. Longer names are ellipsised into the tooltip.
6
7
  */
7
- export declare const MIN_TAG_WIDTH = "28px";
8
+ export declare const MAX_TAG_WIDTH = "200px";
9
+ /**
10
+ * The tag sits on the line above the change, so it has to paint over that line to be readable —
11
+ * including any diff highlight already on it, which would otherwise slice straight through the tag.
12
+ * Hence one level above `akEditorUnitZIndex`, where the highlights sit; exported because the
13
+ * change's own highlight is stacked one level below it — see `stackBelowContributorTagStyle` in
14
+ * `createInlineChangedDecoration`.
15
+ *
16
+ * Painting above its own highlight is only safe because the tag can never reach it: `bottom: 100%`
17
+ * puts the root's bottom edge exactly on the highlight's top edge, and the root's `overflow: clip`
18
+ * keeps every pixel the tag paints inside it.
19
+ */
20
+ export declare const CONTRIBUTOR_TAG_Z_INDEX = 2;
21
+ /**
22
+ * The plugin's whole side of the tag's motion: the fade and the hidden state are
23
+ * `contributorTagStyles` in both EditorContentContainer stylesheets, keyed on this literal and the
24
+ * one below — rename in step. This package cannot import `@atlaskit/editor-core` at runtime, so the
25
+ * plugin emits the hook and editor-core owns the rule, as `VanillaTooltip` does with
26
+ * `VANILLA_TOOLTIP_DEFAULT_CLASS`.
27
+ */
28
+ export declare const CONTRIBUTOR_TAG_CLASS = "ak-editor-diff-contributor-tag";
29
+ /** Toggled by the controller to move the tag between the fade's two ends. */
30
+ export declare const CONTRIBUTOR_TAG_REVEALED_ATTRIBUTE = "data-revealed";
31
+ /**
32
+ * Backstop for removing a fading-out tag: a cancelled transition fires no `transitionend`. Must stay
33
+ * above the fade's 600ms, which lives in another package's stylesheet and cannot be read back as a
34
+ * number — hence the wide margin. Raise in step with that duration.
35
+ */
36
+ export declare const TAG_EXIT_FALLBACK_MS = 1200;
8
37
  export type ContributorTagDom = {
9
38
  avatars: HTMLSpanElement;
10
39
  name: HTMLSpanElement;
@@ -38,11 +38,19 @@ export declare class ContributorTagController {
38
38
  private fullLabel;
39
39
  private host;
40
40
  private isDestroyed;
41
- private isHighlightHovered;
42
- private isHoveredOrFocused;
43
- private maxWidthPx;
41
+ /** Last visibility written, so a teardown knows whether there is anything on screen to fade. */
42
+ private isVisible;
44
43
  private model;
45
- private resizeObserver;
44
+ /**
45
+ * Everything currently asking for the tag to be up, other than the model: each hovered highlight,
46
+ * the tag itself while the pointer is on it, and `FOCUS_SOURCE` while it holds focus.
47
+ *
48
+ * One set rather than independent booleans, which is what let the exit's trailing delay go: a
49
+ * pointer crossing from the highlight onto the tag removes one member and adds another in the same
50
+ * dispatch, so no style is resolved in between and the browser never starts the exit. Booleans
51
+ * could not see each other, so the crossing wrote the tag out and straight back in.
52
+ */
53
+ private revealSources;
46
54
  private tooltip;
47
55
  private tooltipContent;
48
56
  private unbindHighlights;
@@ -53,28 +61,40 @@ export declare class ContributorTagController {
53
61
  destroy(): void;
54
62
  private findModel;
55
63
  private applyModel;
56
- /** Takes the tag's DOM down, keeping the subscription so a republished model redraws it. */
64
+ /**
65
+ * Takes the tag's DOM down, keeping the subscription so a republished model redraws it.
66
+ *
67
+ * A tag that is still on screen is released here and fades out on its own, so a republished
68
+ * model's tag fades in over the top of the outgoing one — see `fadeTagOut`.
69
+ */
57
70
  private teardownTag;
58
71
  private bindTag;
72
+ /** One listener shape for every reveal source, so all of them land in the same set. */
73
+ private trackRevealSource;
59
74
  private renderAvatars;
60
75
  private renderLabels;
61
76
  private accentOf;
62
77
  private applyAccent;
63
- /** Width clamp and visibility — the two things that are not carried by the model alone. */
64
- private applyStyles;
65
78
  /**
66
- * The full label is always the tooltip, whether or not the name it draws is clipped: the tag is
67
- * never wider than the highlight, so a name that fits at one width is ellipsised at the next, and
68
- * a connected pair spells out a contributor the tag does not show at any width.
79
+ * Visibility — the one thing the model does not carry on its own, because hover is not in it.
80
+ *
81
+ * One idempotent attribute write, with nothing to guard: whether the flip is a real one, whether
82
+ * the tag is attached yet, and whether motion is wanted at all are all the stylesheet's to answer.
83
+ */
84
+ private applyVisibility;
85
+ /**
86
+ * The full label is always the tooltip, whether or not the name it draws is clipped: a name longer
87
+ * than `MAX_TAG_WIDTH` is ellipsised, and a connected pair spells out a contributor the tag does
88
+ * not show at any width.
69
89
  *
70
90
  * `VanillaTooltip` has no "set new content" call, so it is rebuilt when the label changes — the
71
91
  * shape of `syncVanillaDisabledTooltip` in `mentionNodeView`.
72
92
  */
73
93
  private syncTooltip;
74
94
  /**
75
- * Observes the highlight this tag describes, for what CSS here cannot see: its hover state (the
95
+ * Observes the highlight this tag describes, for what CSS here cannot see: its hover state. The
76
96
  * highlight is a ProseMirror decoration in a different DOM subtree, so listeners are bound
77
- * imperatively via `data-diff-id`) and the width the tag is clamped to.
97
+ * imperatively via `data-diff-id`.
78
98
  *
79
99
  * Deliberately not routed through plugin state: a transaction per pointer move would re-run the
80
100
  * memoised decoration calculation.