@atlaskit/editor-plugin-show-diff 12.1.2 → 12.1.4

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 (36) hide show
  1. package/CHANGELOG.md +42 -0
  2. package/compass.yml +3 -3
  3. package/dist/cjs/pm-plugins/calculateDiff/attrAwareTokenEncoder.js +76 -0
  4. package/dist/cjs/pm-plugins/calculateDiff/calculateDiffDecorations.js +47 -124
  5. package/dist/cjs/pm-plugins/calculateDiff/computeDiffChanges.js +8 -1
  6. package/dist/cjs/pm-plugins/calculateDiff/diffBySteps.js +3 -125
  7. package/dist/cjs/pm-plugins/calculateDiff/smart/classifySmartChanges.js +9 -0
  8. package/dist/cjs/pm-plugins/decorations/colorSchemes/factory.js +678 -0
  9. package/dist/cjs/pm-plugins/decorations/colorSchemes/schemes.js +39 -0
  10. package/dist/cjs/pm-plugins/decorations/colorSchemes/types.js +1 -0
  11. package/dist/es2019/pm-plugins/calculateDiff/attrAwareTokenEncoder.js +64 -0
  12. package/dist/es2019/pm-plugins/calculateDiff/calculateDiffDecorations.js +15 -89
  13. package/dist/es2019/pm-plugins/calculateDiff/computeDiffChanges.js +8 -1
  14. package/dist/es2019/pm-plugins/calculateDiff/diffBySteps.js +2 -105
  15. package/dist/es2019/pm-plugins/calculateDiff/smart/classifySmartChanges.js +9 -0
  16. package/dist/es2019/pm-plugins/decorations/colorSchemes/factory.js +625 -0
  17. package/dist/es2019/pm-plugins/decorations/colorSchemes/schemes.js +33 -0
  18. package/dist/es2019/pm-plugins/decorations/colorSchemes/types.js +0 -0
  19. package/dist/esm/pm-plugins/calculateDiff/attrAwareTokenEncoder.js +70 -0
  20. package/dist/esm/pm-plugins/calculateDiff/calculateDiffDecorations.js +48 -125
  21. package/dist/esm/pm-plugins/calculateDiff/computeDiffChanges.js +8 -1
  22. package/dist/esm/pm-plugins/calculateDiff/diffBySteps.js +2 -124
  23. package/dist/esm/pm-plugins/calculateDiff/smart/classifySmartChanges.js +9 -0
  24. package/dist/esm/pm-plugins/decorations/colorSchemes/factory.js +622 -0
  25. package/dist/esm/pm-plugins/decorations/colorSchemes/schemes.js +33 -0
  26. package/dist/esm/pm-plugins/decorations/colorSchemes/types.js +0 -0
  27. package/dist/types/pm-plugins/calculateDiff/attrAwareTokenEncoder.d.ts +7 -0
  28. package/dist/types/pm-plugins/calculateDiff/diffBySteps.d.ts +0 -20
  29. package/dist/types/pm-plugins/decorations/colorSchemes/factory.d.ts +101 -0
  30. package/dist/types/pm-plugins/decorations/colorSchemes/schemes.d.ts +5 -0
  31. package/dist/types/pm-plugins/decorations/colorSchemes/types.d.ts +38 -0
  32. package/package.json +6 -6
  33. package/dist/cjs/pm-plugins/decorations/createGranularBlockReferenceWidget.js +0 -125
  34. package/dist/es2019/pm-plugins/decorations/createGranularBlockReferenceWidget.js +0 -120
  35. package/dist/esm/pm-plugins/decorations/createGranularBlockReferenceWidget.js +0 -120
  36. package/dist/types/pm-plugins/decorations/createGranularBlockReferenceWidget.d.ts +0 -36
package/CHANGELOG.md CHANGED
@@ -1,5 +1,47 @@
1
1
  # @atlaskit/editor-plugin-show-diff
2
2
 
3
+ ## 12.1.4
4
+
5
+ ### Patch Changes
6
+
7
+ - [`522f40dd846c1`](https://bitbucket.org/atlassian/atlassian-frontend-monorepo/commits/522f40dd846c1) -
8
+ Fix table-cell diffs in the Post Stream Review experience. Recolouring a table cell is now
9
+ surfaced in the review with a highlight and a navigable step-through segment, and stepping onto a
10
+ cell no longer renders a blank cell.
11
+ - The AI modal mount-point widget could be positioned directly inside a table row, where the
12
+ browser wrapped it in an anonymous table cell — a blank column that hid the real cell's content.
13
+ It is now kept out of table structure.
14
+ - `prosemirror-changeset`'s default token encoder ignores node attributes, so a cell recolour
15
+ produced no change at all. An attribute-aware encoder now folds an allow-list of table-cell
16
+ attributes into the diff.
17
+ - The smart classifier discarded attribute changes on table cells because they sit on the node
18
+ boundary rather than inside the cell's content. It now emits a whole-cell change when a rigid
19
+ wrapper's own attributes differ.
20
+
21
+ - [`3fd2c0404aff0`](https://bitbucket.org/atlassian/atlassian-frontend-monorepo/commits/3fd2c0404aff0) -
22
+ Introduce a data-driven colour-scheme pattern for show-diff decorations: a `DiffColorScheme` type
23
+ (`types.ts`), shared CSS-generating functions (`factory.ts`) and the two scheme definitions
24
+ (`schemes.ts`).
25
+
26
+ Most scheme fields are ADS accent colour names; seven encode structural differences that no colour
27
+ can express (`insertUnderlineStyle`, `deletedQuoteNodeShape`, `deletedCellBorderProminence`,
28
+ `deletedRowTreatment`, `deletedBlockOutlineActiveEmphasis`, `addedCellOverlayZIndex`,
29
+ `roundedAddedCellOverlayInheritsBorder`), so a new scheme has to decide each of those too.
30
+
31
+ Additive only — nothing consumes the factory yet. `standard.ts` and `traditional.ts` are unchanged
32
+ and remain the source of every rendered style, so there is no behaviour or visual change. Wiring
33
+ the decoration consumers up behind a feature gate follows separately.
34
+
35
+ - Updated dependencies
36
+
37
+ ## 12.1.3
38
+
39
+ ### Patch Changes
40
+
41
+ - [`774c01e2f1cdd`](https://bitbucket.org/atlassian/atlassian-frontend-monorepo/commits/774c01e2f1cdd) -
42
+ Remove the `platform_editor_diff_granular_extended` experiment and retain its disabled behavior.
43
+ - Updated dependencies
44
+
3
45
  ## 12.1.2
4
46
 
5
47
  ### Patch Changes
package/compass.yml CHANGED
@@ -1,7 +1,7 @@
1
1
  configVersion: 1
2
2
  id: ari:cloud:compass:a436116f-02ce-4520-8fbb-7301462a1674:component/c5751cc6-3513-4070-9deb-af31e86aed34/1617e71c-fdf1-40c0-b143-03bf8d5a1648
3
3
  name: '@atlaskit/editor-plugin-show-diff'
4
- ownerId: ari:cloud:identity::team/ce353f56-2a90-491b-b272-b90dca4fffa2 # Editor FE Platform
4
+ ownerId: ari:cloud:identity::team/cf6a670b-5252-4c68-a769-9207de366beb # Editor Developers
5
5
  labels:
6
6
  - platform-code
7
7
  - platform-afm
@@ -16,14 +16,14 @@ links:
16
16
  url: https://bitbucket.org/atlassian/atlassian-frontend-monorepo/src/master
17
17
  - name: Slack Channel
18
18
  type: CHAT_CHANNEL
19
- url: https://atlassian.enterprise.slack.com/archives/C03SD6QJWPP # #cc-editor-lego
19
+ url: https://atlassian.enterprise.slack.com/archives/CFG3PSQ9E # #help-editor
20
20
  - name: Editor Plugin Show Diff
21
21
  type: REPOSITORY
22
22
  url: https://bitbucket.org/atlassian/atlassian-frontend-monorepo/src/master/platform/packages/editor/editor-plugin-show-diff
23
23
  customFields:
24
24
  - name: Department
25
25
  type: text
26
- value: Eng - Editor Frontend Platform
26
+ value: Editor Developers
27
27
  - name: Trusted Reviewer Teams
28
28
  type: text
29
29
  value: ari:cloud:identity::team/cf6a670b-5252-4c68-a769-9207de366beb
@@ -0,0 +1,76 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.attrAwareTokenEncoder = void 0;
7
+ /**
8
+ * Attribute-aware token encoder for `prosemirror-changeset`.
9
+ *
10
+ * The library's default encoder reduces a node's open token to `node.type.name`,
11
+ * ignoring attributes. An attribute-only change (e.g. recolouring a table cell's
12
+ * `background`) therefore tokenises identically on both sides and reports no
13
+ * change at all.
14
+ *
15
+ * This encoder folds an allow-listed set of attributes into the open token so
16
+ * such changes register. Everything else encodes exactly as the default. The
17
+ * allow-list is deliberately narrow — folding in ephemeral attrs like `localId`
18
+ * would produce phantom diffs.
19
+ */
20
+
21
+ var DIFFED_ATTRS_BY_NODE_TYPE = {
22
+ tableCell: ['background', 'colspan', 'rowspan'],
23
+ tableHeader: ['background', 'colspan', 'rowspan']
24
+ };
25
+
26
+ /**
27
+ * Builds a stable composite token. Iterates the allow-list rather than
28
+ * `Object.keys` so ordering is deterministic for string comparison.
29
+ */
30
+ var encodeNodeWithAttrs = function encodeNodeWithAttrs(node, attrNames) {
31
+ var _node$attrs;
32
+ var attrs = (_node$attrs = node.attrs) !== null && _node$attrs !== void 0 ? _node$attrs : {};
33
+ // Deterministic order: iterate the allow-list, not `Object.keys(attrs)`.
34
+ var parts = attrNames.map(function (name) {
35
+ var _attrs$name;
36
+ return "".concat(name, "=").concat(JSON.stringify((_attrs$name = attrs[name]) !== null && _attrs$name !== void 0 ? _attrs$name : null));
37
+ });
38
+ return "".concat(node.type.name, "|").concat(parts.join('|'));
39
+ };
40
+
41
+ /**
42
+ * Identical to the library default except that allow-listed node types encode
43
+ * their allow-listed attributes into the open token. Characters and node-end
44
+ * tokens are left as-is, hence the `string | number` token type.
45
+ */
46
+ var attrAwareTokenEncoder = exports.attrAwareTokenEncoder = {
47
+ encodeCharacter: function encodeCharacter(char, _marks) {
48
+ return char;
49
+ },
50
+ encodeNodeStart: function encodeNodeStart(node) {
51
+ var attrNames = DIFFED_ATTRS_BY_NODE_TYPE[node.type.name];
52
+ if (attrNames) {
53
+ return encodeNodeWithAttrs(node, attrNames);
54
+ }
55
+ return node.type.name;
56
+ },
57
+ encodeNodeEnd: function encodeNodeEnd(node) {
58
+ return -typeID(node.type);
59
+ },
60
+ compareTokens: function compareTokens(a, b) {
61
+ return a === b;
62
+ }
63
+ };
64
+
65
+ /**
66
+ * Mirrors the library's private `typeID` so node-end tokens match the default
67
+ * encoding exactly. Reimplemented here because it is not exported.
68
+ */
69
+ function typeID(type) {
70
+ var cache = type.schema.cached.changeSetIDs || (type.schema.cached.changeSetIDs = Object.create(null));
71
+ var id = cache[type.name];
72
+ if (id == null) {
73
+ cache[type.name] = id = Object.keys(type.schema.nodes).indexOf(type.name) + 1;
74
+ }
75
+ return id;
76
+ }
@@ -19,13 +19,13 @@ var _expValEquals = require("@atlaskit/tmp-editor-statsig/exp-val-equals");
19
19
  var _areDocsEqualByBlockStructureAndText = require("../areDocsEqualByBlockStructureAndText");
20
20
  var _createAnchorDecorationWidgets = require("../decorations/createAnchorDecorationWidgets");
21
21
  var _createBlockChangedDecoration = require("../decorations/createBlockChangedDecoration");
22
- var _createGranularBlockReferenceWidget = require("../decorations/createGranularBlockReferenceWidget");
23
22
  var _createInlineChangedDecoration = require("../decorations/createInlineChangedDecoration");
24
23
  var _createNodeChangedDecorationWidget = require("../decorations/createNodeChangedDecorationWidget");
25
24
  var _decorationKeys = require("../decorations/decorationKeys");
26
25
  var _getAttrChangeRanges = require("../decorations/utils/getAttrChangeRanges");
27
26
  var _getMarkChangeRanges = require("../decorations/utils/getMarkChangeRanges");
28
27
  var _isExtendedEnabled = require("../isExtendedEnabled");
28
+ var _attrAwareTokenEncoder = require("./attrAwareTokenEncoder");
29
29
  var _diffBySteps = require("./diffBySteps");
30
30
  var _groupChangesByBlock = require("./groupChangesByBlock");
31
31
  var _optimizeChanges = require("./optimizeChanges");
@@ -238,7 +238,11 @@ var calculateDiffDecorationsInner = function calculateDiffDecorationsInner(_ref4
238
238
  };
239
239
  }
240
240
  }
241
- var changeset = _prosemirrorChangeset.ChangeSet.create(originalDoc).addSteps(steppedDoc, stepMaps, tr.doc);
241
+ // The attribute-aware encoder is only needed by the smart classifier and is
242
+ // gated with it; other diff types keep the library default so their output is
243
+ // unchanged.
244
+ var tokenEncoder = diffType === 'smart' && (0, _platformFeatureFlags.fg)('platform_editor_ai_smart_diff') ? _attrAwareTokenEncoder.attrAwareTokenEncoder : undefined;
245
+ var changeset = _prosemirrorChangeset.ChangeSet.create(originalDoc, undefined, tokenEncoder).addSteps(steppedDoc, stepMaps, tr.doc);
242
246
  var changes = getChanges({
243
247
  changeset: changeset,
244
248
  originalDoc: originalDoc,
@@ -260,7 +264,7 @@ var calculateDiffDecorationsInner = function calculateDiffDecorationsInner(_ref4
260
264
 
261
265
  // Our default operations are insertions, so it should match the opposite of isInverted.
262
266
  var isInserted = !isInverted;
263
- var createDecorationsForChange = function createDecorationsForChange(change, showGranularWithBlock) {
267
+ var createDecorationsForChange = function createDecorationsForChange(change) {
264
268
  var isActive = activeIndexPos && change.fromB === activeIndexPos.from && change.toB === activeIndexPos.to;
265
269
 
266
270
  // Hoisted because it decides BOTH where the deleted widget is anchored and — since the
@@ -272,10 +276,8 @@ var calculateDiffDecorationsInner = function calculateDiffDecorationsInner(_ref4
272
276
  inlineDeletedDiffPlacement: inlineDeletedDiffPlacement
273
277
  });
274
278
  if (change.inserted.length > 0) {
275
- // shouldHideDeleted for block/node decorations: suppressed when isInverted + hideDeletedDiffs,
276
- // or when showGranularWithBlock (block reference widget is shown instead).
277
- // isInverted gates both — on an inverted diff the inserted side is visually the deleted side.
278
- var shouldHideDeleted = (0, _isExtendedEnabled.isExtendedEnabled)(diffType) ? isInverted && (hideDeletedDiffs || showGranularWithBlock && change.deleted.length > 0) : false;
279
+ // On an inverted diff the inserted side is visually the deleted side.
280
+ var shouldHideDeleted = (0, _isExtendedEnabled.isExtendedEnabled)(diffType) ? isInverted && hideDeletedDiffs : false;
279
281
 
280
282
  // For `smart` NODE-level promotions the change range spans a whole container
281
283
  // (e.g. an entire list/table/layout, using outer node bounds). Applying a SINGLE
@@ -292,7 +294,7 @@ var calculateDiffDecorationsInner = function calculateDiffDecorationsInner(_ref4
292
294
  // change. Used to decide if indicator anchor positions should be adjusted
293
295
  // inward — when the widget is present the anchor must stay at the block
294
296
  // boundary to keep the indicator bar continuous with the deleted content.
295
- var willRenderDeletedWidget = change.deleted.length > 0 && !((0, _isExtendedEnabled.isExtendedEnabled)(diffType) && !isInverted && (hideDeletedDiffs || showGranularWithBlock) && change.inserted.length > 0);
297
+ var willRenderDeletedWidget = change.deleted.length > 0 && !((0, _isExtendedEnabled.isExtendedEnabled)(diffType) && !isInverted && hideDeletedDiffs && change.inserted.length > 0);
296
298
  if (isSmartNodeLevel) {
297
299
  var _iterator2 = _createForOfIteratorHelper(leafTextblockRanges(tr.doc, change.fromB, change.toB)),
298
300
  _step2;
@@ -356,7 +358,7 @@ var calculateDiffDecorationsInner = function calculateDiffDecorationsInner(_ref4
356
358
  }))));
357
359
  }
358
360
  if (change.deleted.length > 0) {
359
- var _shouldHideDeleted = (0, _isExtendedEnabled.isExtendedEnabled)(diffType) ? !isInverted && (hideDeletedDiffs || showGranularWithBlock) && change.inserted.length > 0 : false;
361
+ var _shouldHideDeleted = (0, _isExtendedEnabled.isExtendedEnabled)(diffType) ? !isInverted && hideDeletedDiffs && change.inserted.length > 0 : false;
360
362
  if (!_shouldHideDeleted) {
361
363
  decorations.push.apply(decorations, (0, _toConsumableArray2.default)((0, _createNodeChangedDecorationWidget.createNodeChangedDecorationWidget)(_objectSpread(_objectSpread({
362
364
  change: change,
@@ -377,88 +379,9 @@ var calculateDiffDecorationsInner = function calculateDiffDecorationsInner(_ref4
377
379
  }
378
380
  }
379
381
  };
380
- if (diffType === 'step' && (0, _expValEquals.expValEquals)('platform_editor_diff_granular_extended', 'isEnabled', true)) {
381
- // Uses getStepChanges instead of getChanges so that we have per-step granularity metadata.
382
- // Specifically, we need to know how many granular changes each step produced in order to
383
- // apply the shouldHideDeleted suppression threshold (> 3 granular changes per step).
384
- // getChanges returns a flat Change[] with no per-step grouping, making this count impossible
385
- // to derive after the fact without re-introducing per-change metadata.
386
- var stepChanges = (0, _diffBySteps.getStepChanges)(originalDoc, steps);
387
- stepChanges.forEach(function (_ref5) {
388
- var isGranular = _ref5.isGranular,
389
- stepChangeList = _ref5.changes;
390
- var granularCount = isGranular ? stepChangeList.length : 0;
391
-
392
- // Calculate the average ratio of changed content on both A (original) and B (new)
393
- // sides of the diff. If 30% or more of the block has changed on average, we show
394
- // the block reference widget even if the granular change count is below the threshold.
395
- // Block length is derived from the enclosing text block boundaries rather than the
396
- // first/last change positions, so unchanged words at the start/end are accounted for.
397
- var avgChangedRatio = 0;
398
- if (isGranular && stepChangeList.length > 0) {
399
- var first = stepChangeList[0];
400
- var last = stepChangeList[stepChangeList.length - 1];
401
- var resolvedA = originalDoc.resolve(first.fromA);
402
- var resolvedB = tr.doc.resolve(first.fromB);
403
- var blockStartA = first.fromA;
404
- var blockEndA = last.toA;
405
- var blockStartB = first.fromB;
406
- var blockEndB = last.toB;
407
- for (var depth = resolvedA.depth; depth >= 0; depth--) {
408
- var node = resolvedA.node(depth);
409
- if (node.isTextblock) {
410
- blockStartA = resolvedA.start(depth);
411
- blockEndA = blockStartA + node.nodeSize - 2; // exclude open/close tokens
412
- break;
413
- }
414
- }
415
- for (var _depth = resolvedB.depth; _depth >= 0; _depth--) {
416
- var _node = resolvedB.node(_depth);
417
- if (_node.isTextblock) {
418
- blockStartB = resolvedB.start(_depth);
419
- blockEndB = blockStartB + _node.nodeSize - 2; // exclude open/close tokens
420
- break;
421
- }
422
- }
423
- var blockLengthA = blockEndA - blockStartA;
424
- var blockLengthB = blockEndB - blockStartB;
425
- var totalChangedA = stepChangeList.reduce(function (sum, c) {
426
- return sum + (c.toA - c.fromA);
427
- }, 0);
428
- var totalChangedB = stepChangeList.reduce(function (sum, c) {
429
- return sum + (c.toB - c.fromB);
430
- }, 0);
431
- var ratioA = blockLengthA > 0 ? totalChangedA / blockLengthA : 0;
432
- var ratioB = blockLengthB > 0 ? totalChangedB / blockLengthB : 0;
433
- avgChangedRatio = (ratioA + ratioB) / 2;
434
- }
435
- var showGranularWithBlock = isGranular && granularCount !== 1 && (granularCount > 3 || avgChangedRatio >= 0.3);
436
- stepChangeList.forEach(function (change) {
437
- createDecorationsForChange(change, showGranularWithBlock);
438
- });
439
- if (showGranularWithBlock && stepChangeList.length > 0 && !hideDeletedDiffs) {
440
- var lastChange = stepChangeList[stepChangeList.length - 1];
441
- var granularBlockDiffId = crypto.randomUUID();
442
- var blockWidgets = (0, _createGranularBlockReferenceWidget.createGranularBlockReferenceWidget)({
443
- change: lastChange,
444
- originalDoc: originalDoc,
445
- newDoc: tr.doc,
446
- isInverted: isInverted,
447
- nodeViewSerializer: nodeViewSerializer,
448
- colorScheme: colorScheme,
449
- intl: intl,
450
- activeIndexPos: activeIndexPos,
451
- diffId: granularBlockDiffId,
452
- showIndicators: showIndicators
453
- });
454
- decorations.push.apply(decorations, (0, _toConsumableArray2.default)(blockWidgets));
455
- }
456
- });
457
- } else {
458
- changes.forEach(function (change) {
459
- createDecorationsForChange(change, /* showGranularWithBlock */false);
460
- });
461
- }
382
+ changes.forEach(function (change) {
383
+ createDecorationsForChange(change);
384
+ });
462
385
  (0, _getMarkChangeRanges.getMarkChangeRanges)(steps).forEach(function (change) {
463
386
  var isActive = activeIndexPos && change.fromB === activeIndexPos.from && change.toB === activeIndexPos.to;
464
387
  decorations.push.apply(decorations, (0, _toConsumableArray2.default)((0, _createInlineChangedDecoration.createInlineChangedDecoration)({
@@ -530,42 +453,42 @@ var calculateDiffDecorationsInner = function calculateDiffDecorationsInner(_ref4
530
453
  };
531
454
  var calculateDiffDecorations = exports.calculateDiffDecorations = (0, _memoizeOne.default)(calculateDiffDecorationsInner,
532
455
  // Cache results unless relevant inputs change
533
- function (_ref6, _ref7) {
534
- var _ref1;
456
+ function (_ref5, _ref6) {
457
+ var _ref0;
458
+ var _ref7 = (0, _slicedToArray2.default)(_ref5, 1),
459
+ _ref7$ = _ref7[0],
460
+ pluginState = _ref7$.pluginState,
461
+ state = _ref7$.state,
462
+ colorScheme = _ref7$.colorScheme,
463
+ intl = _ref7$.intl,
464
+ activeIndexPos = _ref7$.activeIndexPos,
465
+ isInverted = _ref7$.isInverted,
466
+ diffType = _ref7$.diffType,
467
+ hideDeletedDiffs = _ref7$.hideDeletedDiffs,
468
+ hideAddedDiffsUnderline = _ref7$.hideAddedDiffsUnderline,
469
+ showIndicators = _ref7$.showIndicators,
470
+ smartThresholds = _ref7$.smartThresholds,
471
+ deletedDiffPlacement = _ref7$.deletedDiffPlacement,
472
+ inlineDeletedDiffPlacement = _ref7$.inlineDeletedDiffPlacement;
535
473
  var _ref8 = (0, _slicedToArray2.default)(_ref6, 1),
536
474
  _ref8$ = _ref8[0],
537
- pluginState = _ref8$.pluginState,
538
- state = _ref8$.state,
539
- colorScheme = _ref8$.colorScheme,
540
- intl = _ref8$.intl,
541
- activeIndexPos = _ref8$.activeIndexPos,
542
- isInverted = _ref8$.isInverted,
543
- diffType = _ref8$.diffType,
544
- hideDeletedDiffs = _ref8$.hideDeletedDiffs,
545
- hideAddedDiffsUnderline = _ref8$.hideAddedDiffsUnderline,
546
- showIndicators = _ref8$.showIndicators,
547
- smartThresholds = _ref8$.smartThresholds,
548
- deletedDiffPlacement = _ref8$.deletedDiffPlacement,
549
- inlineDeletedDiffPlacement = _ref8$.inlineDeletedDiffPlacement;
550
- var _ref9 = (0, _slicedToArray2.default)(_ref7, 1),
551
- _ref9$ = _ref9[0],
552
- lastPluginState = _ref9$.pluginState,
553
- lastState = _ref9$.state,
554
- lastColorScheme = _ref9$.colorScheme,
555
- lastIntl = _ref9$.intl,
556
- lastActiveIndexPos = _ref9$.activeIndexPos,
557
- lastIsInverted = _ref9$.isInverted,
558
- lastDiffType = _ref9$.diffType,
559
- lastHideDeletedDiffs = _ref9$.hideDeletedDiffs,
560
- lastHideAddedDiffsUnderline = _ref9$.hideAddedDiffsUnderline,
561
- lastShowIndicators = _ref9$.showIndicators,
562
- lastSmartThresholds = _ref9$.smartThresholds,
563
- lastDeletedDiffPlacement = _ref9$.deletedDiffPlacement,
564
- lastInlineDeletedDiffPlacement = _ref9$.inlineDeletedDiffPlacement;
475
+ lastPluginState = _ref8$.pluginState,
476
+ lastState = _ref8$.state,
477
+ lastColorScheme = _ref8$.colorScheme,
478
+ lastIntl = _ref8$.intl,
479
+ lastActiveIndexPos = _ref8$.activeIndexPos,
480
+ lastIsInverted = _ref8$.isInverted,
481
+ lastDiffType = _ref8$.diffType,
482
+ lastHideDeletedDiffs = _ref8$.hideDeletedDiffs,
483
+ lastHideAddedDiffsUnderline = _ref8$.hideAddedDiffsUnderline,
484
+ lastShowIndicators = _ref8$.showIndicators,
485
+ lastSmartThresholds = _ref8$.smartThresholds,
486
+ lastDeletedDiffPlacement = _ref8$.deletedDiffPlacement,
487
+ lastInlineDeletedDiffPlacement = _ref8$.inlineDeletedDiffPlacement;
565
488
  var originalDocIsSame = lastPluginState.originalDoc && pluginState.originalDoc && pluginState.originalDoc.eq(lastPluginState.originalDoc);
566
489
  if ((0, _isExtendedEnabled.isExtendedEnabled)(diffType)) {
567
- var _ref0;
568
- return (_ref0 = colorScheme === lastColorScheme && intl.locale === lastIntl.locale && isInverted === lastIsInverted && diffType === lastDiffType && (0, _isEqual.default)(activeIndexPos, lastActiveIndexPos) && originalDocIsSame && (0, _isEqual.default)(pluginState.steps, lastPluginState.steps) && state.doc.eq(lastState.doc) && hideDeletedDiffs === lastHideDeletedDiffs && hideAddedDiffsUnderline === lastHideAddedDiffsUnderline && showIndicators === lastShowIndicators && (0, _isEqual.default)(smartThresholds, lastSmartThresholds) && deletedDiffPlacement === lastDeletedDiffPlacement && inlineDeletedDiffPlacement === lastInlineDeletedDiffPlacement) !== null && _ref0 !== void 0 ? _ref0 : false;
490
+ var _ref9;
491
+ return (_ref9 = colorScheme === lastColorScheme && intl.locale === lastIntl.locale && isInverted === lastIsInverted && diffType === lastDiffType && (0, _isEqual.default)(activeIndexPos, lastActiveIndexPos) && originalDocIsSame && (0, _isEqual.default)(pluginState.steps, lastPluginState.steps) && state.doc.eq(lastState.doc) && hideDeletedDiffs === lastHideDeletedDiffs && hideAddedDiffsUnderline === lastHideAddedDiffsUnderline && showIndicators === lastShowIndicators && (0, _isEqual.default)(smartThresholds, lastSmartThresholds) && deletedDiffPlacement === lastDeletedDiffPlacement && inlineDeletedDiffPlacement === lastInlineDeletedDiffPlacement) !== null && _ref9 !== void 0 ? _ref9 : false;
569
492
  }
570
- return (_ref1 = originalDocIsSame && (0, _isEqual.default)(pluginState.steps, lastPluginState.steps) && state.doc.eq(lastState.doc) && colorScheme === lastColorScheme && intl.locale === lastIntl.locale && (0, _isEqual.default)(activeIndexPos, lastActiveIndexPos) && hideDeletedDiffs === lastHideDeletedDiffs) !== null && _ref1 !== void 0 ? _ref1 : false;
493
+ return (_ref0 = originalDocIsSame && (0, _isEqual.default)(pluginState.steps, lastPluginState.steps) && state.doc.eq(lastState.doc) && colorScheme === lastColorScheme && intl.locale === lastIntl.locale && (0, _isEqual.default)(activeIndexPos, lastActiveIndexPos) && hideDeletedDiffs === lastHideDeletedDiffs) !== null && _ref0 !== void 0 ? _ref0 : false;
571
494
  });
@@ -5,6 +5,7 @@ Object.defineProperty(exports, "__esModule", {
5
5
  });
6
6
  exports.computeDiffChanges = void 0;
7
7
  var _prosemirrorChangeset = require("prosemirror-changeset");
8
+ var _attrAwareTokenEncoder = require("./attrAwareTokenEncoder");
8
9
  var _diffBySteps = require("./diffBySteps");
9
10
  var _groupChangesByBlock = require("./groupChangesByBlock");
10
11
  var _optimizeChanges = require("./optimizeChanges");
@@ -80,7 +81,13 @@ var computeDiffChanges = exports.computeDiffChanges = function computeDiffChange
80
81
  newDoc: originalDoc
81
82
  };
82
83
  }
83
- var changeset = _prosemirrorChangeset.ChangeSet.create(originalDoc).addSteps(steppedDoc, stepMaps, steppedDoc);
84
+
85
+ // The attribute-aware encoder only affects the `smart` classification, so it is
86
+ // applied only for that type. (This utility intentionally applies no feature
87
+ // gate — see the file docstring — but a caller requesting `smart` is already
88
+ // behind the smart-diff gate.)
89
+ var tokenEncoder = diffType === 'smart' ? _attrAwareTokenEncoder.attrAwareTokenEncoder : undefined;
90
+ var changeset = _prosemirrorChangeset.ChangeSet.create(originalDoc, undefined, tokenEncoder).addSteps(steppedDoc, stepMaps, steppedDoc);
84
91
  if (diffType === 'smart') {
85
92
  return {
86
93
  changes: (0, _classifySmartChanges.classifySmartChanges)({
@@ -4,12 +4,13 @@ var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefau
4
4
  Object.defineProperty(exports, "__esModule", {
5
5
  value: true
6
6
  });
7
- exports.getStepChanges = exports.diffBySteps = void 0;
7
+ exports.diffBySteps = void 0;
8
8
  var _defineProperty2 = _interopRequireDefault(require("@babel/runtime/helpers/defineProperty"));
9
9
  var _toConsumableArray2 = _interopRequireDefault(require("@babel/runtime/helpers/toConsumableArray"));
10
10
  var _prosemirrorChangeset = require("prosemirror-changeset");
11
11
  var _model = require("@atlaskit/editor-prosemirror/model");
12
12
  var _transform = require("@atlaskit/editor-prosemirror/transform");
13
+ var _attrAwareTokenEncoder = require("./attrAwareTokenEncoder");
13
14
  var _optimizeChanges = require("./optimizeChanges");
14
15
  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
16
  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; }
@@ -328,7 +329,7 @@ var diffBySteps = exports.diffBySteps = function diffBySteps(originalDoc, steps)
328
329
  var fromB = mapPosition(afterStepToFinal, fromAfterStep);
329
330
  var toB = mapPosition(afterStepToFinal, toAfterStep);
330
331
  if (shouldCheckGranularDiff(rangedStep.step, rangedStep.before, rangedStep.from, rangedStep.to)) {
331
- var granularStepChanges = _prosemirrorChangeset.ChangeSet.create(rangedStep.before).addSteps(rangedStep.doc, [rangedStep.stepMap], null);
332
+ var granularStepChanges = _prosemirrorChangeset.ChangeSet.create(rangedStep.before, undefined, _attrAwareTokenEncoder.attrAwareTokenEncoder).addSteps(rangedStep.doc, [rangedStep.stepMap], null);
332
333
 
333
334
  // `simplifyChanges` reads text using `Change.fromB`/`toB`, which are
334
335
  // positions in the post-step doc (the "B" doc). Passing the pre-step
@@ -407,127 +408,4 @@ var diffBySteps = exports.diffBySteps = function diffBySteps(originalDoc, steps)
407
408
  });
408
409
  }
409
410
  return mergeOverlappingByNewDocRange(changes);
410
- };
411
-
412
- /**
413
- * A fork of `diffBySteps` that returns changes grouped per step, rather than as a flat list.
414
- *
415
- * Why forked rather than refactoring `diffBySteps`:
416
- * - `diffBySteps` returns a flat `Change[]` and is consumed by the existing decoration path.
417
- * Changing its return shape would require threading per-step metadata through all callers,
418
- * adding complexity to a stable code path.
419
- * - The per-step grouping is only needed for the `platform_editor_diff_granular_extended` gate,
420
- * where we need to know how many granular changes a single step produced in order to decide
421
- * whether to suppress deleted decorations (threshold: > 3 granular changes per step).
422
- * - Keeping the two functions separate means each has a clear, focused contract and neither
423
- * accumulates the other's concerns. Shared logic (mapping helpers, `mergeOverlappingByNewDocRange`,
424
- * `shouldCheckGranularDiff`, etc.) is already extracted and reused by both.
425
- */
426
- var getStepChanges = exports.getStepChanges = function getStepChanges(originalDoc, steps) {
427
- var result = [];
428
- var currentDoc = originalDoc;
429
- var successfulStepMaps = [];
430
- var rangedSteps = [];
431
- var _iterator4 = _createForOfIteratorHelper(steps),
432
- _step4;
433
- try {
434
- for (_iterator4.s(); !(_step4 = _iterator4.n()).done;) {
435
- var step = _step4.value;
436
- var before = currentDoc;
437
- var stepResult = step.apply(currentDoc);
438
- if (stepResult.failed !== null || !stepResult.doc) {
439
- continue;
440
- }
441
- var stepMap = step.getMap();
442
- var rangeStep = step;
443
- if (typeof rangeStep.from === 'number' && typeof rangeStep.to === 'number') {
444
- rangedSteps.push({
445
- before: before,
446
- doc: stepResult.doc,
447
- from: rangeStep.from,
448
- to: rangeStep.to,
449
- mapIndex: successfulStepMaps.length,
450
- step: step,
451
- stepMap: stepMap
452
- });
453
- }
454
- successfulStepMaps.push(stepMap);
455
- currentDoc = stepResult.doc;
456
- }
457
- } catch (err) {
458
- _iterator4.e(err);
459
- } finally {
460
- _iterator4.f();
461
- }
462
- for (var _i2 = 0, _rangedSteps2 = rangedSteps; _i2 < _rangedSteps2.length; _i2++) {
463
- var rangedStep = _rangedSteps2[_i2];
464
- var originalToBeforeStep = createMapping(successfulStepMaps.slice(0, rangedStep.mapIndex));
465
- var beforeStepToOriginal = originalToBeforeStep.invert();
466
- var fromA = mapPosition(beforeStepToOriginal, rangedStep.from);
467
- var toA = mapPosition(beforeStepToOriginal, rangedStep.to);
468
- var fromAfterStep = rangedStep.stepMap.map(rangedStep.from, -1);
469
- var toAfterStep = rangedStep.stepMap.map(rangedStep.to, 1);
470
- var afterStepToFinal = createMapping(successfulStepMaps.slice(rangedStep.mapIndex + 1));
471
- var fromB = mapPosition(afterStepToFinal, fromAfterStep);
472
- var toB = mapPosition(afterStepToFinal, toAfterStep);
473
- if (shouldCheckGranularDiff(rangedStep.step, rangedStep.before, rangedStep.from, rangedStep.to)) {
474
- var granularStepChanges = _prosemirrorChangeset.ChangeSet.create(rangedStep.before).addSteps(rangedStep.doc, [rangedStep.stepMap], null);
475
- var optimizedGranularStepChanges = (0, _optimizeChanges.optimizeChanges)((0, _prosemirrorChangeset.simplifyChanges)(granularStepChanges.changes, rangedStep.doc));
476
- var stepChanges = [];
477
- var _iterator5 = _createForOfIteratorHelper(optimizedGranularStepChanges),
478
- _step5;
479
- try {
480
- for (_iterator5.s(); !(_step5 = _iterator5.n()).done;) {
481
- var granularChange = _step5.value;
482
- var expandedA = expandToWordBoundaries(rangedStep.before, granularChange.fromA, granularChange.toA);
483
- var expandedB = expandToWordBoundaries(rangedStep.doc, granularChange.fromB, granularChange.toB);
484
- var aLeftDelta = granularChange.fromA - expandedA.from;
485
- var aRightDelta = expandedA.to - granularChange.toA;
486
- var bLeftDelta = granularChange.fromB - expandedB.from;
487
- var bRightDelta = expandedB.to - granularChange.toB;
488
- var finalA = expandedA;
489
- var finalB = expandedB;
490
- if (aLeftDelta > bLeftDelta || aRightDelta > bRightDelta) {
491
- var extraLeft = Math.max(0, aLeftDelta - bLeftDelta);
492
- var extraRight = Math.max(0, aRightDelta - bRightDelta);
493
- finalB = expandToWordBoundaries(rangedStep.doc, Math.max(expandedB.from - extraLeft, 0), expandedB.to + extraRight);
494
- }
495
- if (bLeftDelta > aLeftDelta || bRightDelta > aRightDelta) {
496
- var _extraLeft2 = Math.max(0, bLeftDelta - aLeftDelta);
497
- var _extraRight2 = Math.max(0, bRightDelta - aRightDelta);
498
- finalA = expandToWordBoundaries(rangedStep.before, Math.max(expandedA.from - _extraLeft2, 0), expandedA.to + _extraRight2);
499
- }
500
- stepChanges.push({
501
- fromA: mapPosition(beforeStepToOriginal, finalA.from),
502
- toA: mapPosition(beforeStepToOriginal, finalA.to),
503
- fromB: mapPosition(afterStepToFinal, finalB.from),
504
- toB: mapPosition(afterStepToFinal, finalB.to),
505
- deleted: createSpans(Math.max(0, finalA.to - finalA.from)),
506
- inserted: createSpans(Math.max(0, finalB.to - finalB.from))
507
- });
508
- }
509
- } catch (err) {
510
- _iterator5.e(err);
511
- } finally {
512
- _iterator5.f();
513
- }
514
- result.push({
515
- isGranular: true,
516
- changes: mergeOverlappingByNewDocRange(stepChanges)
517
- });
518
- continue;
519
- }
520
- result.push({
521
- isGranular: false,
522
- changes: [{
523
- fromA: fromA,
524
- toA: toA,
525
- fromB: fromB,
526
- toB: toB,
527
- deleted: createSpans(Math.max(0, toA - fromA)),
528
- inserted: createSpans(Math.max(0, toB - fromB))
529
- }]
530
- });
531
- }
532
- return result;
533
411
  };
@@ -772,6 +772,15 @@ var _classifyChild = function classifyChild(childA, childB, changes, originalDoc
772
772
  var childrenB = childRefs(blockB);
773
773
  var childrenA = wrapperA ? childRefs(wrapperA) : [];
774
774
  var out = [];
775
+
776
+ // An attribute-only change on the wrapper itself (e.g. a table cell's
777
+ // `background`) sits on the node boundary, not inside any inner child, so the
778
+ // recursion below would emit nothing and the change would be dropped. Emit a
779
+ // whole-wrapper change instead, which also subsumes any inner content change.
780
+ if (wrapperA && !wrapperA.node.sameMarkup(blockB.node)) {
781
+ out.push((0, _helpers.makePromotedChange)(wrapperA.from, wrapperA.to, blockB.from, blockB.to, 'node'));
782
+ return out;
783
+ }
775
784
  // LCS-align inner children (mirrors classifyContainer) so a paragraph inserted/deleted
776
785
  // inside the cell/column does not mis-pair every subsequent inner child by index. We never
777
786
  // promote the wrapper itself here — we only classify each inner child.