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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (24) hide show
  1. package/CHANGELOG.md +23 -0
  2. package/dist/cjs/pm-plugins/decorations/colorSchemes/attributions.js +32 -7
  3. package/dist/cjs/pm-plugins/getScrollableDecorations.js +35 -4
  4. package/dist/cjs/pm-plugins/resolveDiffContributors.js +58 -7
  5. package/dist/cjs/pm-plugins/scrollToDiff.js +10 -5
  6. package/dist/cjs/ui/ContributorTag/contributorAvatarRenderer.js +8 -2
  7. package/dist/cjs/ui/ContributorTag/contributorTagIcons.js +5 -1
  8. package/dist/es2019/pm-plugins/decorations/colorSchemes/attributions.js +17 -0
  9. package/dist/es2019/pm-plugins/getScrollableDecorations.js +31 -4
  10. package/dist/es2019/pm-plugins/resolveDiffContributors.js +57 -4
  11. package/dist/es2019/pm-plugins/scrollToDiff.js +10 -5
  12. package/dist/es2019/ui/ContributorTag/contributorAvatarRenderer.js +8 -2
  13. package/dist/es2019/ui/ContributorTag/contributorTagIcons.js +5 -1
  14. package/dist/esm/pm-plugins/decorations/colorSchemes/attributions.js +32 -7
  15. package/dist/esm/pm-plugins/getScrollableDecorations.js +35 -4
  16. package/dist/esm/pm-plugins/resolveDiffContributors.js +58 -7
  17. package/dist/esm/pm-plugins/scrollToDiff.js +10 -5
  18. package/dist/esm/ui/ContributorTag/contributorAvatarRenderer.js +8 -2
  19. package/dist/esm/ui/ContributorTag/contributorTagIcons.js +5 -1
  20. package/dist/types/pm-plugins/decorations/decorationKeys.d.ts +8 -0
  21. package/dist/types/pm-plugins/getScrollableDecorations.d.ts +4 -1
  22. package/dist/types/showDiffPluginType.d.ts +2 -2
  23. package/dist/types/ui/ContributorTag/contributorTagIcons.d.ts +2 -2
  24. package/package.json +4 -4
package/CHANGELOG.md CHANGED
@@ -1,5 +1,28 @@
1
1
  # @atlaskit/editor-plugin-show-diff
2
2
 
3
+ ## 15.1.9
4
+
5
+ ### Patch Changes
6
+
7
+ - [`e3e068dfdea34`](https://bitbucket.org/atlassian/atlassian-frontend-monorepo/commits/e3e068dfdea34) -
8
+ [ux] Behind `platform_editor_ai_show_diff_patch_1`, fix scrolling to a diff landing below the top
9
+ of the change when `confluence_ncs_step_diffing_version_history` is enabled. Deleted content shown
10
+ above the content that replaced it is a zero-width widget on a more negative `side` at the same
11
+ position, and grouping collapsed the pair into one entry represented by the added content — so
12
+ scrolling resolved that range and left the deleted block above the viewport. Decorations sharing a
13
+ position are now ranked by the side they paint on, and a group that starts with deleted content is
14
+ represented by that widget, so scrolling reaches the visual start of the edit.
15
+
16
+ ## 15.1.8
17
+
18
+ ### Patch Changes
19
+
20
+ - [`920a9bc294e07`](https://bitbucket.org/atlassian/atlassian-frontend-monorepo/commits/920a9bc294e07) -
21
+ Show names for supported external agents, plus Claude branding and its consistent orange diff
22
+ colour. Share the Claude colour override through getParticipantColor for diff attribution and
23
+ collaboration telepointers behind confluence_ncs_step_diffing_version_history.
24
+ - Updated dependencies
25
+
3
26
  ## 15.1.7
4
27
 
5
28
  ### Patch Changes
@@ -132,29 +132,54 @@ var isContributorTagsEnabled = exports.isContributorTagsEnabled = function isCon
132
132
  var createAttributionColorMap = exports.createAttributionColorMap = function createAttributionColorMap(stepAttributions) {
133
133
  var colors = new Map();
134
134
  var allocatedColors = new Set();
135
+
136
+ // Reserve shared brand colours before hashing other actors, regardless of step order.
135
137
  var _iterator2 = _createForOfIteratorHelper(stepAttributions),
136
138
  _step2;
137
139
  try {
138
140
  for (_iterator2.s(); !(_step2 = _iterator2.n()).done;) {
139
141
  var attribution = _step2.value;
140
142
  var identity = getAttributionIdentity(attribution);
141
- if (!identity || colors.has(identity.key)) {
143
+ if (!identity) {
142
144
  continue;
143
145
  }
144
- var _getParticipantColor = (0, _utils.getParticipantColor)(identity.colorSeed),
145
- index = _getParticipantColor.index;
146
- var hashedColor = HASHED_PARTICIPANT_COLOR_SCHEMES[index];
146
+ var _getParticipantColor = (0, _utils.getParticipantColor)(identity.colorSeed, attribution === null || attribution === void 0 ? void 0 : attribution.agentType),
147
+ index = _getParticipantColor.index,
148
+ isFixed = _getParticipantColor.isFixed;
149
+ var color = HASHED_PARTICIPANT_COLOR_SCHEMES[index];
150
+ if (isFixed && color) {
151
+ colors.set(identity.key, color);
152
+ allocatedColors.add(color);
153
+ }
154
+ }
155
+ } catch (err) {
156
+ _iterator2.e(err);
157
+ } finally {
158
+ _iterator2.f();
159
+ }
160
+ var _iterator3 = _createForOfIteratorHelper(stepAttributions),
161
+ _step3;
162
+ try {
163
+ for (_iterator3.s(); !(_step3 = _iterator3.n()).done;) {
164
+ var _attribution = _step3.value;
165
+ var _identity = getAttributionIdentity(_attribution);
166
+ if (!_identity || colors.has(_identity.key)) {
167
+ continue;
168
+ }
169
+ var _getParticipantColor2 = (0, _utils.getParticipantColor)(_identity.colorSeed),
170
+ _index = _getParticipantColor2.index;
171
+ var hashedColor = HASHED_PARTICIPANT_COLOR_SCHEMES[_index];
147
172
  if (!hashedColor) {
148
173
  continue;
149
174
  }
150
175
  var participantColorScheme = getAvailableColor(hashedColor, allocatedColors);
151
- colors.set(identity.key, participantColorScheme);
176
+ colors.set(_identity.key, participantColorScheme);
152
177
  allocatedColors.add(participantColorScheme);
153
178
  }
154
179
  } catch (err) {
155
- _iterator2.e(err);
180
+ _iterator3.e(err);
156
181
  } finally {
157
- _iterator2.f();
182
+ _iterator3.f();
158
183
  }
159
184
  return colors;
160
185
  };
@@ -6,12 +6,15 @@ Object.defineProperty(exports, "__esModule", {
6
6
  });
7
7
  exports.getScrollableDecorations = void 0;
8
8
  exports.isInlineDiffDecorationRenderableInDoc = isInlineDiffDecorationRenderableInDoc;
9
+ var _defineProperty2 = _interopRequireDefault(require("@babel/runtime/helpers/defineProperty"));
9
10
  var _toConsumableArray2 = _interopRequireDefault(require("@babel/runtime/helpers/toConsumableArray"));
10
11
  var _typeof2 = _interopRequireDefault(require("@babel/runtime/helpers/typeof"));
11
12
  var _view = require("@atlaskit/editor-prosemirror/view");
12
13
  var _fg = require("@atlaskit/platform-feature-flags/fg");
13
14
  var _decorationKeys = require("./decorations/decorationKeys");
14
15
  var _isExtendedEnabled = require("./isExtendedEnabled");
16
+ function ownKeys(e, r) { var t = Object.keys(e); if (Object.getOwnPropertySymbols) { var o = Object.getOwnPropertySymbols(e); r && (o = o.filter(function (r) { return Object.getOwnPropertyDescriptor(e, r).enumerable; })), t.push.apply(t, o); } return t; }
17
+ function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? ownKeys(Object(t), !0).forEach(function (r) { (0, _defineProperty2.default)(e, r, t[r]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function (r) { Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r)); }); } return e; }
15
18
  /**
16
19
  * True if `fragment` contains at least one inline node (text, hardBreak, emoji, mention, etc.).
17
20
  * Block-only subtrees (e.g. empty paragraphs, block cards with no inline children) return false.
@@ -49,6 +52,15 @@ function specHasDiffKeyPrefix(spec, keyPrefix) {
49
52
  return Boolean(spec && (0, _typeof2.default)(spec) === 'object' && 'key' in spec && typeof spec.key === 'string' && spec.key.startsWith(keyPrefix));
50
53
  }
51
54
 
55
+ /**
56
+ * Where a decoration paints relative to others at the same position: negative before the node at
57
+ * that position, positive after it. Only deleted-content widgets carry one — everything else is
58
+ * ranged, and so paints where its range starts.
59
+ */
60
+ function decorationSide(decoration) {
61
+ return (0, _decorationKeys.isDiffDecoration)(decoration) && decoration.spec.side || 0;
62
+ }
63
+
52
64
  /**
53
65
  * Collapses decorations that represent one continuous customer-facing edit.
54
66
  *
@@ -94,13 +106,29 @@ function groupTouchingDecorations(decorations, isInlineDecoration) {
94
106
  var representative = (_ref2 = (_group$find = group.find(isInlineDecoration)) !== null && _group$find !== void 0 ? _group$find : group.find(function (decoration) {
95
107
  return decoration.from !== decoration.to;
96
108
  })) !== null && _ref2 !== void 0 ? _ref2 : group[0];
97
- if (!representative || representative.from === from && representative.to === to) {
109
+ if (!representative) {
110
+ return representative;
111
+ }
112
+
113
+ // Deleted content is a widget at the start of the added content that replaced it, on a more
114
+ // negative side so it paints above. That makes it the visual start of the edit, reachable
115
+ // only through its own DOM — resolving the group's start position lands on the added content
116
+ // painted after it. The group keeps its range, which navigation and the active-range
117
+ // calculation both need, and reports the widget as what to scroll to.
118
+ var scrollTarget = (0, _fg.fg)('platform_editor_ai_show_diff_patch_1') ? group.find(function (decoration) {
119
+ return decoration.from === from && decorationSide(decoration) < decorationSide(representative);
120
+ }) : undefined;
121
+ if (!scrollTarget && representative.from === from && representative.to === to) {
98
122
  return representative;
99
123
  }
100
124
 
101
125
  // This decoration is only used for navigation and active-range calculation. The actual
102
- // visual decorations remain in the DecorationSet with their original ranges and styles.
103
- return isInlineDecoration(representative) ? _view.Decoration.inline(from, to, {}, representative.spec) : _view.Decoration.node(from, to, {}, representative.spec);
126
+ // visual decorations remain in the DecorationSet with their original ranges and styles —
127
+ // including their spec, hence a copy to add `scrollTarget` for `scrollToDiff` to read.
128
+ var spec = _objectSpread(_objectSpread({}, representative.spec), {}, {
129
+ scrollTarget: scrollTarget
130
+ });
131
+ return isInlineDecoration(representative) ? _view.Decoration.inline(from, to, {}, spec) : _view.Decoration.node(from, to, {}, spec);
104
132
  });
105
133
  }
106
134
 
@@ -117,7 +145,10 @@ function groupTouchingDecorations(decorations, isInlineDecoration) {
117
145
  * 5. When `confluence_ncs_step_diffing_version_history` is enabled, groups overlapping or
118
146
  * directly touching ranges across decoration types into one result, using the union of all
119
147
  * grouped ranges
120
- * (zero-width widgets can join a group without extending it)
148
+ * (zero-width widgets can join a group without extending it). Under
149
+ * `platform_editor_ai_show_diff_patch_1`, a group that starts with content painting above
150
+ * the content that replaced it — reports that widget as its `scrollTarget` spec,
151
+ * so scrolling reaches the visual start of the edit
121
152
  * 6. Results are sorted by from position, then by to position
122
153
  *
123
154
  * @param set - The DecorationSet to extract scrollable decorations from
@@ -7,16 +7,59 @@ Object.defineProperty(exports, "__esModule", {
7
7
  exports.resolveDiffContributors = void 0;
8
8
  var _defineProperty2 = _interopRequireDefault(require("@babel/runtime/helpers/defineProperty"));
9
9
  var _attributions = require("./decorations/colorSchemes/attributions");
10
- 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; }
11
- 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; }
12
10
  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; } } }; }
13
11
  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; } }
14
12
  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; }
13
+ 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; }
14
+ function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? ownKeys(Object(t), !0).forEach(function (r) { (0, _defineProperty2.default)(e, r, t[r]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function (r) { Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r)); }); } return e; }
15
15
  /** A brand name, so a literal rather than a translated message. */
16
16
  var ROVO_AGENT_NAME = 'Rovo';
17
17
 
18
18
  /** `agentType` values that render as "Rovo" rather than as a generic external agent. */
19
19
  var ROVO_AGENT_TYPES = new Set(['convo-ai', 'rovo_chat']);
20
+ /** Known external agent types emitted by Confluence's agent identification service. */
21
+ var EXTERNAL_AGENT_PRESENTATIONS = {
22
+ claude: {
23
+ agentKind: 'claude',
24
+ name: 'Claude'
25
+ },
26
+ codex: {
27
+ agentKind: 'external',
28
+ name: 'Codex'
29
+ },
30
+ vscode: {
31
+ agentKind: 'external',
32
+ name: 'VS Code'
33
+ },
34
+ hermes: {
35
+ agentKind: 'external',
36
+ name: 'Hermes'
37
+ },
38
+ runlayer: {
39
+ agentKind: 'external',
40
+ name: 'Runlayer'
41
+ },
42
+ zed: {
43
+ agentKind: 'external',
44
+ name: 'Zed'
45
+ },
46
+ ampcode: {
47
+ agentKind: 'external',
48
+ name: 'Ampcode'
49
+ },
50
+ antigravity: {
51
+ agentKind: 'external',
52
+ name: 'Antigravity'
53
+ },
54
+ devin_cli: {
55
+ agentKind: 'external',
56
+ name: 'Devin CLI'
57
+ },
58
+ pi_agent: {
59
+ agentKind: 'external',
60
+ name: 'Pi Agent'
61
+ }
62
+ };
20
63
  var hasAgentIdentity = function hasAgentIdentity(attribution) {
21
64
  var _attribution$agentId, _attribution$agentTyp;
22
65
  return Boolean(((_attribution$agentId = attribution.agentId) === null || _attribution$agentId === void 0 ? void 0 : _attribution$agentId.trim()) || ((_attribution$agentTyp = attribution.agentType) === null || _attribution$agentTyp === void 0 ? void 0 : _attribution$agentTyp.trim()));
@@ -41,9 +84,16 @@ var resolveUser = function resolveUser(userId, profilesByAccountId) {
41
84
  } : undefined;
42
85
  };
43
86
 
44
- /** An agent always resolves: its profile, else "Rovo" for a first-party type, else external. */
87
+ /** Resolve branding or a profile first, then a known type name or the external-agent fallback. */
45
88
  var resolveAgent = function resolveAgent(attribution, profilesByAccountId) {
46
- var _attribution$agentId2, _attribution$agentTyp2;
89
+ var _attribution$agentTyp2, _attribution$agentId2;
90
+ var agentType = (_attribution$agentTyp2 = attribution.agentType) === null || _attribution$agentTyp2 === void 0 ? void 0 : _attribution$agentTyp2.trim().toLowerCase();
91
+ var agentPresentation = agentType ? EXTERNAL_AGENT_PRESENTATIONS[agentType] : undefined;
92
+ if (agentPresentation && agentPresentation.agentKind !== 'external') {
93
+ return _objectSpread(_objectSpread({}, agentPresentation), {}, {
94
+ kind: 'agent'
95
+ });
96
+ }
47
97
  var agentId = (_attribution$agentId2 = attribution.agentId) === null || _attribution$agentId2 === void 0 ? void 0 : _attribution$agentId2.trim();
48
98
  var agentProfile = agentId ? profilesByAccountId.get(agentId) : undefined;
49
99
  if (agentProfile) {
@@ -54,14 +104,15 @@ var resolveAgent = function resolveAgent(attribution, profilesByAccountId) {
54
104
  name: agentProfile.name
55
105
  };
56
106
  }
57
- var agentType = (_attribution$agentTyp2 = attribution.agentType) === null || _attribution$agentTyp2 === void 0 ? void 0 : _attribution$agentTyp2.trim().toLowerCase();
58
107
 
59
- // An empty name lets the tag fall back to its own localised "External agent" label.
108
+ // Unknown types keep an empty name for the tag's localised "External agent" label.
60
109
  return agentType && ROVO_AGENT_TYPES.has(agentType) ? {
61
110
  agentKind: 'rovo',
62
111
  kind: 'agent',
63
112
  name: ROVO_AGENT_NAME
64
- } : {
113
+ } : agentPresentation ? _objectSpread(_objectSpread({}, agentPresentation), {}, {
114
+ kind: 'agent'
115
+ }) : {
65
116
  agentKind: 'external',
66
117
  kind: 'agent',
67
118
  name: ''
@@ -51,17 +51,22 @@ var scrollToDecoration = exports.scrollToDecoration = function scrollToDecoratio
51
51
  if (!decoration) {
52
52
  return function () {};
53
53
  }
54
+
55
+ // A grouped decoration spans a whole customer-facing edit, which can visually start with a
56
+ // deleted-content widget rather than at the group's `from` position — see `scrollTarget` in
57
+ // `getScrollableDecorations`. Only set under `platform_editor_ai_show_diff_patch_1`.
58
+ var target = (0, _decorationKeys.isDiffDecoration)(decoration) && decoration.spec.scrollTarget || decoration;
54
59
  var rafId = requestAnimationFrame(function () {
55
60
  rafId = null;
56
- if ((0, _decorationKeys.isDiffDecoration)(decoration) && decoration.spec.decorationType === 'widget') {
57
- var _decoration$type;
61
+ if ((0, _decorationKeys.isDiffDecoration)(target) && target.spec.decorationType === 'widget') {
62
+ var _target$type;
58
63
  // @ts-expect-error - decoration.type is not typed public API
59
- var widgetDom = decoration === null || decoration === void 0 || (_decoration$type = decoration.type) === null || _decoration$type === void 0 ? void 0 : _decoration$type.toDOM;
64
+ var widgetDom = target === null || target === void 0 || (_target$type = target.type) === null || _target$type === void 0 ? void 0 : _target$type.toDOM;
60
65
  scrollToSelection(widgetDom);
61
66
  } else {
62
67
  var _view$domAtPos;
63
- var targetNode = view.nodeDOM(decoration === null || decoration === void 0 ? void 0 : decoration.from);
64
- var node = targetNode instanceof Element ? targetNode : (_view$domAtPos = view.domAtPos(decoration === null || decoration === void 0 ? void 0 : decoration.from)) === null || _view$domAtPos === void 0 ? void 0 : _view$domAtPos.node;
68
+ var targetNode = view.nodeDOM(target === null || target === void 0 ? void 0 : target.from);
69
+ var node = targetNode instanceof Element ? targetNode : (_view$domAtPos = view.domAtPos(target === null || target === void 0 ? void 0 : target.from)) === null || _view$domAtPos === void 0 ? void 0 : _view$domAtPos.node;
65
70
  scrollToSelection(node);
66
71
  }
67
72
  });
@@ -9,6 +9,12 @@ var _avatarSizes = require("@atlaskit/avatar/avatar-sizes");
9
9
  var _default = require("@atlaskit/avatar/constants/default");
10
10
  var _lazyNodeView = require("@atlaskit/editor-common/lazy-node-view");
11
11
  var _contributorTagIcons = require("./contributorTagIcons");
12
+ /** Agent kinds with a custom contributor-tag icon. */
13
+ var AGENT_KIND_ICONS = {
14
+ claude: 'claude',
15
+ rovo: 'rovoHex'
16
+ };
17
+
12
18
  /** The size `@atlaskit/avatar`'s `xxsmall` rendered at. */
13
19
  var AVATAR_SIZE = _avatarSizes.AVATAR_SIZES.xxsmall;
14
20
  /** `xxsmall` also carried a `space.025` margin — the same 2px — which is where the ring is painted. */
@@ -86,7 +92,7 @@ var contributorAvatarRenderer = exports.contributorAvatarRenderer = function con
86
92
  var image;
87
93
  var unbindImageError;
88
94
  var showFallback = function showFallback() {
89
- var _unbindImageError, _image;
95
+ var _unbindImageError, _image, _AGENT_KIND_ICONS, _contributor$agentKin;
90
96
  (_unbindImageError = unbindImageError) === null || _unbindImageError === void 0 || _unbindImageError();
91
97
  unbindImageError = undefined;
92
98
  (_image = image) === null || _image === void 0 || _image.remove();
@@ -98,7 +104,7 @@ var contributorAvatarRenderer = exports.contributorAvatarRenderer = function con
98
104
  shape.appendChild(fallback);
99
105
  return;
100
106
  }
101
- shape.appendChild((0, _contributorTagIcons.getContributorTagIcon)(contributor.agentKind === 'rovo' ? 'rovoHex' : 'aiBot', doc));
107
+ shape.appendChild((0, _contributorTagIcons.getContributorTagIcon)((_AGENT_KIND_ICONS = AGENT_KIND_ICONS[(_contributor$agentKin = contributor.agentKind) !== null && _contributor$agentKin !== void 0 ? _contributor$agentKin : 'external']) !== null && _AGENT_KIND_ICONS !== void 0 ? _AGENT_KIND_ICONS : 'aiBot', doc));
102
108
  };
103
109
  if (contributor.avatarUrl) {
104
110
  image = doc.createElement('img');
@@ -8,7 +8,7 @@ exports.getContributorTagIcon = void 0;
8
8
  * The icons a contributor tag can draw, as inline SVG.
9
9
  *
10
10
  * Neither `@atlaskit/icon`, `@atlaskit/icon-lab` nor `@atlaskit/logo` exposes a raw-SVG entry
11
- * point, so the markup is copied here rather than kept as a React island for three glyphs. Keep in
11
+ * point, so the markup is copied here rather than kept as a React island for these glyphs. Keep in
12
12
  * sync with the sources named on each constant.
13
13
  *
14
14
  * The Rovo hex is the `brand` appearance, whose `--rovo-*-color` variables resolve to `initial` in
@@ -23,8 +23,12 @@ var AI_BOT_ICON_SVG = "<svg width=\"12\" height=\"12\" fill=\"none\" viewBox=\"0
23
23
 
24
24
  /** `RovoHexIcon` from `@atlaskit/logo/rovo-hex/icon`, at the 16px `xxsmall` renders. */
25
25
  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>";
26
+
27
+ /** ClaudeLogo from ai-mate/studio-browse-kit, inset to fit the agent hexagon. */
28
+ 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>";
26
29
  var markup = {
27
30
  aiBot: AI_BOT_ICON_SVG,
31
+ claude: CLAUDE_ICON_SVG,
28
32
  person: PERSON_ICON_SVG,
29
33
  rovoHex: ROVO_HEX_ICON_SVG
30
34
  };
@@ -103,6 +103,23 @@ export const isContributorTagsEnabled = (stepAttributions, contributors) => areA
103
103
  export const createAttributionColorMap = stepAttributions => {
104
104
  const colors = new Map();
105
105
  const allocatedColors = new Set();
106
+
107
+ // Reserve shared brand colours before hashing other actors, regardless of step order.
108
+ for (const attribution of stepAttributions) {
109
+ const identity = getAttributionIdentity(attribution);
110
+ if (!identity) {
111
+ continue;
112
+ }
113
+ const {
114
+ index,
115
+ isFixed
116
+ } = getParticipantColor(identity.colorSeed, attribution === null || attribution === void 0 ? void 0 : attribution.agentType);
117
+ const color = HASHED_PARTICIPANT_COLOR_SCHEMES[index];
118
+ if (isFixed && color) {
119
+ colors.set(identity.key, color);
120
+ allocatedColors.add(color);
121
+ }
122
+ }
106
123
  for (const attribution of stepAttributions) {
107
124
  const identity = getAttributionIdentity(attribution);
108
125
  if (!identity || colors.has(identity.key)) {
@@ -40,6 +40,15 @@ function specHasDiffKeyPrefix(spec, keyPrefix) {
40
40
  return Boolean(spec && typeof spec === 'object' && 'key' in spec && typeof spec.key === 'string' && spec.key.startsWith(keyPrefix));
41
41
  }
42
42
 
43
+ /**
44
+ * Where a decoration paints relative to others at the same position: negative before the node at
45
+ * that position, positive after it. Only deleted-content widgets carry one — everything else is
46
+ * ranged, and so paints where its range starts.
47
+ */
48
+ function decorationSide(decoration) {
49
+ return isDiffDecoration(decoration) && decoration.spec.side || 0;
50
+ }
51
+
43
52
  /**
44
53
  * Collapses decorations that represent one continuous customer-facing edit.
45
54
  *
@@ -82,13 +91,28 @@ function groupTouchingDecorations(decorations, isInlineDecoration) {
82
91
  }) => {
83
92
  var _ref, _group$find;
84
93
  const representative = (_ref = (_group$find = group.find(isInlineDecoration)) !== null && _group$find !== void 0 ? _group$find : group.find(decoration => decoration.from !== decoration.to)) !== null && _ref !== void 0 ? _ref : group[0];
85
- if (!representative || representative.from === from && representative.to === to) {
94
+ if (!representative) {
95
+ return representative;
96
+ }
97
+
98
+ // Deleted content is a widget at the start of the added content that replaced it, on a more
99
+ // negative side so it paints above. That makes it the visual start of the edit, reachable
100
+ // only through its own DOM — resolving the group's start position lands on the added content
101
+ // painted after it. The group keeps its range, which navigation and the active-range
102
+ // calculation both need, and reports the widget as what to scroll to.
103
+ const scrollTarget = fg('platform_editor_ai_show_diff_patch_1') ? group.find(decoration => decoration.from === from && decorationSide(decoration) < decorationSide(representative)) : undefined;
104
+ if (!scrollTarget && representative.from === from && representative.to === to) {
86
105
  return representative;
87
106
  }
88
107
 
89
108
  // This decoration is only used for navigation and active-range calculation. The actual
90
- // visual decorations remain in the DecorationSet with their original ranges and styles.
91
- return isInlineDecoration(representative) ? Decoration.inline(from, to, {}, representative.spec) : Decoration.node(from, to, {}, representative.spec);
109
+ // visual decorations remain in the DecorationSet with their original ranges and styles —
110
+ // including their spec, hence a copy to add `scrollTarget` for `scrollToDiff` to read.
111
+ const spec = {
112
+ ...representative.spec,
113
+ scrollTarget
114
+ };
115
+ return isInlineDecoration(representative) ? Decoration.inline(from, to, {}, spec) : Decoration.node(from, to, {}, spec);
92
116
  });
93
117
  }
94
118
 
@@ -105,7 +129,10 @@ function groupTouchingDecorations(decorations, isInlineDecoration) {
105
129
  * 5. When `confluence_ncs_step_diffing_version_history` is enabled, groups overlapping or
106
130
  * directly touching ranges across decoration types into one result, using the union of all
107
131
  * grouped ranges
108
- * (zero-width widgets can join a group without extending it)
132
+ * (zero-width widgets can join a group without extending it). Under
133
+ * `platform_editor_ai_show_diff_patch_1`, a group that starts with content painting above
134
+ * the content that replaced it — reports that widget as its `scrollTarget` spec,
135
+ * so scrolling reaches the visual start of the edit
109
136
  * 6. Results are sorted by from position, then by to position
110
137
  *
111
138
  * @param set - The DecorationSet to extract scrollable decorations from
@@ -5,6 +5,49 @@ const ROVO_AGENT_NAME = 'Rovo';
5
5
 
6
6
  /** `agentType` values that render as "Rovo" rather than as a generic external agent. */
7
7
  const ROVO_AGENT_TYPES = new Set(['convo-ai', 'rovo_chat']);
8
+ /** Known external agent types emitted by Confluence's agent identification service. */
9
+ const EXTERNAL_AGENT_PRESENTATIONS = {
10
+ claude: {
11
+ agentKind: 'claude',
12
+ name: 'Claude'
13
+ },
14
+ codex: {
15
+ agentKind: 'external',
16
+ name: 'Codex'
17
+ },
18
+ vscode: {
19
+ agentKind: 'external',
20
+ name: 'VS Code'
21
+ },
22
+ hermes: {
23
+ agentKind: 'external',
24
+ name: 'Hermes'
25
+ },
26
+ runlayer: {
27
+ agentKind: 'external',
28
+ name: 'Runlayer'
29
+ },
30
+ zed: {
31
+ agentKind: 'external',
32
+ name: 'Zed'
33
+ },
34
+ ampcode: {
35
+ agentKind: 'external',
36
+ name: 'Ampcode'
37
+ },
38
+ antigravity: {
39
+ agentKind: 'external',
40
+ name: 'Antigravity'
41
+ },
42
+ devin_cli: {
43
+ agentKind: 'external',
44
+ name: 'Devin CLI'
45
+ },
46
+ pi_agent: {
47
+ agentKind: 'external',
48
+ name: 'Pi Agent'
49
+ }
50
+ };
8
51
  const hasAgentIdentity = attribution => {
9
52
  var _attribution$agentId, _attribution$agentTyp;
10
53
  return Boolean(((_attribution$agentId = attribution.agentId) === null || _attribution$agentId === void 0 ? void 0 : _attribution$agentId.trim()) || ((_attribution$agentTyp = attribution.agentType) === null || _attribution$agentTyp === void 0 ? void 0 : _attribution$agentTyp.trim()));
@@ -29,9 +72,17 @@ const resolveUser = (userId, profilesByAccountId) => {
29
72
  } : undefined;
30
73
  };
31
74
 
32
- /** An agent always resolves: its profile, else "Rovo" for a first-party type, else external. */
75
+ /** Resolve branding or a profile first, then a known type name or the external-agent fallback. */
33
76
  const resolveAgent = (attribution, profilesByAccountId) => {
34
- var _attribution$agentId2, _attribution$agentTyp2;
77
+ var _attribution$agentTyp2, _attribution$agentId2;
78
+ const agentType = (_attribution$agentTyp2 = attribution.agentType) === null || _attribution$agentTyp2 === void 0 ? void 0 : _attribution$agentTyp2.trim().toLowerCase();
79
+ const agentPresentation = agentType ? EXTERNAL_AGENT_PRESENTATIONS[agentType] : undefined;
80
+ if (agentPresentation && agentPresentation.agentKind !== 'external') {
81
+ return {
82
+ ...agentPresentation,
83
+ kind: 'agent'
84
+ };
85
+ }
35
86
  const agentId = (_attribution$agentId2 = attribution.agentId) === null || _attribution$agentId2 === void 0 ? void 0 : _attribution$agentId2.trim();
36
87
  const agentProfile = agentId ? profilesByAccountId.get(agentId) : undefined;
37
88
  if (agentProfile) {
@@ -42,13 +93,15 @@ const resolveAgent = (attribution, profilesByAccountId) => {
42
93
  name: agentProfile.name
43
94
  };
44
95
  }
45
- const agentType = (_attribution$agentTyp2 = attribution.agentType) === null || _attribution$agentTyp2 === void 0 ? void 0 : _attribution$agentTyp2.trim().toLowerCase();
46
96
 
47
- // An empty name lets the tag fall back to its own localised "External agent" label.
97
+ // Unknown types keep an empty name for the tag's localised "External agent" label.
48
98
  return agentType && ROVO_AGENT_TYPES.has(agentType) ? {
49
99
  agentKind: 'rovo',
50
100
  kind: 'agent',
51
101
  name: ROVO_AGENT_NAME
102
+ } : agentPresentation ? {
103
+ ...agentPresentation,
104
+ kind: 'agent'
52
105
  } : {
53
106
  agentKind: 'external',
54
107
  kind: 'agent',
@@ -45,17 +45,22 @@ export const scrollToDecoration = (view, decorations, activeIndex = 0) => {
45
45
  if (!decoration) {
46
46
  return () => {};
47
47
  }
48
+
49
+ // A grouped decoration spans a whole customer-facing edit, which can visually start with a
50
+ // deleted-content widget rather than at the group's `from` position — see `scrollTarget` in
51
+ // `getScrollableDecorations`. Only set under `platform_editor_ai_show_diff_patch_1`.
52
+ const target = isDiffDecoration(decoration) && decoration.spec.scrollTarget || decoration;
48
53
  let rafId = requestAnimationFrame(() => {
49
54
  rafId = null;
50
- if (isDiffDecoration(decoration) && decoration.spec.decorationType === 'widget') {
51
- var _decoration$type;
55
+ if (isDiffDecoration(target) && target.spec.decorationType === 'widget') {
56
+ var _target$type;
52
57
  // @ts-expect-error - decoration.type is not typed public API
53
- const widgetDom = decoration === null || decoration === void 0 ? void 0 : (_decoration$type = decoration.type) === null || _decoration$type === void 0 ? void 0 : _decoration$type.toDOM;
58
+ const widgetDom = target === null || target === void 0 ? void 0 : (_target$type = target.type) === null || _target$type === void 0 ? void 0 : _target$type.toDOM;
54
59
  scrollToSelection(widgetDom);
55
60
  } else {
56
61
  var _view$domAtPos;
57
- const targetNode = view.nodeDOM(decoration === null || decoration === void 0 ? void 0 : decoration.from);
58
- const node = targetNode instanceof Element ? targetNode : (_view$domAtPos = view.domAtPos(decoration === null || decoration === void 0 ? void 0 : decoration.from)) === null || _view$domAtPos === void 0 ? void 0 : _view$domAtPos.node;
62
+ const targetNode = view.nodeDOM(target === null || target === void 0 ? void 0 : target.from);
63
+ const node = targetNode instanceof Element ? targetNode : (_view$domAtPos = view.domAtPos(target === null || target === void 0 ? void 0 : target.from)) === null || _view$domAtPos === void 0 ? void 0 : _view$domAtPos.node;
59
64
  scrollToSelection(node);
60
65
  }
61
66
  });
@@ -4,6 +4,12 @@ import { BORDER_WIDTH } from '@atlaskit/avatar/constants/default';
4
4
  import { convertToInlineCss } from '@atlaskit/editor-common/lazy-node-view';
5
5
  import { getContributorTagIcon } from './contributorTagIcons';
6
6
 
7
+ /** Agent kinds with a custom contributor-tag icon. */
8
+ const AGENT_KIND_ICONS = {
9
+ claude: 'claude',
10
+ rovo: 'rovoHex'
11
+ };
12
+
7
13
  /** The size `@atlaskit/avatar`'s `xxsmall` rendered at. */
8
14
  const AVATAR_SIZE = AVATAR_SIZES.xxsmall;
9
15
  /** `xxsmall` also carried a `space.025` margin — the same 2px — which is where the ring is painted. */
@@ -82,7 +88,7 @@ export const contributorAvatarRenderer = ({
82
88
  let image;
83
89
  let unbindImageError;
84
90
  const showFallback = () => {
85
- var _unbindImageError, _image;
91
+ var _unbindImageError, _image, _AGENT_KIND_ICONS, _contributor$agentKin;
86
92
  (_unbindImageError = unbindImageError) === null || _unbindImageError === void 0 ? void 0 : _unbindImageError();
87
93
  unbindImageError = undefined;
88
94
  (_image = image) === null || _image === void 0 ? void 0 : _image.remove();
@@ -94,7 +100,7 @@ export const contributorAvatarRenderer = ({
94
100
  shape.appendChild(fallback);
95
101
  return;
96
102
  }
97
- shape.appendChild(getContributorTagIcon(contributor.agentKind === 'rovo' ? 'rovoHex' : 'aiBot', doc));
103
+ shape.appendChild(getContributorTagIcon((_AGENT_KIND_ICONS = AGENT_KIND_ICONS[(_contributor$agentKin = contributor.agentKind) !== null && _contributor$agentKin !== void 0 ? _contributor$agentKin : 'external']) !== null && _AGENT_KIND_ICONS !== void 0 ? _AGENT_KIND_ICONS : 'aiBot', doc));
98
104
  };
99
105
  if (contributor.avatarUrl) {
100
106
  image = doc.createElement('img');
@@ -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 @@ const AI_BOT_ICON_SVG = `<svg width="12" height="12" fill="none" viewBox="0 0 16
17
17
 
18
18
  /** `RovoHexIcon` from `@atlaskit/logo/rovo-hex/icon`, at the 16px `xxsmall` renders. */
19
19
  const 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
+ const 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
  const 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
  };
@@ -126,29 +126,54 @@ export var isContributorTagsEnabled = function isContributorTagsEnabled(stepAttr
126
126
  export var createAttributionColorMap = function createAttributionColorMap(stepAttributions) {
127
127
  var colors = new Map();
128
128
  var allocatedColors = new Set();
129
+
130
+ // Reserve shared brand colours before hashing other actors, regardless of step order.
129
131
  var _iterator2 = _createForOfIteratorHelper(stepAttributions),
130
132
  _step2;
131
133
  try {
132
134
  for (_iterator2.s(); !(_step2 = _iterator2.n()).done;) {
133
135
  var attribution = _step2.value;
134
136
  var identity = getAttributionIdentity(attribution);
135
- if (!identity || colors.has(identity.key)) {
137
+ if (!identity) {
136
138
  continue;
137
139
  }
138
- var _getParticipantColor = getParticipantColor(identity.colorSeed),
139
- index = _getParticipantColor.index;
140
- var hashedColor = HASHED_PARTICIPANT_COLOR_SCHEMES[index];
140
+ var _getParticipantColor = getParticipantColor(identity.colorSeed, attribution === null || attribution === void 0 ? void 0 : attribution.agentType),
141
+ index = _getParticipantColor.index,
142
+ isFixed = _getParticipantColor.isFixed;
143
+ var color = HASHED_PARTICIPANT_COLOR_SCHEMES[index];
144
+ if (isFixed && color) {
145
+ colors.set(identity.key, color);
146
+ allocatedColors.add(color);
147
+ }
148
+ }
149
+ } catch (err) {
150
+ _iterator2.e(err);
151
+ } finally {
152
+ _iterator2.f();
153
+ }
154
+ var _iterator3 = _createForOfIteratorHelper(stepAttributions),
155
+ _step3;
156
+ try {
157
+ for (_iterator3.s(); !(_step3 = _iterator3.n()).done;) {
158
+ var _attribution = _step3.value;
159
+ var _identity = getAttributionIdentity(_attribution);
160
+ if (!_identity || colors.has(_identity.key)) {
161
+ continue;
162
+ }
163
+ var _getParticipantColor2 = getParticipantColor(_identity.colorSeed),
164
+ _index = _getParticipantColor2.index;
165
+ var hashedColor = HASHED_PARTICIPANT_COLOR_SCHEMES[_index];
141
166
  if (!hashedColor) {
142
167
  continue;
143
168
  }
144
169
  var participantColorScheme = getAvailableColor(hashedColor, allocatedColors);
145
- colors.set(identity.key, participantColorScheme);
170
+ colors.set(_identity.key, participantColorScheme);
146
171
  allocatedColors.add(participantColorScheme);
147
172
  }
148
173
  } catch (err) {
149
- _iterator2.e(err);
174
+ _iterator3.e(err);
150
175
  } finally {
151
- _iterator2.f();
176
+ _iterator3.f();
152
177
  }
153
178
  return colors;
154
179
  };
@@ -1,5 +1,8 @@
1
+ import _defineProperty from "@babel/runtime/helpers/defineProperty";
1
2
  import _toConsumableArray from "@babel/runtime/helpers/toConsumableArray";
2
3
  import _typeof from "@babel/runtime/helpers/typeof";
4
+ function ownKeys(e, r) { var t = Object.keys(e); if (Object.getOwnPropertySymbols) { var o = Object.getOwnPropertySymbols(e); r && (o = o.filter(function (r) { return Object.getOwnPropertyDescriptor(e, r).enumerable; })), t.push.apply(t, o); } return t; }
5
+ function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? ownKeys(Object(t), !0).forEach(function (r) { _defineProperty(e, r, t[r]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function (r) { Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r)); }); } return e; }
3
6
  import { Decoration } from '@atlaskit/editor-prosemirror/view';
4
7
  import { fg } from '@atlaskit/platform-feature-flags/fg';
5
8
  import { DiffDecorationKey, isDiffDecoration, isDiffDecorationSpec } from './decorations/decorationKeys';
@@ -42,6 +45,15 @@ function specHasDiffKeyPrefix(spec, keyPrefix) {
42
45
  return Boolean(spec && _typeof(spec) === 'object' && 'key' in spec && typeof spec.key === 'string' && spec.key.startsWith(keyPrefix));
43
46
  }
44
47
 
48
+ /**
49
+ * Where a decoration paints relative to others at the same position: negative before the node at
50
+ * that position, positive after it. Only deleted-content widgets carry one — everything else is
51
+ * ranged, and so paints where its range starts.
52
+ */
53
+ function decorationSide(decoration) {
54
+ return isDiffDecoration(decoration) && decoration.spec.side || 0;
55
+ }
56
+
45
57
  /**
46
58
  * Collapses decorations that represent one continuous customer-facing edit.
47
59
  *
@@ -87,13 +99,29 @@ function groupTouchingDecorations(decorations, isInlineDecoration) {
87
99
  var representative = (_ref2 = (_group$find = group.find(isInlineDecoration)) !== null && _group$find !== void 0 ? _group$find : group.find(function (decoration) {
88
100
  return decoration.from !== decoration.to;
89
101
  })) !== null && _ref2 !== void 0 ? _ref2 : group[0];
90
- if (!representative || representative.from === from && representative.to === to) {
102
+ if (!representative) {
103
+ return representative;
104
+ }
105
+
106
+ // Deleted content is a widget at the start of the added content that replaced it, on a more
107
+ // negative side so it paints above. That makes it the visual start of the edit, reachable
108
+ // only through its own DOM — resolving the group's start position lands on the added content
109
+ // painted after it. The group keeps its range, which navigation and the active-range
110
+ // calculation both need, and reports the widget as what to scroll to.
111
+ var scrollTarget = fg('platform_editor_ai_show_diff_patch_1') ? group.find(function (decoration) {
112
+ return decoration.from === from && decorationSide(decoration) < decorationSide(representative);
113
+ }) : undefined;
114
+ if (!scrollTarget && representative.from === from && representative.to === to) {
91
115
  return representative;
92
116
  }
93
117
 
94
118
  // This decoration is only used for navigation and active-range calculation. The actual
95
- // visual decorations remain in the DecorationSet with their original ranges and styles.
96
- return isInlineDecoration(representative) ? Decoration.inline(from, to, {}, representative.spec) : Decoration.node(from, to, {}, representative.spec);
119
+ // visual decorations remain in the DecorationSet with their original ranges and styles —
120
+ // including their spec, hence a copy to add `scrollTarget` for `scrollToDiff` to read.
121
+ var spec = _objectSpread(_objectSpread({}, representative.spec), {}, {
122
+ scrollTarget: scrollTarget
123
+ });
124
+ return isInlineDecoration(representative) ? Decoration.inline(from, to, {}, spec) : Decoration.node(from, to, {}, spec);
97
125
  });
98
126
  }
99
127
 
@@ -110,7 +138,10 @@ function groupTouchingDecorations(decorations, isInlineDecoration) {
110
138
  * 5. When `confluence_ncs_step_diffing_version_history` is enabled, groups overlapping or
111
139
  * directly touching ranges across decoration types into one result, using the union of all
112
140
  * grouped ranges
113
- * (zero-width widgets can join a group without extending it)
141
+ * (zero-width widgets can join a group without extending it). Under
142
+ * `platform_editor_ai_show_diff_patch_1`, a group that starts with content painting above
143
+ * the content that replaced it — reports that widget as its `scrollTarget` spec,
144
+ * so scrolling reaches the visual start of the edit
114
145
  * 6. Results are sorted by from position, then by to position
115
146
  *
116
147
  * @param set - The DecorationSet to extract scrollable decorations from
@@ -1,9 +1,9 @@
1
1
  import _defineProperty from "@babel/runtime/helpers/defineProperty";
2
- 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; }
3
- function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? ownKeys(Object(t), !0).forEach(function (r) { _defineProperty(e, r, t[r]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function (r) { Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r)); }); } return e; }
4
2
  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; } } }; }
5
3
  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; } }
6
4
  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; }
5
+ 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; }
6
+ function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? ownKeys(Object(t), !0).forEach(function (r) { _defineProperty(e, r, t[r]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function (r) { Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r)); }); } return e; }
7
7
  import { getAttributionKey } from './decorations/colorSchemes/attributions';
8
8
 
9
9
  /** A brand name, so a literal rather than a translated message. */
@@ -11,6 +11,49 @@ var ROVO_AGENT_NAME = 'Rovo';
11
11
 
12
12
  /** `agentType` values that render as "Rovo" rather than as a generic external agent. */
13
13
  var ROVO_AGENT_TYPES = new Set(['convo-ai', 'rovo_chat']);
14
+ /** Known external agent types emitted by Confluence's agent identification service. */
15
+ var EXTERNAL_AGENT_PRESENTATIONS = {
16
+ claude: {
17
+ agentKind: 'claude',
18
+ name: 'Claude'
19
+ },
20
+ codex: {
21
+ agentKind: 'external',
22
+ name: 'Codex'
23
+ },
24
+ vscode: {
25
+ agentKind: 'external',
26
+ name: 'VS Code'
27
+ },
28
+ hermes: {
29
+ agentKind: 'external',
30
+ name: 'Hermes'
31
+ },
32
+ runlayer: {
33
+ agentKind: 'external',
34
+ name: 'Runlayer'
35
+ },
36
+ zed: {
37
+ agentKind: 'external',
38
+ name: 'Zed'
39
+ },
40
+ ampcode: {
41
+ agentKind: 'external',
42
+ name: 'Ampcode'
43
+ },
44
+ antigravity: {
45
+ agentKind: 'external',
46
+ name: 'Antigravity'
47
+ },
48
+ devin_cli: {
49
+ agentKind: 'external',
50
+ name: 'Devin CLI'
51
+ },
52
+ pi_agent: {
53
+ agentKind: 'external',
54
+ name: 'Pi Agent'
55
+ }
56
+ };
14
57
  var hasAgentIdentity = function hasAgentIdentity(attribution) {
15
58
  var _attribution$agentId, _attribution$agentTyp;
16
59
  return Boolean(((_attribution$agentId = attribution.agentId) === null || _attribution$agentId === void 0 ? void 0 : _attribution$agentId.trim()) || ((_attribution$agentTyp = attribution.agentType) === null || _attribution$agentTyp === void 0 ? void 0 : _attribution$agentTyp.trim()));
@@ -35,9 +78,16 @@ var resolveUser = function resolveUser(userId, profilesByAccountId) {
35
78
  } : undefined;
36
79
  };
37
80
 
38
- /** An agent always resolves: its profile, else "Rovo" for a first-party type, else external. */
81
+ /** Resolve branding or a profile first, then a known type name or the external-agent fallback. */
39
82
  var resolveAgent = function resolveAgent(attribution, profilesByAccountId) {
40
- var _attribution$agentId2, _attribution$agentTyp2;
83
+ var _attribution$agentTyp2, _attribution$agentId2;
84
+ var agentType = (_attribution$agentTyp2 = attribution.agentType) === null || _attribution$agentTyp2 === void 0 ? void 0 : _attribution$agentTyp2.trim().toLowerCase();
85
+ var agentPresentation = agentType ? EXTERNAL_AGENT_PRESENTATIONS[agentType] : undefined;
86
+ if (agentPresentation && agentPresentation.agentKind !== 'external') {
87
+ return _objectSpread(_objectSpread({}, agentPresentation), {}, {
88
+ kind: 'agent'
89
+ });
90
+ }
41
91
  var agentId = (_attribution$agentId2 = attribution.agentId) === null || _attribution$agentId2 === void 0 ? void 0 : _attribution$agentId2.trim();
42
92
  var agentProfile = agentId ? profilesByAccountId.get(agentId) : undefined;
43
93
  if (agentProfile) {
@@ -48,14 +98,15 @@ var resolveAgent = function resolveAgent(attribution, profilesByAccountId) {
48
98
  name: agentProfile.name
49
99
  };
50
100
  }
51
- var agentType = (_attribution$agentTyp2 = attribution.agentType) === null || _attribution$agentTyp2 === void 0 ? void 0 : _attribution$agentTyp2.trim().toLowerCase();
52
101
 
53
- // An empty name lets the tag fall back to its own localised "External agent" label.
102
+ // Unknown types keep an empty name for the tag's localised "External agent" label.
54
103
  return agentType && ROVO_AGENT_TYPES.has(agentType) ? {
55
104
  agentKind: 'rovo',
56
105
  kind: 'agent',
57
106
  name: ROVO_AGENT_NAME
58
- } : {
107
+ } : agentPresentation ? _objectSpread(_objectSpread({}, agentPresentation), {}, {
108
+ kind: 'agent'
109
+ }) : {
59
110
  agentKind: 'external',
60
111
  kind: 'agent',
61
112
  name: ''
@@ -46,17 +46,22 @@ export var scrollToDecoration = function scrollToDecoration(view, decorations) {
46
46
  if (!decoration) {
47
47
  return function () {};
48
48
  }
49
+
50
+ // A grouped decoration spans a whole customer-facing edit, which can visually start with a
51
+ // deleted-content widget rather than at the group's `from` position — see `scrollTarget` in
52
+ // `getScrollableDecorations`. Only set under `platform_editor_ai_show_diff_patch_1`.
53
+ var target = isDiffDecoration(decoration) && decoration.spec.scrollTarget || decoration;
49
54
  var rafId = requestAnimationFrame(function () {
50
55
  rafId = null;
51
- if (isDiffDecoration(decoration) && decoration.spec.decorationType === 'widget') {
52
- var _decoration$type;
56
+ if (isDiffDecoration(target) && target.spec.decorationType === 'widget') {
57
+ var _target$type;
53
58
  // @ts-expect-error - decoration.type is not typed public API
54
- var widgetDom = decoration === null || decoration === void 0 || (_decoration$type = decoration.type) === null || _decoration$type === void 0 ? void 0 : _decoration$type.toDOM;
59
+ var widgetDom = target === null || target === void 0 || (_target$type = target.type) === null || _target$type === void 0 ? void 0 : _target$type.toDOM;
55
60
  scrollToSelection(widgetDom);
56
61
  } else {
57
62
  var _view$domAtPos;
58
- var targetNode = view.nodeDOM(decoration === null || decoration === void 0 ? void 0 : decoration.from);
59
- var node = targetNode instanceof Element ? targetNode : (_view$domAtPos = view.domAtPos(decoration === null || decoration === void 0 ? void 0 : decoration.from)) === null || _view$domAtPos === void 0 ? void 0 : _view$domAtPos.node;
63
+ var targetNode = view.nodeDOM(target === null || target === void 0 ? void 0 : target.from);
64
+ var node = targetNode instanceof Element ? targetNode : (_view$domAtPos = view.domAtPos(target === null || target === void 0 ? void 0 : target.from)) === null || _view$domAtPos === void 0 ? void 0 : _view$domAtPos.node;
60
65
  scrollToSelection(node);
61
66
  }
62
67
  });
@@ -4,6 +4,12 @@ import { BORDER_WIDTH } from '@atlaskit/avatar/constants/default';
4
4
  import { convertToInlineCss } from '@atlaskit/editor-common/lazy-node-view';
5
5
  import { getContributorTagIcon } from './contributorTagIcons';
6
6
 
7
+ /** Agent kinds with a custom contributor-tag icon. */
8
+ var AGENT_KIND_ICONS = {
9
+ claude: 'claude',
10
+ rovo: 'rovoHex'
11
+ };
12
+
7
13
  /** The size `@atlaskit/avatar`'s `xxsmall` rendered at. */
8
14
  var AVATAR_SIZE = AVATAR_SIZES.xxsmall;
9
15
  /** `xxsmall` also carried a `space.025` margin — the same 2px — which is where the ring is painted. */
@@ -81,7 +87,7 @@ export var contributorAvatarRenderer = function contributorAvatarRenderer(_ref)
81
87
  var image;
82
88
  var unbindImageError;
83
89
  var showFallback = function showFallback() {
84
- var _unbindImageError, _image;
90
+ var _unbindImageError, _image, _AGENT_KIND_ICONS, _contributor$agentKin;
85
91
  (_unbindImageError = unbindImageError) === null || _unbindImageError === void 0 || _unbindImageError();
86
92
  unbindImageError = undefined;
87
93
  (_image = image) === null || _image === void 0 || _image.remove();
@@ -93,7 +99,7 @@ export var contributorAvatarRenderer = function contributorAvatarRenderer(_ref)
93
99
  shape.appendChild(fallback);
94
100
  return;
95
101
  }
96
- shape.appendChild(getContributorTagIcon(contributor.agentKind === 'rovo' ? 'rovoHex' : 'aiBot', doc));
102
+ shape.appendChild(getContributorTagIcon((_AGENT_KIND_ICONS = AGENT_KIND_ICONS[(_contributor$agentKin = contributor.agentKind) !== null && _contributor$agentKin !== void 0 ? _contributor$agentKin : 'external']) !== null && _AGENT_KIND_ICONS !== void 0 ? _AGENT_KIND_ICONS : 'aiBot', doc));
97
103
  };
98
104
  if (contributor.avatarUrl) {
99
105
  image = doc.createElement('img');
@@ -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
  };
@@ -32,6 +32,14 @@ export type DiffDecorationSpec = BaseDecorationSpec<typeof DecorationFamily.diff
32
32
  */
33
33
  isInserted?: boolean;
34
34
  nodeName?: string;
35
+ /**
36
+ * The decoration to scroll to, when it is not the one carrying this spec: the member of a merged
37
+ * navigation group that paints first. Deleted content is a widget shown above the added content
38
+ * it replaces, and only that widget's own DOM can be scrolled to — resolving the group's `from`
39
+ * position lands on the added content painted after it. Set by `getScrollableDecorations`,
40
+ * consumed by `scrollToDiff`; never present on a decoration in the plugin's `DecorationSet`.
41
+ */
42
+ scrollTarget?: Decoration;
35
43
  };
36
44
  export declare const AnchorTypeKey: {
37
45
  readonly from: 'from';
@@ -20,7 +20,10 @@ export declare function isInlineDiffDecorationRenderableInDoc(doc: PMNode, from:
20
20
  * 5. When `confluence_ncs_step_diffing_version_history` is enabled, groups overlapping or
21
21
  * directly touching ranges across decoration types into one result, using the union of all
22
22
  * grouped ranges
23
- * (zero-width widgets can join a group without extending it)
23
+ * (zero-width widgets can join a group without extending it). Under
24
+ * `platform_editor_ai_show_diff_patch_1`, a group that starts with content painting above
25
+ * the content that replaced it — reports that widget as its `scrollTarget` spec,
26
+ * so scrolling reaches the visual start of the edit
24
27
  * 6. Results are sorted by from position, then by to position
25
28
  *
26
29
  * @param set - The DecorationSet to extract scrollable decorations from
@@ -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
@@ -2,11 +2,11 @@
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
9
9
  * both themes — the fallbacks below are the rendered colours, so no theme observer is needed.
10
10
  */
11
- export type ContributorTagIcon = 'aiBot' | 'person' | 'rovoHex';
11
+ export type ContributorTagIcon = 'aiBot' | 'claude' | 'person' | 'rovoHex';
12
12
  export declare const getContributorTagIcon: (icon: ContributorTagIcon, doc: Document) => DocumentFragment;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@atlaskit/editor-plugin-show-diff",
3
- "version": "15.1.7",
3
+ "version": "15.1.9",
4
4
  "description": "ShowDiff plugin for @atlaskit/editor-core",
5
5
  "author": "Atlassian Pty Ltd",
6
6
  "license": "Apache-2.0",
@@ -26,13 +26,13 @@
26
26
  "@atlaskit/editor-plugin-analytics": "^17.0.0",
27
27
  "@atlaskit/editor-plugin-user-intent": "^15.0.0",
28
28
  "@atlaskit/editor-prosemirror": "^8.0.0",
29
- "@atlaskit/editor-shared-styles": "^4.1.0",
29
+ "@atlaskit/editor-shared-styles": "^4.2.0",
30
30
  "@atlaskit/editor-tables": "^3.2.0",
31
31
  "@atlaskit/platform-feature-experiments": "^0.3.0",
32
32
  "@atlaskit/platform-feature-flags": "^2.2.0",
33
33
  "@atlaskit/primitives": "^22.5.0",
34
- "@atlaskit/tmp-editor-statsig": "^185.0.0",
35
- "@atlaskit/tokens": "^16.11.0",
34
+ "@atlaskit/tmp-editor-statsig": "^186.0.0",
35
+ "@atlaskit/tokens": "^16.12.0",
36
36
  "@babel/runtime": "^7.0.0",
37
37
  "@compiled/react": "^1.0.2",
38
38
  "bind-event-listener": "^3.0.0",