@atlaskit/editor-plugin-show-diff 17.0.1 → 17.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (37) hide show
  1. package/CHANGELOG.md +15 -0
  2. package/dist/cjs/pm-plugins/calculateDiff/calculateDiffDecorations.js +126 -49
  3. package/dist/cjs/pm-plugins/calculateDiff/groupDeletedColumnChanges.js +265 -0
  4. package/dist/cjs/pm-plugins/decorations/createChangedRowDecorationWidgets.js +100 -34
  5. package/dist/cjs/pm-plugins/decorations/createContributorTagWidget.js +18 -13
  6. package/dist/cjs/pm-plugins/decorations/createNodeChangedDecorationWidget.js +82 -20
  7. package/dist/cjs/pm-plugins/decorations/utils/createDeletedLineBreakWidget.js +106 -0
  8. package/dist/cjs/pm-plugins/decorations/utils/tableDiffMode.js +36 -0
  9. package/dist/cjs/pm-plugins/decorations/utils/wrapBlockNodeView.js +17 -6
  10. package/dist/cjs/pm-plugins/utils/emptyTextBlocks.js +66 -0
  11. package/dist/es2019/pm-plugins/calculateDiff/calculateDiffDecorations.js +61 -1
  12. package/dist/es2019/pm-plugins/calculateDiff/groupDeletedColumnChanges.js +210 -0
  13. package/dist/es2019/pm-plugins/decorations/createChangedRowDecorationWidgets.js +79 -13
  14. package/dist/es2019/pm-plugins/decorations/createContributorTagWidget.js +8 -2
  15. package/dist/es2019/pm-plugins/decorations/createNodeChangedDecorationWidget.js +85 -15
  16. package/dist/es2019/pm-plugins/decorations/utils/createDeletedLineBreakWidget.js +100 -0
  17. package/dist/es2019/pm-plugins/decorations/utils/tableDiffMode.js +27 -0
  18. package/dist/es2019/pm-plugins/decorations/utils/wrapBlockNodeView.js +15 -5
  19. package/dist/es2019/pm-plugins/utils/emptyTextBlocks.js +58 -0
  20. package/dist/esm/pm-plugins/calculateDiff/calculateDiffDecorations.js +127 -50
  21. package/dist/esm/pm-plugins/calculateDiff/groupDeletedColumnChanges.js +258 -0
  22. package/dist/esm/pm-plugins/decorations/createChangedRowDecorationWidgets.js +100 -34
  23. package/dist/esm/pm-plugins/decorations/createContributorTagWidget.js +18 -13
  24. package/dist/esm/pm-plugins/decorations/createNodeChangedDecorationWidget.js +84 -22
  25. package/dist/esm/pm-plugins/decorations/utils/createDeletedLineBreakWidget.js +100 -0
  26. package/dist/esm/pm-plugins/decorations/utils/tableDiffMode.js +30 -0
  27. package/dist/esm/pm-plugins/decorations/utils/wrapBlockNodeView.js +16 -5
  28. package/dist/esm/pm-plugins/utils/emptyTextBlocks.js +60 -0
  29. package/dist/types/pm-plugins/calculateDiff/groupDeletedColumnChanges.d.ts +9 -0
  30. package/dist/types/pm-plugins/decorations/createChangedRowDecorationWidgets.d.ts +3 -1
  31. package/dist/types/pm-plugins/decorations/createContributorTagWidget.d.ts +5 -1
  32. package/dist/types/pm-plugins/decorations/createNodeChangedDecorationWidget.d.ts +2 -1
  33. package/dist/types/pm-plugins/decorations/utils/createDeletedLineBreakWidget.d.ts +50 -0
  34. package/dist/types/pm-plugins/decorations/utils/tableDiffMode.d.ts +26 -0
  35. package/dist/types/pm-plugins/decorations/utils/wrapBlockNodeView.d.ts +4 -0
  36. package/dist/types/pm-plugins/utils/emptyTextBlocks.d.ts +16 -0
  37. package/package.json +2 -2
@@ -22,6 +22,7 @@ var _createChangedRowDecorationWidgetsStyles = require("./createChangedRowDecora
22
22
  var _createContributorTagWidget = require("./createContributorTagWidget");
23
23
  var _decorationKeys = require("./decorationKeys");
24
24
  var _findSafeInsertPos = require("./utils/findSafeInsertPos");
25
+ var _wrapBlockNodeView = require("./utils/wrapBlockNodeView");
25
26
  var _tableCellEdgeAttrs = require("./utils/tableCellEdgeAttrs");
26
27
  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; }
27
28
  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; }
@@ -168,17 +169,27 @@ var isEmptyRow = function isEmptyRow(rowNode) {
168
169
  });
169
170
  return isEmpty;
170
171
  };
171
-
172
172
  /**
173
173
  * Creates a DOM representation of a deleted table row
174
174
  */
175
- var createChangedRowDOM = function createChangedRowDOM(rowNode, cellEdgeAttrs, nodeViewSerializer, colorScheme, isInserted, diffType) {
175
+ var createChangedRowDOM = function createChangedRowDOM(_ref3) {
176
+ var rowNode = _ref3.rowNode,
177
+ cellEdgeAttrs = _ref3.cellEdgeAttrs,
178
+ nodeViewSerializer = _ref3.nodeViewSerializer,
179
+ colorScheme = _ref3.colorScheme,
180
+ isInserted = _ref3.isInserted,
181
+ diffType = _ref3.diffType,
182
+ hasAnchoredRemovedLozenge = _ref3.hasAnchoredRemovedLozenge,
183
+ intl = _ref3.intl,
184
+ isActive = _ref3.isActive;
176
185
  var tr = document.createElement('tr');
177
186
  var colors = _schemes.colorSchemeRegistry[colorScheme !== null && colorScheme !== void 0 ? colorScheme : 'standard'];
187
+ var deletedTreatment = (0, _isExperimentEnabled.isExperimentEnabled)('platform_editor_show_diff_color_scheme_refactor') ? (0, _factory.buildDeletedRowStyle)(colors) : (0, _createChangedRowDecorationWidgetsStyles.resolveDeletedRowStyleLegacy)((0, _schemes.getLegacyColorScheme)(colorScheme));
188
+ var hostsRemovedLozenge = Boolean(intl) && (0, _isExtendedEnabled.isExtendedEnabled)(diffType) && !isInserted && !hasAnchoredRemovedLozenge;
178
189
 
179
190
  // Inserted rows keep their natural styling; the row strikethrough is deletions only.
180
191
  if (!(0, _isExtendedEnabled.isExtendedEnabled)(diffType) || !isInserted) {
181
- tr.setAttribute('style', (0, _isExperimentEnabled.isExperimentEnabled)('platform_editor_show_diff_color_scheme_refactor') ? (0, _factory.buildDeletedRowStyle)(colors) : (0, _createChangedRowDecorationWidgetsStyles.resolveDeletedRowStyleLegacy)((0, _schemes.getLegacyColorScheme)(colorScheme)));
192
+ tr.setAttribute('style', deletedTreatment);
182
193
  }
183
194
  // Mirrors the strikethrough condition above: under the extended experience an `isInserted` row
184
195
  // is ADDED content, so it must not claim the "deleted" testid — page models match that as
@@ -219,17 +230,50 @@ var createChangedRowDOM = function createChangedRowDOM(rowNode, cellEdgeAttrs, n
219
230
  cellIndex++;
220
231
  }
221
232
  });
233
+
234
+ // A "Removed" label on the row's last cell, so it reads at the row's top right corner. Deletions
235
+ // only: an inserted row is added content and carries no removal label.
236
+ if (intl && hostsRemovedLozenge) {
237
+ var lastCell = tr.lastElementChild;
238
+ if (lastCell instanceof HTMLElement) {
239
+ lastCell.style.position = 'relative';
240
+ // Append, never prepend. Editor CSS resets the top margin of a cell's first child, so a
241
+ // prepended label pushes the paragraph out of that reset and the row grows taller.
242
+ lastCell.append((0, _wrapBlockNodeView.createRemovedLozenge)(intl, isActive, colorScheme, true));
243
+ }
244
+ }
222
245
  return tr;
223
246
  };
247
+ var supportsAnchorPositioning = function supportsAnchorPositioning() {
248
+ return typeof CSS !== 'undefined' && typeof CSS.supports === 'function' && CSS.supports('top', 'anchor(--a top)');
249
+ };
250
+ var createAnchoredRemovedLozengeWidget = function createAnchoredRemovedLozengeWidget(_ref4) {
251
+ var anchorName = _ref4.anchorName,
252
+ colorScheme = _ref4.colorScheme,
253
+ from = _ref4.from,
254
+ intl = _ref4.intl,
255
+ isActive = _ref4.isActive;
256
+ var lozenge = (0, _wrapBlockNodeView.createRemovedLozenge)(intl, isActive, colorScheme, true);
257
+ var inset = lozenge.style.top;
258
+ lozenge.style.setProperty('position', 'fixed');
259
+ lozenge.style.setProperty('top', "calc(anchor(--".concat(anchorName, " top) + ").concat(inset, ")"));
260
+ lozenge.style.setProperty('left', "calc(anchor(--".concat(anchorName, " right) - ").concat(inset, ")"));
261
+ lozenge.style.setProperty('right', 'auto');
262
+ lozenge.style.setProperty('transform', 'translateX(-100%)');
263
+ return _view.Decoration.widget(from, lozenge, {
264
+ key: "removed-lozenge-".concat(anchorName),
265
+ side: -1
266
+ });
267
+ };
224
268
 
225
269
  /**
226
270
  * Expands a diff to include whole changed rows when table rows are affected
227
271
  */
228
- var expandDiffForChangedRows = function expandDiffForChangedRows(_ref3) {
229
- var changes = _ref3.changes,
230
- originalDoc = _ref3.originalDoc,
231
- newDoc = _ref3.newDoc,
232
- diffType = _ref3.diffType;
272
+ var expandDiffForChangedRows = function expandDiffForChangedRows(_ref5) {
273
+ var changes = _ref5.changes,
274
+ originalDoc = _ref5.originalDoc,
275
+ newDoc = _ref5.newDoc,
276
+ diffType = _ref5.diffType;
233
277
  var rowInfo = [];
234
278
  var _iterator = _createForOfIteratorHelper(changes),
235
279
  _step;
@@ -258,22 +302,23 @@ var expandDiffForChangedRows = function expandDiffForChangedRows(_ref3) {
258
302
  /**
259
303
  * Main function to handle deleted rows - computes diff and creates decorations
260
304
  */
261
- var createChangedRowDecorationWidgets = exports.createChangedRowDecorationWidgets = function createChangedRowDecorationWidgets(_ref4) {
262
- var attributionKey = _ref4.attributionKey,
263
- changes = _ref4.changes,
264
- originalDoc = _ref4.originalDoc,
265
- newDoc = _ref4.newDoc,
266
- nodeViewSerializer = _ref4.nodeViewSerializer,
267
- colorScheme = _ref4.colorScheme,
268
- isActive = _ref4.isActive,
269
- _ref4$isInserted = _ref4.isInserted,
270
- isInserted = _ref4$isInserted === void 0 ? false : _ref4$isInserted,
271
- diffType = _ref4.diffType,
272
- _ref4$showIndicators = _ref4.showIndicators,
273
- showIndicators = _ref4$showIndicators === void 0 ? false : _ref4$showIndicators,
274
- _ref4$showContributor = _ref4.showContributorTags,
275
- showContributorTags = _ref4$showContributor === void 0 ? false : _ref4$showContributor,
276
- tagMountContext = _ref4.tagMountContext;
305
+ var createChangedRowDecorationWidgets = exports.createChangedRowDecorationWidgets = function createChangedRowDecorationWidgets(_ref6) {
306
+ var attributionKey = _ref6.attributionKey,
307
+ changes = _ref6.changes,
308
+ originalDoc = _ref6.originalDoc,
309
+ newDoc = _ref6.newDoc,
310
+ nodeViewSerializer = _ref6.nodeViewSerializer,
311
+ colorScheme = _ref6.colorScheme,
312
+ isActive = _ref6.isActive,
313
+ _ref6$isInserted = _ref6.isInserted,
314
+ isInserted = _ref6$isInserted === void 0 ? false : _ref6$isInserted,
315
+ diffType = _ref6.diffType,
316
+ intl = _ref6.intl,
317
+ _ref6$showIndicators = _ref6.showIndicators,
318
+ showIndicators = _ref6$showIndicators === void 0 ? false : _ref6$showIndicators,
319
+ _ref6$showContributor = _ref6.showContributorTags,
320
+ showContributorTags = _ref6$showContributor === void 0 ? false : _ref6$showContributor,
321
+ tagMountContext = _ref6.tagMountContext;
277
322
  // First, expand the changes to include complete deleted rows
278
323
  var changedRows = expandDiffForChangedRows({
279
324
  changes: changes.filter(function (change) {
@@ -284,16 +329,25 @@ var createChangedRowDecorationWidgets = exports.createChangedRowDecorationWidget
284
329
  diffType: diffType
285
330
  });
286
331
  return changedRows.flatMap(function (changedRow) {
287
- var rowDOM = createChangedRowDOM(changedRow.rowNode, changedRow.cellEdgeAttrs, nodeViewSerializer, colorScheme, isInserted, diffType);
332
+ // Keep the ID stable while contributor tags are enabled so recalculation preserves their state.
333
+ var diffId = showContributorTags ? "widget-row-".concat(changedRow.fromA, "-").concat(changedRow.toA) : crypto.randomUUID();
334
+ var hasAnchoredRemovedLozenge = Boolean(intl) && (0, _isExtendedEnabled.isExtendedEnabled)(diffType) && !isInserted && supportsAnchorPositioning();
335
+ var rowDOM = createChangedRowDOM({
336
+ rowNode: changedRow.rowNode,
337
+ cellEdgeAttrs: changedRow.cellEdgeAttrs,
338
+ nodeViewSerializer: nodeViewSerializer,
339
+ colorScheme: colorScheme,
340
+ isInserted: isInserted,
341
+ diffType: diffType,
342
+ hasAnchoredRemovedLozenge: hasAnchoredRemovedLozenge,
343
+ intl: intl,
344
+ isActive: isActive
345
+ });
288
346
 
289
347
  // Find safe insertion position for the deleted row
290
348
  var safeInsertPos = (0, _findSafeInsertPos.findSafeInsertPos)(newDoc, changedRow.fromB - 1,
291
349
  // -1 to find the first safe position from the table
292
350
  originalDoc.slice(changedRow.fromA, changedRow.toA));
293
-
294
- // Stable while contributor tags are enabled so a decoration recalculation preserves the tag's
295
- // identity and controller state.
296
- var diffId = showContributorTags ? "widget-row-".concat(changedRow.fromA, "-").concat(changedRow.toA) : crypto.randomUUID();
297
351
  var decorations = [];
298
352
  var rowAnchorNames = [];
299
353
 
@@ -322,6 +376,11 @@ var createChangedRowDecorationWidgets = exports.createChangedRowDecorationWidget
322
376
  diffId: diffId,
323
377
  anchorType: _decorationKeys.AnchorTypeKey.tag
324
378
  }) : undefined;
379
+ var removedLozengeAnchorName = hasAnchoredRemovedLozenge ? tagAnchorName !== null && tagAnchorName !== void 0 ? tagAnchorName : (0, _decorationKeys.buildAnchorDecorationKey)({
380
+ diffId: diffId,
381
+ anchorType: _decorationKeys.AnchorTypeKey.tag
382
+ }) : undefined;
383
+ var rowLabelAnchorName = tagAnchorName !== null && tagAnchorName !== void 0 ? tagAnchorName : removedLozengeAnchorName;
325
384
  var tagWidget = tagAnchorName ? (0, _createContributorTagWidget.createContributorTagWidget)({
326
385
  anchorAtRangeStart: true,
327
386
  anchorName: tagAnchorName,
@@ -331,11 +390,9 @@ var createChangedRowDecorationWidgets = exports.createChangedRowDecorationWidget
331
390
  mountContext: tagMountContext,
332
391
  to: safeInsertPos
333
392
  }) : undefined;
334
- if (tagAnchorName) {
335
- // The tag host is a separate, table-safe widget. CSS anchor positioning aligns it to this
336
- // synthetic row without placing non-cell DOM inside the `<tr>` or inheriting its deletion
337
- // opacity and strikethrough.
338
- rowAnchorNames.push(tagAnchorName);
393
+ if (rowLabelAnchorName) {
394
+ // Labels stay outside the table row so they do not inherit its deletion treatment.
395
+ rowAnchorNames.push(rowLabelAnchorName);
339
396
  }
340
397
  if (rowAnchorNames.length > 0) {
341
398
  rowDOM.style.setProperty('anchor-name', rowAnchorNames.map(function (anchorName) {
@@ -356,6 +413,15 @@ var createChangedRowDecorationWidgets = exports.createChangedRowDecorationWidget
356
413
  diffType: diffType
357
414
  })));
358
415
  decorations.push(rowWidget);
416
+ if (intl && removedLozengeAnchorName) {
417
+ decorations.push(createAnchoredRemovedLozengeWidget({
418
+ anchorName: removedLozengeAnchorName,
419
+ colorScheme: colorScheme,
420
+ from: safeInsertPos,
421
+ intl: intl,
422
+ isActive: isActive
423
+ }));
424
+ }
359
425
  if (tagWidget) {
360
426
  decorations.push(tagWidget);
361
427
  }
@@ -101,14 +101,19 @@ var supportsAnchorPositioning = function supportsAnchorPositioning() {
101
101
  * The host for a tag on deleted content, which renders inside its own widget decoration rather than
102
102
  * needing one of its own. Unmount with `unmountContributorTag` from that widget's `destroy`.
103
103
  */
104
- var createContributorTagHost = exports.createContributorTagHost = function createContributorTagHost(diffId, mountContext) {
104
+ var createContributorTagHost = exports.createContributorTagHost = function createContributorTagHost(_ref2) {
105
+ var anchorName = _ref2.anchorName,
106
+ diffId = _ref2.diffId,
107
+ mountContext = _ref2.mountContext;
105
108
  if (!isContributorTagWidgetEnabled()) {
106
109
  return undefined;
107
110
  }
108
- var host = buildContributorTagHost(diffId);
111
+ var tagAnchorName = anchorName && supportsAnchorPositioning() ? anchorName : undefined;
112
+ var host = buildContributorTagHost(diffId, tagAnchorName);
109
113
  return {
110
114
  host: host,
111
115
  mount: mountContributorTag({
116
+ anchorName: tagAnchorName,
112
117
  diffId: diffId,
113
118
  host: host,
114
119
  mountContext: mountContext
@@ -132,7 +137,7 @@ var createContributorTagHost = exports.createContributorTagHost = function creat
132
137
  * (see `hasVisibleContent`).
133
138
  */
134
139
  var resolveTagAnchorPos = function resolveTagAnchorPos(doc, from, to) {
135
- var _ref2;
140
+ var _ref3;
136
141
  var edge;
137
142
  var visibleEdge;
138
143
  doc.nodesBetween(from, to, function (node, nodePos) {
@@ -165,7 +170,7 @@ var resolveTagAnchorPos = function resolveTagAnchorPos(doc, from, to) {
165
170
  // Textblocks have no block children to descend into.
166
171
  return false;
167
172
  });
168
- return (_ref2 = visibleEdge !== null && visibleEdge !== void 0 ? visibleEdge : edge) !== null && _ref2 !== void 0 ? _ref2 : from;
173
+ return (_ref3 = visibleEdge !== null && visibleEdge !== void 0 ? visibleEdge : edge) !== null && _ref3 !== void 0 ? _ref3 : from;
169
174
  };
170
175
 
171
176
  /**
@@ -193,15 +198,15 @@ var resolveCodeBlockStart = function resolveCodeBlockStart(doc, pos) {
193
198
  * The tag is mounted in `toDOM` and taken down in `destroy`, so it lives exactly as long as the host
194
199
  * element ProseMirror drew it into — see `mountContributorTag`.
195
200
  */
196
- var createContributorTagWidget = exports.createContributorTagWidget = function createContributorTagWidget(_ref3) {
197
- var _ref3$anchorAtRangeSt = _ref3.anchorAtRangeStart,
198
- anchorAtRangeStart = _ref3$anchorAtRangeSt === void 0 ? false : _ref3$anchorAtRangeSt,
199
- anchorName = _ref3.anchorName,
200
- doc = _ref3.doc,
201
- from = _ref3.from,
202
- to = _ref3.to,
203
- diffId = _ref3.diffId,
204
- mountContext = _ref3.mountContext;
201
+ var createContributorTagWidget = exports.createContributorTagWidget = function createContributorTagWidget(_ref4) {
202
+ var _ref4$anchorAtRangeSt = _ref4.anchorAtRangeStart,
203
+ anchorAtRangeStart = _ref4$anchorAtRangeSt === void 0 ? false : _ref4$anchorAtRangeSt,
204
+ anchorName = _ref4.anchorName,
205
+ doc = _ref4.doc,
206
+ from = _ref4.from,
207
+ to = _ref4.to,
208
+ diffId = _ref4.diffId,
209
+ mountContext = _ref4.mountContext;
205
210
  if (!isContributorTagWidgetEnabled()) {
206
211
  return undefined;
207
212
  }
@@ -9,16 +9,19 @@ var _defineProperty2 = _interopRequireDefault(require("@babel/runtime/helpers/de
9
9
  var _view = require("@atlaskit/editor-prosemirror/view");
10
10
  var _fg = require("@atlaskit/platform-feature-flags/fg");
11
11
  var _isExtendedEnabled = require("../isExtendedEnabled");
12
+ var _emptyTextBlocks = require("../utils/emptyTextBlocks");
12
13
  var _isEmptyParagraphSlice = require("../utils/isEmptyParagraphSlice");
13
14
  var _createAnchorDecorationWidgets = require("./createAnchorDecorationWidgets");
14
15
  var _createChangedRowDecorationWidgets = require("./createChangedRowDecorationWidgets");
15
16
  var _createContributorTagWidget = require("./createContributorTagWidget");
16
17
  var _decorationKeys = require("./decorationKeys");
17
18
  var _absorbFirstChildMarginReset = require("./utils/absorbFirstChildMarginReset");
19
+ var _createDeletedLineBreakWidget = require("./utils/createDeletedLineBreakWidget");
18
20
  var _createMarginAbsorber = require("./utils/createMarginAbsorber");
19
21
  var _createNodeShapedMarginSpacer = require("./utils/createNodeShapedMarginSpacer");
20
22
  var _findSafeInsertPos = require("./utils/findSafeInsertPos");
21
23
  var _safeResolve = require("./utils/safeResolve");
24
+ var _tableDiffMode = require("./utils/tableDiffMode");
22
25
  var _wrapBlockNodeView = require("./utils/wrapBlockNodeView");
23
26
  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; }
24
27
  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; }
@@ -129,9 +132,18 @@ var createNodeChangedDecorationWidget = exports.createNodeChangedDecorationWidge
129
132
  _ref2$hideAddedDiffsU = _ref2.hideAddedDiffsUnderline,
130
133
  hideAddedDiffsUnderline = _ref2$hideAddedDiffsU === void 0 ? false : _ref2$hideAddedDiffsU,
131
134
  reveal = _ref2.reveal,
132
- tagMountContext = _ref2.tagMountContext;
135
+ tagMountContext = _ref2.tagMountContext,
136
+ deletedColumns = _ref2.deletedColumns;
133
137
  var slice = doc.slice(change.fromA, change.toA);
134
138
  var shouldSkipDeletedEmptyParagraphDecoration = !isInserted && (0, _isEmptyParagraphSlice.isEmptyParagraphSlice)(slice);
139
+ // How many blank lines the deletion took, when it took nothing else. Blank means an empty
140
+ // paragraph or heading — both are a line the author put there deliberately, and neither leaves
141
+ // the widget any content to serialize.
142
+ //
143
+ // Kept separate from `shouldSkipDeletedEmptyParagraphDecoration` and gated, so the ungated early
144
+ // return below still covers only the single-empty-paragraph case it always did. Everything else
145
+ // degrades to what it rendered before the gate: an empty block, serialized invisibly.
146
+ var deletedBlankLineCount = !isInserted && (0, _fg.fg)('platform_editor_ai_show_diff_patch_2') ? (0, _emptyTextBlocks.countEmptyTextBlockOnlySlice)(slice) : 0;
135
147
  // Widget decoration used for deletions as the content is not in the document
136
148
  // and we want to display the deleted content with a style.
137
149
  // For `placeBelow`, anchor at the END of the new content (change.toB) so the deleted
@@ -139,24 +151,33 @@ var createNodeChangedDecorationWidget = exports.createNodeChangedDecorationWidge
139
151
  var anchorPos = placeBelow ? change.toB : change.fromB;
140
152
  var safeInsertPos = (0, _findSafeInsertPos.findSafeInsertPos)(newDoc, anchorPos, slice);
141
153
  var isActive = activeIndexPos && ((0, _fg.fg)('confluence_ncs_step_diffing_version_history') ? safeInsertPos >= activeIndexPos.from && safeInsertPos <= activeIndexPos.to : safeInsertPos === activeIndexPos.from && safeInsertPos === activeIndexPos.to);
142
- if (slice.content.content.length === 0 || shouldSkipDeletedEmptyParagraphDecoration) {
154
+ if (deletedBlankLineCount > 0) {
155
+ // The blocks have no content to serialize, so the deletion is shown as one struck-through
156
+ // return glyph per line the author removed. A single widget, not one per line: the lines are
157
+ // gone from `newDoc`, so they all resolve to the same anchor and separate widgets would stack.
158
+ //
159
+ // No attribution: the glyph is not a tag host, matching `canTagWidget` below.
160
+ return [(0, _createDeletedLineBreakWidget.createDeletedLineBreakDecoration)({
161
+ colorScheme: colorScheme,
162
+ count: deletedBlankLineCount,
163
+ diffType: diffType,
164
+ isActive: isActive,
165
+ pos: safeInsertPos,
166
+ reveal: reveal,
167
+ side: placeBelow ? 1 : -1
168
+ })];
169
+ }
170
+ if (shouldSkipDeletedEmptyParagraphDecoration) {
143
171
  return [];
144
172
  }
145
- var isTableCellContent = slice.content.content.some(function () {
146
- return slice.content.content.some(function (siblingNode) {
147
- return ['tableHeader', 'tableCell'].includes(siblingNode.type.name);
148
- });
149
- });
150
- var isTableRowContent = slice.content.content.some(function () {
151
- return slice.content.content.some(function (siblingNode) {
152
- return ['tableRow'].includes(siblingNode.type.name);
153
- });
173
+ if (slice.content.content.length === 0) {
174
+ return [];
175
+ }
176
+ var tableDiffMode = (0, _tableDiffMode.getTableDiffMode)({
177
+ slice: slice,
178
+ isInserted: isInserted
154
179
  });
155
-
156
- // Added whole cells (e.g. "Add table headers"): render per-cell content widgets.
157
- // Deleted whole cells (delete row/col) fall through — they don't exist in `newDoc`,
158
- // so there's nowhere to anchor content and we keep the original "render nothing".
159
- if (isTableCellContent && isInserted) {
180
+ if (tableDiffMode.kind === 'cells') {
160
181
  return createTableCellContentWidgets({
161
182
  slice: slice,
162
183
  newDoc: newDoc,
@@ -167,11 +188,10 @@ var createNodeChangedDecorationWidget = exports.createNodeChangedDecorationWidge
167
188
  diffType: diffType
168
189
  });
169
190
  }
170
- if (isTableCellContent) {
171
- // Deleted whole cells: nothing to render (see note above).
191
+ if (tableDiffMode.kind === 'none') {
172
192
  return [];
173
193
  }
174
- if (isTableRowContent) {
194
+ if (tableDiffMode.kind === 'rows') {
175
195
  return (0, _createChangedRowDecorationWidgets.createChangedRowDecorationWidgets)({
176
196
  attributionKey: attributionKey,
177
197
  changes: [change],
@@ -182,6 +202,7 @@ var createNodeChangedDecorationWidget = exports.createNodeChangedDecorationWidge
182
202
  isActive: isActive,
183
203
  isInserted: isInserted,
184
204
  diffType: diffType,
205
+ intl: intl,
185
206
  // Needed for the row's own indicator anchor; this path returns before the
186
207
  // `anchor-name` assignment further down.
187
208
  showIndicators: showIndicators,
@@ -189,6 +210,9 @@ var createNodeChangedDecorationWidget = exports.createNodeChangedDecorationWidge
189
210
  tagMountContext: tagMountContext
190
211
  });
191
212
  }
213
+ // `wholeTable` and `generic` both continue into the generic block widget below. Splitting the
214
+ // whole-table case into its own renderer is the next step of this refactor.
215
+
192
216
  var serializer = nodeViewSerializer;
193
217
 
194
218
  // For non-table content, use the existing span wrapper approach
@@ -390,6 +414,40 @@ var createNodeChangedDecorationWidget = exports.createNodeChangedDecorationWidge
390
414
  }
391
415
  }
392
416
  });
417
+ var contributorTagAnchorName;
418
+ if (!isInserted && deletedColumns !== null && deletedColumns !== void 0 && deletedColumns.length) {
419
+ var table = dom.querySelector('table');
420
+ var firstRow = table === null || table === void 0 ? void 0 : table.rows[0];
421
+ if (table && canTagWidget) {
422
+ contributorTagAnchorName = (0, _decorationKeys.buildAnchorDecorationKey)({
423
+ diffId: diffId,
424
+ anchorType: _decorationKeys.AnchorTypeKey.tag
425
+ });
426
+ // The widget host sits before the table's preserved block margin. Anchor the tag to the
427
+ // table itself so its bottom edge meets the table's top edge with no visual gap.
428
+ table.style.setProperty('anchor-name', "--".concat(contributorTagAnchorName));
429
+ }
430
+ deletedColumns.forEach(function (column) {
431
+ var _table$rows;
432
+ Array.from((_table$rows = table === null || table === void 0 ? void 0 : table.rows) !== null && _table$rows !== void 0 ? _table$rows : []).forEach(function (row) {
433
+ var _cell$children;
434
+ var cell = row.cells[column];
435
+ Array.from((_cell$children = cell === null || cell === void 0 ? void 0 : cell.children) !== null && _cell$children !== void 0 ? _cell$children : []).forEach(function (child) {
436
+ if (child instanceof HTMLElement) {
437
+ child.style.setProperty('text-decoration-line', 'line-through');
438
+ }
439
+ });
440
+ });
441
+ var headerCell = firstRow === null || firstRow === void 0 ? void 0 : firstRow.cells[column];
442
+ if (headerCell) {
443
+ headerCell.style.position = 'relative';
444
+ // Append, never prepend. Editor CSS resets the top margin of a cell's first child, so
445
+ // a prepended label pushes the paragraph out of that reset and the cell grows.
446
+ // The label is absolutely positioned, so DOM order does not move it.
447
+ headerCell.append((0, _wrapBlockNodeView.createRemovedLozenge)(intl, isActive, colorScheme, true));
448
+ }
449
+ });
450
+ }
393
451
  dom.setAttribute('data-testid', 'show-diff-deleted-decoration');
394
452
  if ((0, _fg.fg)('platform_editor_ai_show_diff_patch_1')) {
395
453
  dom.style.setProperty('scroll-margin-top', _decorationKeys.scrollMarginTopValue);
@@ -427,7 +485,11 @@ var createNodeChangedDecorationWidget = exports.createNodeChangedDecorationWidge
427
485
 
428
486
  // Hosted at the start of the deleted run rather than on `dom` itself: `dom` is inline, so once
429
487
  // the deleted content wraps its box is the union of its line fragments.
430
- var tagHost = (0, _createContributorTagWidget.createContributorTagHost)(diffId, tagMountContext);
488
+ var tagHost = (0, _createContributorTagWidget.createContributorTagHost)({
489
+ anchorName: contributorTagAnchorName,
490
+ diffId: diffId,
491
+ mountContext: tagMountContext
492
+ });
431
493
  if (tagHost) {
432
494
  dom.prepend(tagHost.host);
433
495
  tagMount = tagHost.mount;
@@ -0,0 +1,106 @@
1
+ "use strict";
2
+
3
+ var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
4
+ Object.defineProperty(exports, "__esModule", {
5
+ value: true
6
+ });
7
+ exports.createDeletedLineBreakWidget = exports.createDeletedLineBreakDecoration = exports.RETURN_GLYPH = void 0;
8
+ var _defineProperty2 = _interopRequireDefault(require("@babel/runtime/helpers/defineProperty"));
9
+ var _view = require("@atlaskit/editor-prosemirror/view");
10
+ var _fg = require("@atlaskit/platform-feature-flags/fg");
11
+ var _isExtendedEnabled = require("../../isExtendedEnabled");
12
+ var _decorationKeys = require("../decorationKeys");
13
+ var _wrapBlockNodeView = require("./wrapBlockNodeView");
14
+ 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; }
15
+ 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; }
16
+ /**
17
+ * U+2936 ARROW POINTING DOWNWARDS THEN CURVING LEFTWARDS — the return-key glyph.
18
+ *
19
+ * Exported so tests assert against the glyph actually rendered rather than a copy of it.
20
+ */
21
+ var RETURN_GLYPH = exports.RETURN_GLYPH = '⤶';
22
+
23
+ /**
24
+ * The DOM for a removed blank line: the return glyph, struck through in the deleted colours.
25
+ *
26
+ * An empty paragraph or heading is a line the author deliberately added, but it has no content for
27
+ * the deleted-content widget to serialize, so a deletion of one used to render nothing at all — the
28
+ * reviewer saw the surrounding text close up with no sign of what was removed. Standing the glyph
29
+ * in for the absent content keeps the deletion visible without inventing content that was not there.
30
+ *
31
+ * `createContentWrapper` supplies the highlight and the strikethrough line, so the glyph is styled
32
+ * exactly like any other run of deleted inline content, including the active and reveal states.
33
+ *
34
+ * Hidden from assistive technology: the glyph is a picture of a key, and read out it is announced as
35
+ * its Unicode name rather than as a removed line.
36
+ */
37
+ var createDeletedLineBreakWidget = exports.createDeletedLineBreakWidget = function createDeletedLineBreakWidget(_ref) {
38
+ var colorScheme = _ref.colorScheme,
39
+ _ref$count = _ref.count,
40
+ count = _ref$count === void 0 ? 1 : _ref$count,
41
+ diffType = _ref.diffType,
42
+ _ref$isActive = _ref.isActive,
43
+ isActive = _ref$isActive === void 0 ? false : _ref$isActive,
44
+ reveal = _ref.reveal;
45
+ var dom = document.createElement('span');
46
+ // The testid deleted content is matched by, so the glyph is found wherever deleted content is.
47
+ dom.setAttribute('data-testid', 'show-diff-deleted-decoration');
48
+ dom.setAttribute('aria-hidden', 'true');
49
+ dom.contentEditable = 'false';
50
+ // Scroll navigation aligns on the widget itself, as it does for every other deleted widget.
51
+ dom.style.setProperty('scroll-margin-top', _decorationKeys.scrollMarginTopValue);
52
+ for (var index = 0; index < Math.max(count, 1); index++) {
53
+ if (index > 0) {
54
+ // Each removed line gets its own line in the widget, so a run of blank lines reads as the
55
+ // number of lines it was. `br` rather than a block wrapper: the widget is inline, and the
56
+ // deleted-content wrapper is styled as an inline run.
57
+ dom.append(dom.ownerDocument.createElement('br'));
58
+ }
59
+ var wrapper = (0, _wrapBlockNodeView.createContentWrapper)(colorScheme, isActive, false, diffType, reveal);
60
+ wrapper.append(dom.ownerDocument.createTextNode(RETURN_GLYPH));
61
+ dom.append(wrapper);
62
+ }
63
+ return dom;
64
+ };
65
+
66
+ /**
67
+ * The glyph as a widget decoration anchored at `pos`.
68
+ *
69
+ * Shared by the two shapes a blank-line removal arrives in. On a forward diff the block is gone from
70
+ * the new document, so the change lands on the deleted side and the glyph is anchored where the
71
+ * block used to be. On an inverted diff — what AI suggested edits renders — the block is still in
72
+ * the displayed document and the change lands on the *inserted* side, so the glyph is anchored
73
+ * inside the surviving block, on the blank line itself.
74
+ */
75
+ var createDeletedLineBreakDecoration = exports.createDeletedLineBreakDecoration = function createDeletedLineBreakDecoration(_ref2) {
76
+ var attributionKey = _ref2.attributionKey,
77
+ colorScheme = _ref2.colorScheme,
78
+ count = _ref2.count,
79
+ diffType = _ref2.diffType,
80
+ _ref2$isActive = _ref2.isActive,
81
+ isActive = _ref2$isActive === void 0 ? false : _ref2$isActive,
82
+ pos = _ref2.pos,
83
+ reveal = _ref2.reveal,
84
+ side = _ref2.side;
85
+ return _view.Decoration.widget(pos, createDeletedLineBreakWidget({
86
+ colorScheme: colorScheme,
87
+ count: count,
88
+ diffType: diffType,
89
+ isActive: isActive,
90
+ reveal: reveal
91
+ }), _objectSpread(_objectSpread({}, (0, _decorationKeys.buildDiffDecorationSpec)(_objectSpread({
92
+ attributionKey: attributionKey,
93
+ colorScheme: colorScheme,
94
+ decorationType: 'widget',
95
+ diffId: crypto.randomUUID(),
96
+ isActive: isActive,
97
+ // The glyph always stands in for removed content, whichever side of the changeset the
98
+ // change itself landed on.
99
+ isInserted: false,
100
+ diffType: diffType
101
+ }, (0, _isExtendedEnabled.isExtendedEnabled)(diffType) && side !== undefined && {
102
+ side: side
103
+ }))), (0, _fg.fg)('platform_editor_diff_inline_mark_changes') && {
104
+ marks: []
105
+ }));
106
+ };
@@ -0,0 +1,36 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.getTableDiffMode = void 0;
7
+ var CELL_TYPE_NAMES = ['tableCell', 'tableHeader'];
8
+ var getTableDiffMode = exports.getTableDiffMode = function getTableDiffMode(_ref) {
9
+ var slice = _ref.slice,
10
+ isInserted = _ref.isInserted;
11
+ var topLevelNodes = slice.content.content;
12
+ if (topLevelNodes.some(function (node) {
13
+ return CELL_TYPE_NAMES.includes(node.type.name);
14
+ })) {
15
+ return isInserted ? {
16
+ kind: 'cells'
17
+ } : {
18
+ kind: 'none'
19
+ };
20
+ }
21
+ if (topLevelNodes.some(function (node) {
22
+ return node.type.name === 'tableRow';
23
+ })) {
24
+ return {
25
+ kind: 'rows'
26
+ };
27
+ }
28
+ if (topLevelNodes.length === 1 && topLevelNodes[0].type.name === 'table' && slice.openStart === 0 && slice.openEnd === 0) {
29
+ return {
30
+ kind: 'wholeTable'
31
+ };
32
+ }
33
+ return {
34
+ kind: 'generic'
35
+ };
36
+ };
@@ -4,7 +4,7 @@ var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefau
4
4
  Object.defineProperty(exports, "__esModule", {
5
5
  value: true
6
6
  });
7
- exports.wrapBlockNodeView = exports.injectInnerWrapper = exports.createContentWrapper = void 0;
7
+ exports.wrapBlockNodeView = exports.injectInnerWrapper = exports.createRemovedLozenge = exports.createContentWrapper = void 0;
8
8
  var _toConsumableArray2 = _interopRequireDefault(require("@babel/runtime/helpers/toConsumableArray"));
9
9
  var _lazyNodeView = require("@atlaskit/editor-common/lazy-node-view");
10
10
  var _messages = require("@atlaskit/editor-common/messages");
@@ -148,26 +148,37 @@ var applyCellOverlayStyles = function applyCellOverlayStyles(_ref4) {
148
148
  /**
149
149
  * Creates a "Removed" lozenge to be displayed at the top right corner of deleted block nodes
150
150
  */
151
- var createRemovedLozenge = function createRemovedLozenge(intl) {
151
+ var createRemovedLozenge = exports.createRemovedLozenge = function createRemovedLozenge(intl) {
152
152
  var isActive = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : false;
153
153
  var colorScheme = arguments.length > 2 ? arguments[2] : undefined;
154
+ var inCell = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : false;
154
155
  var container = document.createElement('span');
155
156
  var containerStyle = (0, _lazyNodeView.convertToInlineCss)({
156
157
  position: 'absolute',
157
158
  top: "var(--ds-space-075, 6px)",
158
159
  right: "var(--ds-space-075, 6px)",
159
- zIndex: 2,
160
+ // Rounded table-cell overlays use z-index 2 and are appended after the cell content.
161
+ // Keep the lozenge above them so the overlay cannot tint or obscure it.
162
+ zIndex: 3,
160
163
  pointerEvents: 'none',
161
164
  display: 'flex'
162
165
  });
163
- container.setAttribute('style', containerStyle);
164
- container.setAttribute('data-testid', 'show-diff-removed-lozenge');
165
166
 
166
167
  // Create vanilla HTML lozenge element with Atlaskit Lozenge styling (visual refresh)
167
168
  var lozengeElement = document.createElement('span');
168
- var lozengeInnerStyle = (0, _wrapBlockNodeViewStyles.resolveRemovedLozengeStyle)(colorScheme, isActive);
169
+
170
+ // A column label sits over an already-grey deleted cell, so use the red Removed treatment.
171
+ var lozengeInnerStyle = (0, _wrapBlockNodeViewStyles.resolveRemovedLozengeStyle)(colorScheme, inCell || isActive);
169
172
  lozengeElement.setAttribute('style', lozengeInnerStyle);
170
173
  lozengeElement.textContent = intl.formatMessage(_messages.trackChangesMessages.removed).toUpperCase();
174
+ if (inCell) {
175
+ lozengeElement.setAttribute('style', "".concat(lozengeInnerStyle, ";").concat(containerStyle));
176
+ lozengeElement.setAttribute('data-testid', 'show-diff-removed-lozenge');
177
+ lozengeElement.contentEditable = 'false';
178
+ return lozengeElement;
179
+ }
180
+ container.setAttribute('style', containerStyle);
181
+ container.setAttribute('data-testid', 'show-diff-removed-lozenge');
171
182
  container.appendChild(lozengeElement);
172
183
  return container;
173
184
  };