@atlaskit/editor-plugin-show-diff 14.3.2 → 14.4.0

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 (77) hide show
  1. package/CHANGELOG.md +18 -0
  2. package/afm-cc/tsconfig.json +18 -0
  3. package/afm-products/tsconfig.json +18 -0
  4. package/dist/cjs/pm-plugins/calculateDiff/calculateDiffDecorations.js +47 -26
  5. package/dist/cjs/pm-plugins/calculateDiff/simplifyChangesWithAttribution.js +61 -9
  6. package/dist/cjs/pm-plugins/decorations/colorSchemes/attributions.js +77 -20
  7. package/dist/cjs/pm-plugins/decorations/colorSchemes/factory.js +10 -0
  8. package/dist/cjs/pm-plugins/decorations/colorSchemes/types.js +2 -0
  9. package/dist/cjs/pm-plugins/decorations/createAnchorDecorationWidgets.js +52 -7
  10. package/dist/cjs/pm-plugins/decorations/createContributorTagWidget.js +172 -0
  11. package/dist/cjs/pm-plugins/decorations/createInlineChangedDecoration.js +37 -5
  12. package/dist/cjs/pm-plugins/decorations/createNodeChangedDecorationWidget.js +40 -7
  13. package/dist/cjs/pm-plugins/decorations/decorationKeys.js +31 -5
  14. package/dist/cjs/pm-plugins/decorations/extractContributorTags.js +128 -0
  15. package/dist/cjs/pm-plugins/main.js +57 -6
  16. package/dist/cjs/pm-plugins/resolveDiffContributors.js +143 -0
  17. package/dist/cjs/showDiffPlugin.js +21 -6
  18. package/dist/cjs/ui/ContributorTag/ContributorTag.compiled.css +29 -0
  19. package/dist/cjs/ui/ContributorTag/ContributorTag.js +383 -0
  20. package/dist/cjs/ui/ContributorTag/ContributorTagWidget.js +50 -0
  21. package/dist/cjs/ui/ContributorTag/messages.js +24 -0
  22. package/dist/es2019/pm-plugins/calculateDiff/calculateDiffDecorations.js +48 -27
  23. package/dist/es2019/pm-plugins/calculateDiff/simplifyChangesWithAttribution.js +39 -0
  24. package/dist/es2019/pm-plugins/decorations/colorSchemes/attributions.js +53 -11
  25. package/dist/es2019/pm-plugins/decorations/colorSchemes/factory.js +9 -0
  26. package/dist/es2019/pm-plugins/decorations/colorSchemes/types.js +2 -0
  27. package/dist/es2019/pm-plugins/decorations/createAnchorDecorationWidgets.js +43 -1
  28. package/dist/es2019/pm-plugins/decorations/createContributorTagWidget.js +163 -0
  29. package/dist/es2019/pm-plugins/decorations/createInlineChangedDecoration.js +38 -5
  30. package/dist/es2019/pm-plugins/decorations/createNodeChangedDecorationWidget.js +38 -6
  31. package/dist/es2019/pm-plugins/decorations/decorationKeys.js +30 -2
  32. package/dist/es2019/pm-plugins/decorations/extractContributorTags.js +101 -0
  33. package/dist/es2019/pm-plugins/main.js +52 -3
  34. package/dist/es2019/pm-plugins/resolveDiffContributors.js +128 -0
  35. package/dist/es2019/showDiffPlugin.js +20 -5
  36. package/dist/es2019/ui/ContributorTag/ContributorTag.compiled.css +29 -0
  37. package/dist/es2019/ui/ContributorTag/ContributorTag.js +312 -0
  38. package/dist/es2019/ui/ContributorTag/ContributorTagWidget.js +43 -0
  39. package/dist/es2019/ui/ContributorTag/messages.js +18 -0
  40. package/dist/esm/pm-plugins/calculateDiff/calculateDiffDecorations.js +49 -28
  41. package/dist/esm/pm-plugins/calculateDiff/simplifyChangesWithAttribution.js +60 -8
  42. package/dist/esm/pm-plugins/decorations/colorSchemes/attributions.js +77 -19
  43. package/dist/esm/pm-plugins/decorations/colorSchemes/factory.js +9 -0
  44. package/dist/esm/pm-plugins/decorations/colorSchemes/types.js +2 -0
  45. package/dist/esm/pm-plugins/decorations/createAnchorDecorationWidgets.js +51 -6
  46. package/dist/esm/pm-plugins/decorations/createContributorTagWidget.js +166 -0
  47. package/dist/esm/pm-plugins/decorations/createInlineChangedDecoration.js +37 -5
  48. package/dist/esm/pm-plugins/decorations/createNodeChangedDecorationWidget.js +40 -7
  49. package/dist/esm/pm-plugins/decorations/decorationKeys.js +30 -5
  50. package/dist/esm/pm-plugins/decorations/extractContributorTags.js +122 -0
  51. package/dist/esm/pm-plugins/main.js +57 -6
  52. package/dist/esm/pm-plugins/resolveDiffContributors.js +137 -0
  53. package/dist/esm/showDiffPlugin.js +21 -6
  54. package/dist/esm/ui/ContributorTag/ContributorTag.compiled.css +29 -0
  55. package/dist/esm/ui/ContributorTag/ContributorTag.js +375 -0
  56. package/dist/esm/ui/ContributorTag/ContributorTagWidget.js +43 -0
  57. package/dist/esm/ui/ContributorTag/messages.js +18 -0
  58. package/dist/types/entry-points/show-diff-plugin-type.d.ts +1 -1
  59. package/dist/types/pm-plugins/calculateDiff/calculateDiffDecorations.d.ts +7 -1
  60. package/dist/types/pm-plugins/calculateDiff/simplifyChangesWithAttribution.d.ts +10 -0
  61. package/dist/types/pm-plugins/decorations/colorSchemes/attributions.d.ts +24 -2
  62. package/dist/types/pm-plugins/decorations/colorSchemes/factory.d.ts +7 -1
  63. package/dist/types/pm-plugins/decorations/colorSchemes/schemes.d.ts +1 -2
  64. package/dist/types/pm-plugins/decorations/colorSchemes/types.d.ts +2 -1
  65. package/dist/types/pm-plugins/decorations/createAnchorDecorationWidgets.d.ts +14 -0
  66. package/dist/types/pm-plugins/decorations/createContributorTagWidget.d.ts +55 -0
  67. package/dist/types/pm-plugins/decorations/createInlineChangedDecoration.d.ts +5 -1
  68. package/dist/types/pm-plugins/decorations/createNodeChangedDecorationWidget.d.ts +5 -1
  69. package/dist/types/pm-plugins/decorations/decorationKeys.d.ts +28 -3
  70. package/dist/types/pm-plugins/decorations/extractContributorTags.d.ts +9 -0
  71. package/dist/types/pm-plugins/main.d.ts +8 -2
  72. package/dist/types/pm-plugins/resolveDiffContributors.d.ts +7 -0
  73. package/dist/types/showDiffPluginType.d.ts +65 -2
  74. package/dist/types/ui/ContributorTag/ContributorTag.d.ts +37 -0
  75. package/dist/types/ui/ContributorTag/ContributorTagWidget.d.ts +22 -0
  76. package/dist/types/ui/ContributorTag/messages.d.ts +6 -0
  77. package/package.json +16 -7
@@ -0,0 +1,166 @@
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
+ import { createElement } from 'react';
5
+ import { Decoration } from '@atlaskit/editor-prosemirror/view';
6
+ import { fg } from '@atlaskit/platform-feature-flags/fg';
7
+ import { ContributorTagWidget } from '../../ui/ContributorTag/ContributorTagWidget';
8
+ import { clampAnchorPosIntoCell } from './createAnchorDecorationWidgets';
9
+ import { buildContributorTagDecorationSpec, DecorationFamily } from './decorationKeys';
10
+
11
+ /** Marks the host element of one tag, so a tag can be found in the document by its own diff. */
12
+ export var CONTRIBUTOR_TAG_HOST_ATTRIBUTE = 'data-contributor-tag-host';
13
+
14
+ // Contributor tags only render behind this gate — see `areAttributionColorGatesEnabled`.
15
+ var isContributorTagWidgetEnabled = function isContributorTagWidgetEnabled() {
16
+ return fg('confluence_ncs_step_diffing_version_history');
17
+ };
18
+
19
+ /**
20
+ * Everything a tag needs to mount itself into the document, plumbed down from the plugin. Absent in
21
+ * unit tests that build decorations directly: the hosts are still emitted, they just stay empty.
22
+ */
23
+
24
+ /** A mounted tag, so whoever emitted the host can take it down again. */
25
+
26
+ /**
27
+ * Unique per mount rather than per diff, because ProseMirror draws a replacement widget before it
28
+ * destroys the widget being replaced: on a shared key the outgoing widget's `destroy` would
29
+ * unregister the incoming widget's portal.
30
+ */
31
+ var nextContributorTagPortalId = 0;
32
+
33
+ /**
34
+ * Renders the tag into `host` through the editor's portal provider, the way the rest of the editor
35
+ * mounts React into decorations. The tag resolves its own model from plugin state, so a redraw of
36
+ * the host re-parents the tag instead of stranding it, and `isActive` changes arrive as a re-render.
37
+ */
38
+ export var mountContributorTag = function mountContributorTag(_ref) {
39
+ var diffId = _ref.diffId,
40
+ host = _ref.host,
41
+ mountContext = _ref.mountContext;
42
+ if (!mountContext) {
43
+ return undefined;
44
+ }
45
+ var api = mountContext.api,
46
+ getEditorRoot = mountContext.getEditorRoot,
47
+ portalProviderAPI = mountContext.portalProviderAPI;
48
+ var key = "".concat(DecorationFamily.contributorTag, "-").concat(diffId, "-").concat(nextContributorTagPortalId++);
49
+ portalProviderAPI.render(function () {
50
+ return /*#__PURE__*/createElement(ContributorTagWidget, {
51
+ api: api,
52
+ diffId: diffId,
53
+ getEditorRoot: getEditorRoot
54
+ });
55
+ }, host, key);
56
+ return {
57
+ key: key,
58
+ portalProviderAPI: portalProviderAPI
59
+ };
60
+ };
61
+ export var unmountContributorTag = function unmountContributorTag(mount) {
62
+ mount === null || mount === void 0 || mount.portalProviderAPI.remove(mount.key);
63
+ };
64
+
65
+ /**
66
+ * A zero-size inline host for one contributor tag. `position: relative` makes it the containing
67
+ * block of the absolutely positioned tag, so the tag sits on the change with no measurement.
68
+ */
69
+ var buildContributorTagHost = function buildContributorTagHost(diffId) {
70
+ var host = document.createElement('span');
71
+ host.setAttribute(CONTRIBUTOR_TAG_HOST_ATTRIBUTE, diffId);
72
+ host.style.setProperty('position', 'relative');
73
+ return host;
74
+ };
75
+
76
+ /**
77
+ * The host for a tag on deleted content, which renders inside its own widget decoration rather than
78
+ * needing one of its own. Unmount with `unmountContributorTag` from that widget's `destroy`.
79
+ */
80
+ export var createContributorTagHost = function createContributorTagHost(diffId, mountContext) {
81
+ if (!isContributorTagWidgetEnabled()) {
82
+ return undefined;
83
+ }
84
+ var host = buildContributorTagHost(diffId);
85
+ return {
86
+ host: host,
87
+ mount: mountContributorTag({
88
+ diffId: diffId,
89
+ host: host,
90
+ mountContext: mountContext
91
+ })
92
+ };
93
+ };
94
+
95
+ /**
96
+ * The start of the inline content that `[from, to)` actually highlights.
97
+ *
98
+ * `from` is not sufficient: a range can begin at the tail of one paragraph and only cover text in a
99
+ * later block, and ProseMirror clips `Decoration.inline` to the content it can paint — so a host
100
+ * left at `from` would sit lines away from the highlight.
101
+ */
102
+ var resolveInlineContentStart = function resolveInlineContentStart(doc, from, to) {
103
+ var edge;
104
+ doc.nodesBetween(from, to, function (node, nodePos) {
105
+ if (!node.isTextblock) {
106
+ return edge === undefined;
107
+ }
108
+
109
+ // The slice of this block the range actually covers; empty means nothing paintable.
110
+ var contentFrom = Math.max(nodePos + 1, from);
111
+ var contentTo = Math.min(nodePos + 1 + node.content.size, to);
112
+ if (contentTo > contentFrom && edge === undefined) {
113
+ edge = contentFrom;
114
+ }
115
+
116
+ // Textblocks have no block children to descend into.
117
+ return false;
118
+ });
119
+ return edge !== null && edge !== void 0 ? edge : from;
120
+ };
121
+
122
+ /**
123
+ * A widget hosting the contributor tag of one inline diff range, placed on the first character the
124
+ * range highlights.
125
+ *
126
+ * The tag is mounted in `toDOM` and taken down in `destroy`, so it lives exactly as long as the host
127
+ * element ProseMirror drew it into — see `mountContributorTag`.
128
+ */
129
+ export var createContributorTagWidget = function createContributorTagWidget(_ref2) {
130
+ var doc = _ref2.doc,
131
+ from = _ref2.from,
132
+ to = _ref2.to,
133
+ diffId = _ref2.diffId,
134
+ mountContext = _ref2.mountContext;
135
+ if (!isContributorTagWidgetEnabled()) {
136
+ return undefined;
137
+ }
138
+
139
+ // Reassigned when ProseMirror redraws this decoration, which it may do more than once for the
140
+ // same `Decoration` instance.
141
+ var mount;
142
+ return Decoration.widget(
143
+ // Keep the host out of the table row's grid (EDITOR-8442).
144
+ clampAnchorPosIntoCell(doc, resolveInlineContentStart(doc, from, to), 1), function () {
145
+ var host = buildContributorTagHost(diffId);
146
+ mount = mountContributorTag({
147
+ diffId: diffId,
148
+ host: host,
149
+ mountContext: mountContext
150
+ });
151
+ return host;
152
+ }, _objectSpread(_objectSpread({}, buildContributorTagDecorationSpec(diffId)), {}, {
153
+ side: 1,
154
+ // The tag is chrome: it inherits no surrounding mark, and its own events are not the
155
+ // document's to handle.
156
+ marks: [],
157
+ ignoreSelection: true,
158
+ stopEvent: function stopEvent() {
159
+ return true;
160
+ },
161
+ destroy: function destroy() {
162
+ unmountContributorTag(mount);
163
+ mount = undefined;
164
+ }
165
+ }));
166
+ };
@@ -9,6 +9,7 @@ import { isExtendedEnabled } from '../isExtendedEnabled';
9
9
  import { buildAtomicInlineChangedCSSVariables, buildDeletedInlineContentStyleExtended, buildDeletedInlineStyle, buildDeletedInlineStyleStandard, buildInsertStyle, buildInsertStyleActive, buildInsertStyleExtended, buildInsertStyleExtendedActive, buildInsertStyleExtendedNoUnderline, buildInsertStyleExtendedNoUnderlineActive } from './colorSchemes/factory';
10
10
  import { colorSchemeRegistry, getLegacyColorScheme } from './colorSchemes/schemes';
11
11
  import { createInlineIndicatorAnchorWidgets } from './createAnchorDecorationWidgets';
12
+ import { createContributorTagWidget } from './createContributorTagWidget';
12
13
  import { getAtomicInlineNodeClassNameLegacy, resolveInlineChangedStyleLegacy } from './createInlineChangedDecoration.styles.legacy';
13
14
  import { buildDiffDecorationSpec } from './decorationKeys';
14
15
  var displayNoneStyle = convertToInlineCss({
@@ -102,7 +103,8 @@ export var getAtomicInlineChangedAttrs = function getAtomicInlineChangedAttrs(in
102
103
  */
103
104
 
104
105
  export var createInlineChangedDecoration = function createInlineChangedDecoration(_ref2) {
105
- var change = _ref2.change,
106
+ var attributionKey = _ref2.attributionKey,
107
+ change = _ref2.change,
106
108
  colorScheme = _ref2.colorScheme,
107
109
  _ref2$isActive = _ref2.isActive,
108
110
  isActive = _ref2$isActive === void 0 ? false : _ref2$isActive,
@@ -112,6 +114,8 @@ export var createInlineChangedDecoration = function createInlineChangedDecoratio
112
114
  isAtomicInlineNode = _ref2$isAtomicInlineN === void 0 ? false : _ref2$isAtomicInlineN,
113
115
  _ref2$shouldHideDelet = _ref2.shouldHideDeleted,
114
116
  shouldHideDeleted = _ref2$shouldHideDelet === void 0 ? false : _ref2$shouldHideDelet,
117
+ _ref2$showContributor = _ref2.showContributorTags,
118
+ showContributorTags = _ref2$showContributor === void 0 ? false : _ref2$showContributor,
115
119
  _ref2$showIndicators = _ref2.showIndicators,
116
120
  showIndicators = _ref2$showIndicators === void 0 ? false : _ref2$showIndicators,
117
121
  doc = _ref2.doc,
@@ -122,15 +126,23 @@ export var createInlineChangedDecoration = function createInlineChangedDecoratio
122
126
  _ref2$hasDeletedWidge = _ref2.hasDeletedWidget,
123
127
  hasDeletedWidget = _ref2$hasDeletedWidge === void 0 ? false : _ref2$hasDeletedWidge,
124
128
  _ref2$isDeletedWidget = _ref2.isDeletedWidgetBelow,
125
- isDeletedWidgetBelow = _ref2$isDeletedWidget === void 0 ? false : _ref2$isDeletedWidget;
126
- var diffId = crypto.randomUUID();
129
+ isDeletedWidgetBelow = _ref2$isDeletedWidget === void 0 ? false : _ref2$isDeletedWidget,
130
+ tagMountContext = _ref2.tagMountContext;
131
+ // Derived from the range so it survives a recalculation: stepping to the next/previous change
132
+ // rebuilds decorations without touching the doc, and a fresh id would change the React key and the
133
+ // tag host's key, remounting every tag mid-step. Random ids are kept when tags are off.
134
+ var diffId = showContributorTags ? "inline-".concat(change.fromB, "-").concat(change.toB) : crypto.randomUUID();
127
135
  if (shouldHideDeleted) {
128
136
  return [Decoration.inline(change.fromB, change.toB, {
129
137
  style: displayNoneStyle
130
138
  }, buildDiffDecorationSpec({
139
+ // No attribution: this decoration paints nothing and emits no tag host, so a tag
140
+ // resolved from it would have nowhere to render.
141
+ colorScheme: colorScheme,
131
142
  decorationType: 'inline',
132
143
  diffId: diffId,
133
- isActive: isActive
144
+ isActive: isActive,
145
+ isInserted: isInserted
134
146
  }))];
135
147
  }
136
148
  var colors = getColorScheme(colorScheme);
@@ -150,10 +162,15 @@ export var createInlineChangedDecoration = function createInlineChangedDecoratio
150
162
  class: atomicInlineAttrs.className
151
163
  }), {}, {
152
164
  'data-testid': 'show-diff-changed-decoration'
165
+ }, showContributorTags && {
166
+ 'data-diff-id': diffId
153
167
  }), buildDiffDecorationSpec({
168
+ attributionKey: attributionKey,
169
+ colorScheme: colorScheme,
154
170
  decorationType: 'inline',
155
171
  diffId: diffId,
156
- isActive: isActive
172
+ isActive: isActive,
173
+ isInserted: isInserted
157
174
  }))];
158
175
  if (showIndicators && doc && isExtendedEnabled(diffType)) {
159
176
  // For paragraphs, fromB/toB land on the outer block
@@ -190,5 +207,20 @@ export var createInlineChangedDecoration = function createInlineChangedDecoratio
190
207
  diffId: diffId
191
208
  })));
192
209
  }
210
+
211
+ // Built from the raw change range, not the anchors above: a tag lines up with the changed text,
212
+ // while the bar's anchors are held on a block boundary when a deleted widget renders beside them.
213
+ if (showContributorTags && doc && isExtendedEnabled(diffType)) {
214
+ var tagWidget = createContributorTagWidget({
215
+ doc: doc,
216
+ from: change.fromB,
217
+ to: change.toB,
218
+ diffId: diffId,
219
+ mountContext: tagMountContext
220
+ });
221
+ if (tagWidget) {
222
+ decorations.push(tagWidget);
223
+ }
224
+ }
193
225
  return decorations;
194
226
  };
@@ -6,6 +6,7 @@ import { fg } from '@atlaskit/platform-feature-flags/fg';
6
6
  import { isExtendedEnabled } from '../isExtendedEnabled';
7
7
  import { createLeftAnchorWidget } from './createAnchorDecorationWidgets';
8
8
  import { createChangedRowDecorationWidgets } from './createChangedRowDecorationWidgets';
9
+ import { createContributorTagHost, unmountContributorTag } from './createContributorTagWidget';
9
10
  import { buildDiffDecorationSpec, buildAnchorDecorationKey } from './decorationKeys';
10
11
  import { findSafeInsertPos } from './utils/findSafeInsertPos';
11
12
  import { wrapBlockNodeView, injectInnerWrapper, createContentWrapper } from './utils/wrapBlockNodeView';
@@ -91,7 +92,8 @@ var createTableCellContentWidgets = function createTableCellContentWidgets(_ref)
91
92
  */
92
93
  export var createNodeChangedDecorationWidget = function createNodeChangedDecorationWidget(_ref2) {
93
94
  var _slice$content, _slice$content2, _slice$content3, _$safeInsertPos$nodeB, _slice$content$firstC, _newDoc$firstChild, _slice$content$firstC2;
94
- var change = _ref2.change,
95
+ var attributionKey = _ref2.attributionKey,
96
+ change = _ref2.change,
95
97
  doc = _ref2.doc,
96
98
  nodeViewSerializer = _ref2.nodeViewSerializer,
97
99
  colorScheme = _ref2.colorScheme,
@@ -100,13 +102,16 @@ export var createNodeChangedDecorationWidget = function createNodeChangedDecorat
100
102
  activeIndexPos = _ref2.activeIndexPos,
101
103
  _ref2$isInserted = _ref2.isInserted,
102
104
  isInserted = _ref2$isInserted === void 0 ? false : _ref2$isInserted,
105
+ _ref2$showContributor = _ref2.showContributorTags,
106
+ showContributorTags = _ref2$showContributor === void 0 ? false : _ref2$showContributor,
103
107
  _ref2$showIndicators = _ref2.showIndicators,
104
108
  showIndicators = _ref2$showIndicators === void 0 ? false : _ref2$showIndicators,
105
109
  _ref2$placeBelow = _ref2.placeBelow,
106
110
  placeBelow = _ref2$placeBelow === void 0 ? false : _ref2$placeBelow,
107
111
  diffType = _ref2.diffType,
108
112
  _ref2$hideAddedDiffsU = _ref2.hideAddedDiffsUnderline,
109
- hideAddedDiffsUnderline = _ref2$hideAddedDiffsU === void 0 ? false : _ref2$hideAddedDiffsU;
113
+ hideAddedDiffsUnderline = _ref2$hideAddedDiffsU === void 0 ? false : _ref2$hideAddedDiffsU,
114
+ tagMountContext = _ref2.tagMountContext;
110
115
  var slice = doc.slice(change.fromA, change.toA);
111
116
  var shouldSkipDeletedEmptyParagraphDecoration = !isInserted && (slice === null || slice === void 0 || (_slice$content = slice.content) === null || _slice$content === void 0 ? void 0 : _slice$content.childCount) === 1 && (slice === null || slice === void 0 || (_slice$content2 = slice.content) === null || _slice$content2 === void 0 || (_slice$content2 = _slice$content2.firstChild) === null || _slice$content2 === void 0 ? void 0 : _slice$content2.type.name) === 'paragraph' && (slice === null || slice === void 0 || (_slice$content3 = slice.content) === null || _slice$content3 === void 0 || (_slice$content3 = _slice$content3.firstChild) === null || _slice$content3 === void 0 ? void 0 : _slice$content3.content.size) === 0;
112
117
  // Widget decoration used for deletions as the content is not in the document
@@ -204,7 +209,9 @@ export var createNodeChangedDecorationWidget = function createNodeChangedDecorat
204
209
  subtree: true
205
210
  });
206
211
  }
207
- var diffId = crypto.randomUUID();
212
+ // Derived from the deleted range so it survives a recalculation — see the same reasoning in
213
+ // `createInlineChangedDecoration`.
214
+ var diffId = showContributorTags ? "widget-".concat(change.fromA, "-").concat(change.toA) : crypto.randomUUID();
208
215
  var decorations = [];
209
216
  var replacementNode = newDoc.nodeAt(change.fromB);
210
217
  var firstReplacedNode = slice.content.firstChild;
@@ -327,23 +334,47 @@ export var createNodeChangedDecorationWidget = function createNodeChangedDecorat
327
334
  }
328
335
  });
329
336
  dom.setAttribute('data-testid', 'show-diff-deleted-decoration');
337
+
338
+ // Needed even when the indicator bar is off, because a contributor tag also anchors against the
339
+ // widget.
340
+ if ((showIndicators || showContributorTags) && isExtendedEnabled(diffType)) {
341
+ dom.style.setProperty('anchor-name', "--".concat(buildAnchorDecorationKey({
342
+ diffId: diffId
343
+ })));
344
+ }
345
+
346
+ // Taken down in the widget's `destroy` below: `dom` is rebuilt on every recalculation, so the tag
347
+ // mounted into it must not outlive the copy ProseMirror is drawing.
348
+ var tagMount;
349
+ if (showContributorTags) {
350
+ // Lets the contributor tag find the deleted content on hover.
351
+ dom.setAttribute('data-diff-id', diffId);
352
+
353
+ // Hosted at the start of the deleted run rather than on `dom` itself: `dom` is inline, so once
354
+ // the deleted content wraps its box is the union of its line fragments.
355
+ var tagHost = createContributorTagHost(diffId, tagMountContext);
356
+ if (tagHost) {
357
+ dom.prepend(tagHost.host);
358
+ tagMount = tagHost.mount;
359
+ }
360
+ }
330
361
  if (showIndicators && isExtendedEnabled(diffType)) {
331
362
  var leftAnchor = createLeftAnchorWidget({
332
363
  doc: newDoc,
333
364
  from: safeInsertPos,
334
365
  diffId: diffId
335
366
  });
336
- dom.style.setProperty('anchor-name', "--".concat(buildAnchorDecorationKey({
337
- diffId: diffId
338
- })));
339
367
  if (leftAnchor) {
340
368
  decorations.push(leftAnchor);
341
369
  }
342
370
  }
343
371
  decorations.push(Decoration.widget(safeInsertPos, dom, _objectSpread(_objectSpread(_objectSpread({}, buildDiffDecorationSpec(_objectSpread({
372
+ attributionKey: attributionKey,
373
+ colorScheme: colorScheme,
344
374
  decorationType: 'widget',
345
375
  diffId: diffId,
346
376
  isActive: isActive,
377
+ isInserted: isInserted,
347
378
  diffType: diffType
348
379
  }, isExtendedEnabled(diffType) && {
349
380
  // placeBelow anchors at the end of the new content, so render on the
@@ -354,7 +385,9 @@ export var createNodeChangedDecorationWidget = function createNodeChangedDecorat
354
385
  }), {}, {
355
386
  destroy: function destroy() {
356
387
  var _constrainMediaObserv2;
357
- return (_constrainMediaObserv2 = constrainMediaObserver) === null || _constrainMediaObserv2 === void 0 ? void 0 : _constrainMediaObserv2.disconnect();
388
+ (_constrainMediaObserv2 = constrainMediaObserver) === null || _constrainMediaObserv2 === void 0 || _constrainMediaObserv2.disconnect();
389
+ unmountContributorTag(tagMount);
390
+ tagMount = undefined;
358
391
  }
359
392
  })));
360
393
 
@@ -3,14 +3,14 @@ import _defineProperty from "@babel/runtime/helpers/defineProperty";
3
3
  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; }
4
4
  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; }
5
5
  import { isExtendedEnabled } from '../isExtendedEnabled';
6
-
7
6
  /**
8
7
  * Decoration families produced by the show-diff plugin.
9
8
  * Each family owns its own key space and spec shape.
10
9
  */
11
10
  export var DecorationFamily = {
12
11
  diff: 'diff',
13
- anchor: 'anchor'
12
+ anchor: 'anchor',
13
+ contributorTag: 'contributor-tag'
14
14
  };
15
15
  export var AnchorTypeKey = {
16
16
  from: 'from',
@@ -18,6 +18,12 @@ export var AnchorTypeKey = {
18
18
  left: 'left',
19
19
  docMargin: 'doc-margin'
20
20
  };
21
+
22
+ /**
23
+ * The host widget one contributor tag renders into. Its own family, so the tag never reaches the
24
+ * consumers that read diff decorations — change counting, navigation and `getDeletedWidgets`.
25
+ */
26
+
21
27
  /**
22
28
  * The diff-decoration kinds produced by the show-diff plugin. Each value is
23
29
  * the leading segment of the generated key, so a diff decoration's kind can be
@@ -51,13 +57,16 @@ export var buildDiffDecorationKey = function buildDiffDecorationKey(_ref2) {
51
57
  return isActive !== undefined ? "".concat(decorationKeyPrefix, "-").concat(isActive ? 'active' : 'inactive') : decorationKeyPrefix;
52
58
  };
53
59
  export var buildDiffDecorationSpec = function buildDiffDecorationSpec(_ref3) {
54
- var decorationType = _ref3.decorationType,
60
+ var attributionKey = _ref3.attributionKey,
61
+ colorScheme = _ref3.colorScheme,
62
+ decorationType = _ref3.decorationType,
55
63
  diffId = _ref3.diffId,
56
64
  isActive = _ref3.isActive,
65
+ isInserted = _ref3.isInserted,
57
66
  nodeName = _ref3.nodeName,
58
67
  side = _ref3.side,
59
68
  diffType = _ref3.diffType;
60
- return _objectSpread(_objectSpread({
69
+ return _objectSpread(_objectSpread(_objectSpread(_objectSpread(_objectSpread(_objectSpread({
61
70
  decorationFamily: DecorationFamily.diff,
62
71
  decorationType: decorationType,
63
72
  diffId: diffId,
@@ -67,7 +76,15 @@ export var buildDiffDecorationSpec = function buildDiffDecorationSpec(_ref3) {
67
76
  isActive: isActive,
68
77
  diffType: diffType
69
78
  })
70
- }, nodeName ? {
79
+ }, attributionKey ? {
80
+ attributionKey: attributionKey
81
+ } : {}), attributionKey && isActive !== undefined ? {
82
+ isActive: isActive
83
+ } : {}), attributionKey && isInserted !== undefined ? {
84
+ isInserted: isInserted
85
+ } : {}), attributionKey && colorScheme ? {
86
+ colorScheme: colorScheme
87
+ } : {}), nodeName ? {
71
88
  nodeName: nodeName
72
89
  } : {}), side !== undefined ? {
73
90
  side: side
@@ -101,6 +118,14 @@ export function buildAnchorDecorationSpec(_ref4) {
101
118
  side: side
102
119
  } : {});
103
120
  }
121
+ export var buildContributorTagDecorationSpec = function buildContributorTagDecorationSpec(diffId) {
122
+ return {
123
+ decorationFamily: DecorationFamily.contributorTag,
124
+ diffId: diffId,
125
+ // Stable across recalculations, so ProseMirror keeps the host element the tag is portalled into.
126
+ key: "".concat(DecorationFamily.contributorTag, "-").concat(diffId)
127
+ };
128
+ };
104
129
  export var isDiffDecorationSpec = function isDiffDecorationSpec(spec) {
105
130
  return Boolean(spec && _typeof(spec) === 'object' && 'decorationFamily' in spec && spec.decorationFamily === DecorationFamily.diff);
106
131
  };
@@ -0,0 +1,122 @@
1
+ import _defineProperty from "@babel/runtime/helpers/defineProperty";
2
+ import _toConsumableArray from "@babel/runtime/helpers/toConsumableArray";
3
+ import _objectWithoutProperties from "@babel/runtime/helpers/objectWithoutProperties";
4
+ var _excluded = ["connectedToKey"];
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
+ 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; } } }; }
8
+ 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; } }
9
+ 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; }
10
+ import { isDiffDecoration, isDiffDecorationSpec } from './decorationKeys';
11
+
12
+ /**
13
+ * Diff kinds that expose an anchor a tag can be pinned to: `inline` is added/changed content,
14
+ * `widget` is deleted content. Block diffs carry no attribution.
15
+ */
16
+ var TAGGABLE_DECORATION_TYPES = new Set(['inline', 'widget']);
17
+
18
+ /** The connection is an internal key, so it is dropped on the way to the tag. */
19
+ var toTagContributor = function toTagContributor(_ref) {
20
+ var _connectedToKey = _ref.connectedToKey,
21
+ tagContributor = _objectWithoutProperties(_ref, _excluded);
22
+ return tagContributor;
23
+ };
24
+ /**
25
+ * Whether a deleted-content widget belongs to the same change as an inline decoration. A replacement
26
+ * renders both — the inserted text inline, the deleted text as a widget anchored to one end of that
27
+ * inline range — and the two must not each claim a tag.
28
+ */
29
+ var isSameChange = function isSameChange(widget, inline) {
30
+ return widget.spec.attributionKey === inline.spec.attributionKey && widget.from >= inline.from && widget.from <= inline.to;
31
+ };
32
+
33
+ /**
34
+ * One model per diff decoration whose attribution resolves to a supplied contributor; anything
35
+ * unattributed, untaggable or unresolved is skipped. A replacement's two decorations are collapsed
36
+ * into the single inline tag — see `isSameChange`.
37
+ */
38
+ export var extractContributorTags = function extractContributorTags(decorations, contributors) {
39
+ if (!contributors) {
40
+ return [];
41
+ }
42
+ var targets = [];
43
+ var _iterator = _createForOfIteratorHelper(decorations.find(undefined, undefined, isDiffDecorationSpec)),
44
+ _step;
45
+ try {
46
+ for (_iterator.s(); !(_step = _iterator.n()).done;) {
47
+ var decoration = _step.value;
48
+ if (!isDiffDecoration(decoration)) {
49
+ continue;
50
+ }
51
+ var spec = decoration.spec;
52
+ if (!spec.attributionKey || !TAGGABLE_DECORATION_TYPES.has(spec.decorationType)) {
53
+ continue;
54
+ }
55
+ var contributor = contributors[spec.attributionKey];
56
+ if (!contributor) {
57
+ continue;
58
+ }
59
+ targets.push({
60
+ contributor: contributor,
61
+ decoration: decoration
62
+ });
63
+ }
64
+
65
+ // The inline decoration keeps the tag: it captions content still in the document.
66
+ } catch (err) {
67
+ _iterator.e(err);
68
+ } finally {
69
+ _iterator.f();
70
+ }
71
+ var inlineTargets = targets.filter(function (_ref2) {
72
+ var decoration = _ref2.decoration;
73
+ return decoration.spec.decorationType === 'inline';
74
+ });
75
+ var linkedDiffIds = new Map();
76
+ var folded = new Set();
77
+ var _loop = function _loop() {
78
+ var _linkedDiffIds$get;
79
+ var target = _targets[_i];
80
+ if (target.decoration.spec.decorationType !== 'widget') {
81
+ return 0; // continue
82
+ }
83
+ var host = inlineTargets.find(function (_ref3) {
84
+ var decoration = _ref3.decoration;
85
+ return isSameChange(target.decoration, decoration);
86
+ });
87
+ if (!host) {
88
+ return 0; // continue
89
+ }
90
+ folded.add(target);
91
+ var hostDiffId = host.decoration.spec.diffId;
92
+ linkedDiffIds.set(hostDiffId, [].concat(_toConsumableArray((_linkedDiffIds$get = linkedDiffIds.get(hostDiffId)) !== null && _linkedDiffIds$get !== void 0 ? _linkedDiffIds$get : []), [target.decoration.spec.diffId]));
93
+ },
94
+ _ret;
95
+ for (var _i = 0, _targets = targets; _i < _targets.length; _i++) {
96
+ _ret = _loop();
97
+ if (_ret === 0) continue;
98
+ }
99
+ return targets.filter(function (target) {
100
+ return !folded.has(target);
101
+ }).map(function (_ref4) {
102
+ var contributor = _ref4.contributor,
103
+ spec = _ref4.decoration.spec;
104
+ var connected = contributor.connectedToKey ? contributors[contributor.connectedToKey] : undefined;
105
+ var connectedContributor = connected ? toTagContributor(connected) : undefined;
106
+ var linked = linkedDiffIds.get(spec.diffId);
107
+ return _objectSpread(_objectSpread(_objectSpread(_objectSpread(_objectSpread({
108
+ contributor: toTagContributor(contributor),
109
+ diffId: spec.diffId
110
+ }, connectedContributor ? {
111
+ connectedContributor: connectedContributor
112
+ } : {}), spec.colorScheme ? {
113
+ colorScheme: spec.colorScheme
114
+ } : {}), spec.isActive !== undefined ? {
115
+ isActive: spec.isActive
116
+ } : {}), spec.isInserted !== undefined ? {
117
+ isInserted: spec.isInserted
118
+ } : {}), linked ? {
119
+ linkedDiffIds: linked
120
+ } : {});
121
+ });
122
+ };