@atlaskit/editor-plugin-show-diff 13.1.4 → 13.1.6

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 +39 -0
  2. package/dist/cjs/pm-plugins/calculateDiff/attrAwareTokenEncoder.js +28 -4
  3. package/dist/cjs/pm-plugins/calculateDiff/calculateDiffDecorations.js +29 -1
  4. package/dist/cjs/pm-plugins/calculateDiff/smart/classifySmartChanges.js +8 -0
  5. package/dist/cjs/pm-plugins/decorations/createChangedRowDecorationWidgets.js +4 -3
  6. package/dist/cjs/pm-plugins/decorations/utils/diffableAttrs.js +5 -0
  7. package/dist/cjs/pm-plugins/decorations/utils/getAttrChangeRanges.js +19 -0
  8. package/dist/cjs/pm-plugins/decorations/utils/tableCellEdgeAttrs.js +47 -20
  9. package/dist/es2019/pm-plugins/calculateDiff/attrAwareTokenEncoder.js +27 -3
  10. package/dist/es2019/pm-plugins/calculateDiff/calculateDiffDecorations.js +29 -1
  11. package/dist/es2019/pm-plugins/calculateDiff/smart/classifySmartChanges.js +8 -0
  12. package/dist/es2019/pm-plugins/decorations/createChangedRowDecorationWidgets.js +5 -4
  13. package/dist/es2019/pm-plugins/decorations/utils/diffableAttrs.js +5 -0
  14. package/dist/es2019/pm-plugins/decorations/utils/getAttrChangeRanges.js +18 -0
  15. package/dist/es2019/pm-plugins/decorations/utils/tableCellEdgeAttrs.js +45 -18
  16. package/dist/esm/pm-plugins/calculateDiff/attrAwareTokenEncoder.js +28 -4
  17. package/dist/esm/pm-plugins/calculateDiff/calculateDiffDecorations.js +29 -1
  18. package/dist/esm/pm-plugins/calculateDiff/smart/classifySmartChanges.js +8 -0
  19. package/dist/esm/pm-plugins/decorations/createChangedRowDecorationWidgets.js +5 -4
  20. package/dist/esm/pm-plugins/decorations/utils/diffableAttrs.js +5 -0
  21. package/dist/esm/pm-plugins/decorations/utils/getAttrChangeRanges.js +19 -0
  22. package/dist/esm/pm-plugins/decorations/utils/tableCellEdgeAttrs.js +46 -19
  23. package/dist/types/pm-plugins/decorations/utils/tableCellEdgeAttrs.d.ts +4 -3
  24. package/package.json +12 -12
package/CHANGELOG.md CHANGED
@@ -1,5 +1,44 @@
1
1
  # @atlaskit/editor-plugin-show-diff
2
2
 
3
+ ## 13.1.6
4
+
5
+ ### Patch Changes
6
+
7
+ - [`72bb78136922f`](https://bitbucket.org/atlassian/atlassian-frontend-monorepo/commits/72bb78136922f) -
8
+ [CCI-18917] Render node attribute-only changes (e.g. a panel type change note → warning) in the AI
9
+ Review Moment as a before/after diff.
10
+
11
+ A container's own attribute change touches no inner child, so the smart-diff classifier's
12
+ child-density check previously dropped it and nothing rendered. The smart-diff token encoder now
13
+ folds the container's diffable attributes into its node-start token (normalising node variants
14
+ such as `panel_c1` → `panel`), the classifier promotes the whole container to a node-level change,
15
+ and the decoration builder renders the new node plus the original node as a "deleted" widget so
16
+ the reviewer sees the before/after. Gated to the smart node-level AIFC Review Moment path.
17
+
18
+ - [`4747ee2b7b275`](https://bitbucket.org/atlassian/atlassian-frontend-monorepo/commits/4747ee2b7b275) -
19
+ Only inspect logical top and bottom rows when assigning table edge attributes under
20
+ platform_editor_table_diff_rounded_corners.
21
+ - Updated dependencies
22
+
23
+ ## 13.1.5
24
+
25
+ ### Patch Changes
26
+
27
+ - [`fd3620d4e137b`](https://bitbucket.org/atlassian/atlassian-frontend-monorepo/commits/fd3620d4e137b) -
28
+ [CCI-18915] Surface and render formatting-only (mark) agent edits in the AI Review Moment.
29
+
30
+ Mark-only agent steps (`AddMarkStep`/`RemoveMarkStep` — e.g. making text bold, italic, or a link)
31
+ have an empty `StepMap`, so the agent review-segment and shimmer builders — which derived their
32
+ changed ranges purely from `StepMap` geometry — never produced a range for them. As a result a
33
+ formatting-only agent edit opened no Review Moment and showed no shimmer. Both builders now detect
34
+ mark steps and derive their range from `step.from`/`step.to`.
35
+
36
+ The smart-diff token encoder additionally folds each character's marks (type + attrs) into its
37
+ token, so once the moment opens the change renders as a real diff (e.g. a bold-only edit, or a
38
+ link whose `href` changes) instead of an empty one.
39
+
40
+ - Updated dependencies
41
+
3
42
  ## 13.1.4
4
43
 
5
44
  ### Patch Changes
@@ -4,6 +4,7 @@ Object.defineProperty(exports, "__esModule", {
4
4
  value: true
5
5
  });
6
6
  exports.attrAwareTokenEncoder = void 0;
7
+ var _nodeTypeUtils = require("@atlaskit/editor-common/utils/node-type-utils");
7
8
  var _diffableAttrs = require("../decorations/utils/diffableAttrs");
8
9
  /**
9
10
  * Attribute-aware token encoder for `prosemirror-changeset`. The default encoder
@@ -24,15 +25,38 @@ var encodeNodeWithAttrs = function encodeNodeWithAttrs(node, attrNames) {
24
25
  return "".concat(node.type.name, "|").concat(parts.join('|'));
25
26
  };
26
27
 
28
+ // Fold a character's marks (type + attrs) into its token so a formatting-only change
29
+ // (bold/italic/link/colour, or a link whose `href` changes) registers as a real diff.
30
+ // The default encoder returns just the char code, so marked and unmarked text tokenise
31
+ // identically. Marks and their attrs are sorted so the token is order-independent.
32
+ var encodeCharacterWithMarks = function encodeCharacterWithMarks(char, marks) {
33
+ if (marks.length === 0) {
34
+ // Fast path: unmarked text keeps the numeric char code (unchanged behaviour).
35
+ return char;
36
+ }
37
+ var markKey = marks.map(function (mark) {
38
+ var _mark$attrs;
39
+ var attrs = (_mark$attrs = mark.attrs) !== null && _mark$attrs !== void 0 ? _mark$attrs : {};
40
+ var attrParts = Object.keys(attrs).sort().map(function (key) {
41
+ var _attrs$key;
42
+ return "".concat(key, "=").concat(JSON.stringify((_attrs$key = attrs[key]) !== null && _attrs$key !== void 0 ? _attrs$key : null));
43
+ });
44
+ return attrParts.length > 0 ? "".concat(mark.type.name, "(").concat(attrParts.join(','), ")") : mark.type.name;
45
+ }).sort().join('&');
46
+ return "".concat(char, "\0").concat(markKey);
47
+ };
48
+
27
49
  // Like the library default, but node types with a `diffableAttrs` rule fold their
28
- // attrs into the open token. Chars/node-end are unchanged, hence `string | number`.
50
+ // attrs into the open token, and text characters fold in their marks. Node-end is
51
+ // unchanged, hence `string | number`.
29
52
  var attrAwareTokenEncoder = exports.attrAwareTokenEncoder = {
30
- encodeCharacter: function encodeCharacter(char, _marks) {
31
- return char;
53
+ encodeCharacter: function encodeCharacter(char, marks) {
54
+ return encodeCharacterWithMarks(char, marks);
32
55
  },
33
56
  encodeNodeStart: function encodeNodeStart(node) {
34
57
  var _node$attrs2;
35
- var attrNames = (0, _diffableAttrs.getDiffableAttrNames)(node.type.name, (_node$attrs2 = node.attrs) !== null && _node$attrs2 !== void 0 ? _node$attrs2 : {});
58
+ // Normalise variants (e.g. `panel_c1` → `panel`) so `diffableAttrs` needs one entry.
59
+ var attrNames = (0, _diffableAttrs.getDiffableAttrNames)((0, _nodeTypeUtils.getBaseNodeTypeName)(node.type), (_node$attrs2 = node.attrs) !== null && _node$attrs2 !== void 0 ? _node$attrs2 : {});
36
60
  if (attrNames && attrNames.length > 0) {
37
61
  return encodeNodeWithAttrs(node, attrNames);
38
62
  }
@@ -322,7 +322,7 @@ var calculateDiffDecorationsInner = function calculateDiffDecorationsInner(_ref4
322
322
  var isSmartNodeLevel = diffType === 'smart' && (0, _platformFeatureFlags.fg)('platform_editor_ai_smart_diff') && (0, _helpers.smartChangeLevel)(change) === 'node';
323
323
  // For a large inserted table, skip the per-cell inline decorations that
324
324
  // freeze the browser on re-render. Only active in the AIFC experience.
325
- var useCoarseTableDecoration = isSmartNodeLevel && (0, _expValEquals.expValEquals)('platform_editor_ai_new_aifc_editor_experience', 'isEnabled', true) && isLargeInsertedTableRange(tr.doc, change.fromB, change.toB);
325
+ var useCoarseTableDecoration = isSmartNodeLevel && (0, _expValEquals.expValEquals)('platform_editor_ai_new_aifc_editor_experience', 'isBackendReviewMomentEnabled', true) && isLargeInsertedTableRange(tr.doc, change.fromB, change.toB);
326
326
  // Whether the deleted-content widget will actually be rendered for this
327
327
  // change. Used to decide if indicator anchor positions should be adjusted
328
328
  // inward — when the widget is present the anchor must stay at the block
@@ -480,6 +480,34 @@ var calculateDiffDecorationsInner = function calculateDiffDecorationsInner(_ref4
480
480
  intl: intl,
481
481
  showIndicators: showIndicators
482
482
  })));
483
+ // If the original node position is known (e.g. a panel type change), also render
484
+ // the old block node as a "deleted" widget so the reviewer sees the before/after.
485
+ var isSmartNodeLevelAttrChange = diffType === 'smart' && (0, _platformFeatureFlags.fg)('platform_editor_ai_smart_diff') && (0, _expValEquals.expValEquals)('platform_editor_ai_new_aifc_editor_experience', 'isEnabled', true);
486
+ if (isSmartNodeLevelAttrChange && change.fromA !== undefined && change.toA !== undefined) {
487
+ var placeBelow = deletedDiffPlacement === 'bottom';
488
+ decorations.push.apply(decorations, (0, _toConsumableArray2.default)((0, _createNodeChangedDecorationWidget.createNodeChangedDecorationWidget)(_objectSpread(_objectSpread({
489
+ change: {
490
+ fromA: change.fromA,
491
+ toA: change.toA,
492
+ fromB: change.fromB,
493
+ toB: change.toB,
494
+ deleted: []
495
+ },
496
+ doc: originalDoc,
497
+ nodeViewSerializer: nodeViewSerializer,
498
+ colorScheme: colorScheme,
499
+ newDoc: tr.doc,
500
+ intl: intl,
501
+ activeIndexPos: activeIndexPos,
502
+ isInserted: false
503
+ }, (0, _isExtendedEnabled.isExtendedEnabled)(diffType) && {
504
+ diffType: diffType,
505
+ hideAddedDiffsUnderline: hideAddedDiffsUnderline,
506
+ placeBelow: placeBelow
507
+ }), {}, {
508
+ showIndicators: showIndicators
509
+ }))));
510
+ }
483
511
  }
484
512
  });
485
513
  var decorationSet = _view.DecorationSet.empty.add(tr.doc, decorations);
@@ -542,6 +542,14 @@ var classifyContainer = function classifyContainer(blockA, blockB, changes, orig
542
542
  if (typeName === TABLE_TYPE) {
543
543
  return classifyTable(blockA, blockB, changes, originalDoc, newDoc, locale, thresholds);
544
544
  }
545
+
546
+ // Container's own markup changed (same type, differing attrs) — e.g. a panel type
547
+ // change. It touches no inner child, so the child-density check below would drop it.
548
+ // Promote the whole container to a before/after change. (Table cells take the
549
+ // `classifyChild` path, not this one.)
550
+ if (!blockA.node.sameMarkup(blockB.node)) {
551
+ return [wholeBlockChange(blockA, blockB, changes)];
552
+ }
545
553
  var childrenB = childRefs(blockB);
546
554
  var childrenA = childRefs(blockA);
547
555
  var changed = changedChildIndices(blockB, childrenB, changes);
@@ -65,6 +65,7 @@ var extractChangedRows = function extractChangedRows(_ref) {
65
65
  // Find which rows were changed by analyzing the change range
66
66
  var changeStartInTable = change.fromA - tableOld.pos - 1;
67
67
  var changeEndInTable = change.toA - tableOld.pos - 1;
68
+ var edgeAttrsByOffset = (0, _isExtendedEnabled.isExtendedEnabled)(diffType) && (0, _expValEquals.expValEquals)('platform_editor_table_q4_loveability', 'isEnabled', true) && (0, _expValEquals.expValEquals)('platform_editor_table_diff_rounded_corners', 'isEnabled', true) ? (0, _tableCellEdgeAttrs.getTableTopAndBottomCellEdgeAttrs)(oldTableMap) : undefined;
68
69
  var currentOffset = 0;
69
70
  var rowIndex = 0;
70
71
  tableOld.node.content.forEach(function (rowNode) {
@@ -74,10 +75,10 @@ var extractChangedRows = function extractChangedRows(_ref) {
74
75
  // Check if this row overlaps with the deletion range
75
76
  var rowOverlapsChange = rowStart >= changeStartInTable && rowStart < changeEndInTable || rowEnd > changeStartInTable && rowEnd <= changeEndInTable || rowStart < changeStartInTable && rowEnd > changeEndInTable;
76
77
  if (rowOverlapsChange && rowNode.type.name === 'tableRow' && ((0, _isExtendedEnabled.isExtendedEnabled)(diffType) || !isEmptyRow(rowNode))) {
77
- var cellEdgeAttrs = (0, _isExtendedEnabled.isExtendedEnabled)(diffType) && (0, _expValEquals.expValEquals)('platform_editor_table_q4_loveability', 'isEnabled', true) && (0, _expValEquals.expValEquals)('platform_editor_table_diff_rounded_corners', 'isEnabled', true) ? (0, _tableCellEdgeAttrs.getRowCellEdgeAttrs)({
78
+ var cellEdgeAttrs = edgeAttrsByOffset ? (0, _tableCellEdgeAttrs.getRowCellEdgeAttrs)({
79
+ edgeAttrsByOffset: edgeAttrsByOffset,
78
80
  rowNode: rowNode,
79
- rowStart: rowStart,
80
- tableMap: oldTableMap
81
+ rowStart: rowStart
81
82
  }) : undefined;
82
83
  var startOfRow = newTableMap.mapByRow.slice().reverse().find(function (row) {
83
84
  return row[0] + tableNew.pos < change.fromB && change.fromB < row[row.length - 1] + tableNew.pos;
@@ -38,6 +38,11 @@ var DIFFABLE_ATTRS_BY_NODE_TYPE = exports.DIFFABLE_ATTRS_BY_NODE_TYPE = {
38
38
  media: {
39
39
  include: ['id', 'collection', 'url']
40
40
  },
41
+ // Panel: type/colour/icon change is formatting-only but reviewable. Keyed by base
42
+ // node type — the encoder normalises variants (e.g. `panel_c1`) to `panel` first.
43
+ panel: {
44
+ include: ['panelType', 'panelColor', 'panelIcon', 'panelIconId', 'panelIconText']
45
+ },
41
46
  // Extensions: any attribute change is meaningful except localId.
42
47
  extension: {
43
48
  exclude: ['localId']
@@ -8,6 +8,7 @@ exports.stepIsValidAttrChange = exports.getAttrChangeRanges = void 0;
8
8
  var _defineProperty2 = _interopRequireDefault(require("@babel/runtime/helpers/defineProperty"));
9
9
  var _steps = require("@atlaskit/adf-schema/steps");
10
10
  var _deprecatedPlatformFeatureExperiments = require("@atlaskit/editor-common/deprecated-platform-feature-experiments");
11
+ var _nodeTypeUtils = require("@atlaskit/editor-common/utils/node-type-utils");
11
12
  var _transform = require("@atlaskit/editor-prosemirror/transform");
12
13
  var _expValEquals = require("@atlaskit/tmp-editor-statsig/exp-val-equals");
13
14
  var _diffableAttrs = require("./diffableAttrs");
@@ -21,6 +22,7 @@ var filterUndefined = function filterUndefined(x) {
21
22
  var mediaAttrs = (0, _diffableAttrs.getRequiredIncludedDiffableAttrs)('media');
22
23
  var dateAttrs = (0, _diffableAttrs.getRequiredIncludedDiffableAttrs)('date');
23
24
  var taskItemAttrs = (0, _diffableAttrs.getRequiredIncludedDiffableAttrs)('taskItem');
25
+ var panelAttrs = (0, _diffableAttrs.getRequiredIncludedDiffableAttrs)('panel');
24
26
  var emojiAttrs = (0, _diffableAttrs.getRequiredIncludedDiffableAttrs)('emoji');
25
27
  var mentionAttrs = (0, _diffableAttrs.getRequiredIncludedDiffableAttrs)('mention');
26
28
  var statusAttrs = (0, _diffableAttrs.getRequiredIncludedDiffableAttrs)('status');
@@ -107,6 +109,23 @@ var getAttrChangeRanges = exports.getAttrChangeRanges = function getAttrChangeRa
107
109
  };
108
110
  }
109
111
 
112
+ // panel node (incl. variants like panel_c1): type/colour/icon attribute change
113
+ // (e.g. note -> warning) — highlight the new panel and, when the original node
114
+ // is resolvable, expose its range (fromA/toA) so the caller can render the old
115
+ // panel as a "deleted" widget for a before/after comparison.
116
+ if (stepAttrs.some(function (v) {
117
+ return panelAttrs.includes(v);
118
+ }) && nodeAtPos && (0, _nodeTypeUtils.getBaseNodeTypeName)(nodeAtPos.type) === 'panel') {
119
+ var _originalNodeAtPos = originalDoc === null || originalDoc === void 0 ? void 0 : originalDoc.nodeAt(step.pos);
120
+ return _objectSpread({
121
+ fromB: step.pos,
122
+ toB: step.pos + nodeAtPos.nodeSize
123
+ }, _originalNodeAtPos && {
124
+ fromA: step.pos,
125
+ toA: step.pos + _originalNodeAtPos.nodeSize
126
+ });
127
+ }
128
+
110
129
  // extension nodes: highlight on any diffable attr change (exclude rule in shared map)
111
130
  if (nodeAtPos && extensionNodeNames.includes(nodeAtPos.type.name) && stepAttrs.some(function (v) {
112
131
  return (0, _diffableAttrs.isDiffableAttr)(nodeAtPos.type.name, v);
@@ -3,24 +3,46 @@
3
3
  Object.defineProperty(exports, "__esModule", {
4
4
  value: true
5
5
  });
6
- exports.getRowCellEdgeAttrs = exports.applyTableCellEdgeAttrs = exports.applyCellEdgeAttrs = void 0;
6
+ exports.getTableTopAndBottomCellEdgeAttrs = exports.getRowCellEdgeAttrs = exports.applyTableCellEdgeAttrs = exports.applyCellEdgeAttrs = void 0;
7
7
  var _tableMap = require("@atlaskit/editor-tables/table-map");
8
+ var getTableTopAndBottomCellEdgeAttrs = exports.getTableTopAndBottomCellEdgeAttrs = function getTableTopAndBottomCellEdgeAttrs(tableMap) {
9
+ var edgeAttrsByOffset = new Map();
10
+ var addLogicalRow = function addLogicalRow(offsets, edge) {
11
+ offsets.forEach(function (offset, columnIndex) {
12
+ var _edgeAttrsByOffset$ge;
13
+ var edgeAttrs = (_edgeAttrsByOffset$ge = edgeAttrsByOffset.get(offset)) !== null && _edgeAttrsByOffset$ge !== void 0 ? _edgeAttrsByOffset$ge : {
14
+ reachesBottom: false,
15
+ reachesLeft: false,
16
+ reachesRight: false,
17
+ reachesTop: false
18
+ };
19
+ edgeAttrs.reachesBottom || (edgeAttrs.reachesBottom = edge === 'bottom');
20
+ edgeAttrs.reachesLeft || (edgeAttrs.reachesLeft = columnIndex === 0);
21
+ edgeAttrs.reachesRight || (edgeAttrs.reachesRight = columnIndex === tableMap.width - 1);
22
+ edgeAttrs.reachesTop || (edgeAttrs.reachesTop = edge === 'top');
23
+ edgeAttrsByOffset.set(offset, edgeAttrs);
24
+ });
25
+ };
26
+ var topRow = tableMap.mapByRow[0];
27
+ var bottomRow = tableMap.mapByRow[tableMap.height - 1];
28
+ if (topRow) {
29
+ addLogicalRow(topRow, 'top');
30
+ }
31
+ if (bottomRow) {
32
+ addLogicalRow(bottomRow, 'bottom');
33
+ }
34
+ return edgeAttrsByOffset;
35
+ };
8
36
  var getRowCellEdgeAttrs = exports.getRowCellEdgeAttrs = function getRowCellEdgeAttrs(_ref) {
9
- var rowNode = _ref.rowNode,
10
- rowStart = _ref.rowStart,
11
- tableMap = _ref.tableMap;
37
+ var edgeAttrsByOffset = _ref.edgeAttrsByOffset,
38
+ rowNode = _ref.rowNode,
39
+ rowStart = _ref.rowStart;
12
40
  var cellEdgeAttrs = [];
13
41
  rowNode.content.forEach(function (cellNode, cellOffset) {
14
42
  if (cellNode.type.name !== 'tableCell' && cellNode.type.name !== 'tableHeader') {
15
43
  return;
16
44
  }
17
- var cellRect = tableMap.findCell(rowStart + 1 + cellOffset);
18
- cellEdgeAttrs.push({
19
- reachesBottom: cellRect.bottom >= tableMap.height,
20
- reachesLeft: cellRect.left === 0,
21
- reachesRight: cellRect.right >= tableMap.width,
22
- reachesTop: cellRect.top === 0
23
- });
45
+ cellEdgeAttrs.push(edgeAttrsByOffset.get(rowStart + 1 + cellOffset));
24
46
  });
25
47
  return cellEdgeAttrs;
26
48
  };
@@ -53,20 +75,25 @@ var applyTableCellEdgeAttrs = exports.applyTableCellEdgeAttrs = function applyTa
53
75
  var cells = Array.from(table.rows).flatMap(function (row) {
54
76
  return Array.from(row.cells);
55
77
  });
78
+ var cellsByOffset = new Map();
56
79
  var cellIndex = 0;
57
80
  tableNode.content.forEach(function (rowNode, rowStart) {
58
- getRowCellEdgeAttrs({
59
- rowNode: rowNode,
60
- rowStart: rowStart,
61
- tableMap: tableMap
62
- }).forEach(function (edgeAttrs) {
63
- var cell = cells[cellIndex];
64
- cellIndex++;
65
- if (cell) {
66
- applyCellEdgeAttrs(cell, edgeAttrs);
81
+ rowNode.content.forEach(function (cellNode, cellOffset) {
82
+ if (cellNode.type.name === 'tableCell' || cellNode.type.name === 'tableHeader') {
83
+ var cell = cells[cellIndex];
84
+ cellIndex++;
85
+ if (cell) {
86
+ cellsByOffset.set(rowStart + 1 + cellOffset, cell);
87
+ }
67
88
  }
68
89
  });
69
90
  });
91
+ getTableTopAndBottomCellEdgeAttrs(tableMap).forEach(function (edgeAttrs, offset) {
92
+ var cell = cellsByOffset.get(offset);
93
+ if (cell) {
94
+ applyCellEdgeAttrs(cell, edgeAttrs);
95
+ }
96
+ });
70
97
  } catch (_unused) {
71
98
  // Table structure can be transient while widget DOM is being assembled.
72
99
  }
@@ -1,3 +1,4 @@
1
+ import { getBaseNodeTypeName } from '@atlaskit/editor-common/utils/node-type-utils';
1
2
  import { getDiffableAttrNames } from '../decorations/utils/diffableAttrs';
2
3
 
3
4
  /**
@@ -19,13 +20,36 @@ const encodeNodeWithAttrs = (node, attrNames) => {
19
20
  return `${node.type.name}|${parts.join('|')}`;
20
21
  };
21
22
 
23
+ // Fold a character's marks (type + attrs) into its token so a formatting-only change
24
+ // (bold/italic/link/colour, or a link whose `href` changes) registers as a real diff.
25
+ // The default encoder returns just the char code, so marked and unmarked text tokenise
26
+ // identically. Marks and their attrs are sorted so the token is order-independent.
27
+ const encodeCharacterWithMarks = (char, marks) => {
28
+ if (marks.length === 0) {
29
+ // Fast path: unmarked text keeps the numeric char code (unchanged behaviour).
30
+ return char;
31
+ }
32
+ const markKey = marks.map(mark => {
33
+ var _mark$attrs;
34
+ const attrs = (_mark$attrs = mark.attrs) !== null && _mark$attrs !== void 0 ? _mark$attrs : {};
35
+ const attrParts = Object.keys(attrs).sort().map(key => {
36
+ var _attrs$key;
37
+ return `${key}=${JSON.stringify((_attrs$key = attrs[key]) !== null && _attrs$key !== void 0 ? _attrs$key : null)}`;
38
+ });
39
+ return attrParts.length > 0 ? `${mark.type.name}(${attrParts.join(',')})` : mark.type.name;
40
+ }).sort().join('&');
41
+ return `${char}\u0000${markKey}`;
42
+ };
43
+
22
44
  // Like the library default, but node types with a `diffableAttrs` rule fold their
23
- // attrs into the open token. Chars/node-end are unchanged, hence `string | number`.
45
+ // attrs into the open token, and text characters fold in their marks. Node-end is
46
+ // unchanged, hence `string | number`.
24
47
  export const attrAwareTokenEncoder = {
25
- encodeCharacter: (char, _marks) => char,
48
+ encodeCharacter: (char, marks) => encodeCharacterWithMarks(char, marks),
26
49
  encodeNodeStart: node => {
27
50
  var _node$attrs2;
28
- const attrNames = getDiffableAttrNames(node.type.name, (_node$attrs2 = node.attrs) !== null && _node$attrs2 !== void 0 ? _node$attrs2 : {});
51
+ // Normalise variants (e.g. `panel_c1` → `panel`) so `diffableAttrs` needs one entry.
52
+ const attrNames = getDiffableAttrNames(getBaseNodeTypeName(node.type), (_node$attrs2 = node.attrs) !== null && _node$attrs2 !== void 0 ? _node$attrs2 : {});
29
53
  if (attrNames && attrNames.length > 0) {
30
54
  return encodeNodeWithAttrs(node, attrNames);
31
55
  }
@@ -295,7 +295,7 @@ const calculateDiffDecorationsInner = ({
295
295
  const isSmartNodeLevel = diffType === 'smart' && fg('platform_editor_ai_smart_diff') && smartChangeLevel(change) === 'node';
296
296
  // For a large inserted table, skip the per-cell inline decorations that
297
297
  // freeze the browser on re-render. Only active in the AIFC experience.
298
- const useCoarseTableDecoration = isSmartNodeLevel && expValEquals('platform_editor_ai_new_aifc_editor_experience', 'isEnabled', true) && isLargeInsertedTableRange(tr.doc, change.fromB, change.toB);
298
+ const useCoarseTableDecoration = isSmartNodeLevel && expValEquals('platform_editor_ai_new_aifc_editor_experience', 'isBackendReviewMomentEnabled', true) && isLargeInsertedTableRange(tr.doc, change.fromB, change.toB);
299
299
  // Whether the deleted-content widget will actually be rendered for this
300
300
  // change. Used to decide if indicator anchor positions should be adjusted
301
301
  // inward — when the widget is present the anchor must stay at the block
@@ -440,6 +440,34 @@ const calculateDiffDecorationsInner = ({
440
440
  intl,
441
441
  showIndicators
442
442
  }));
443
+ // If the original node position is known (e.g. a panel type change), also render
444
+ // the old block node as a "deleted" widget so the reviewer sees the before/after.
445
+ const isSmartNodeLevelAttrChange = diffType === 'smart' && fg('platform_editor_ai_smart_diff') && expValEquals('platform_editor_ai_new_aifc_editor_experience', 'isEnabled', true);
446
+ if (isSmartNodeLevelAttrChange && change.fromA !== undefined && change.toA !== undefined) {
447
+ const placeBelow = deletedDiffPlacement === 'bottom';
448
+ decorations.push(...createNodeChangedDecorationWidget({
449
+ change: {
450
+ fromA: change.fromA,
451
+ toA: change.toA,
452
+ fromB: change.fromB,
453
+ toB: change.toB,
454
+ deleted: []
455
+ },
456
+ doc: originalDoc,
457
+ nodeViewSerializer,
458
+ colorScheme,
459
+ newDoc: tr.doc,
460
+ intl,
461
+ activeIndexPos,
462
+ isInserted: false,
463
+ ...(isExtendedEnabled(diffType) && {
464
+ diffType,
465
+ hideAddedDiffsUnderline,
466
+ placeBelow
467
+ }),
468
+ showIndicators
469
+ }));
470
+ }
443
471
  }
444
472
  });
445
473
  const decorationSet = DecorationSet.empty.add(tr.doc, decorations);
@@ -462,6 +462,14 @@ const classifyContainer = (blockA, blockB, changes, originalDoc, newDoc, locale,
462
462
  if (typeName === TABLE_TYPE) {
463
463
  return classifyTable(blockA, blockB, changes, originalDoc, newDoc, locale, thresholds);
464
464
  }
465
+
466
+ // Container's own markup changed (same type, differing attrs) — e.g. a panel type
467
+ // change. It touches no inner child, so the child-density check below would drop it.
468
+ // Promote the whole container to a before/after change. (Table cells take the
469
+ // `classifyChild` path, not this one.)
470
+ if (!blockA.node.sameMarkup(blockB.node)) {
471
+ return [wholeBlockChange(blockA, blockB, changes)];
472
+ }
465
473
  const childrenB = childRefs(blockB);
466
474
  const childrenA = childRefs(blockA);
467
475
  const changed = changedChildIndices(blockB, childrenB, changes);
@@ -10,7 +10,7 @@ import { colorSchemeRegistry } from './colorSchemes/schemes';
10
10
  import { resolveCellOverlayStyleLegacy, resolveDeletedRowStyleLegacy } from './createChangedRowDecorationWidgets.styles.legacy';
11
11
  import { buildDiffDecorationSpec } from './decorationKeys';
12
12
  import { findSafeInsertPos } from './utils/findSafeInsertPos';
13
- import { applyCellEdgeAttrs, getRowCellEdgeAttrs } from './utils/tableCellEdgeAttrs';
13
+ import { applyCellEdgeAttrs, getRowCellEdgeAttrs, getTableTopAndBottomCellEdgeAttrs } from './utils/tableCellEdgeAttrs';
14
14
  const DEFAULT_COLOR_SCHEME = 'standard';
15
15
  /**
16
16
  * Extracts information about deleted table rows from a change
@@ -48,6 +48,7 @@ const extractChangedRows = ({
48
48
  // Find which rows were changed by analyzing the change range
49
49
  const changeStartInTable = change.fromA - tableOld.pos - 1;
50
50
  const changeEndInTable = change.toA - tableOld.pos - 1;
51
+ const edgeAttrsByOffset = isExtendedEnabled(diffType) && expValEquals('platform_editor_table_q4_loveability', 'isEnabled', true) && expValEquals('platform_editor_table_diff_rounded_corners', 'isEnabled', true) ? getTableTopAndBottomCellEdgeAttrs(oldTableMap) : undefined;
51
52
  let currentOffset = 0;
52
53
  let rowIndex = 0;
53
54
  tableOld.node.content.forEach(rowNode => {
@@ -57,10 +58,10 @@ const extractChangedRows = ({
57
58
  // Check if this row overlaps with the deletion range
58
59
  const rowOverlapsChange = rowStart >= changeStartInTable && rowStart < changeEndInTable || rowEnd > changeStartInTable && rowEnd <= changeEndInTable || rowStart < changeStartInTable && rowEnd > changeEndInTable;
59
60
  if (rowOverlapsChange && rowNode.type.name === 'tableRow' && (isExtendedEnabled(diffType) || !isEmptyRow(rowNode))) {
60
- const cellEdgeAttrs = isExtendedEnabled(diffType) && expValEquals('platform_editor_table_q4_loveability', 'isEnabled', true) && expValEquals('platform_editor_table_diff_rounded_corners', 'isEnabled', true) ? getRowCellEdgeAttrs({
61
+ const cellEdgeAttrs = edgeAttrsByOffset ? getRowCellEdgeAttrs({
62
+ edgeAttrsByOffset,
61
63
  rowNode,
62
- rowStart,
63
- tableMap: oldTableMap
64
+ rowStart
64
65
  }) : undefined;
65
66
  const startOfRow = newTableMap.mapByRow.slice().reverse().find(row => row[0] + tableNew.pos < change.fromB && change.fromB < row[row.length - 1] + tableNew.pos);
66
67
  changedRows.push({
@@ -32,6 +32,11 @@ export const DIFFABLE_ATTRS_BY_NODE_TYPE = {
32
32
  media: {
33
33
  include: ['id', 'collection', 'url']
34
34
  },
35
+ // Panel: type/colour/icon change is formatting-only but reviewable. Keyed by base
36
+ // node type — the encoder normalises variants (e.g. `panel_c1`) to `panel` first.
37
+ panel: {
38
+ include: ['panelType', 'panelColor', 'panelIcon', 'panelIconId', 'panelIconText']
39
+ },
35
40
  // Extensions: any attribute change is meaningful except localId.
36
41
  extension: {
37
42
  exclude: ['localId']
@@ -1,5 +1,6 @@
1
1
  import { SetAttrsStep } from '@atlaskit/adf-schema/steps';
2
2
  import { isExperimentEnabled } from '@atlaskit/editor-common/deprecated-platform-feature-experiments';
3
+ import { getBaseNodeTypeName } from '@atlaskit/editor-common/utils/node-type-utils';
3
4
  import { AttrStep } from '@atlaskit/editor-prosemirror/transform';
4
5
  import { expValEquals } from '@atlaskit/tmp-editor-statsig/exp-val-equals';
5
6
  import { getRequiredIncludedDiffableAttrs, isDiffableAttr } from './diffableAttrs';
@@ -9,6 +10,7 @@ const filterUndefined = x => !!x;
9
10
  const mediaAttrs = getRequiredIncludedDiffableAttrs('media');
10
11
  const dateAttrs = getRequiredIncludedDiffableAttrs('date');
11
12
  const taskItemAttrs = getRequiredIncludedDiffableAttrs('taskItem');
13
+ const panelAttrs = getRequiredIncludedDiffableAttrs('panel');
12
14
  const emojiAttrs = getRequiredIncludedDiffableAttrs('emoji');
13
15
  const mentionAttrs = getRequiredIncludedDiffableAttrs('mention');
14
16
  const statusAttrs = getRequiredIncludedDiffableAttrs('status');
@@ -84,6 +86,22 @@ export const getAttrChangeRanges = (doc, steps, originalDoc) => {
84
86
  };
85
87
  }
86
88
 
89
+ // panel node (incl. variants like panel_c1): type/colour/icon attribute change
90
+ // (e.g. note -> warning) — highlight the new panel and, when the original node
91
+ // is resolvable, expose its range (fromA/toA) so the caller can render the old
92
+ // panel as a "deleted" widget for a before/after comparison.
93
+ if (stepAttrs.some(v => panelAttrs.includes(v)) && nodeAtPos && getBaseNodeTypeName(nodeAtPos.type) === 'panel') {
94
+ const originalNodeAtPos = originalDoc === null || originalDoc === void 0 ? void 0 : originalDoc.nodeAt(step.pos);
95
+ return {
96
+ fromB: step.pos,
97
+ toB: step.pos + nodeAtPos.nodeSize,
98
+ ...(originalNodeAtPos && {
99
+ fromA: step.pos,
100
+ toA: step.pos + originalNodeAtPos.nodeSize
101
+ })
102
+ };
103
+ }
104
+
87
105
  // extension nodes: highlight on any diffable attr change (exclude rule in shared map)
88
106
  if (nodeAtPos && extensionNodeNames.includes(nodeAtPos.type.name) && stepAttrs.some(v => isDiffableAttr(nodeAtPos.type.name, v))) {
89
107
  const isInline = nodeAtPos.type.name === 'inlineExtension';
@@ -1,21 +1,43 @@
1
1
  import { TableMap } from '@atlaskit/editor-tables/table-map';
2
+ export const getTableTopAndBottomCellEdgeAttrs = tableMap => {
3
+ const edgeAttrsByOffset = new Map();
4
+ const addLogicalRow = (offsets, edge) => {
5
+ offsets.forEach((offset, columnIndex) => {
6
+ var _edgeAttrsByOffset$ge;
7
+ const edgeAttrs = (_edgeAttrsByOffset$ge = edgeAttrsByOffset.get(offset)) !== null && _edgeAttrsByOffset$ge !== void 0 ? _edgeAttrsByOffset$ge : {
8
+ reachesBottom: false,
9
+ reachesLeft: false,
10
+ reachesRight: false,
11
+ reachesTop: false
12
+ };
13
+ edgeAttrs.reachesBottom ||= edge === 'bottom';
14
+ edgeAttrs.reachesLeft ||= columnIndex === 0;
15
+ edgeAttrs.reachesRight ||= columnIndex === tableMap.width - 1;
16
+ edgeAttrs.reachesTop ||= edge === 'top';
17
+ edgeAttrsByOffset.set(offset, edgeAttrs);
18
+ });
19
+ };
20
+ const topRow = tableMap.mapByRow[0];
21
+ const bottomRow = tableMap.mapByRow[tableMap.height - 1];
22
+ if (topRow) {
23
+ addLogicalRow(topRow, 'top');
24
+ }
25
+ if (bottomRow) {
26
+ addLogicalRow(bottomRow, 'bottom');
27
+ }
28
+ return edgeAttrsByOffset;
29
+ };
2
30
  export const getRowCellEdgeAttrs = ({
31
+ edgeAttrsByOffset,
3
32
  rowNode,
4
- rowStart,
5
- tableMap
33
+ rowStart
6
34
  }) => {
7
35
  const cellEdgeAttrs = [];
8
36
  rowNode.content.forEach((cellNode, cellOffset) => {
9
37
  if (cellNode.type.name !== 'tableCell' && cellNode.type.name !== 'tableHeader') {
10
38
  return;
11
39
  }
12
- const cellRect = tableMap.findCell(rowStart + 1 + cellOffset);
13
- cellEdgeAttrs.push({
14
- reachesBottom: cellRect.bottom >= tableMap.height,
15
- reachesLeft: cellRect.left === 0,
16
- reachesRight: cellRect.right >= tableMap.width,
17
- reachesTop: cellRect.top === 0
18
- });
40
+ cellEdgeAttrs.push(edgeAttrsByOffset.get(rowStart + 1 + cellOffset));
19
41
  });
20
42
  return cellEdgeAttrs;
21
43
  };
@@ -47,20 +69,25 @@ export const applyTableCellEdgeAttrs = ({
47
69
  }
48
70
  const tableMap = TableMap.get(tableNode);
49
71
  const cells = Array.from(table.rows).flatMap(row => Array.from(row.cells));
72
+ const cellsByOffset = new Map();
50
73
  let cellIndex = 0;
51
74
  tableNode.content.forEach((rowNode, rowStart) => {
52
- getRowCellEdgeAttrs({
53
- rowNode,
54
- rowStart,
55
- tableMap
56
- }).forEach(edgeAttrs => {
57
- const cell = cells[cellIndex];
58
- cellIndex++;
59
- if (cell) {
60
- applyCellEdgeAttrs(cell, edgeAttrs);
75
+ rowNode.content.forEach((cellNode, cellOffset) => {
76
+ if (cellNode.type.name === 'tableCell' || cellNode.type.name === 'tableHeader') {
77
+ const cell = cells[cellIndex];
78
+ cellIndex++;
79
+ if (cell) {
80
+ cellsByOffset.set(rowStart + 1 + cellOffset, cell);
81
+ }
61
82
  }
62
83
  });
63
84
  });
85
+ getTableTopAndBottomCellEdgeAttrs(tableMap).forEach((edgeAttrs, offset) => {
86
+ const cell = cellsByOffset.get(offset);
87
+ if (cell) {
88
+ applyCellEdgeAttrs(cell, edgeAttrs);
89
+ }
90
+ });
64
91
  } catch {
65
92
  // Table structure can be transient while widget DOM is being assembled.
66
93
  }
@@ -1,3 +1,4 @@
1
+ import { getBaseNodeTypeName } from '@atlaskit/editor-common/utils/node-type-utils';
1
2
  import { getDiffableAttrNames } from '../decorations/utils/diffableAttrs';
2
3
 
3
4
  /**
@@ -19,15 +20,38 @@ var encodeNodeWithAttrs = function encodeNodeWithAttrs(node, attrNames) {
19
20
  return "".concat(node.type.name, "|").concat(parts.join('|'));
20
21
  };
21
22
 
23
+ // Fold a character's marks (type + attrs) into its token so a formatting-only change
24
+ // (bold/italic/link/colour, or a link whose `href` changes) registers as a real diff.
25
+ // The default encoder returns just the char code, so marked and unmarked text tokenise
26
+ // identically. Marks and their attrs are sorted so the token is order-independent.
27
+ var encodeCharacterWithMarks = function encodeCharacterWithMarks(char, marks) {
28
+ if (marks.length === 0) {
29
+ // Fast path: unmarked text keeps the numeric char code (unchanged behaviour).
30
+ return char;
31
+ }
32
+ var markKey = marks.map(function (mark) {
33
+ var _mark$attrs;
34
+ var attrs = (_mark$attrs = mark.attrs) !== null && _mark$attrs !== void 0 ? _mark$attrs : {};
35
+ var attrParts = Object.keys(attrs).sort().map(function (key) {
36
+ var _attrs$key;
37
+ return "".concat(key, "=").concat(JSON.stringify((_attrs$key = attrs[key]) !== null && _attrs$key !== void 0 ? _attrs$key : null));
38
+ });
39
+ return attrParts.length > 0 ? "".concat(mark.type.name, "(").concat(attrParts.join(','), ")") : mark.type.name;
40
+ }).sort().join('&');
41
+ return "".concat(char, "\0").concat(markKey);
42
+ };
43
+
22
44
  // Like the library default, but node types with a `diffableAttrs` rule fold their
23
- // attrs into the open token. Chars/node-end are unchanged, hence `string | number`.
45
+ // attrs into the open token, and text characters fold in their marks. Node-end is
46
+ // unchanged, hence `string | number`.
24
47
  export var attrAwareTokenEncoder = {
25
- encodeCharacter: function encodeCharacter(char, _marks) {
26
- return char;
48
+ encodeCharacter: function encodeCharacter(char, marks) {
49
+ return encodeCharacterWithMarks(char, marks);
27
50
  },
28
51
  encodeNodeStart: function encodeNodeStart(node) {
29
52
  var _node$attrs2;
30
- var attrNames = getDiffableAttrNames(node.type.name, (_node$attrs2 = node.attrs) !== null && _node$attrs2 !== void 0 ? _node$attrs2 : {});
53
+ // Normalise variants (e.g. `panel_c1` → `panel`) so `diffableAttrs` needs one entry.
54
+ var attrNames = getDiffableAttrNames(getBaseNodeTypeName(node.type), (_node$attrs2 = node.attrs) !== null && _node$attrs2 !== void 0 ? _node$attrs2 : {});
31
55
  if (attrNames && attrNames.length > 0) {
32
56
  return encodeNodeWithAttrs(node, attrNames);
33
57
  }
@@ -315,7 +315,7 @@ var calculateDiffDecorationsInner = function calculateDiffDecorationsInner(_ref4
315
315
  var isSmartNodeLevel = diffType === 'smart' && fg('platform_editor_ai_smart_diff') && smartChangeLevel(change) === 'node';
316
316
  // For a large inserted table, skip the per-cell inline decorations that
317
317
  // freeze the browser on re-render. Only active in the AIFC experience.
318
- var useCoarseTableDecoration = isSmartNodeLevel && expValEquals('platform_editor_ai_new_aifc_editor_experience', 'isEnabled', true) && isLargeInsertedTableRange(tr.doc, change.fromB, change.toB);
318
+ var useCoarseTableDecoration = isSmartNodeLevel && expValEquals('platform_editor_ai_new_aifc_editor_experience', 'isBackendReviewMomentEnabled', true) && isLargeInsertedTableRange(tr.doc, change.fromB, change.toB);
319
319
  // Whether the deleted-content widget will actually be rendered for this
320
320
  // change. Used to decide if indicator anchor positions should be adjusted
321
321
  // inward — when the widget is present the anchor must stay at the block
@@ -473,6 +473,34 @@ var calculateDiffDecorationsInner = function calculateDiffDecorationsInner(_ref4
473
473
  intl: intl,
474
474
  showIndicators: showIndicators
475
475
  })));
476
+ // If the original node position is known (e.g. a panel type change), also render
477
+ // the old block node as a "deleted" widget so the reviewer sees the before/after.
478
+ var isSmartNodeLevelAttrChange = diffType === 'smart' && fg('platform_editor_ai_smart_diff') && expValEquals('platform_editor_ai_new_aifc_editor_experience', 'isEnabled', true);
479
+ if (isSmartNodeLevelAttrChange && change.fromA !== undefined && change.toA !== undefined) {
480
+ var placeBelow = deletedDiffPlacement === 'bottom';
481
+ decorations.push.apply(decorations, _toConsumableArray(createNodeChangedDecorationWidget(_objectSpread(_objectSpread({
482
+ change: {
483
+ fromA: change.fromA,
484
+ toA: change.toA,
485
+ fromB: change.fromB,
486
+ toB: change.toB,
487
+ deleted: []
488
+ },
489
+ doc: originalDoc,
490
+ nodeViewSerializer: nodeViewSerializer,
491
+ colorScheme: colorScheme,
492
+ newDoc: tr.doc,
493
+ intl: intl,
494
+ activeIndexPos: activeIndexPos,
495
+ isInserted: false
496
+ }, isExtendedEnabled(diffType) && {
497
+ diffType: diffType,
498
+ hideAddedDiffsUnderline: hideAddedDiffsUnderline,
499
+ placeBelow: placeBelow
500
+ }), {}, {
501
+ showIndicators: showIndicators
502
+ }))));
503
+ }
476
504
  }
477
505
  });
478
506
  var decorationSet = DecorationSet.empty.add(tr.doc, decorations);
@@ -535,6 +535,14 @@ var classifyContainer = function classifyContainer(blockA, blockB, changes, orig
535
535
  if (typeName === TABLE_TYPE) {
536
536
  return classifyTable(blockA, blockB, changes, originalDoc, newDoc, locale, thresholds);
537
537
  }
538
+
539
+ // Container's own markup changed (same type, differing attrs) — e.g. a panel type
540
+ // change. It touches no inner child, so the child-density check below would drop it.
541
+ // Promote the whole container to a before/after change. (Table cells take the
542
+ // `classifyChild` path, not this one.)
543
+ if (!blockA.node.sameMarkup(blockB.node)) {
544
+ return [wholeBlockChange(blockA, blockB, changes)];
545
+ }
538
546
  var childrenB = childRefs(blockB);
539
547
  var childrenA = childRefs(blockA);
540
548
  var changed = changedChildIndices(blockB, childrenB, changes);
@@ -17,7 +17,7 @@ import { colorSchemeRegistry } from './colorSchemes/schemes';
17
17
  import { resolveCellOverlayStyleLegacy, resolveDeletedRowStyleLegacy } from './createChangedRowDecorationWidgets.styles.legacy';
18
18
  import { buildDiffDecorationSpec } from './decorationKeys';
19
19
  import { findSafeInsertPos } from './utils/findSafeInsertPos';
20
- import { applyCellEdgeAttrs, getRowCellEdgeAttrs } from './utils/tableCellEdgeAttrs';
20
+ import { applyCellEdgeAttrs, getRowCellEdgeAttrs, getTableTopAndBottomCellEdgeAttrs } from './utils/tableCellEdgeAttrs';
21
21
  var DEFAULT_COLOR_SCHEME = 'standard';
22
22
  /**
23
23
  * Extracts information about deleted table rows from a change
@@ -58,6 +58,7 @@ var extractChangedRows = function extractChangedRows(_ref) {
58
58
  // Find which rows were changed by analyzing the change range
59
59
  var changeStartInTable = change.fromA - tableOld.pos - 1;
60
60
  var changeEndInTable = change.toA - tableOld.pos - 1;
61
+ var edgeAttrsByOffset = isExtendedEnabled(diffType) && expValEquals('platform_editor_table_q4_loveability', 'isEnabled', true) && expValEquals('platform_editor_table_diff_rounded_corners', 'isEnabled', true) ? getTableTopAndBottomCellEdgeAttrs(oldTableMap) : undefined;
61
62
  var currentOffset = 0;
62
63
  var rowIndex = 0;
63
64
  tableOld.node.content.forEach(function (rowNode) {
@@ -67,10 +68,10 @@ var extractChangedRows = function extractChangedRows(_ref) {
67
68
  // Check if this row overlaps with the deletion range
68
69
  var rowOverlapsChange = rowStart >= changeStartInTable && rowStart < changeEndInTable || rowEnd > changeStartInTable && rowEnd <= changeEndInTable || rowStart < changeStartInTable && rowEnd > changeEndInTable;
69
70
  if (rowOverlapsChange && rowNode.type.name === 'tableRow' && (isExtendedEnabled(diffType) || !isEmptyRow(rowNode))) {
70
- var cellEdgeAttrs = isExtendedEnabled(diffType) && expValEquals('platform_editor_table_q4_loveability', 'isEnabled', true) && expValEquals('platform_editor_table_diff_rounded_corners', 'isEnabled', true) ? getRowCellEdgeAttrs({
71
+ var cellEdgeAttrs = edgeAttrsByOffset ? getRowCellEdgeAttrs({
72
+ edgeAttrsByOffset: edgeAttrsByOffset,
71
73
  rowNode: rowNode,
72
- rowStart: rowStart,
73
- tableMap: oldTableMap
74
+ rowStart: rowStart
74
75
  }) : undefined;
75
76
  var startOfRow = newTableMap.mapByRow.slice().reverse().find(function (row) {
76
77
  return row[0] + tableNew.pos < change.fromB && change.fromB < row[row.length - 1] + tableNew.pos;
@@ -32,6 +32,11 @@ export var DIFFABLE_ATTRS_BY_NODE_TYPE = {
32
32
  media: {
33
33
  include: ['id', 'collection', 'url']
34
34
  },
35
+ // Panel: type/colour/icon change is formatting-only but reviewable. Keyed by base
36
+ // node type — the encoder normalises variants (e.g. `panel_c1`) to `panel` first.
37
+ panel: {
38
+ include: ['panelType', 'panelColor', 'panelIcon', 'panelIconId', 'panelIconText']
39
+ },
35
40
  // Extensions: any attribute change is meaningful except localId.
36
41
  extension: {
37
42
  exclude: ['localId']
@@ -3,6 +3,7 @@ function ownKeys(e, r) { var t = Object.keys(e); if (Object.getOwnPropertySymbol
3
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
4
  import { SetAttrsStep } from '@atlaskit/adf-schema/steps';
5
5
  import { isExperimentEnabled } from '@atlaskit/editor-common/deprecated-platform-feature-experiments';
6
+ import { getBaseNodeTypeName } from '@atlaskit/editor-common/utils/node-type-utils';
6
7
  import { AttrStep } from '@atlaskit/editor-prosemirror/transform';
7
8
  import { expValEquals } from '@atlaskit/tmp-editor-statsig/exp-val-equals';
8
9
  import { getRequiredIncludedDiffableAttrs, isDiffableAttr } from './diffableAttrs';
@@ -14,6 +15,7 @@ var filterUndefined = function filterUndefined(x) {
14
15
  var mediaAttrs = getRequiredIncludedDiffableAttrs('media');
15
16
  var dateAttrs = getRequiredIncludedDiffableAttrs('date');
16
17
  var taskItemAttrs = getRequiredIncludedDiffableAttrs('taskItem');
18
+ var panelAttrs = getRequiredIncludedDiffableAttrs('panel');
17
19
  var emojiAttrs = getRequiredIncludedDiffableAttrs('emoji');
18
20
  var mentionAttrs = getRequiredIncludedDiffableAttrs('mention');
19
21
  var statusAttrs = getRequiredIncludedDiffableAttrs('status');
@@ -100,6 +102,23 @@ export var getAttrChangeRanges = function getAttrChangeRanges(doc, steps, origin
100
102
  };
101
103
  }
102
104
 
105
+ // panel node (incl. variants like panel_c1): type/colour/icon attribute change
106
+ // (e.g. note -> warning) — highlight the new panel and, when the original node
107
+ // is resolvable, expose its range (fromA/toA) so the caller can render the old
108
+ // panel as a "deleted" widget for a before/after comparison.
109
+ if (stepAttrs.some(function (v) {
110
+ return panelAttrs.includes(v);
111
+ }) && nodeAtPos && getBaseNodeTypeName(nodeAtPos.type) === 'panel') {
112
+ var _originalNodeAtPos = originalDoc === null || originalDoc === void 0 ? void 0 : originalDoc.nodeAt(step.pos);
113
+ return _objectSpread({
114
+ fromB: step.pos,
115
+ toB: step.pos + nodeAtPos.nodeSize
116
+ }, _originalNodeAtPos && {
117
+ fromA: step.pos,
118
+ toA: step.pos + _originalNodeAtPos.nodeSize
119
+ });
120
+ }
121
+
103
122
  // extension nodes: highlight on any diffable attr change (exclude rule in shared map)
104
123
  if (nodeAtPos && extensionNodeNames.includes(nodeAtPos.type.name) && stepAttrs.some(function (v) {
105
124
  return isDiffableAttr(nodeAtPos.type.name, v);
@@ -1,20 +1,42 @@
1
1
  import { TableMap } from '@atlaskit/editor-tables/table-map';
2
+ export var getTableTopAndBottomCellEdgeAttrs = function getTableTopAndBottomCellEdgeAttrs(tableMap) {
3
+ var edgeAttrsByOffset = new Map();
4
+ var addLogicalRow = function addLogicalRow(offsets, edge) {
5
+ offsets.forEach(function (offset, columnIndex) {
6
+ var _edgeAttrsByOffset$ge;
7
+ var edgeAttrs = (_edgeAttrsByOffset$ge = edgeAttrsByOffset.get(offset)) !== null && _edgeAttrsByOffset$ge !== void 0 ? _edgeAttrsByOffset$ge : {
8
+ reachesBottom: false,
9
+ reachesLeft: false,
10
+ reachesRight: false,
11
+ reachesTop: false
12
+ };
13
+ edgeAttrs.reachesBottom || (edgeAttrs.reachesBottom = edge === 'bottom');
14
+ edgeAttrs.reachesLeft || (edgeAttrs.reachesLeft = columnIndex === 0);
15
+ edgeAttrs.reachesRight || (edgeAttrs.reachesRight = columnIndex === tableMap.width - 1);
16
+ edgeAttrs.reachesTop || (edgeAttrs.reachesTop = edge === 'top');
17
+ edgeAttrsByOffset.set(offset, edgeAttrs);
18
+ });
19
+ };
20
+ var topRow = tableMap.mapByRow[0];
21
+ var bottomRow = tableMap.mapByRow[tableMap.height - 1];
22
+ if (topRow) {
23
+ addLogicalRow(topRow, 'top');
24
+ }
25
+ if (bottomRow) {
26
+ addLogicalRow(bottomRow, 'bottom');
27
+ }
28
+ return edgeAttrsByOffset;
29
+ };
2
30
  export var getRowCellEdgeAttrs = function getRowCellEdgeAttrs(_ref) {
3
- var rowNode = _ref.rowNode,
4
- rowStart = _ref.rowStart,
5
- tableMap = _ref.tableMap;
31
+ var edgeAttrsByOffset = _ref.edgeAttrsByOffset,
32
+ rowNode = _ref.rowNode,
33
+ rowStart = _ref.rowStart;
6
34
  var cellEdgeAttrs = [];
7
35
  rowNode.content.forEach(function (cellNode, cellOffset) {
8
36
  if (cellNode.type.name !== 'tableCell' && cellNode.type.name !== 'tableHeader') {
9
37
  return;
10
38
  }
11
- var cellRect = tableMap.findCell(rowStart + 1 + cellOffset);
12
- cellEdgeAttrs.push({
13
- reachesBottom: cellRect.bottom >= tableMap.height,
14
- reachesLeft: cellRect.left === 0,
15
- reachesRight: cellRect.right >= tableMap.width,
16
- reachesTop: cellRect.top === 0
17
- });
39
+ cellEdgeAttrs.push(edgeAttrsByOffset.get(rowStart + 1 + cellOffset));
18
40
  });
19
41
  return cellEdgeAttrs;
20
42
  };
@@ -47,20 +69,25 @@ export var applyTableCellEdgeAttrs = function applyTableCellEdgeAttrs(_ref2) {
47
69
  var cells = Array.from(table.rows).flatMap(function (row) {
48
70
  return Array.from(row.cells);
49
71
  });
72
+ var cellsByOffset = new Map();
50
73
  var cellIndex = 0;
51
74
  tableNode.content.forEach(function (rowNode, rowStart) {
52
- getRowCellEdgeAttrs({
53
- rowNode: rowNode,
54
- rowStart: rowStart,
55
- tableMap: tableMap
56
- }).forEach(function (edgeAttrs) {
57
- var cell = cells[cellIndex];
58
- cellIndex++;
59
- if (cell) {
60
- applyCellEdgeAttrs(cell, edgeAttrs);
75
+ rowNode.content.forEach(function (cellNode, cellOffset) {
76
+ if (cellNode.type.name === 'tableCell' || cellNode.type.name === 'tableHeader') {
77
+ var cell = cells[cellIndex];
78
+ cellIndex++;
79
+ if (cell) {
80
+ cellsByOffset.set(rowStart + 1 + cellOffset, cell);
81
+ }
61
82
  }
62
83
  });
63
84
  });
85
+ getTableTopAndBottomCellEdgeAttrs(tableMap).forEach(function (edgeAttrs, offset) {
86
+ var cell = cellsByOffset.get(offset);
87
+ if (cell) {
88
+ applyCellEdgeAttrs(cell, edgeAttrs);
89
+ }
90
+ });
64
91
  } catch (_unused) {
65
92
  // Table structure can be transient while widget DOM is being assembled.
66
93
  }
@@ -6,11 +6,12 @@ export type CellEdgeAttrs = {
6
6
  reachesRight: boolean;
7
7
  reachesTop: boolean;
8
8
  };
9
- export declare const getRowCellEdgeAttrs: ({ rowNode, rowStart, tableMap, }: {
9
+ export declare const getTableTopAndBottomCellEdgeAttrs: (tableMap: TableMap) => Map<number, CellEdgeAttrs>;
10
+ export declare const getRowCellEdgeAttrs: ({ edgeAttrsByOffset, rowNode, rowStart, }: {
11
+ edgeAttrsByOffset: ReadonlyMap<number, CellEdgeAttrs>;
10
12
  rowNode: PMNode;
11
13
  rowStart: number;
12
- tableMap: TableMap;
13
- }) => CellEdgeAttrs[];
14
+ }) => Array<CellEdgeAttrs | undefined>;
14
15
  export declare const applyCellEdgeAttrs: (cell: HTMLElement, edgeAttrs?: CellEdgeAttrs) => void;
15
16
  export declare const applyTableCellEdgeAttrs: ({ element, tableNode, }: {
16
17
  element: HTMLElement;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@atlaskit/editor-plugin-show-diff",
3
- "version": "13.1.4",
3
+ "version": "13.1.6",
4
4
  "description": "ShowDiff plugin for @atlaskit/editor-core",
5
5
  "author": "Atlassian Pty Ltd",
6
6
  "license": "Apache-2.0",
@@ -28,8 +28,8 @@
28
28
  "@atlaskit/editor-tables": "^3.0.0",
29
29
  "@atlaskit/platform-feature-experiments": "^0.3.0",
30
30
  "@atlaskit/platform-feature-flags": "^2.1.0",
31
- "@atlaskit/tmp-editor-statsig": "^155.0.0",
32
- "@atlaskit/tokens": "^16.7.0",
31
+ "@atlaskit/tmp-editor-statsig": "^157.0.0",
32
+ "@atlaskit/tokens": "^16.8.0",
33
33
  "@babel/runtime": "^7.0.0",
34
34
  "@compiled/react": "^1.0.2",
35
35
  "lodash": "^4.17.21",
@@ -38,15 +38,15 @@
38
38
  },
39
39
  "devDependencies": {
40
40
  "@atlaskit/adf-utils": "^20.7.0",
41
- "@atlaskit/button": "^25.1.0",
41
+ "@atlaskit/button": "^25.2.0",
42
42
  "@atlaskit/css": "^1.0.0",
43
- "@atlaskit/dropdown-menu": "^18.1.0",
44
- "@atlaskit/editor-core": "^224.2.0",
45
- "@atlaskit/editor-json-transformer": "^9.4.0",
46
- "@atlaskit/form": "^17.0.0",
47
- "@atlaskit/primitives": "^22.3.0",
48
- "@atlaskit/section-message": "^10.0.0",
49
- "@atlaskit/textarea": "^10.0.0",
43
+ "@atlaskit/dropdown-menu": "^18.2.0",
44
+ "@atlaskit/editor-core": "^224.3.0",
45
+ "@atlaskit/editor-json-transformer": "^9.6.0",
46
+ "@atlaskit/form": "^17.1.0",
47
+ "@atlaskit/primitives": "^22.4.0",
48
+ "@atlaskit/section-message": "^10.1.0",
49
+ "@atlaskit/textarea": "^10.1.0",
50
50
  "@atlassian/confluence-presets": "workspace:^",
51
51
  "@atlassian/content-reconciliation": "^0.1.3506",
52
52
  "@atlassian/structured-docs-types": "workspace:^",
@@ -54,7 +54,7 @@
54
54
  "react-intl": "^7.0.0"
55
55
  },
56
56
  "peerDependencies": {
57
- "@atlaskit/editor-common": "^119.10.0",
57
+ "@atlaskit/editor-common": "^119.13.0",
58
58
  "react": "^18.2.0 || ^19.2.0"
59
59
  },
60
60
  "techstack": {