@atlaskit/editor-plugin-show-diff 13.0.1 → 13.0.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.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,31 @@
1
1
  # @atlaskit/editor-plugin-show-diff
2
2
 
3
+ ## 13.0.4
4
+
5
+ ### Patch Changes
6
+
7
+ - [`fe858303d6a81`](https://bitbucket.org/atlassian/atlassian-frontend-monorepo/commits/fe858303d6a81) -
8
+ [ux] Under `platform_editor_diff_plugin_extended`, prevent a replacement of the first document
9
+ heading from overlapping its diff.
10
+
11
+ ## 13.0.3
12
+
13
+ ### Patch Changes
14
+
15
+ - Updated dependencies
16
+
17
+ ## 13.0.2
18
+
19
+ ### Patch Changes
20
+
21
+ - [`0a2a45bf998e8`](https://bitbucket.org/atlassian/atlassian-frontend-monorepo/commits/0a2a45bf998e8) -
22
+ [ux] Fix AI suggested-edits diff for table cells (behind the
23
+ `platform_editor_diff_plugin_extended` gate). When a suggestion fills multiple empty cells at once
24
+ (e.g. "Add table headers"), the added content now shows in every cell, the row-spanning indicator
25
+ anchors stay inside their cells instead of collapsing the table, and a cell being filled is
26
+ highlighted as an addition (purple) rather than a deletion (grey).
27
+ - Updated dependencies
28
+
3
29
  ## 13.0.1
4
30
 
5
31
  ### Patch Changes
@@ -36,7 +36,7 @@ function ownKeys(e, r) { var t = Object.keys(e); if (Object.getOwnPropertySymbol
36
36
  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; }
37
37
  function _createForOfIteratorHelper(r, e) { var t = "undefined" != typeof Symbol && r[Symbol.iterator] || r["@@iterator"]; if (!t) { if (Array.isArray(r) || (t = _unsupportedIterableToArray(r)) || e && r && "number" == typeof r.length) { t && (r = t); var _n = 0, F = function F() {}; return { s: F, n: function n() { return _n >= r.length ? { done: !0 } : { done: !1, value: r[_n++] }; }, e: function e(r) { throw r; }, f: F }; } throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); } var o, a = !0, u = !1; return { s: function s() { t = t.call(r); }, n: function n() { var r = t.next(); return a = r.done, r; }, e: function e(r) { u = !0, o = r; }, f: function f() { try { a || null == t.return || t.return(); } finally { if (u) throw o; } } }; }
38
38
  function _unsupportedIterableToArray(r, a) { if (r) { if ("string" == typeof r) return _arrayLikeToArray(r, a); var t = {}.toString.call(r).slice(8, -1); return "Object" === t && r.constructor && (t = r.constructor.name), "Map" === t || "Set" === t ? Array.from(r) : "Arguments" === t || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(t) ? _arrayLikeToArray(r, a) : void 0; } }
39
- function _arrayLikeToArray(r, a) { (null == a || a > r.length) && (a = r.length); for (var e = 0, n = Array(a); e < a; e++) n[e] = r[e]; return n; } // eslint-disable-next-line @atlassian/tangerine/import/entry-points
39
+ function _arrayLikeToArray(r, a) { (null == a || a > r.length) && (a = r.length); for (var e = 0, n = Array(a); e < a; e++) n[e] = r[e]; return n; }
40
40
  var getChanges = function getChanges(_ref) {
41
41
  var changeset = _ref.changeset,
42
42
  originalDoc = _ref.originalDoc,
@@ -17,11 +17,8 @@ function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t =
17
17
  function _createForOfIteratorHelper(r, e) { var t = "undefined" != typeof Symbol && r[Symbol.iterator] || r["@@iterator"]; if (!t) { if (Array.isArray(r) || (t = _unsupportedIterableToArray(r)) || e && r && "number" == typeof r.length) { t && (r = t); var _n = 0, F = function F() {}; return { s: F, n: function n() { return _n >= r.length ? { done: !0 } : { done: !1, value: r[_n++] }; }, e: function e(r) { throw r; }, f: F }; } throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); } var o, a = !0, u = !1; return { s: function s() { t = t.call(r); }, n: function n() { var r = t.next(); return a = r.done, r; }, e: function e(r) { u = !0, o = r; }, f: function f() { try { a || null == t.return || t.return(); } finally { if (u) throw o; } } }; }
18
18
  function _unsupportedIterableToArray(r, a) { if (r) { if ("string" == typeof r) return _arrayLikeToArray(r, a); var t = {}.toString.call(r).slice(8, -1); return "Object" === t && r.constructor && (t = r.constructor.name), "Map" === t || "Set" === t ? Array.from(r) : "Arguments" === t || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(t) ? _arrayLikeToArray(r, a) : void 0; } }
19
19
  function _arrayLikeToArray(r, a) { (null == a || a > r.length) && (a = r.length); for (var e = 0, n = Array(a); e < a; e++) n[e] = r[e]; return n; }
20
- // @ts-ignore TS1501: This regular expression flag is only available when targeting 'es6' or later.
21
20
  var WORD_CHAR_REGEX = /(?:[0-9A-Z_a-z\xAA\xB2\xB3\xB5\xB9\xBA\xBC-\xBE\xC0-\xD6\xD8-\xF6\xF8-\u02C1\u02C6-\u02D1\u02E0-\u02E4\u02EC\u02EE\u0370-\u0374\u0376\u0377\u037A-\u037D\u037F\u0386\u0388-\u038A\u038C\u038E-\u03A1\u03A3-\u03F5\u03F7-\u0481\u048A-\u052F\u0531-\u0556\u0559\u0560-\u0588\u05D0-\u05EA\u05EF-\u05F2\u0620-\u064A\u0660-\u0669\u066E\u066F\u0671-\u06D3\u06D5\u06E5\u06E6\u06EE-\u06FC\u06FF\u0710\u0712-\u072F\u074D-\u07A5\u07B1\u07C0-\u07EA\u07F4\u07F5\u07FA\u0800-\u0815\u081A\u0824\u0828\u0840-\u0858\u0860-\u086A\u0870-\u0887\u0889-\u088F\u08A0-\u08C9\u0904-\u0939\u093D\u0950\u0958-\u0961\u0966-\u096F\u0971-\u0980\u0985-\u098C\u098F\u0990\u0993-\u09A8\u09AA-\u09B0\u09B2\u09B6-\u09B9\u09BD\u09CE\u09DC\u09DD\u09DF-\u09E1\u09E6-\u09F1\u09F4-\u09F9\u09FC\u0A05-\u0A0A\u0A0F\u0A10\u0A13-\u0A28\u0A2A-\u0A30\u0A32\u0A33\u0A35\u0A36\u0A38\u0A39\u0A59-\u0A5C\u0A5E\u0A66-\u0A6F\u0A72-\u0A74\u0A85-\u0A8D\u0A8F-\u0A91\u0A93-\u0AA8\u0AAA-\u0AB0\u0AB2\u0AB3\u0AB5-\u0AB9\u0ABD\u0AD0\u0AE0\u0AE1\u0AE6-\u0AEF\u0AF9\u0B05-\u0B0C\u0B0F\u0B10\u0B13-\u0B28\u0B2A-\u0B30\u0B32\u0B33\u0B35-\u0B39\u0B3D\u0B5C\u0B5D\u0B5F-\u0B61\u0B66-\u0B6F\u0B71-\u0B77\u0B83\u0B85-\u0B8A\u0B8E-\u0B90\u0B92-\u0B95\u0B99\u0B9A\u0B9C\u0B9E\u0B9F\u0BA3\u0BA4\u0BA8-\u0BAA\u0BAE-\u0BB9\u0BD0\u0BE6-\u0BF2\u0C05-\u0C0C\u0C0E-\u0C10\u0C12-\u0C28\u0C2A-\u0C39\u0C3D\u0C58-\u0C5A\u0C5C\u0C5D\u0C60\u0C61\u0C66-\u0C6F\u0C78-\u0C7E\u0C80\u0C85-\u0C8C\u0C8E-\u0C90\u0C92-\u0CA8\u0CAA-\u0CB3\u0CB5-\u0CB9\u0CBD\u0CDC-\u0CDE\u0CE0\u0CE1\u0CE6-\u0CEF\u0CF1\u0CF2\u0D04-\u0D0C\u0D0E-\u0D10\u0D12-\u0D3A\u0D3D\u0D4E\u0D54-\u0D56\u0D58-\u0D61\u0D66-\u0D78\u0D7A-\u0D7F\u0D85-\u0D96\u0D9A-\u0DB1\u0DB3-\u0DBB\u0DBD\u0DC0-\u0DC6\u0DE6-\u0DEF\u0E01-\u0E30\u0E32\u0E33\u0E40-\u0E46\u0E50-\u0E59\u0E81\u0E82\u0E84\u0E86-\u0E8A\u0E8C-\u0EA3\u0EA5\u0EA7-\u0EB0\u0EB2\u0EB3\u0EBD\u0EC0-\u0EC4\u0EC6\u0ED0-\u0ED9\u0EDC-\u0EDF\u0F00\u0F20-\u0F33\u0F40-\u0F47\u0F49-\u0F6C\u0F88-\u0F8C\u1000-\u102A\u103F-\u1049\u1050-\u1055\u105A-\u105D\u1061\u1065\u1066\u106E-\u1070\u1075-\u1081\u108E\u1090-\u1099\u10A0-\u10C5\u10C7\u10CD\u10D0-\u10FA\u10FC-\u1248\u124A-\u124D\u1250-\u1256\u1258\u125A-\u125D\u1260-\u1288\u128A-\u128D\u1290-\u12B0\u12B2-\u12B5\u12B8-\u12BE\u12C0\u12C2-\u12C5\u12C8-\u12D6\u12D8-\u1310\u1312-\u1315\u1318-\u135A\u1369-\u137C\u1380-\u138F\u13A0-\u13F5\u13F8-\u13FD\u1401-\u166C\u166F-\u167F\u1681-\u169A\u16A0-\u16EA\u16EE-\u16F8\u1700-\u1711\u171F-\u1731\u1740-\u1751\u1760-\u176C\u176E-\u1770\u1780-\u17B3\u17D7\u17DC\u17E0-\u17E9\u17F0-\u17F9\u1810-\u1819\u1820-\u1878\u1880-\u1884\u1887-\u18A8\u18AA\u18B0-\u18F5\u1900-\u191E\u1946-\u196D\u1970-\u1974\u1980-\u19AB\u19B0-\u19C9\u19D0-\u19DA\u1A00-\u1A16\u1A20-\u1A54\u1A80-\u1A89\u1A90-\u1A99\u1AA7\u1B05-\u1B33\u1B45-\u1B4C\u1B50-\u1B59\u1B83-\u1BA0\u1BAE-\u1BE5\u1C00-\u1C23\u1C40-\u1C49\u1C4D-\u1C7D\u1C80-\u1C8A\u1C90-\u1CBA\u1CBD-\u1CBF\u1CE9-\u1CEC\u1CEE-\u1CF3\u1CF5\u1CF6\u1CFA\u1D00-\u1DBF\u1E00-\u1F15\u1F18-\u1F1D\u1F20-\u1F45\u1F48-\u1F4D\u1F50-\u1F57\u1F59\u1F5B\u1F5D\u1F5F-\u1F7D\u1F80-\u1FB4\u1FB6-\u1FBC\u1FBE\u1FC2-\u1FC4\u1FC6-\u1FCC\u1FD0-\u1FD3\u1FD6-\u1FDB\u1FE0-\u1FEC\u1FF2-\u1FF4\u1FF6-\u1FFC\u2070\u2071\u2074-\u2079\u207F-\u2089\u2090-\u209C\u2102\u2107\u210A-\u2113\u2115\u2119-\u211D\u2124\u2126\u2128\u212A-\u212D\u212F-\u2139\u213C-\u213F\u2145-\u2149\u214E\u2150-\u2189\u2460-\u249B\u24EA-\u24FF\u2776-\u2793\u2C00-\u2CE4\u2CEB-\u2CEE\u2CF2\u2CF3\u2CFD\u2D00-\u2D25\u2D27\u2D2D\u2D30-\u2D67\u2D6F\u2D80-\u2D96\u2DA0-\u2DA6\u2DA8-\u2DAE\u2DB0-\u2DB6\u2DB8-\u2DBE\u2DC0-\u2DC6\u2DC8-\u2DCE\u2DD0-\u2DD6\u2DD8-\u2DDE\u2E2F\u3005-\u3007\u3021-\u3029\u3031-\u3035\u3038-\u303C\u3041-\u3096\u309D-\u309F\u30A1-\u30FA\u30FC-\u30FF\u3105-\u312F\u3131-\u318E\u3192-\u3195\u31A0-\u31BF\u31F0-\u31FF\u3220-\u3229\u3248-\u324F\u3251-\u325F\u3280-\u3289\u32B1-\u32BF\u3400-\u4DBF\u4E00-\uA48C\uA4D0-\uA4FD\uA500-\uA60C\uA610-\uA62B\uA640-\uA66E\uA67F-\uA69D\uA6A0-\uA6EF\uA717-\uA71F\uA722-\uA788\uA78B-\uA7DC\uA7F1-\uA801\uA803-\uA805\uA807-\uA80A\uA80C-\uA822\uA830-\uA835\uA840-\uA873\uA882-\uA8B3\uA8D0-\uA8D9\uA8F2-\uA8F7\uA8FB\uA8FD\uA8FE\uA900-\uA925\uA930-\uA946\uA960-\uA97C\uA984-\uA9B2\uA9CF-\uA9D9\uA9E0-\uA9E4\uA9E6-\uA9FE\uAA00-\uAA28\uAA40-\uAA42\uAA44-\uAA4B\uAA50-\uAA59\uAA60-\uAA76\uAA7A\uAA7E-\uAAAF\uAAB1\uAAB5\uAAB6\uAAB9-\uAABD\uAAC0\uAAC2\uAADB-\uAADD\uAAE0-\uAAEA\uAAF2-\uAAF4\uAB01-\uAB06\uAB09-\uAB0E\uAB11-\uAB16\uAB20-\uAB26\uAB28-\uAB2E\uAB30-\uAB5A\uAB5C-\uAB69\uAB70-\uABE2\uABF0-\uABF9\uAC00-\uD7A3\uD7B0-\uD7C6\uD7CB-\uD7FB\uF900-\uFA6D\uFA70-\uFAD9\uFB00-\uFB06\uFB13-\uFB17\uFB1D\uFB1F-\uFB28\uFB2A-\uFB36\uFB38-\uFB3C\uFB3E\uFB40\uFB41\uFB43\uFB44\uFB46-\uFBB1\uFBD3-\uFD3D\uFD50-\uFD8F\uFD92-\uFDC7\uFDF0-\uFDFB\uFE70-\uFE74\uFE76-\uFEFC\uFF10-\uFF19\uFF21-\uFF3A\uFF41-\uFF5A\uFF66-\uFFBE\uFFC2-\uFFC7\uFFCA-\uFFCF\uFFD2-\uFFD7\uFFDA-\uFFDC]|\uD800[\uDC00-\uDC0B\uDC0D-\uDC26\uDC28-\uDC3A\uDC3C\uDC3D\uDC3F-\uDC4D\uDC50-\uDC5D\uDC80-\uDCFA\uDD07-\uDD33\uDD40-\uDD78\uDD8A\uDD8B\uDE80-\uDE9C\uDEA0-\uDED0\uDEE1-\uDEFB\uDF00-\uDF23\uDF2D-\uDF4A\uDF50-\uDF75\uDF80-\uDF9D\uDFA0-\uDFC3\uDFC8-\uDFCF\uDFD1-\uDFD5]|\uD801[\uDC00-\uDC9D\uDCA0-\uDCA9\uDCB0-\uDCD3\uDCD8-\uDCFB\uDD00-\uDD27\uDD30-\uDD63\uDD70-\uDD7A\uDD7C-\uDD8A\uDD8C-\uDD92\uDD94\uDD95\uDD97-\uDDA1\uDDA3-\uDDB1\uDDB3-\uDDB9\uDDBB\uDDBC\uDDC0-\uDDF3\uDE00-\uDF36\uDF40-\uDF55\uDF60-\uDF67\uDF80-\uDF85\uDF87-\uDFB0\uDFB2-\uDFBA]|\uD802[\uDC00-\uDC05\uDC08\uDC0A-\uDC35\uDC37\uDC38\uDC3C\uDC3F-\uDC55\uDC58-\uDC76\uDC79-\uDC9E\uDCA7-\uDCAF\uDCE0-\uDCF2\uDCF4\uDCF5\uDCFB-\uDD1B\uDD20-\uDD39\uDD40-\uDD59\uDD80-\uDDB7\uDDBC-\uDDCF\uDDD2-\uDE00\uDE10-\uDE13\uDE15-\uDE17\uDE19-\uDE35\uDE40-\uDE48\uDE60-\uDE7E\uDE80-\uDE9F\uDEC0-\uDEC7\uDEC9-\uDEE4\uDEEB-\uDEEF\uDF00-\uDF35\uDF40-\uDF55\uDF58-\uDF72\uDF78-\uDF91\uDFA9-\uDFAF]|\uD803[\uDC00-\uDC48\uDC80-\uDCB2\uDCC0-\uDCF2\uDCFA-\uDD23\uDD30-\uDD39\uDD40-\uDD65\uDD6F-\uDD85\uDE60-\uDE7E\uDE80-\uDEA9\uDEB0\uDEB1\uDEC2-\uDEC7\uDF00-\uDF27\uDF30-\uDF45\uDF51-\uDF54\uDF70-\uDF81\uDFB0-\uDFCB\uDFE0-\uDFF6]|\uD804[\uDC03-\uDC37\uDC52-\uDC6F\uDC71\uDC72\uDC75\uDC83-\uDCAF\uDCD0-\uDCE8\uDCF0-\uDCF9\uDD03-\uDD26\uDD36-\uDD3F\uDD44\uDD47\uDD50-\uDD72\uDD76\uDD83-\uDDB2\uDDC1-\uDDC4\uDDD0-\uDDDA\uDDDC\uDDE1-\uDDF4\uDE00-\uDE11\uDE13-\uDE2B\uDE3F\uDE40\uDE80-\uDE86\uDE88\uDE8A-\uDE8D\uDE8F-\uDE9D\uDE9F-\uDEA8\uDEB0-\uDEDE\uDEF0-\uDEF9\uDF05-\uDF0C\uDF0F\uDF10\uDF13-\uDF28\uDF2A-\uDF30\uDF32\uDF33\uDF35-\uDF39\uDF3D\uDF50\uDF5D-\uDF61\uDF80-\uDF89\uDF8B\uDF8E\uDF90-\uDFB5\uDFB7\uDFD1\uDFD3]|\uD805[\uDC00-\uDC34\uDC47-\uDC4A\uDC50-\uDC59\uDC5F-\uDC61\uDC80-\uDCAF\uDCC4\uDCC5\uDCC7\uDCD0-\uDCD9\uDD80-\uDDAE\uDDD8-\uDDDB\uDE00-\uDE2F\uDE44\uDE50-\uDE59\uDE80-\uDEAA\uDEB8\uDEC0-\uDEC9\uDED0-\uDEE3\uDF00-\uDF1A\uDF30-\uDF3B\uDF40-\uDF46]|\uD806[\uDC00-\uDC2B\uDCA0-\uDCF2\uDCFF-\uDD06\uDD09\uDD0C-\uDD13\uDD15\uDD16\uDD18-\uDD2F\uDD3F\uDD41\uDD50-\uDD59\uDDA0-\uDDA7\uDDAA-\uDDD0\uDDE1\uDDE3\uDE00\uDE0B-\uDE32\uDE3A\uDE50\uDE5C-\uDE89\uDE9D\uDEB0-\uDEF8\uDFC0-\uDFE0\uDFF0-\uDFF9]|\uD807[\uDC00-\uDC08\uDC0A-\uDC2E\uDC40\uDC50-\uDC6C\uDC72-\uDC8F\uDD00-\uDD06\uDD08\uDD09\uDD0B-\uDD30\uDD46\uDD50-\uDD59\uDD60-\uDD65\uDD67\uDD68\uDD6A-\uDD89\uDD98\uDDA0-\uDDA9\uDDB0-\uDDDB\uDDE0-\uDDE9\uDEE0-\uDEF2\uDF02\uDF04-\uDF10\uDF12-\uDF33\uDF50-\uDF59\uDFB0\uDFC0-\uDFD4]|\uD808[\uDC00-\uDF99]|\uD809[\uDC00-\uDC6E\uDC80-\uDD43]|\uD80B[\uDF90-\uDFF0]|[\uD80C\uD80E\uD80F\uD81C-\uD822\uD840-\uD868\uD86A-\uD86D\uD86F-\uD872\uD874-\uD879\uD880-\uD883\uD885-\uD88C][\uDC00-\uDFFF]|\uD80D[\uDC00-\uDC2F\uDC41-\uDC46\uDC60-\uDFFF]|\uD810[\uDC00-\uDFFA]|\uD811[\uDC00-\uDE46]|\uD818[\uDD00-\uDD1D\uDD30-\uDD39]|\uD81A[\uDC00-\uDE38\uDE40-\uDE5E\uDE60-\uDE69\uDE70-\uDEBE\uDEC0-\uDEC9\uDED0-\uDEED\uDF00-\uDF2F\uDF40-\uDF43\uDF50-\uDF59\uDF5B-\uDF61\uDF63-\uDF77\uDF7D-\uDF8F]|\uD81B[\uDD40-\uDD6C\uDD70-\uDD79\uDE40-\uDE96\uDEA0-\uDEB8\uDEBB-\uDED3\uDF00-\uDF4A\uDF50\uDF93-\uDF9F\uDFE0\uDFE1\uDFE3\uDFF2-\uDFF6]|\uD823[\uDC00-\uDCD5\uDCFF-\uDD1E\uDD80-\uDDF2]|\uD82B[\uDFF0-\uDFF3\uDFF5-\uDFFB\uDFFD\uDFFE]|\uD82C[\uDC00-\uDD22\uDD32\uDD50-\uDD52\uDD55\uDD64-\uDD67\uDD70-\uDEFB]|\uD82F[\uDC00-\uDC6A\uDC70-\uDC7C\uDC80-\uDC88\uDC90-\uDC99]|\uD833[\uDCF0-\uDCF9]|\uD834[\uDEC0-\uDED3\uDEE0-\uDEF3\uDF60-\uDF78]|\uD835[\uDC00-\uDC54\uDC56-\uDC9C\uDC9E\uDC9F\uDCA2\uDCA5\uDCA6\uDCA9-\uDCAC\uDCAE-\uDCB9\uDCBB\uDCBD-\uDCC3\uDCC5-\uDD05\uDD07-\uDD0A\uDD0D-\uDD14\uDD16-\uDD1C\uDD1E-\uDD39\uDD3B-\uDD3E\uDD40-\uDD44\uDD46\uDD4A-\uDD50\uDD52-\uDEA5\uDEA8-\uDEC0\uDEC2-\uDEDA\uDEDC-\uDEFA\uDEFC-\uDF14\uDF16-\uDF34\uDF36-\uDF4E\uDF50-\uDF6E\uDF70-\uDF88\uDF8A-\uDFA8\uDFAA-\uDFC2\uDFC4-\uDFCB\uDFCE-\uDFFF]|\uD837[\uDF00-\uDF1E\uDF25-\uDF2A]|\uD838[\uDC30-\uDC6D\uDD00-\uDD2C\uDD37-\uDD3D\uDD40-\uDD49\uDD4E\uDE90-\uDEAD\uDEC0-\uDEEB\uDEF0-\uDEF9]|\uD839[\uDCD0-\uDCEB\uDCF0-\uDCF9\uDDD0-\uDDED\uDDF0-\uDDFA\uDEC0-\uDEDE\uDEE0-\uDEE2\uDEE4\uDEE5\uDEE7-\uDEED\uDEF0-\uDEF4\uDEFE\uDEFF\uDFE0-\uDFE6\uDFE8-\uDFEB\uDFED\uDFEE\uDFF0-\uDFFE]|\uD83A[\uDC00-\uDCC4\uDCC7-\uDCCF\uDD00-\uDD43\uDD4B\uDD50-\uDD59]|\uD83B[\uDC71-\uDCAB\uDCAD-\uDCAF\uDCB1-\uDCB4\uDD01-\uDD2D\uDD2F-\uDD3D\uDE00-\uDE03\uDE05-\uDE1F\uDE21\uDE22\uDE24\uDE27\uDE29-\uDE32\uDE34-\uDE37\uDE39\uDE3B\uDE42\uDE47\uDE49\uDE4B\uDE4D-\uDE4F\uDE51\uDE52\uDE54\uDE57\uDE59\uDE5B\uDE5D\uDE5F\uDE61\uDE62\uDE64\uDE67-\uDE6A\uDE6C-\uDE72\uDE74-\uDE77\uDE79-\uDE7C\uDE7E\uDE80-\uDE89\uDE8B-\uDE9B\uDEA1-\uDEA3\uDEA5-\uDEA9\uDEAB-\uDEBB]|\uD83C[\uDD00-\uDD0C]|\uD83E[\uDFF0-\uDFF9]|\uD869[\uDC00-\uDEDF\uDF00-\uDFFF]|\uD86E[\uDC00-\uDC1D\uDC20-\uDFFF]|\uD873[\uDC00-\uDEAD\uDEB0-\uDFFF]|\uD87A[\uDC00-\uDFE0\uDFF0-\uDFFF]|\uD87B[\uDC00-\uDE5D]|\uD87E[\uDC00-\uDE1D]|\uD884[\uDC00-\uDF4A\uDF50-\uDFFF]|\uD88D[\uDC00-\uDC79])/;
22
- // @ts-ignore TS1501: This regular expression flag is only available when targeting 'es6' or later.
23
21
  var PUNCTUATION_REGEX = /(?:[!-#%-\*,-\/:;\?@\[-\]_\{\}\xA1\xA7\xAB\xB6\xB7\xBB\xBF\u037E\u0387\u055A-\u055F\u0589\u058A\u05BE\u05C0\u05C3\u05C6\u05F3\u05F4\u0609\u060A\u060C\u060D\u061B\u061D-\u061F\u066A-\u066D\u06D4\u0700-\u070D\u07F7-\u07F9\u0830-\u083E\u085E\u0964\u0965\u0970\u09FD\u0A76\u0AF0\u0C77\u0C84\u0DF4\u0E4F\u0E5A\u0E5B\u0F04-\u0F12\u0F14\u0F3A-\u0F3D\u0F85\u0FD0-\u0FD4\u0FD9\u0FDA\u104A-\u104F\u10FB\u1360-\u1368\u1400\u166E\u169B\u169C\u16EB-\u16ED\u1735\u1736\u17D4-\u17D6\u17D8-\u17DA\u1800-\u180A\u1944\u1945\u1A1E\u1A1F\u1AA0-\u1AA6\u1AA8-\u1AAD\u1B4E\u1B4F\u1B5A-\u1B60\u1B7D-\u1B7F\u1BFC-\u1BFF\u1C3B-\u1C3F\u1C7E\u1C7F\u1CC0-\u1CC7\u1CD3\u2010-\u2027\u2030-\u2043\u2045-\u2051\u2053-\u205E\u207D\u207E\u208D\u208E\u2308-\u230B\u2329\u232A\u2768-\u2775\u27C5\u27C6\u27E6-\u27EF\u2983-\u2998\u29D8-\u29DB\u29FC\u29FD\u2CF9-\u2CFC\u2CFE\u2CFF\u2D70\u2E00-\u2E2E\u2E30-\u2E4F\u2E52-\u2E5D\u3001-\u3003\u3008-\u3011\u3014-\u301F\u3030\u303D\u30A0\u30FB\uA4FE\uA4FF\uA60D-\uA60F\uA673\uA67E\uA6F2-\uA6F7\uA874-\uA877\uA8CE\uA8CF\uA8F8-\uA8FA\uA8FC\uA92E\uA92F\uA95F\uA9C1-\uA9CD\uA9DE\uA9DF\uAA5C-\uAA5F\uAADE\uAADF\uAAF0\uAAF1\uABEB\uFD3E\uFD3F\uFE10-\uFE19\uFE30-\uFE52\uFE54-\uFE61\uFE63\uFE68\uFE6A\uFE6B\uFF01-\uFF03\uFF05-\uFF0A\uFF0C-\uFF0F\uFF1A\uFF1B\uFF1F\uFF20\uFF3B-\uFF3D\uFF3F\uFF5B\uFF5D\uFF5F-\uFF65]|\uD800[\uDD00-\uDD02\uDF9F\uDFD0]|\uD801\uDD6F|\uD802[\uDC57\uDD1F\uDD3F\uDE50-\uDE58\uDE7F\uDEF0-\uDEF6\uDF39-\uDF3F\uDF99-\uDF9C]|\uD803[\uDD6E\uDEAD\uDED0\uDF55-\uDF59\uDF86-\uDF89]|\uD804[\uDC47-\uDC4D\uDCBB\uDCBC\uDCBE-\uDCC1\uDD40-\uDD43\uDD74\uDD75\uDDC5-\uDDC8\uDDCD\uDDDB\uDDDD-\uDDDF\uDE38-\uDE3D\uDEA9\uDFD4\uDFD5\uDFD7\uDFD8]|\uD805[\uDC4B-\uDC4F\uDC5A\uDC5B\uDC5D\uDCC6\uDDC1-\uDDD7\uDE41-\uDE43\uDE60-\uDE6C\uDEB9\uDF3C-\uDF3E]|\uD806[\uDC3B\uDD44-\uDD46\uDDE2\uDE3F-\uDE46\uDE9A-\uDE9C\uDE9E-\uDEA2\uDF00-\uDF09\uDFE1]|\uD807[\uDC41-\uDC45\uDC70\uDC71\uDEF7\uDEF8\uDF43-\uDF4F\uDFFF]|\uD809[\uDC70-\uDC74]|\uD80B[\uDFF1\uDFF2]|\uD81A[\uDE6E\uDE6F\uDEF5\uDF37-\uDF3B\uDF44]|\uD81B[\uDD6D-\uDD6F\uDE97-\uDE9A\uDFE2]|\uD82F\uDC9F|\uD836[\uDE87-\uDE8B]|\uD839\uDDFF|\uD83A[\uDD5E\uDD5F])/;
24
- // @ts-ignore TS1501: This regular expression flag is only available when targeting 'es6' or later.
25
22
  var WHITESPACE_REGEX = /[\t-\r \xA0\u1680\u2000-\u200A\u2028\u2029\u202F\u205F\u3000\uFEFF]/;
26
23
  var mapPosition = function mapPosition(mapping, pos) {
27
24
  return mapping.map(pos);
@@ -318,13 +318,28 @@ var createBlockIndicatorAnchorWidgets = exports.createBlockIndicatorAnchorWidget
318
318
  };
319
319
 
320
320
  /**
321
- * Creates invisible anchor widgets for a single inline diff range so that the
322
- * `IndicatorBar` can use CSS anchor positioning to align itself with the diff.
323
- *
324
- * - A `from` anchor is placed at the start of the range (top of the bar).
325
- * - A `to` anchor is placed at the end of the range (bottom of the bar).
326
- * - An optional `left` anchor is placed before the first textblock when the
327
- * diff is inside a resizable block node (table, layout, expand).
321
+ * A `from`/`to` on a `tableRow` boundary makes the anchor a direct `<tr>` (CSS grid)
322
+ * child, adding a phantom column that collapses the cells (EDITOR-8442). Clamp it
323
+ * inward into the neighbouring cell (`direction: 1` forward, `-1` back). Positions
324
+ * not on a row boundary are returned as-is.
325
+ */
326
+ var clampAnchorPosIntoCell = function clampAnchorPosIntoCell(doc, pos, direction) {
327
+ var $pos = doc.resolve(pos);
328
+ if ($pos.parent.type.name !== 'tableRow') {
329
+ return pos;
330
+ }
331
+ var cell = direction === 1 ? $pos.nodeAfter : $pos.nodeBefore;
332
+ if (!cell || cell.type.name !== 'tableHeader' && cell.type.name !== 'tableCell') {
333
+ return pos;
334
+ }
335
+ // +2 past the cell and its first child boundary = inside the cell's content;
336
+ // for the backward case, step back the same amount from the cell's end.
337
+ return direction === 1 ? pos + 2 : pos - 2;
338
+ };
339
+
340
+ /**
341
+ * Invisible `from`/`to` (and optional `left`) anchor widgets for one inline diff
342
+ * range, so the `IndicatorBar` can align itself via CSS anchor positioning.
328
343
  */
329
344
  var createInlineIndicatorAnchorWidgets = exports.createInlineIndicatorAnchorWidgets = function createInlineIndicatorAnchorWidgets(_ref3) {
330
345
  var doc = _ref3.doc,
@@ -338,6 +353,10 @@ var createInlineIndicatorAnchorWidgets = exports.createInlineIndicatorAnchorWidg
338
353
  });
339
354
  var maybeLeftAnchor = leftAnchor ? [leftAnchor] : [];
340
355
 
356
+ // Keep the start/end anchors out of the table row's grid (see helper above).
357
+ var fromPos = clampAnchorPosIntoCell(doc, from, 1);
358
+ var toPos = clampAnchorPosIntoCell(doc, to, -1);
359
+
341
360
  /**
342
361
  * Two widgets mark the start and end of the inline range so the
343
362
  * IndicatorBar can determine top/bottom even if
@@ -347,7 +366,7 @@ var createInlineIndicatorAnchorWidgets = exports.createInlineIndicatorAnchorWidg
347
366
  diffId: diffId,
348
367
  anchorType: _decorationKeys.AnchorTypeKey.from
349
368
  });
350
- var fromWidget = _view.Decoration.widget(from, function () {
369
+ var fromWidget = _view.Decoration.widget(fromPos, function () {
351
370
  var span = document.createElement('span');
352
371
  span.style.setProperty('anchor-name', "--".concat(fromAnchorKey));
353
372
  return span;
@@ -360,7 +379,7 @@ var createInlineIndicatorAnchorWidgets = exports.createInlineIndicatorAnchorWidg
360
379
  diffId: diffId,
361
380
  anchorType: _decorationKeys.AnchorTypeKey.to
362
381
  });
363
- var toWidget = _view.Decoration.widget(to, function () {
382
+ var toWidget = _view.Decoration.widget(toPos, function () {
364
383
  var span = document.createElement('span');
365
384
  span.style.setProperty('anchor-name', "--".concat(toAnchorKey));
366
385
  return span;
@@ -119,6 +119,32 @@ var getBlockNodeStyle = function getBlockNodeStyle(_ref) {
119
119
  return isTraditional ? isActive ? _traditional.traditionalStyleNodeActive : _traditional.traditionalStyleNodeNew : _standard.editingStyleNode;
120
120
  };
121
121
 
122
+ /**
123
+ * A table cell is "empty" only when it has no content at all — no text AND no
124
+ * non-text leaf/inline nodes (media, emoji, mention, inlineCard, status, date…).
125
+ * `textContent` alone misses those non-text nodes, so we also reject any leaf or
126
+ * inline descendant. An empty ADF cell (`tableCell > empty paragraph`) returns
127
+ * true; a cell containing only media/emoji returns false.
128
+ */
129
+ var isCellEmpty = function isCellEmpty(cellNode) {
130
+ if (cellNode.textContent.length > 0) {
131
+ return false;
132
+ }
133
+ var hasNonTextContent = false;
134
+ cellNode.descendants(function (node) {
135
+ if (hasNonTextContent) {
136
+ return false;
137
+ }
138
+ // A non-text leaf or inline node (media, emoji, mention, etc.) = real content.
139
+ if (!node.isText && (node.isLeaf || node.isInline)) {
140
+ hasNonTextContent = true;
141
+ return false;
142
+ }
143
+ return true;
144
+ });
145
+ return !hasNonTextContent;
146
+ };
147
+
122
148
  /**
123
149
  * Node decoration used for block-level insertions. When isActive, uses emphasised (pressed) styling.
124
150
  *
@@ -160,7 +186,13 @@ var createBlockChangedDecoration = exports.createBlockChangedDecoration = functi
160
186
  var isRoundedTable = (0, _expValEquals.expValEquals)('platform_editor_table_diff_rounded_corners', 'isEnabled', true);
161
187
  var addedCellStyle = isTraditional ? _traditional.traditionalAddedCellOverlayStyle : _standard.addedCellOverlayStyle;
162
188
  var deletedCellStyle = isTraditional ? isRoundedTable ? _traditional.deletedTraditionalCellOverlayRoundedStyle : _traditional.deletedTraditionalCellOverlayStyle : isRoundedTable ? _standard.deletedCellOverlayRoundedStyle : _standard.deletedCellOverlayStyle;
163
- var cellOverlayStyle = isInserted ? addedCellStyle : deletedCellStyle;
189
+
190
+ // On an inverted diff, an empty cell being filled is an addition, so give it the
191
+ // added (purple) overlay instead of the deleted (grey) one (EDITOR-8442).
192
+ var cellNode = doc === null || doc === void 0 ? void 0 : doc.nodeAt(change.from);
193
+ var isEmptyCellBeingFilled = !isInserted && !!cellNode && isCellEmpty(cellNode);
194
+ var useAddedStyle = isInserted || isEmptyCellBeingFilled;
195
+ var cellOverlayStyle = useAddedStyle ? addedCellStyle : deletedCellStyle;
164
196
  cellOverlay.setAttribute('style', cellOverlayStyle);
165
197
  decorations.push(
166
198
  // change.to - 1 to position the overlay inside the end of the cell
@@ -20,27 +20,99 @@ var isHeadingLevel = function isHeadingLevel(level) {
20
20
  };
21
21
 
22
22
  /**
23
- * This function is used to create a decoration widget to show content
24
- * that is not in the current document.
23
+ * Render one content widget per table cell, positioned inside the matching cell in
24
+ * `newDoc`, so a whole-cell replacement shows its added content in-place instead of
25
+ * being dropped (EDITOR-8442).
25
26
  */
26
- var createNodeChangedDecorationWidget = exports.createNodeChangedDecorationWidget = function createNodeChangedDecorationWidget(_ref) {
27
- var _slice$content, _slice$content2, _slice$content3, _$safeInsertPos$nodeB, _slice$content$firstC;
28
- var change = _ref.change,
29
- doc = _ref.doc,
27
+ var createTableCellContentWidgets = function createTableCellContentWidgets(_ref) {
28
+ var slice = _ref.slice,
29
+ newDoc = _ref.newDoc,
30
+ change = _ref.change,
30
31
  nodeViewSerializer = _ref.nodeViewSerializer,
31
32
  colorScheme = _ref.colorScheme,
32
- newDoc = _ref.newDoc,
33
- intl = _ref.intl,
34
- activeIndexPos = _ref.activeIndexPos,
35
- _ref$isInserted = _ref.isInserted,
36
- isInserted = _ref$isInserted === void 0 ? false : _ref$isInserted,
37
- _ref$showIndicators = _ref.showIndicators,
38
- showIndicators = _ref$showIndicators === void 0 ? false : _ref$showIndicators,
39
- _ref$placeBelow = _ref.placeBelow,
40
- placeBelow = _ref$placeBelow === void 0 ? false : _ref$placeBelow,
41
- diffType = _ref.diffType,
42
- _ref$hideAddedDiffsUn = _ref.hideAddedDiffsUnderline,
43
- hideAddedDiffsUnderline = _ref$hideAddedDiffsUn === void 0 ? false : _ref$hideAddedDiffsUn;
33
+ isInserted = _ref.isInserted,
34
+ diffType = _ref.diffType;
35
+ // Collect the inner content position of each cell in the new document within
36
+ // the change range. Whole-cell replacement preserves cell count/order, so the
37
+ // nth new-doc cell corresponds to the nth cell in the slice.
38
+ var newCellInnerPositions = [];
39
+ newDoc.nodesBetween(change.fromB, change.toB, function (node, pos) {
40
+ if (node.type.name === 'tableHeader' || node.type.name === 'tableCell') {
41
+ // Skip malformed cells with no block child: `pos + 2` would be out of bounds.
42
+ if (node.childCount === 0) {
43
+ return false;
44
+ }
45
+ // +1 into the cell, +1 into its first child (the paragraph) = inner content.
46
+ newCellInnerPositions.push(pos + 2);
47
+ return false;
48
+ }
49
+ return true;
50
+ });
51
+ var decorations = [];
52
+ slice.content.forEach(function (cellNode, _offset, index) {
53
+ var targetPos = newCellInnerPositions[index];
54
+ if (targetPos === undefined || cellNode.content.size === 0) {
55
+ return;
56
+ }
57
+ var dom = document.createElement('span');
58
+ // A cell contains block content (paragraphs); serialize the cell's content
59
+ // so every block inside the cell is rendered.
60
+ cellNode.content.forEach(function (blockNode) {
61
+ var nodeView = nodeViewSerializer.tryCreateNodeView(blockNode);
62
+ var wrapper = (0, _wrapBlockNodeView.createContentWrapper)(colorScheme, false, isInserted, diffType);
63
+ if (nodeView) {
64
+ wrapper.append(nodeView);
65
+ } else {
66
+ var serialized = blockNode.type.inlineContent ? nodeViewSerializer.serializeFragment(blockNode.content) : nodeViewSerializer.serializeNode(blockNode);
67
+ if (serialized) {
68
+ wrapper.append(serialized);
69
+ }
70
+ }
71
+ dom.append(wrapper);
72
+ });
73
+ if (dom.childNodes.length === 0) {
74
+ return;
75
+ }
76
+
77
+ // This helper only runs for added content, so use the "changed" testid — not
78
+ // "deleted", which page models match as removed content.
79
+ dom.setAttribute('data-testid', 'show-diff-changed-decoration');
80
+
81
+ // Skip findSafeInsertPos: it walks forward to a schema-valid cell-insert point
82
+ // and pushes the widget into the next cell. `targetPos` is the correct spot.
83
+ decorations.push(_view.Decoration.widget(targetPos, dom, _objectSpread({}, (0, _decorationKeys.buildDiffDecorationSpec)(_objectSpread({
84
+ decorationType: 'widget',
85
+ diffId: crypto.randomUUID(),
86
+ diffType: diffType
87
+ }, (0, _isExtendedEnabled.isExtendedEnabled)(diffType) && {
88
+ side: -1
89
+ })))));
90
+ });
91
+ return decorations;
92
+ };
93
+
94
+ /**
95
+ * This function is used to create a decoration widget to show content
96
+ * that is not in the current document.
97
+ */
98
+ var createNodeChangedDecorationWidget = exports.createNodeChangedDecorationWidget = function createNodeChangedDecorationWidget(_ref2) {
99
+ var _slice$content, _slice$content2, _slice$content3, _$safeInsertPos$nodeB, _slice$content$firstC, _newDoc$firstChild, _slice$content$firstC2;
100
+ var change = _ref2.change,
101
+ doc = _ref2.doc,
102
+ nodeViewSerializer = _ref2.nodeViewSerializer,
103
+ colorScheme = _ref2.colorScheme,
104
+ newDoc = _ref2.newDoc,
105
+ intl = _ref2.intl,
106
+ activeIndexPos = _ref2.activeIndexPos,
107
+ _ref2$isInserted = _ref2.isInserted,
108
+ isInserted = _ref2$isInserted === void 0 ? false : _ref2$isInserted,
109
+ _ref2$showIndicators = _ref2.showIndicators,
110
+ showIndicators = _ref2$showIndicators === void 0 ? false : _ref2$showIndicators,
111
+ _ref2$placeBelow = _ref2.placeBelow,
112
+ placeBelow = _ref2$placeBelow === void 0 ? false : _ref2$placeBelow,
113
+ diffType = _ref2.diffType,
114
+ _ref2$hideAddedDiffsU = _ref2.hideAddedDiffsUnderline,
115
+ hideAddedDiffsUnderline = _ref2$hideAddedDiffsU === void 0 ? false : _ref2$hideAddedDiffsU;
44
116
  var slice = doc.slice(change.fromA, change.toA);
45
117
  var shouldSkipDeletedEmptyParagraphDecoration = !isInserted && (slice === null || slice === void 0 || (_slice$content = slice.content) === null || _slice$content === void 0 ? void 0 : _slice$content.childCount) === 1 && (slice === null || slice === void 0 || (_slice$content2 = slice.content) === null || _slice$content2 === void 0 || (_slice$content2 = _slice$content2.firstChild) === null || _slice$content2 === void 0 ? void 0 : _slice$content2.type.name) === 'paragraph' && (slice === null || slice === void 0 || (_slice$content3 = slice.content) === null || _slice$content3 === void 0 || (_slice$content3 = _slice$content3.firstChild) === null || _slice$content3 === void 0 ? void 0 : _slice$content3.content.size) === 0;
46
118
  // Widget decoration used for deletions as the content is not in the document
@@ -63,7 +135,23 @@ var createNodeChangedDecorationWidget = exports.createNodeChangedDecorationWidge
63
135
  return ['tableRow'].includes(siblingNode.type.name);
64
136
  });
65
137
  });
138
+
139
+ // Added whole cells (e.g. "Add table headers"): render per-cell content widgets.
140
+ // Deleted whole cells (delete row/col) fall through — they don't exist in `newDoc`,
141
+ // so there's nowhere to anchor content and we keep the original "render nothing".
142
+ if (isTableCellContent && isInserted) {
143
+ return createTableCellContentWidgets({
144
+ slice: slice,
145
+ newDoc: newDoc,
146
+ change: change,
147
+ nodeViewSerializer: nodeViewSerializer,
148
+ colorScheme: colorScheme,
149
+ isInserted: isInserted,
150
+ diffType: diffType
151
+ });
152
+ }
66
153
  if (isTableCellContent) {
154
+ // Deleted whole cells: nothing to render (see note above).
67
155
  return [];
68
156
  }
69
157
  if (isTableRowContent) {
@@ -84,11 +172,18 @@ var createNodeChangedDecorationWidget = exports.createNodeChangedDecorationWidge
84
172
  var $safeInsertPos = newDoc.resolve(safeInsertPos);
85
173
  var isTopLevelInsert = $safeInsertPos.depth === 0;
86
174
  var hasPreviousBlock = ((_$safeInsertPos$nodeB = $safeInsertPos.nodeBefore) === null || _$safeInsertPos$nodeB === void 0 ? void 0 : _$safeInsertPos$nodeB.isBlock) === true;
87
- if ((0, _isExtendedEnabled.isExtendedEnabled)(diffType) && isTopLevelInsert && hasPreviousBlock) {
175
+ var isFirstDocHeadingReplacement = (0, _isExtendedEnabled.isExtendedEnabled)(diffType) && !placeBelow && change.fromB === 0 && ((_slice$content$firstC = slice.content.firstChild) === null || _slice$content$firstC === void 0 ? void 0 : _slice$content$firstC.type.name) === 'heading' && ((_newDoc$firstChild = newDoc.firstChild) === null || _newDoc$firstChild === void 0 ? void 0 : _newDoc$firstChild.type.name) === 'heading';
176
+ if ((0, _isExtendedEnabled.isExtendedEnabled)(diffType) && isTopLevelInsert && hasPreviousBlock && !isFirstDocHeadingReplacement) {
88
177
  // Editor CSS removes the top margin from a block when it is its parent's first child.
89
178
  // Keep the serialized block as a subsequent child so each block type retains its own margin.
90
179
  dom.append(dom.ownerDocument.createElement('div'));
91
180
  }
181
+ if (isFirstDocHeadingReplacement) {
182
+ // The replacement widget is the visual first block, while the real first document node has
183
+ // its top margin reset. Add a compact gap after the widget so the two diff blocks do not overlap.
184
+ dom.style.display = 'block';
185
+ dom.style.marginBottom = "var(--ds-space-200, 16px)";
186
+ }
92
187
  // When mediaSingle nodes are rendered inside a widget decoration (e.g. as part of
93
188
  // a replaced panel), the centering CSS (margin-left: 50%; transform: translateX(-50%))
94
189
  // incorrectly applies because isNestedNode resolves to false (getPos returns 0).
@@ -258,7 +353,7 @@ var createNodeChangedDecorationWidget = exports.createNodeChangedDecorationWidge
258
353
  // block before it. Add an empty widget matching the following heading so the editor's own heading
259
354
  // styles supply the appropriate spacing. For other node types, retain the existing generic spacer.
260
355
  var isPureDeletion = change.fromB === change.toB;
261
- var isSingleBlock = slice.content.childCount === 1 && ((_slice$content$firstC = slice.content.firstChild) === null || _slice$content$firstC === void 0 ? void 0 : _slice$content$firstC.isBlock);
356
+ var isSingleBlock = slice.content.childCount === 1 && ((_slice$content$firstC2 = slice.content.firstChild) === null || _slice$content$firstC2 === void 0 ? void 0 : _slice$content$firstC2.isBlock);
262
357
  var isDiffWidgetAtStartOfDoc = $safeInsertPos.depth === 0 && $safeInsertPos.index(0) === 0;
263
358
  if (isDiffWidgetAtStartOfDoc && isSingleBlock && isPureDeletion && (0, _isExtendedEnabled.isExtendedEnabled)(diffType)) {
264
359
  var followingNode = $safeInsertPos.nodeAfter;
@@ -1,4 +1,3 @@
1
- // eslint-disable-next-line @atlassian/tangerine/import/entry-points
2
1
  import isEqual from 'lodash/isEqual';
3
2
  import memoizeOne from 'memoize-one';
4
3
  import { ChangeSet, simplifyChanges } from 'prosemirror-changeset';
@@ -3,12 +3,8 @@ import { Mark } from '@atlaskit/editor-prosemirror/model';
3
3
  import { Mapping, ReplaceStep } from '@atlaskit/editor-prosemirror/transform';
4
4
  import { attrAwareTokenEncoder } from './attrAwareTokenEncoder';
5
5
  import { optimizeChanges } from './optimizeChanges';
6
-
7
- // @ts-ignore TS1501: This regular expression flag is only available when targeting 'es6' or later.
8
6
  const WORD_CHAR_REGEX = /[\p{L}\p{N}_]/u;
9
- // @ts-ignore TS1501: This regular expression flag is only available when targeting 'es6' or later.
10
7
  const PUNCTUATION_REGEX = /\p{P}/u;
11
- // @ts-ignore TS1501: This regular expression flag is only available when targeting 'es6' or later.
12
8
  const WHITESPACE_REGEX = /\s/u;
13
9
  const mapPosition = (mapping, pos) => mapping.map(pos);
14
10
 
@@ -311,13 +311,28 @@ export const createBlockIndicatorAnchorWidgets = ({
311
311
  };
312
312
 
313
313
  /**
314
- * Creates invisible anchor widgets for a single inline diff range so that the
315
- * `IndicatorBar` can use CSS anchor positioning to align itself with the diff.
316
- *
317
- * - A `from` anchor is placed at the start of the range (top of the bar).
318
- * - A `to` anchor is placed at the end of the range (bottom of the bar).
319
- * - An optional `left` anchor is placed before the first textblock when the
320
- * diff is inside a resizable block node (table, layout, expand).
314
+ * A `from`/`to` on a `tableRow` boundary makes the anchor a direct `<tr>` (CSS grid)
315
+ * child, adding a phantom column that collapses the cells (EDITOR-8442). Clamp it
316
+ * inward into the neighbouring cell (`direction: 1` forward, `-1` back). Positions
317
+ * not on a row boundary are returned as-is.
318
+ */
319
+ const clampAnchorPosIntoCell = (doc, pos, direction) => {
320
+ const $pos = doc.resolve(pos);
321
+ if ($pos.parent.type.name !== 'tableRow') {
322
+ return pos;
323
+ }
324
+ const cell = direction === 1 ? $pos.nodeAfter : $pos.nodeBefore;
325
+ if (!cell || cell.type.name !== 'tableHeader' && cell.type.name !== 'tableCell') {
326
+ return pos;
327
+ }
328
+ // +2 past the cell and its first child boundary = inside the cell's content;
329
+ // for the backward case, step back the same amount from the cell's end.
330
+ return direction === 1 ? pos + 2 : pos - 2;
331
+ };
332
+
333
+ /**
334
+ * Invisible `from`/`to` (and optional `left`) anchor widgets for one inline diff
335
+ * range, so the `IndicatorBar` can align itself via CSS anchor positioning.
321
336
  */
322
337
  export const createInlineIndicatorAnchorWidgets = ({
323
338
  doc,
@@ -332,6 +347,10 @@ export const createInlineIndicatorAnchorWidgets = ({
332
347
  });
333
348
  const maybeLeftAnchor = leftAnchor ? [leftAnchor] : [];
334
349
 
350
+ // Keep the start/end anchors out of the table row's grid (see helper above).
351
+ const fromPos = clampAnchorPosIntoCell(doc, from, 1);
352
+ const toPos = clampAnchorPosIntoCell(doc, to, -1);
353
+
335
354
  /**
336
355
  * Two widgets mark the start and end of the inline range so the
337
356
  * IndicatorBar can determine top/bottom even if
@@ -341,7 +360,7 @@ export const createInlineIndicatorAnchorWidgets = ({
341
360
  diffId,
342
361
  anchorType: AnchorTypeKey.from
343
362
  });
344
- const fromWidget = Decoration.widget(from, () => {
363
+ const fromWidget = Decoration.widget(fromPos, () => {
345
364
  const span = document.createElement('span');
346
365
  span.style.setProperty('anchor-name', `--${fromAnchorKey}`);
347
366
  return span;
@@ -354,7 +373,7 @@ export const createInlineIndicatorAnchorWidgets = ({
354
373
  diffId,
355
374
  anchorType: AnchorTypeKey.to
356
375
  });
357
- const toWidget = Decoration.widget(to, () => {
376
+ const toWidget = Decoration.widget(toPos, () => {
358
377
  const span = document.createElement('span');
359
378
  span.style.setProperty('anchor-name', `--${toAnchorKey}`);
360
379
  return span;
@@ -110,6 +110,32 @@ const getBlockNodeStyle = ({
110
110
  return isTraditional ? isActive ? traditionalStyleNodeActive : traditionalStyleNodeNew : editingStyleNode;
111
111
  };
112
112
 
113
+ /**
114
+ * A table cell is "empty" only when it has no content at all — no text AND no
115
+ * non-text leaf/inline nodes (media, emoji, mention, inlineCard, status, date…).
116
+ * `textContent` alone misses those non-text nodes, so we also reject any leaf or
117
+ * inline descendant. An empty ADF cell (`tableCell > empty paragraph`) returns
118
+ * true; a cell containing only media/emoji returns false.
119
+ */
120
+ const isCellEmpty = cellNode => {
121
+ if (cellNode.textContent.length > 0) {
122
+ return false;
123
+ }
124
+ let hasNonTextContent = false;
125
+ cellNode.descendants(node => {
126
+ if (hasNonTextContent) {
127
+ return false;
128
+ }
129
+ // A non-text leaf or inline node (media, emoji, mention, etc.) = real content.
130
+ if (!node.isText && (node.isLeaf || node.isInline)) {
131
+ hasNonTextContent = true;
132
+ return false;
133
+ }
134
+ return true;
135
+ });
136
+ return !hasNonTextContent;
137
+ };
138
+
113
139
  /**
114
140
  * Node decoration used for block-level insertions. When isActive, uses emphasised (pressed) styling.
115
141
  *
@@ -148,7 +174,13 @@ export const createBlockChangedDecoration = ({
148
174
  const isRoundedTable = expValEquals('platform_editor_table_diff_rounded_corners', 'isEnabled', true);
149
175
  const addedCellStyle = isTraditional ? traditionalAddedCellOverlayStyle : addedCellOverlayStyle;
150
176
  const deletedCellStyle = isTraditional ? isRoundedTable ? deletedTraditionalCellOverlayRoundedStyle : deletedTraditionalCellOverlayStyle : isRoundedTable ? deletedCellOverlayRoundedStyle : deletedCellOverlayStyle;
151
- const cellOverlayStyle = isInserted ? addedCellStyle : deletedCellStyle;
177
+
178
+ // On an inverted diff, an empty cell being filled is an addition, so give it the
179
+ // added (purple) overlay instead of the deleted (grey) one (EDITOR-8442).
180
+ const cellNode = doc === null || doc === void 0 ? void 0 : doc.nodeAt(change.from);
181
+ const isEmptyCellBeingFilled = !isInserted && !!cellNode && isCellEmpty(cellNode);
182
+ const useAddedStyle = isInserted || isEmptyCellBeingFilled;
183
+ const cellOverlayStyle = useAddedStyle ? addedCellStyle : deletedCellStyle;
152
184
  cellOverlay.setAttribute('style', cellOverlayStyle);
153
185
  decorations.push(
154
186
  // change.to - 1 to position the overlay inside the end of the cell
@@ -7,6 +7,82 @@ import { findSafeInsertPos } from './utils/findSafeInsertPos';
7
7
  import { wrapBlockNodeView, injectInnerWrapper, createContentWrapper } from './utils/wrapBlockNodeView';
8
8
  const isHeadingLevel = level => typeof level === 'number' && level >= 1 && level <= 6;
9
9
 
10
+ /**
11
+ * Render one content widget per table cell, positioned inside the matching cell in
12
+ * `newDoc`, so a whole-cell replacement shows its added content in-place instead of
13
+ * being dropped (EDITOR-8442).
14
+ */
15
+ const createTableCellContentWidgets = ({
16
+ slice,
17
+ newDoc,
18
+ change,
19
+ nodeViewSerializer,
20
+ colorScheme,
21
+ isInserted,
22
+ diffType
23
+ }) => {
24
+ // Collect the inner content position of each cell in the new document within
25
+ // the change range. Whole-cell replacement preserves cell count/order, so the
26
+ // nth new-doc cell corresponds to the nth cell in the slice.
27
+ const newCellInnerPositions = [];
28
+ newDoc.nodesBetween(change.fromB, change.toB, (node, pos) => {
29
+ if (node.type.name === 'tableHeader' || node.type.name === 'tableCell') {
30
+ // Skip malformed cells with no block child: `pos + 2` would be out of bounds.
31
+ if (node.childCount === 0) {
32
+ return false;
33
+ }
34
+ // +1 into the cell, +1 into its first child (the paragraph) = inner content.
35
+ newCellInnerPositions.push(pos + 2);
36
+ return false;
37
+ }
38
+ return true;
39
+ });
40
+ const decorations = [];
41
+ slice.content.forEach((cellNode, _offset, index) => {
42
+ const targetPos = newCellInnerPositions[index];
43
+ if (targetPos === undefined || cellNode.content.size === 0) {
44
+ return;
45
+ }
46
+ const dom = document.createElement('span');
47
+ // A cell contains block content (paragraphs); serialize the cell's content
48
+ // so every block inside the cell is rendered.
49
+ cellNode.content.forEach(blockNode => {
50
+ const nodeView = nodeViewSerializer.tryCreateNodeView(blockNode);
51
+ const wrapper = createContentWrapper(colorScheme, false, isInserted, diffType);
52
+ if (nodeView) {
53
+ wrapper.append(nodeView);
54
+ } else {
55
+ const serialized = blockNode.type.inlineContent ? nodeViewSerializer.serializeFragment(blockNode.content) : nodeViewSerializer.serializeNode(blockNode);
56
+ if (serialized) {
57
+ wrapper.append(serialized);
58
+ }
59
+ }
60
+ dom.append(wrapper);
61
+ });
62
+ if (dom.childNodes.length === 0) {
63
+ return;
64
+ }
65
+
66
+ // This helper only runs for added content, so use the "changed" testid — not
67
+ // "deleted", which page models match as removed content.
68
+ dom.setAttribute('data-testid', 'show-diff-changed-decoration');
69
+
70
+ // Skip findSafeInsertPos: it walks forward to a schema-valid cell-insert point
71
+ // and pushes the widget into the next cell. `targetPos` is the correct spot.
72
+ decorations.push(Decoration.widget(targetPos, dom, {
73
+ ...buildDiffDecorationSpec({
74
+ decorationType: 'widget',
75
+ diffId: crypto.randomUUID(),
76
+ diffType,
77
+ ...(isExtendedEnabled(diffType) && {
78
+ side: -1
79
+ })
80
+ })
81
+ }));
82
+ });
83
+ return decorations;
84
+ };
85
+
10
86
  /**
11
87
  * This function is used to create a decoration widget to show content
12
88
  * that is not in the current document.
@@ -29,7 +105,7 @@ export const createNodeChangedDecorationWidget = ({
29
105
  diffType,
30
106
  hideAddedDiffsUnderline = false
31
107
  }) => {
32
- var _slice$content, _slice$content2, _slice$content2$first, _slice$content3, _slice$content3$first, _$safeInsertPos$nodeB, _slice$content$firstC;
108
+ var _slice$content, _slice$content2, _slice$content2$first, _slice$content3, _slice$content3$first, _$safeInsertPos$nodeB, _slice$content$firstC, _newDoc$firstChild, _slice$content$firstC2;
33
109
  const slice = doc.slice(change.fromA, change.toA);
34
110
  const shouldSkipDeletedEmptyParagraphDecoration = !isInserted && (slice === null || slice === void 0 ? void 0 : (_slice$content = slice.content) === null || _slice$content === void 0 ? void 0 : _slice$content.childCount) === 1 && (slice === null || slice === void 0 ? void 0 : (_slice$content2 = slice.content) === null || _slice$content2 === void 0 ? void 0 : (_slice$content2$first = _slice$content2.firstChild) === null || _slice$content2$first === void 0 ? void 0 : _slice$content2$first.type.name) === 'paragraph' && (slice === null || slice === void 0 ? void 0 : (_slice$content3 = slice.content) === null || _slice$content3 === void 0 ? void 0 : (_slice$content3$first = _slice$content3.firstChild) === null || _slice$content3$first === void 0 ? void 0 : _slice$content3$first.content.size) === 0;
35
111
  // Widget decoration used for deletions as the content is not in the document
@@ -44,7 +120,23 @@ export const createNodeChangedDecorationWidget = ({
44
120
  }
45
121
  const isTableCellContent = slice.content.content.some(() => slice.content.content.some(siblingNode => ['tableHeader', 'tableCell'].includes(siblingNode.type.name)));
46
122
  const isTableRowContent = slice.content.content.some(() => slice.content.content.some(siblingNode => ['tableRow'].includes(siblingNode.type.name)));
123
+
124
+ // Added whole cells (e.g. "Add table headers"): render per-cell content widgets.
125
+ // Deleted whole cells (delete row/col) fall through — they don't exist in `newDoc`,
126
+ // so there's nowhere to anchor content and we keep the original "render nothing".
127
+ if (isTableCellContent && isInserted) {
128
+ return createTableCellContentWidgets({
129
+ slice,
130
+ newDoc,
131
+ change,
132
+ nodeViewSerializer,
133
+ colorScheme,
134
+ isInserted,
135
+ diffType
136
+ });
137
+ }
47
138
  if (isTableCellContent) {
139
+ // Deleted whole cells: nothing to render (see note above).
48
140
  return [];
49
141
  }
50
142
  if (isTableRowContent) {
@@ -65,11 +157,18 @@ export const createNodeChangedDecorationWidget = ({
65
157
  const $safeInsertPos = newDoc.resolve(safeInsertPos);
66
158
  const isTopLevelInsert = $safeInsertPos.depth === 0;
67
159
  const hasPreviousBlock = ((_$safeInsertPos$nodeB = $safeInsertPos.nodeBefore) === null || _$safeInsertPos$nodeB === void 0 ? void 0 : _$safeInsertPos$nodeB.isBlock) === true;
68
- if (isExtendedEnabled(diffType) && isTopLevelInsert && hasPreviousBlock) {
160
+ const isFirstDocHeadingReplacement = isExtendedEnabled(diffType) && !placeBelow && change.fromB === 0 && ((_slice$content$firstC = slice.content.firstChild) === null || _slice$content$firstC === void 0 ? void 0 : _slice$content$firstC.type.name) === 'heading' && ((_newDoc$firstChild = newDoc.firstChild) === null || _newDoc$firstChild === void 0 ? void 0 : _newDoc$firstChild.type.name) === 'heading';
161
+ if (isExtendedEnabled(diffType) && isTopLevelInsert && hasPreviousBlock && !isFirstDocHeadingReplacement) {
69
162
  // Editor CSS removes the top margin from a block when it is its parent's first child.
70
163
  // Keep the serialized block as a subsequent child so each block type retains its own margin.
71
164
  dom.append(dom.ownerDocument.createElement('div'));
72
165
  }
166
+ if (isFirstDocHeadingReplacement) {
167
+ // The replacement widget is the visual first block, while the real first document node has
168
+ // its top margin reset. Add a compact gap after the widget so the two diff blocks do not overlap.
169
+ dom.style.display = 'block';
170
+ dom.style.marginBottom = "var(--ds-space-200, 16px)";
171
+ }
73
172
  // When mediaSingle nodes are rendered inside a widget decoration (e.g. as part of
74
173
  // a replaced panel), the centering CSS (margin-left: 50%; transform: translateX(-50%))
75
174
  // incorrectly applies because isNestedNode resolves to false (getPos returns 0).
@@ -237,7 +336,7 @@ export const createNodeChangedDecorationWidget = ({
237
336
  // block before it. Add an empty widget matching the following heading so the editor's own heading
238
337
  // styles supply the appropriate spacing. For other node types, retain the existing generic spacer.
239
338
  const isPureDeletion = change.fromB === change.toB;
240
- const isSingleBlock = slice.content.childCount === 1 && ((_slice$content$firstC = slice.content.firstChild) === null || _slice$content$firstC === void 0 ? void 0 : _slice$content$firstC.isBlock);
339
+ const isSingleBlock = slice.content.childCount === 1 && ((_slice$content$firstC2 = slice.content.firstChild) === null || _slice$content$firstC2 === void 0 ? void 0 : _slice$content$firstC2.isBlock);
241
340
  const isDiffWidgetAtStartOfDoc = $safeInsertPos.depth === 0 && $safeInsertPos.index(0) === 0;
242
341
  if (isDiffWidgetAtStartOfDoc && isSingleBlock && isPureDeletion && isExtendedEnabled(diffType)) {
243
342
  const followingNode = $safeInsertPos.nodeAfter;
@@ -6,7 +6,6 @@ function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t =
6
6
  function _createForOfIteratorHelper(r, e) { var t = "undefined" != typeof Symbol && r[Symbol.iterator] || r["@@iterator"]; if (!t) { if (Array.isArray(r) || (t = _unsupportedIterableToArray(r)) || e && r && "number" == typeof r.length) { t && (r = t); var _n = 0, F = function F() {}; return { s: F, n: function n() { return _n >= r.length ? { done: !0 } : { done: !1, value: r[_n++] }; }, e: function e(r) { throw r; }, f: F }; } throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); } var o, a = !0, u = !1; return { s: function s() { t = t.call(r); }, n: function n() { var r = t.next(); return a = r.done, r; }, e: function e(r) { u = !0, o = r; }, f: function f() { try { a || null == t.return || t.return(); } finally { if (u) throw o; } } }; }
7
7
  function _unsupportedIterableToArray(r, a) { if (r) { if ("string" == typeof r) return _arrayLikeToArray(r, a); var t = {}.toString.call(r).slice(8, -1); return "Object" === t && r.constructor && (t = r.constructor.name), "Map" === t || "Set" === t ? Array.from(r) : "Arguments" === t || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(t) ? _arrayLikeToArray(r, a) : void 0; } }
8
8
  function _arrayLikeToArray(r, a) { (null == a || a > r.length) && (a = r.length); for (var e = 0, n = Array(a); e < a; e++) n[e] = r[e]; return n; }
9
- // eslint-disable-next-line @atlassian/tangerine/import/entry-points
10
9
  import isEqual from 'lodash/isEqual';
11
10
  import memoizeOne from 'memoize-one';
12
11
  import { ChangeSet, simplifyChanges } from 'prosemirror-changeset';
@@ -10,12 +10,8 @@ import { Mark } from '@atlaskit/editor-prosemirror/model';
10
10
  import { Mapping, ReplaceStep } from '@atlaskit/editor-prosemirror/transform';
11
11
  import { attrAwareTokenEncoder } from './attrAwareTokenEncoder';
12
12
  import { optimizeChanges } from './optimizeChanges';
13
-
14
- // @ts-ignore TS1501: This regular expression flag is only available when targeting 'es6' or later.
15
13
  var WORD_CHAR_REGEX = /(?:[0-9A-Z_a-z\xAA\xB2\xB3\xB5\xB9\xBA\xBC-\xBE\xC0-\xD6\xD8-\xF6\xF8-\u02C1\u02C6-\u02D1\u02E0-\u02E4\u02EC\u02EE\u0370-\u0374\u0376\u0377\u037A-\u037D\u037F\u0386\u0388-\u038A\u038C\u038E-\u03A1\u03A3-\u03F5\u03F7-\u0481\u048A-\u052F\u0531-\u0556\u0559\u0560-\u0588\u05D0-\u05EA\u05EF-\u05F2\u0620-\u064A\u0660-\u0669\u066E\u066F\u0671-\u06D3\u06D5\u06E5\u06E6\u06EE-\u06FC\u06FF\u0710\u0712-\u072F\u074D-\u07A5\u07B1\u07C0-\u07EA\u07F4\u07F5\u07FA\u0800-\u0815\u081A\u0824\u0828\u0840-\u0858\u0860-\u086A\u0870-\u0887\u0889-\u088F\u08A0-\u08C9\u0904-\u0939\u093D\u0950\u0958-\u0961\u0966-\u096F\u0971-\u0980\u0985-\u098C\u098F\u0990\u0993-\u09A8\u09AA-\u09B0\u09B2\u09B6-\u09B9\u09BD\u09CE\u09DC\u09DD\u09DF-\u09E1\u09E6-\u09F1\u09F4-\u09F9\u09FC\u0A05-\u0A0A\u0A0F\u0A10\u0A13-\u0A28\u0A2A-\u0A30\u0A32\u0A33\u0A35\u0A36\u0A38\u0A39\u0A59-\u0A5C\u0A5E\u0A66-\u0A6F\u0A72-\u0A74\u0A85-\u0A8D\u0A8F-\u0A91\u0A93-\u0AA8\u0AAA-\u0AB0\u0AB2\u0AB3\u0AB5-\u0AB9\u0ABD\u0AD0\u0AE0\u0AE1\u0AE6-\u0AEF\u0AF9\u0B05-\u0B0C\u0B0F\u0B10\u0B13-\u0B28\u0B2A-\u0B30\u0B32\u0B33\u0B35-\u0B39\u0B3D\u0B5C\u0B5D\u0B5F-\u0B61\u0B66-\u0B6F\u0B71-\u0B77\u0B83\u0B85-\u0B8A\u0B8E-\u0B90\u0B92-\u0B95\u0B99\u0B9A\u0B9C\u0B9E\u0B9F\u0BA3\u0BA4\u0BA8-\u0BAA\u0BAE-\u0BB9\u0BD0\u0BE6-\u0BF2\u0C05-\u0C0C\u0C0E-\u0C10\u0C12-\u0C28\u0C2A-\u0C39\u0C3D\u0C58-\u0C5A\u0C5C\u0C5D\u0C60\u0C61\u0C66-\u0C6F\u0C78-\u0C7E\u0C80\u0C85-\u0C8C\u0C8E-\u0C90\u0C92-\u0CA8\u0CAA-\u0CB3\u0CB5-\u0CB9\u0CBD\u0CDC-\u0CDE\u0CE0\u0CE1\u0CE6-\u0CEF\u0CF1\u0CF2\u0D04-\u0D0C\u0D0E-\u0D10\u0D12-\u0D3A\u0D3D\u0D4E\u0D54-\u0D56\u0D58-\u0D61\u0D66-\u0D78\u0D7A-\u0D7F\u0D85-\u0D96\u0D9A-\u0DB1\u0DB3-\u0DBB\u0DBD\u0DC0-\u0DC6\u0DE6-\u0DEF\u0E01-\u0E30\u0E32\u0E33\u0E40-\u0E46\u0E50-\u0E59\u0E81\u0E82\u0E84\u0E86-\u0E8A\u0E8C-\u0EA3\u0EA5\u0EA7-\u0EB0\u0EB2\u0EB3\u0EBD\u0EC0-\u0EC4\u0EC6\u0ED0-\u0ED9\u0EDC-\u0EDF\u0F00\u0F20-\u0F33\u0F40-\u0F47\u0F49-\u0F6C\u0F88-\u0F8C\u1000-\u102A\u103F-\u1049\u1050-\u1055\u105A-\u105D\u1061\u1065\u1066\u106E-\u1070\u1075-\u1081\u108E\u1090-\u1099\u10A0-\u10C5\u10C7\u10CD\u10D0-\u10FA\u10FC-\u1248\u124A-\u124D\u1250-\u1256\u1258\u125A-\u125D\u1260-\u1288\u128A-\u128D\u1290-\u12B0\u12B2-\u12B5\u12B8-\u12BE\u12C0\u12C2-\u12C5\u12C8-\u12D6\u12D8-\u1310\u1312-\u1315\u1318-\u135A\u1369-\u137C\u1380-\u138F\u13A0-\u13F5\u13F8-\u13FD\u1401-\u166C\u166F-\u167F\u1681-\u169A\u16A0-\u16EA\u16EE-\u16F8\u1700-\u1711\u171F-\u1731\u1740-\u1751\u1760-\u176C\u176E-\u1770\u1780-\u17B3\u17D7\u17DC\u17E0-\u17E9\u17F0-\u17F9\u1810-\u1819\u1820-\u1878\u1880-\u1884\u1887-\u18A8\u18AA\u18B0-\u18F5\u1900-\u191E\u1946-\u196D\u1970-\u1974\u1980-\u19AB\u19B0-\u19C9\u19D0-\u19DA\u1A00-\u1A16\u1A20-\u1A54\u1A80-\u1A89\u1A90-\u1A99\u1AA7\u1B05-\u1B33\u1B45-\u1B4C\u1B50-\u1B59\u1B83-\u1BA0\u1BAE-\u1BE5\u1C00-\u1C23\u1C40-\u1C49\u1C4D-\u1C7D\u1C80-\u1C8A\u1C90-\u1CBA\u1CBD-\u1CBF\u1CE9-\u1CEC\u1CEE-\u1CF3\u1CF5\u1CF6\u1CFA\u1D00-\u1DBF\u1E00-\u1F15\u1F18-\u1F1D\u1F20-\u1F45\u1F48-\u1F4D\u1F50-\u1F57\u1F59\u1F5B\u1F5D\u1F5F-\u1F7D\u1F80-\u1FB4\u1FB6-\u1FBC\u1FBE\u1FC2-\u1FC4\u1FC6-\u1FCC\u1FD0-\u1FD3\u1FD6-\u1FDB\u1FE0-\u1FEC\u1FF2-\u1FF4\u1FF6-\u1FFC\u2070\u2071\u2074-\u2079\u207F-\u2089\u2090-\u209C\u2102\u2107\u210A-\u2113\u2115\u2119-\u211D\u2124\u2126\u2128\u212A-\u212D\u212F-\u2139\u213C-\u213F\u2145-\u2149\u214E\u2150-\u2189\u2460-\u249B\u24EA-\u24FF\u2776-\u2793\u2C00-\u2CE4\u2CEB-\u2CEE\u2CF2\u2CF3\u2CFD\u2D00-\u2D25\u2D27\u2D2D\u2D30-\u2D67\u2D6F\u2D80-\u2D96\u2DA0-\u2DA6\u2DA8-\u2DAE\u2DB0-\u2DB6\u2DB8-\u2DBE\u2DC0-\u2DC6\u2DC8-\u2DCE\u2DD0-\u2DD6\u2DD8-\u2DDE\u2E2F\u3005-\u3007\u3021-\u3029\u3031-\u3035\u3038-\u303C\u3041-\u3096\u309D-\u309F\u30A1-\u30FA\u30FC-\u30FF\u3105-\u312F\u3131-\u318E\u3192-\u3195\u31A0-\u31BF\u31F0-\u31FF\u3220-\u3229\u3248-\u324F\u3251-\u325F\u3280-\u3289\u32B1-\u32BF\u3400-\u4DBF\u4E00-\uA48C\uA4D0-\uA4FD\uA500-\uA60C\uA610-\uA62B\uA640-\uA66E\uA67F-\uA69D\uA6A0-\uA6EF\uA717-\uA71F\uA722-\uA788\uA78B-\uA7DC\uA7F1-\uA801\uA803-\uA805\uA807-\uA80A\uA80C-\uA822\uA830-\uA835\uA840-\uA873\uA882-\uA8B3\uA8D0-\uA8D9\uA8F2-\uA8F7\uA8FB\uA8FD\uA8FE\uA900-\uA925\uA930-\uA946\uA960-\uA97C\uA984-\uA9B2\uA9CF-\uA9D9\uA9E0-\uA9E4\uA9E6-\uA9FE\uAA00-\uAA28\uAA40-\uAA42\uAA44-\uAA4B\uAA50-\uAA59\uAA60-\uAA76\uAA7A\uAA7E-\uAAAF\uAAB1\uAAB5\uAAB6\uAAB9-\uAABD\uAAC0\uAAC2\uAADB-\uAADD\uAAE0-\uAAEA\uAAF2-\uAAF4\uAB01-\uAB06\uAB09-\uAB0E\uAB11-\uAB16\uAB20-\uAB26\uAB28-\uAB2E\uAB30-\uAB5A\uAB5C-\uAB69\uAB70-\uABE2\uABF0-\uABF9\uAC00-\uD7A3\uD7B0-\uD7C6\uD7CB-\uD7FB\uF900-\uFA6D\uFA70-\uFAD9\uFB00-\uFB06\uFB13-\uFB17\uFB1D\uFB1F-\uFB28\uFB2A-\uFB36\uFB38-\uFB3C\uFB3E\uFB40\uFB41\uFB43\uFB44\uFB46-\uFBB1\uFBD3-\uFD3D\uFD50-\uFD8F\uFD92-\uFDC7\uFDF0-\uFDFB\uFE70-\uFE74\uFE76-\uFEFC\uFF10-\uFF19\uFF21-\uFF3A\uFF41-\uFF5A\uFF66-\uFFBE\uFFC2-\uFFC7\uFFCA-\uFFCF\uFFD2-\uFFD7\uFFDA-\uFFDC]|\uD800[\uDC00-\uDC0B\uDC0D-\uDC26\uDC28-\uDC3A\uDC3C\uDC3D\uDC3F-\uDC4D\uDC50-\uDC5D\uDC80-\uDCFA\uDD07-\uDD33\uDD40-\uDD78\uDD8A\uDD8B\uDE80-\uDE9C\uDEA0-\uDED0\uDEE1-\uDEFB\uDF00-\uDF23\uDF2D-\uDF4A\uDF50-\uDF75\uDF80-\uDF9D\uDFA0-\uDFC3\uDFC8-\uDFCF\uDFD1-\uDFD5]|\uD801[\uDC00-\uDC9D\uDCA0-\uDCA9\uDCB0-\uDCD3\uDCD8-\uDCFB\uDD00-\uDD27\uDD30-\uDD63\uDD70-\uDD7A\uDD7C-\uDD8A\uDD8C-\uDD92\uDD94\uDD95\uDD97-\uDDA1\uDDA3-\uDDB1\uDDB3-\uDDB9\uDDBB\uDDBC\uDDC0-\uDDF3\uDE00-\uDF36\uDF40-\uDF55\uDF60-\uDF67\uDF80-\uDF85\uDF87-\uDFB0\uDFB2-\uDFBA]|\uD802[\uDC00-\uDC05\uDC08\uDC0A-\uDC35\uDC37\uDC38\uDC3C\uDC3F-\uDC55\uDC58-\uDC76\uDC79-\uDC9E\uDCA7-\uDCAF\uDCE0-\uDCF2\uDCF4\uDCF5\uDCFB-\uDD1B\uDD20-\uDD39\uDD40-\uDD59\uDD80-\uDDB7\uDDBC-\uDDCF\uDDD2-\uDE00\uDE10-\uDE13\uDE15-\uDE17\uDE19-\uDE35\uDE40-\uDE48\uDE60-\uDE7E\uDE80-\uDE9F\uDEC0-\uDEC7\uDEC9-\uDEE4\uDEEB-\uDEEF\uDF00-\uDF35\uDF40-\uDF55\uDF58-\uDF72\uDF78-\uDF91\uDFA9-\uDFAF]|\uD803[\uDC00-\uDC48\uDC80-\uDCB2\uDCC0-\uDCF2\uDCFA-\uDD23\uDD30-\uDD39\uDD40-\uDD65\uDD6F-\uDD85\uDE60-\uDE7E\uDE80-\uDEA9\uDEB0\uDEB1\uDEC2-\uDEC7\uDF00-\uDF27\uDF30-\uDF45\uDF51-\uDF54\uDF70-\uDF81\uDFB0-\uDFCB\uDFE0-\uDFF6]|\uD804[\uDC03-\uDC37\uDC52-\uDC6F\uDC71\uDC72\uDC75\uDC83-\uDCAF\uDCD0-\uDCE8\uDCF0-\uDCF9\uDD03-\uDD26\uDD36-\uDD3F\uDD44\uDD47\uDD50-\uDD72\uDD76\uDD83-\uDDB2\uDDC1-\uDDC4\uDDD0-\uDDDA\uDDDC\uDDE1-\uDDF4\uDE00-\uDE11\uDE13-\uDE2B\uDE3F\uDE40\uDE80-\uDE86\uDE88\uDE8A-\uDE8D\uDE8F-\uDE9D\uDE9F-\uDEA8\uDEB0-\uDEDE\uDEF0-\uDEF9\uDF05-\uDF0C\uDF0F\uDF10\uDF13-\uDF28\uDF2A-\uDF30\uDF32\uDF33\uDF35-\uDF39\uDF3D\uDF50\uDF5D-\uDF61\uDF80-\uDF89\uDF8B\uDF8E\uDF90-\uDFB5\uDFB7\uDFD1\uDFD3]|\uD805[\uDC00-\uDC34\uDC47-\uDC4A\uDC50-\uDC59\uDC5F-\uDC61\uDC80-\uDCAF\uDCC4\uDCC5\uDCC7\uDCD0-\uDCD9\uDD80-\uDDAE\uDDD8-\uDDDB\uDE00-\uDE2F\uDE44\uDE50-\uDE59\uDE80-\uDEAA\uDEB8\uDEC0-\uDEC9\uDED0-\uDEE3\uDF00-\uDF1A\uDF30-\uDF3B\uDF40-\uDF46]|\uD806[\uDC00-\uDC2B\uDCA0-\uDCF2\uDCFF-\uDD06\uDD09\uDD0C-\uDD13\uDD15\uDD16\uDD18-\uDD2F\uDD3F\uDD41\uDD50-\uDD59\uDDA0-\uDDA7\uDDAA-\uDDD0\uDDE1\uDDE3\uDE00\uDE0B-\uDE32\uDE3A\uDE50\uDE5C-\uDE89\uDE9D\uDEB0-\uDEF8\uDFC0-\uDFE0\uDFF0-\uDFF9]|\uD807[\uDC00-\uDC08\uDC0A-\uDC2E\uDC40\uDC50-\uDC6C\uDC72-\uDC8F\uDD00-\uDD06\uDD08\uDD09\uDD0B-\uDD30\uDD46\uDD50-\uDD59\uDD60-\uDD65\uDD67\uDD68\uDD6A-\uDD89\uDD98\uDDA0-\uDDA9\uDDB0-\uDDDB\uDDE0-\uDDE9\uDEE0-\uDEF2\uDF02\uDF04-\uDF10\uDF12-\uDF33\uDF50-\uDF59\uDFB0\uDFC0-\uDFD4]|\uD808[\uDC00-\uDF99]|\uD809[\uDC00-\uDC6E\uDC80-\uDD43]|\uD80B[\uDF90-\uDFF0]|[\uD80C\uD80E\uD80F\uD81C-\uD822\uD840-\uD868\uD86A-\uD86D\uD86F-\uD872\uD874-\uD879\uD880-\uD883\uD885-\uD88C][\uDC00-\uDFFF]|\uD80D[\uDC00-\uDC2F\uDC41-\uDC46\uDC60-\uDFFF]|\uD810[\uDC00-\uDFFA]|\uD811[\uDC00-\uDE46]|\uD818[\uDD00-\uDD1D\uDD30-\uDD39]|\uD81A[\uDC00-\uDE38\uDE40-\uDE5E\uDE60-\uDE69\uDE70-\uDEBE\uDEC0-\uDEC9\uDED0-\uDEED\uDF00-\uDF2F\uDF40-\uDF43\uDF50-\uDF59\uDF5B-\uDF61\uDF63-\uDF77\uDF7D-\uDF8F]|\uD81B[\uDD40-\uDD6C\uDD70-\uDD79\uDE40-\uDE96\uDEA0-\uDEB8\uDEBB-\uDED3\uDF00-\uDF4A\uDF50\uDF93-\uDF9F\uDFE0\uDFE1\uDFE3\uDFF2-\uDFF6]|\uD823[\uDC00-\uDCD5\uDCFF-\uDD1E\uDD80-\uDDF2]|\uD82B[\uDFF0-\uDFF3\uDFF5-\uDFFB\uDFFD\uDFFE]|\uD82C[\uDC00-\uDD22\uDD32\uDD50-\uDD52\uDD55\uDD64-\uDD67\uDD70-\uDEFB]|\uD82F[\uDC00-\uDC6A\uDC70-\uDC7C\uDC80-\uDC88\uDC90-\uDC99]|\uD833[\uDCF0-\uDCF9]|\uD834[\uDEC0-\uDED3\uDEE0-\uDEF3\uDF60-\uDF78]|\uD835[\uDC00-\uDC54\uDC56-\uDC9C\uDC9E\uDC9F\uDCA2\uDCA5\uDCA6\uDCA9-\uDCAC\uDCAE-\uDCB9\uDCBB\uDCBD-\uDCC3\uDCC5-\uDD05\uDD07-\uDD0A\uDD0D-\uDD14\uDD16-\uDD1C\uDD1E-\uDD39\uDD3B-\uDD3E\uDD40-\uDD44\uDD46\uDD4A-\uDD50\uDD52-\uDEA5\uDEA8-\uDEC0\uDEC2-\uDEDA\uDEDC-\uDEFA\uDEFC-\uDF14\uDF16-\uDF34\uDF36-\uDF4E\uDF50-\uDF6E\uDF70-\uDF88\uDF8A-\uDFA8\uDFAA-\uDFC2\uDFC4-\uDFCB\uDFCE-\uDFFF]|\uD837[\uDF00-\uDF1E\uDF25-\uDF2A]|\uD838[\uDC30-\uDC6D\uDD00-\uDD2C\uDD37-\uDD3D\uDD40-\uDD49\uDD4E\uDE90-\uDEAD\uDEC0-\uDEEB\uDEF0-\uDEF9]|\uD839[\uDCD0-\uDCEB\uDCF0-\uDCF9\uDDD0-\uDDED\uDDF0-\uDDFA\uDEC0-\uDEDE\uDEE0-\uDEE2\uDEE4\uDEE5\uDEE7-\uDEED\uDEF0-\uDEF4\uDEFE\uDEFF\uDFE0-\uDFE6\uDFE8-\uDFEB\uDFED\uDFEE\uDFF0-\uDFFE]|\uD83A[\uDC00-\uDCC4\uDCC7-\uDCCF\uDD00-\uDD43\uDD4B\uDD50-\uDD59]|\uD83B[\uDC71-\uDCAB\uDCAD-\uDCAF\uDCB1-\uDCB4\uDD01-\uDD2D\uDD2F-\uDD3D\uDE00-\uDE03\uDE05-\uDE1F\uDE21\uDE22\uDE24\uDE27\uDE29-\uDE32\uDE34-\uDE37\uDE39\uDE3B\uDE42\uDE47\uDE49\uDE4B\uDE4D-\uDE4F\uDE51\uDE52\uDE54\uDE57\uDE59\uDE5B\uDE5D\uDE5F\uDE61\uDE62\uDE64\uDE67-\uDE6A\uDE6C-\uDE72\uDE74-\uDE77\uDE79-\uDE7C\uDE7E\uDE80-\uDE89\uDE8B-\uDE9B\uDEA1-\uDEA3\uDEA5-\uDEA9\uDEAB-\uDEBB]|\uD83C[\uDD00-\uDD0C]|\uD83E[\uDFF0-\uDFF9]|\uD869[\uDC00-\uDEDF\uDF00-\uDFFF]|\uD86E[\uDC00-\uDC1D\uDC20-\uDFFF]|\uD873[\uDC00-\uDEAD\uDEB0-\uDFFF]|\uD87A[\uDC00-\uDFE0\uDFF0-\uDFFF]|\uD87B[\uDC00-\uDE5D]|\uD87E[\uDC00-\uDE1D]|\uD884[\uDC00-\uDF4A\uDF50-\uDFFF]|\uD88D[\uDC00-\uDC79])/;
16
- // @ts-ignore TS1501: This regular expression flag is only available when targeting 'es6' or later.
17
14
  var PUNCTUATION_REGEX = /(?:[!-#%-\*,-\/:;\?@\[-\]_\{\}\xA1\xA7\xAB\xB6\xB7\xBB\xBF\u037E\u0387\u055A-\u055F\u0589\u058A\u05BE\u05C0\u05C3\u05C6\u05F3\u05F4\u0609\u060A\u060C\u060D\u061B\u061D-\u061F\u066A-\u066D\u06D4\u0700-\u070D\u07F7-\u07F9\u0830-\u083E\u085E\u0964\u0965\u0970\u09FD\u0A76\u0AF0\u0C77\u0C84\u0DF4\u0E4F\u0E5A\u0E5B\u0F04-\u0F12\u0F14\u0F3A-\u0F3D\u0F85\u0FD0-\u0FD4\u0FD9\u0FDA\u104A-\u104F\u10FB\u1360-\u1368\u1400\u166E\u169B\u169C\u16EB-\u16ED\u1735\u1736\u17D4-\u17D6\u17D8-\u17DA\u1800-\u180A\u1944\u1945\u1A1E\u1A1F\u1AA0-\u1AA6\u1AA8-\u1AAD\u1B4E\u1B4F\u1B5A-\u1B60\u1B7D-\u1B7F\u1BFC-\u1BFF\u1C3B-\u1C3F\u1C7E\u1C7F\u1CC0-\u1CC7\u1CD3\u2010-\u2027\u2030-\u2043\u2045-\u2051\u2053-\u205E\u207D\u207E\u208D\u208E\u2308-\u230B\u2329\u232A\u2768-\u2775\u27C5\u27C6\u27E6-\u27EF\u2983-\u2998\u29D8-\u29DB\u29FC\u29FD\u2CF9-\u2CFC\u2CFE\u2CFF\u2D70\u2E00-\u2E2E\u2E30-\u2E4F\u2E52-\u2E5D\u3001-\u3003\u3008-\u3011\u3014-\u301F\u3030\u303D\u30A0\u30FB\uA4FE\uA4FF\uA60D-\uA60F\uA673\uA67E\uA6F2-\uA6F7\uA874-\uA877\uA8CE\uA8CF\uA8F8-\uA8FA\uA8FC\uA92E\uA92F\uA95F\uA9C1-\uA9CD\uA9DE\uA9DF\uAA5C-\uAA5F\uAADE\uAADF\uAAF0\uAAF1\uABEB\uFD3E\uFD3F\uFE10-\uFE19\uFE30-\uFE52\uFE54-\uFE61\uFE63\uFE68\uFE6A\uFE6B\uFF01-\uFF03\uFF05-\uFF0A\uFF0C-\uFF0F\uFF1A\uFF1B\uFF1F\uFF20\uFF3B-\uFF3D\uFF3F\uFF5B\uFF5D\uFF5F-\uFF65]|\uD800[\uDD00-\uDD02\uDF9F\uDFD0]|\uD801\uDD6F|\uD802[\uDC57\uDD1F\uDD3F\uDE50-\uDE58\uDE7F\uDEF0-\uDEF6\uDF39-\uDF3F\uDF99-\uDF9C]|\uD803[\uDD6E\uDEAD\uDED0\uDF55-\uDF59\uDF86-\uDF89]|\uD804[\uDC47-\uDC4D\uDCBB\uDCBC\uDCBE-\uDCC1\uDD40-\uDD43\uDD74\uDD75\uDDC5-\uDDC8\uDDCD\uDDDB\uDDDD-\uDDDF\uDE38-\uDE3D\uDEA9\uDFD4\uDFD5\uDFD7\uDFD8]|\uD805[\uDC4B-\uDC4F\uDC5A\uDC5B\uDC5D\uDCC6\uDDC1-\uDDD7\uDE41-\uDE43\uDE60-\uDE6C\uDEB9\uDF3C-\uDF3E]|\uD806[\uDC3B\uDD44-\uDD46\uDDE2\uDE3F-\uDE46\uDE9A-\uDE9C\uDE9E-\uDEA2\uDF00-\uDF09\uDFE1]|\uD807[\uDC41-\uDC45\uDC70\uDC71\uDEF7\uDEF8\uDF43-\uDF4F\uDFFF]|\uD809[\uDC70-\uDC74]|\uD80B[\uDFF1\uDFF2]|\uD81A[\uDE6E\uDE6F\uDEF5\uDF37-\uDF3B\uDF44]|\uD81B[\uDD6D-\uDD6F\uDE97-\uDE9A\uDFE2]|\uD82F\uDC9F|\uD836[\uDE87-\uDE8B]|\uD839\uDDFF|\uD83A[\uDD5E\uDD5F])/;
18
- // @ts-ignore TS1501: This regular expression flag is only available when targeting 'es6' or later.
19
15
  var WHITESPACE_REGEX = /[\t-\r \xA0\u1680\u2000-\u200A\u2028\u2029\u202F\u205F\u3000\uFEFF]/;
20
16
  var mapPosition = function mapPosition(mapping, pos) {
21
17
  return mapping.map(pos);
@@ -312,13 +312,28 @@ export var createBlockIndicatorAnchorWidgets = function createBlockIndicatorAnch
312
312
  };
313
313
 
314
314
  /**
315
- * Creates invisible anchor widgets for a single inline diff range so that the
316
- * `IndicatorBar` can use CSS anchor positioning to align itself with the diff.
317
- *
318
- * - A `from` anchor is placed at the start of the range (top of the bar).
319
- * - A `to` anchor is placed at the end of the range (bottom of the bar).
320
- * - An optional `left` anchor is placed before the first textblock when the
321
- * diff is inside a resizable block node (table, layout, expand).
315
+ * A `from`/`to` on a `tableRow` boundary makes the anchor a direct `<tr>` (CSS grid)
316
+ * child, adding a phantom column that collapses the cells (EDITOR-8442). Clamp it
317
+ * inward into the neighbouring cell (`direction: 1` forward, `-1` back). Positions
318
+ * not on a row boundary are returned as-is.
319
+ */
320
+ var clampAnchorPosIntoCell = function clampAnchorPosIntoCell(doc, pos, direction) {
321
+ var $pos = doc.resolve(pos);
322
+ if ($pos.parent.type.name !== 'tableRow') {
323
+ return pos;
324
+ }
325
+ var cell = direction === 1 ? $pos.nodeAfter : $pos.nodeBefore;
326
+ if (!cell || cell.type.name !== 'tableHeader' && cell.type.name !== 'tableCell') {
327
+ return pos;
328
+ }
329
+ // +2 past the cell and its first child boundary = inside the cell's content;
330
+ // for the backward case, step back the same amount from the cell's end.
331
+ return direction === 1 ? pos + 2 : pos - 2;
332
+ };
333
+
334
+ /**
335
+ * Invisible `from`/`to` (and optional `left`) anchor widgets for one inline diff
336
+ * range, so the `IndicatorBar` can align itself via CSS anchor positioning.
322
337
  */
323
338
  export var createInlineIndicatorAnchorWidgets = function createInlineIndicatorAnchorWidgets(_ref3) {
324
339
  var doc = _ref3.doc,
@@ -332,6 +347,10 @@ export var createInlineIndicatorAnchorWidgets = function createInlineIndicatorAn
332
347
  });
333
348
  var maybeLeftAnchor = leftAnchor ? [leftAnchor] : [];
334
349
 
350
+ // Keep the start/end anchors out of the table row's grid (see helper above).
351
+ var fromPos = clampAnchorPosIntoCell(doc, from, 1);
352
+ var toPos = clampAnchorPosIntoCell(doc, to, -1);
353
+
335
354
  /**
336
355
  * Two widgets mark the start and end of the inline range so the
337
356
  * IndicatorBar can determine top/bottom even if
@@ -341,7 +360,7 @@ export var createInlineIndicatorAnchorWidgets = function createInlineIndicatorAn
341
360
  diffId: diffId,
342
361
  anchorType: AnchorTypeKey.from
343
362
  });
344
- var fromWidget = Decoration.widget(from, function () {
363
+ var fromWidget = Decoration.widget(fromPos, function () {
345
364
  var span = document.createElement('span');
346
365
  span.style.setProperty('anchor-name', "--".concat(fromAnchorKey));
347
366
  return span;
@@ -354,7 +373,7 @@ export var createInlineIndicatorAnchorWidgets = function createInlineIndicatorAn
354
373
  diffId: diffId,
355
374
  anchorType: AnchorTypeKey.to
356
375
  });
357
- var toWidget = Decoration.widget(to, function () {
376
+ var toWidget = Decoration.widget(toPos, function () {
358
377
  var span = document.createElement('span');
359
378
  span.style.setProperty('anchor-name', "--".concat(toAnchorKey));
360
379
  return span;
@@ -112,6 +112,32 @@ var getBlockNodeStyle = function getBlockNodeStyle(_ref) {
112
112
  return isTraditional ? isActive ? traditionalStyleNodeActive : traditionalStyleNodeNew : editingStyleNode;
113
113
  };
114
114
 
115
+ /**
116
+ * A table cell is "empty" only when it has no content at all — no text AND no
117
+ * non-text leaf/inline nodes (media, emoji, mention, inlineCard, status, date…).
118
+ * `textContent` alone misses those non-text nodes, so we also reject any leaf or
119
+ * inline descendant. An empty ADF cell (`tableCell > empty paragraph`) returns
120
+ * true; a cell containing only media/emoji returns false.
121
+ */
122
+ var isCellEmpty = function isCellEmpty(cellNode) {
123
+ if (cellNode.textContent.length > 0) {
124
+ return false;
125
+ }
126
+ var hasNonTextContent = false;
127
+ cellNode.descendants(function (node) {
128
+ if (hasNonTextContent) {
129
+ return false;
130
+ }
131
+ // A non-text leaf or inline node (media, emoji, mention, etc.) = real content.
132
+ if (!node.isText && (node.isLeaf || node.isInline)) {
133
+ hasNonTextContent = true;
134
+ return false;
135
+ }
136
+ return true;
137
+ });
138
+ return !hasNonTextContent;
139
+ };
140
+
115
141
  /**
116
142
  * Node decoration used for block-level insertions. When isActive, uses emphasised (pressed) styling.
117
143
  *
@@ -153,7 +179,13 @@ export var createBlockChangedDecoration = function createBlockChangedDecoration(
153
179
  var isRoundedTable = expValEquals('platform_editor_table_diff_rounded_corners', 'isEnabled', true);
154
180
  var addedCellStyle = isTraditional ? traditionalAddedCellOverlayStyle : addedCellOverlayStyle;
155
181
  var deletedCellStyle = isTraditional ? isRoundedTable ? deletedTraditionalCellOverlayRoundedStyle : deletedTraditionalCellOverlayStyle : isRoundedTable ? deletedCellOverlayRoundedStyle : deletedCellOverlayStyle;
156
- var cellOverlayStyle = isInserted ? addedCellStyle : deletedCellStyle;
182
+
183
+ // On an inverted diff, an empty cell being filled is an addition, so give it the
184
+ // added (purple) overlay instead of the deleted (grey) one (EDITOR-8442).
185
+ var cellNode = doc === null || doc === void 0 ? void 0 : doc.nodeAt(change.from);
186
+ var isEmptyCellBeingFilled = !isInserted && !!cellNode && isCellEmpty(cellNode);
187
+ var useAddedStyle = isInserted || isEmptyCellBeingFilled;
188
+ var cellOverlayStyle = useAddedStyle ? addedCellStyle : deletedCellStyle;
157
189
  cellOverlay.setAttribute('style', cellOverlayStyle);
158
190
  decorations.push(
159
191
  // change.to - 1 to position the overlay inside the end of the cell
@@ -13,27 +13,99 @@ var isHeadingLevel = function isHeadingLevel(level) {
13
13
  };
14
14
 
15
15
  /**
16
- * This function is used to create a decoration widget to show content
17
- * that is not in the current document.
16
+ * Render one content widget per table cell, positioned inside the matching cell in
17
+ * `newDoc`, so a whole-cell replacement shows its added content in-place instead of
18
+ * being dropped (EDITOR-8442).
18
19
  */
19
- export var createNodeChangedDecorationWidget = function createNodeChangedDecorationWidget(_ref) {
20
- var _slice$content, _slice$content2, _slice$content3, _$safeInsertPos$nodeB, _slice$content$firstC;
21
- var change = _ref.change,
22
- doc = _ref.doc,
20
+ var createTableCellContentWidgets = function createTableCellContentWidgets(_ref) {
21
+ var slice = _ref.slice,
22
+ newDoc = _ref.newDoc,
23
+ change = _ref.change,
23
24
  nodeViewSerializer = _ref.nodeViewSerializer,
24
25
  colorScheme = _ref.colorScheme,
25
- newDoc = _ref.newDoc,
26
- intl = _ref.intl,
27
- activeIndexPos = _ref.activeIndexPos,
28
- _ref$isInserted = _ref.isInserted,
29
- isInserted = _ref$isInserted === void 0 ? false : _ref$isInserted,
30
- _ref$showIndicators = _ref.showIndicators,
31
- showIndicators = _ref$showIndicators === void 0 ? false : _ref$showIndicators,
32
- _ref$placeBelow = _ref.placeBelow,
33
- placeBelow = _ref$placeBelow === void 0 ? false : _ref$placeBelow,
34
- diffType = _ref.diffType,
35
- _ref$hideAddedDiffsUn = _ref.hideAddedDiffsUnderline,
36
- hideAddedDiffsUnderline = _ref$hideAddedDiffsUn === void 0 ? false : _ref$hideAddedDiffsUn;
26
+ isInserted = _ref.isInserted,
27
+ diffType = _ref.diffType;
28
+ // Collect the inner content position of each cell in the new document within
29
+ // the change range. Whole-cell replacement preserves cell count/order, so the
30
+ // nth new-doc cell corresponds to the nth cell in the slice.
31
+ var newCellInnerPositions = [];
32
+ newDoc.nodesBetween(change.fromB, change.toB, function (node, pos) {
33
+ if (node.type.name === 'tableHeader' || node.type.name === 'tableCell') {
34
+ // Skip malformed cells with no block child: `pos + 2` would be out of bounds.
35
+ if (node.childCount === 0) {
36
+ return false;
37
+ }
38
+ // +1 into the cell, +1 into its first child (the paragraph) = inner content.
39
+ newCellInnerPositions.push(pos + 2);
40
+ return false;
41
+ }
42
+ return true;
43
+ });
44
+ var decorations = [];
45
+ slice.content.forEach(function (cellNode, _offset, index) {
46
+ var targetPos = newCellInnerPositions[index];
47
+ if (targetPos === undefined || cellNode.content.size === 0) {
48
+ return;
49
+ }
50
+ var dom = document.createElement('span');
51
+ // A cell contains block content (paragraphs); serialize the cell's content
52
+ // so every block inside the cell is rendered.
53
+ cellNode.content.forEach(function (blockNode) {
54
+ var nodeView = nodeViewSerializer.tryCreateNodeView(blockNode);
55
+ var wrapper = createContentWrapper(colorScheme, false, isInserted, diffType);
56
+ if (nodeView) {
57
+ wrapper.append(nodeView);
58
+ } else {
59
+ var serialized = blockNode.type.inlineContent ? nodeViewSerializer.serializeFragment(blockNode.content) : nodeViewSerializer.serializeNode(blockNode);
60
+ if (serialized) {
61
+ wrapper.append(serialized);
62
+ }
63
+ }
64
+ dom.append(wrapper);
65
+ });
66
+ if (dom.childNodes.length === 0) {
67
+ return;
68
+ }
69
+
70
+ // This helper only runs for added content, so use the "changed" testid — not
71
+ // "deleted", which page models match as removed content.
72
+ dom.setAttribute('data-testid', 'show-diff-changed-decoration');
73
+
74
+ // Skip findSafeInsertPos: it walks forward to a schema-valid cell-insert point
75
+ // and pushes the widget into the next cell. `targetPos` is the correct spot.
76
+ decorations.push(Decoration.widget(targetPos, dom, _objectSpread({}, buildDiffDecorationSpec(_objectSpread({
77
+ decorationType: 'widget',
78
+ diffId: crypto.randomUUID(),
79
+ diffType: diffType
80
+ }, isExtendedEnabled(diffType) && {
81
+ side: -1
82
+ })))));
83
+ });
84
+ return decorations;
85
+ };
86
+
87
+ /**
88
+ * This function is used to create a decoration widget to show content
89
+ * that is not in the current document.
90
+ */
91
+ export var createNodeChangedDecorationWidget = function createNodeChangedDecorationWidget(_ref2) {
92
+ var _slice$content, _slice$content2, _slice$content3, _$safeInsertPos$nodeB, _slice$content$firstC, _newDoc$firstChild, _slice$content$firstC2;
93
+ var change = _ref2.change,
94
+ doc = _ref2.doc,
95
+ nodeViewSerializer = _ref2.nodeViewSerializer,
96
+ colorScheme = _ref2.colorScheme,
97
+ newDoc = _ref2.newDoc,
98
+ intl = _ref2.intl,
99
+ activeIndexPos = _ref2.activeIndexPos,
100
+ _ref2$isInserted = _ref2.isInserted,
101
+ isInserted = _ref2$isInserted === void 0 ? false : _ref2$isInserted,
102
+ _ref2$showIndicators = _ref2.showIndicators,
103
+ showIndicators = _ref2$showIndicators === void 0 ? false : _ref2$showIndicators,
104
+ _ref2$placeBelow = _ref2.placeBelow,
105
+ placeBelow = _ref2$placeBelow === void 0 ? false : _ref2$placeBelow,
106
+ diffType = _ref2.diffType,
107
+ _ref2$hideAddedDiffsU = _ref2.hideAddedDiffsUnderline,
108
+ hideAddedDiffsUnderline = _ref2$hideAddedDiffsU === void 0 ? false : _ref2$hideAddedDiffsU;
37
109
  var slice = doc.slice(change.fromA, change.toA);
38
110
  var shouldSkipDeletedEmptyParagraphDecoration = !isInserted && (slice === null || slice === void 0 || (_slice$content = slice.content) === null || _slice$content === void 0 ? void 0 : _slice$content.childCount) === 1 && (slice === null || slice === void 0 || (_slice$content2 = slice.content) === null || _slice$content2 === void 0 || (_slice$content2 = _slice$content2.firstChild) === null || _slice$content2 === void 0 ? void 0 : _slice$content2.type.name) === 'paragraph' && (slice === null || slice === void 0 || (_slice$content3 = slice.content) === null || _slice$content3 === void 0 || (_slice$content3 = _slice$content3.firstChild) === null || _slice$content3 === void 0 ? void 0 : _slice$content3.content.size) === 0;
39
111
  // Widget decoration used for deletions as the content is not in the document
@@ -56,7 +128,23 @@ export var createNodeChangedDecorationWidget = function createNodeChangedDecorat
56
128
  return ['tableRow'].includes(siblingNode.type.name);
57
129
  });
58
130
  });
131
+
132
+ // Added whole cells (e.g. "Add table headers"): render per-cell content widgets.
133
+ // Deleted whole cells (delete row/col) fall through — they don't exist in `newDoc`,
134
+ // so there's nowhere to anchor content and we keep the original "render nothing".
135
+ if (isTableCellContent && isInserted) {
136
+ return createTableCellContentWidgets({
137
+ slice: slice,
138
+ newDoc: newDoc,
139
+ change: change,
140
+ nodeViewSerializer: nodeViewSerializer,
141
+ colorScheme: colorScheme,
142
+ isInserted: isInserted,
143
+ diffType: diffType
144
+ });
145
+ }
59
146
  if (isTableCellContent) {
147
+ // Deleted whole cells: nothing to render (see note above).
60
148
  return [];
61
149
  }
62
150
  if (isTableRowContent) {
@@ -77,11 +165,18 @@ export var createNodeChangedDecorationWidget = function createNodeChangedDecorat
77
165
  var $safeInsertPos = newDoc.resolve(safeInsertPos);
78
166
  var isTopLevelInsert = $safeInsertPos.depth === 0;
79
167
  var hasPreviousBlock = ((_$safeInsertPos$nodeB = $safeInsertPos.nodeBefore) === null || _$safeInsertPos$nodeB === void 0 ? void 0 : _$safeInsertPos$nodeB.isBlock) === true;
80
- if (isExtendedEnabled(diffType) && isTopLevelInsert && hasPreviousBlock) {
168
+ var isFirstDocHeadingReplacement = isExtendedEnabled(diffType) && !placeBelow && change.fromB === 0 && ((_slice$content$firstC = slice.content.firstChild) === null || _slice$content$firstC === void 0 ? void 0 : _slice$content$firstC.type.name) === 'heading' && ((_newDoc$firstChild = newDoc.firstChild) === null || _newDoc$firstChild === void 0 ? void 0 : _newDoc$firstChild.type.name) === 'heading';
169
+ if (isExtendedEnabled(diffType) && isTopLevelInsert && hasPreviousBlock && !isFirstDocHeadingReplacement) {
81
170
  // Editor CSS removes the top margin from a block when it is its parent's first child.
82
171
  // Keep the serialized block as a subsequent child so each block type retains its own margin.
83
172
  dom.append(dom.ownerDocument.createElement('div'));
84
173
  }
174
+ if (isFirstDocHeadingReplacement) {
175
+ // The replacement widget is the visual first block, while the real first document node has
176
+ // its top margin reset. Add a compact gap after the widget so the two diff blocks do not overlap.
177
+ dom.style.display = 'block';
178
+ dom.style.marginBottom = "var(--ds-space-200, 16px)";
179
+ }
85
180
  // When mediaSingle nodes are rendered inside a widget decoration (e.g. as part of
86
181
  // a replaced panel), the centering CSS (margin-left: 50%; transform: translateX(-50%))
87
182
  // incorrectly applies because isNestedNode resolves to false (getPos returns 0).
@@ -251,7 +346,7 @@ export var createNodeChangedDecorationWidget = function createNodeChangedDecorat
251
346
  // block before it. Add an empty widget matching the following heading so the editor's own heading
252
347
  // styles supply the appropriate spacing. For other node types, retain the existing generic spacer.
253
348
  var isPureDeletion = change.fromB === change.toB;
254
- var isSingleBlock = slice.content.childCount === 1 && ((_slice$content$firstC = slice.content.firstChild) === null || _slice$content$firstC === void 0 ? void 0 : _slice$content$firstC.isBlock);
349
+ var isSingleBlock = slice.content.childCount === 1 && ((_slice$content$firstC2 = slice.content.firstChild) === null || _slice$content$firstC2 === void 0 ? void 0 : _slice$content$firstC2.isBlock);
255
350
  var isDiffWidgetAtStartOfDoc = $safeInsertPos.depth === 0 && $safeInsertPos.index(0) === 0;
256
351
  if (isDiffWidgetAtStartOfDoc && isSingleBlock && isPureDeletion && isExtendedEnabled(diffType)) {
257
352
  var followingNode = $safeInsertPos.nodeAfter;
@@ -39,13 +39,8 @@ export declare const createBlockIndicatorAnchorWidgets: ({ doc, from, to, diffId
39
39
  to: number;
40
40
  }) => Decoration[];
41
41
  /**
42
- * Creates invisible anchor widgets for a single inline diff range so that the
43
- * `IndicatorBar` can use CSS anchor positioning to align itself with the diff.
44
- *
45
- * - A `from` anchor is placed at the start of the range (top of the bar).
46
- * - A `to` anchor is placed at the end of the range (bottom of the bar).
47
- * - An optional `left` anchor is placed before the first textblock when the
48
- * diff is inside a resizable block node (table, layout, expand).
42
+ * Invisible `from`/`to` (and optional `left`) anchor widgets for one inline diff
43
+ * range, so the `IndicatorBar` can align itself via CSS anchor positioning.
49
44
  */
50
45
  export declare const createInlineIndicatorAnchorWidgets: ({ doc, from, to, diffId, }: {
51
46
  diffId: string;
package/docs/0-intro.tsx CHANGED
@@ -1,7 +1,6 @@
1
1
  import React from 'react';
2
2
 
3
3
  import { AtlassianInternalWarning, code, md } from '@atlaskit/docs';
4
- // eslint-disable-next-line @atlassian/tangerine/import/entry-points
5
4
  import { createEditorUseOnlyNotice } from '@atlaskit/editor-common/doc-utils';
6
5
  import { token } from '@atlaskit/tokens';
7
6
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@atlaskit/editor-plugin-show-diff",
3
- "version": "13.0.1",
3
+ "version": "13.0.4",
4
4
  "description": "ShowDiff plugin for @atlaskit/editor-core",
5
5
  "author": "Atlassian Pty Ltd",
6
6
  "license": "Apache-2.0",
@@ -20,7 +20,7 @@
20
20
  "sideEffects": false,
21
21
  "atlaskit:src": "src/index.ts",
22
22
  "dependencies": {
23
- "@atlaskit/adf-schema": "^56.7.0",
23
+ "@atlaskit/adf-schema": "^57.0.0",
24
24
  "@atlaskit/custom-steps": "^1.0.0",
25
25
  "@atlaskit/editor-plugin-analytics": "^15.0.0",
26
26
  "@atlaskit/editor-plugin-user-intent": "^13.0.0",
@@ -36,12 +36,12 @@
36
36
  "prosemirror-changeset": "^2.3.1"
37
37
  },
38
38
  "devDependencies": {
39
- "@atlaskit/adf-utils": "^20.5.0",
39
+ "@atlaskit/adf-utils": "^20.6.0",
40
40
  "@atlaskit/button": "^25.0.0",
41
41
  "@atlaskit/css": "^1.0.0",
42
42
  "@atlaskit/dropdown-menu": "^18.0.0",
43
43
  "@atlaskit/editor-core": "^224.0.0",
44
- "@atlaskit/editor-json-transformer": "^9.2.0",
44
+ "@atlaskit/editor-json-transformer": "^9.4.0",
45
45
  "@atlaskit/form": "^17.0.0",
46
46
  "@atlaskit/primitives": "^22.2.0",
47
47
  "@atlaskit/section-message": "^10.0.0",
@@ -53,7 +53,7 @@
53
53
  "react-intl": "^7.0.0"
54
54
  },
55
55
  "peerDependencies": {
56
- "@atlaskit/editor-common": "^119.1.0",
56
+ "@atlaskit/editor-common": "^119.4.0",
57
57
  "react": "^18.2.0 || ^19.2.0"
58
58
  },
59
59
  "techstack": {