@atlaskit/editor-plugin-show-diff 10.1.22 → 10.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (65) hide show
  1. package/CHANGELOG.md +43 -0
  2. package/dist/cjs/pm-plugins/calculateDiff/calculateDiffDecorations.js +132 -31
  3. package/dist/cjs/pm-plugins/calculateDiff/groupChangesByBlock.js +4 -2
  4. package/dist/cjs/pm-plugins/calculateDiff/smart/classifySmartChanges.js +841 -0
  5. package/dist/cjs/pm-plugins/calculateDiff/smart/helpers.js +135 -0
  6. package/dist/cjs/pm-plugins/calculateDiff/smart/segmentText.js +331 -0
  7. package/dist/cjs/pm-plugins/calculateDiff/smart/thresholds.js +46 -0
  8. package/dist/cjs/pm-plugins/decorations/createBlockChangedDecoration.js +24 -18
  9. package/dist/cjs/pm-plugins/decorations/createChangedRowDecorationWidgets.js +18 -12
  10. package/dist/cjs/pm-plugins/decorations/createInlineChangedDecoration.js +6 -5
  11. package/dist/cjs/pm-plugins/decorations/createNodeChangedDecorationWidget.js +30 -17
  12. package/dist/cjs/pm-plugins/decorations/decorationKeys.js +8 -5
  13. package/dist/cjs/pm-plugins/decorations/utils/wrapBlockNodeView.js +52 -30
  14. package/dist/cjs/pm-plugins/getScrollableDecorations.js +6 -6
  15. package/dist/cjs/pm-plugins/isExtendedEnabled.js +20 -0
  16. package/dist/cjs/pm-plugins/main.js +21 -16
  17. package/dist/es2019/pm-plugins/calculateDiff/calculateDiffDecorations.js +119 -29
  18. package/dist/es2019/pm-plugins/calculateDiff/groupChangesByBlock.js +4 -3
  19. package/dist/es2019/pm-plugins/calculateDiff/smart/classifySmartChanges.js +689 -0
  20. package/dist/es2019/pm-plugins/calculateDiff/smart/helpers.js +106 -0
  21. package/dist/es2019/pm-plugins/calculateDiff/smart/segmentText.js +283 -0
  22. package/dist/es2019/pm-plugins/calculateDiff/smart/thresholds.js +45 -0
  23. package/dist/es2019/pm-plugins/decorations/createBlockChangedDecoration.js +24 -18
  24. package/dist/es2019/pm-plugins/decorations/createChangedRowDecorationWidgets.js +18 -12
  25. package/dist/es2019/pm-plugins/decorations/createInlineChangedDecoration.js +6 -5
  26. package/dist/es2019/pm-plugins/decorations/createNodeChangedDecorationWidget.js +31 -16
  27. package/dist/es2019/pm-plugins/decorations/decorationKeys.js +9 -5
  28. package/dist/es2019/pm-plugins/decorations/utils/wrapBlockNodeView.js +52 -34
  29. package/dist/es2019/pm-plugins/getScrollableDecorations.js +6 -6
  30. package/dist/es2019/pm-plugins/isExtendedEnabled.js +12 -0
  31. package/dist/es2019/pm-plugins/main.js +21 -16
  32. package/dist/esm/pm-plugins/calculateDiff/calculateDiffDecorations.js +132 -31
  33. package/dist/esm/pm-plugins/calculateDiff/groupChangesByBlock.js +4 -3
  34. package/dist/esm/pm-plugins/calculateDiff/smart/classifySmartChanges.js +834 -0
  35. package/dist/esm/pm-plugins/calculateDiff/smart/helpers.js +128 -0
  36. package/dist/esm/pm-plugins/calculateDiff/smart/segmentText.js +325 -0
  37. package/dist/esm/pm-plugins/calculateDiff/smart/thresholds.js +39 -0
  38. package/dist/esm/pm-plugins/decorations/createBlockChangedDecoration.js +24 -18
  39. package/dist/esm/pm-plugins/decorations/createChangedRowDecorationWidgets.js +18 -12
  40. package/dist/esm/pm-plugins/decorations/createInlineChangedDecoration.js +6 -5
  41. package/dist/esm/pm-plugins/decorations/createNodeChangedDecorationWidget.js +30 -17
  42. package/dist/esm/pm-plugins/decorations/decorationKeys.js +9 -5
  43. package/dist/esm/pm-plugins/decorations/utils/wrapBlockNodeView.js +52 -30
  44. package/dist/esm/pm-plugins/getScrollableDecorations.js +6 -6
  45. package/dist/esm/pm-plugins/isExtendedEnabled.js +14 -0
  46. package/dist/esm/pm-plugins/main.js +21 -16
  47. package/dist/types/entry-points/show-diff-plugin-type.d.ts +1 -1
  48. package/dist/types/pm-plugins/calculateDiff/calculateDiffDecorations.d.ts +3 -1
  49. package/dist/types/pm-plugins/calculateDiff/groupChangesByBlock.d.ts +9 -0
  50. package/dist/types/pm-plugins/calculateDiff/smart/classifySmartChanges.d.ts +26 -0
  51. package/dist/types/pm-plugins/calculateDiff/smart/helpers.d.ts +44 -0
  52. package/dist/types/pm-plugins/calculateDiff/smart/segmentText.d.ts +32 -0
  53. package/dist/types/pm-plugins/calculateDiff/smart/thresholds.d.ts +44 -0
  54. package/dist/types/pm-plugins/decorations/createBlockChangedDecoration.d.ts +3 -2
  55. package/dist/types/pm-plugins/decorations/createChangedRowDecorationWidgets.d.ts +3 -2
  56. package/dist/types/pm-plugins/decorations/createInlineChangedDecoration.d.ts +3 -2
  57. package/dist/types/pm-plugins/decorations/createNodeChangedDecorationWidget.d.ts +4 -2
  58. package/dist/types/pm-plugins/decorations/decorationKeys.d.ts +5 -3
  59. package/dist/types/pm-plugins/decorations/utils/wrapBlockNodeView.d.ts +6 -4
  60. package/dist/types/pm-plugins/getScrollableDecorations.d.ts +2 -1
  61. package/dist/types/pm-plugins/isExtendedEnabled.d.ts +11 -0
  62. package/dist/types/pm-plugins/main.d.ts +11 -1
  63. package/dist/types/showDiffPluginType.d.ts +20 -1
  64. package/docs/smart-diff-design.md +240 -0
  65. package/package.json +8 -3
@@ -0,0 +1,135 @@
1
+ "use strict";
2
+
3
+ var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
4
+ Object.defineProperty(exports, "__esModule", {
5
+ value: true
6
+ });
7
+ exports.smartChangeLevel = exports.rangesOverlap = exports.mergeOverlappingByNewDocRange = exports.makePromotedChange = exports.createSpans = void 0;
8
+ var _defineProperty2 = _interopRequireDefault(require("@babel/runtime/helpers/defineProperty"));
9
+ var _typeof2 = _interopRequireDefault(require("@babel/runtime/helpers/typeof"));
10
+ var _toConsumableArray2 = _interopRequireDefault(require("@babel/runtime/helpers/toConsumableArray"));
11
+ 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; }
12
+ 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; }
13
+ 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; } } }; }
14
+ 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; } }
15
+ 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; }
16
+ /**
17
+ * The granularity level a `smart` change was promoted to. Stored on span `data` so the
18
+ * decoration pipeline can render each level differently (e.g. node-level deletions shown
19
+ * below the new content). `null` for non-promoted (inline) changes.
20
+ */
21
+
22
+ /** Span `data` payload carried by promoted `smart` changes. */
23
+
24
+ /**
25
+ * Build the `deleted`/`inserted` span arrays a `Change` needs. A length of 0 yields an
26
+ * empty array (no change on that side).
27
+ */
28
+ var createSpans = exports.createSpans = function createSpans(length) {
29
+ var data = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : null;
30
+ return length > 0 ? [{
31
+ length: length,
32
+ data: data
33
+ }] : [];
34
+ };
35
+
36
+ /**
37
+ * Build a promoted `Change` spanning `[fromA,toA)` in the original doc and `[fromB,toB)`
38
+ * in the new doc, with both sides marked changed (mirrors `groupChangesByBlock` so the
39
+ * decoration pipeline renders both the inserted-block decoration and the deleted-node
40
+ * widget).
41
+ *
42
+ * `level` tags the change's spans so downstream decoration logic can treat node-level
43
+ * promotions specially (see `smartChangeLevel`).
44
+ */
45
+ var makePromotedChange = exports.makePromotedChange = function makePromotedChange(fromA, toA, fromB, toB, level) {
46
+ var data = level ? {
47
+ smartLevel: level
48
+ } : null;
49
+ // `deleted` and `inserted` must be independent arrays: downstream code may mutate one
50
+ // side (push/splice), which would silently corrupt the other if they shared a reference.
51
+ return {
52
+ fromA: fromA,
53
+ toA: toA,
54
+ fromB: fromB,
55
+ toB: toB,
56
+ deleted: createSpans(Math.max(0, toA - fromA), data),
57
+ inserted: createSpans(Math.max(0, toB - fromB), data)
58
+ };
59
+ };
60
+
61
+ /**
62
+ * Read the `smartLevel` tag off a change's spans, if present. Returns `undefined` for
63
+ * changes not produced by the `smart` classifier (e.g. inline/block/step diff types).
64
+ */
65
+ var smartChangeLevel = exports.smartChangeLevel = function smartChangeLevel(change) {
66
+ var spans = [].concat((0, _toConsumableArray2.default)(change.inserted), (0, _toConsumableArray2.default)(change.deleted));
67
+ var _iterator = _createForOfIteratorHelper(spans),
68
+ _step;
69
+ try {
70
+ for (_iterator.s(); !(_step = _iterator.n()).done;) {
71
+ var span = _step.value;
72
+ var data = span.data;
73
+ if (data && (0, _typeof2.default)(data) === 'object' && 'smartLevel' in data) {
74
+ return data.smartLevel;
75
+ }
76
+ }
77
+ } catch (err) {
78
+ _iterator.e(err);
79
+ } finally {
80
+ _iterator.f();
81
+ }
82
+ return undefined;
83
+ };
84
+
85
+ /** True when a change has no original-side (deleted) content — a pure insertion. */
86
+ var isPureInsertion = function isPureInsertion(change) {
87
+ return change.toA <= change.fromA;
88
+ };
89
+
90
+ /**
91
+ * Coalesce changes that overlap in new-doc (B) coordinates.
92
+ *
93
+ * Merging two changes takes the UNION of their A (original-doc) ranges. That is only safe when
94
+ * their A ranges actually touch/overlap — OR when at least one is a pure insertion (empty A).
95
+ * Otherwise, merging a deletion at A[110,152] with a later edit at A[162,168] would fabricate an
96
+ * A span [110,168] covering the untouched gap, which then overlaps a *different* change's A range
97
+ * (making the same original content appear deleted twice). So we require B-overlap AND
98
+ * (A-overlap OR a pure insertion) before coalescing.
99
+ */
100
+ var mergeOverlappingByNewDocRange = exports.mergeOverlappingByNewDocRange = function mergeOverlappingByNewDocRange(changes) {
101
+ if (changes.length <= 1) {
102
+ return changes;
103
+ }
104
+ var sorted = (0, _toConsumableArray2.default)(changes).sort(function (l, r) {
105
+ return l.fromB - r.fromB;
106
+ });
107
+ var merged = [];
108
+ var current = _objectSpread({}, sorted[0]);
109
+ for (var i = 1; i < sorted.length; i++) {
110
+ var next = sorted[i];
111
+ var bOverlaps = next.fromB <= current.toB;
112
+ // A ranges may be unioned only if they touch, or if either side contributes no A content.
113
+ var aMergeable = isPureInsertion(current) || isPureInsertion(next) || rangesOverlap(current.fromA, current.toA, next.fromA, next.toA) || next.fromA <= current.toA; // adjacency (sorted by B, A usually monotonic)
114
+ if (bOverlaps && aMergeable) {
115
+ current = {
116
+ fromA: Math.min(current.fromA, next.fromA),
117
+ toA: Math.max(current.toA, next.toA),
118
+ fromB: Math.min(current.fromB, next.fromB),
119
+ toB: Math.max(current.toB, next.toB),
120
+ deleted: [].concat((0, _toConsumableArray2.default)(current.deleted), (0, _toConsumableArray2.default)(next.deleted)),
121
+ inserted: [].concat((0, _toConsumableArray2.default)(current.inserted), (0, _toConsumableArray2.default)(next.inserted))
122
+ };
123
+ } else {
124
+ merged.push(current);
125
+ current = _objectSpread({}, next);
126
+ }
127
+ }
128
+ merged.push(current);
129
+ return merged;
130
+ };
131
+
132
+ /** True when two half-open ranges overlap. */
133
+ var rangesOverlap = exports.rangesOverlap = function rangesOverlap(aFrom, aTo, bFrom, bTo) {
134
+ return aFrom < bTo && bFrom < aTo;
135
+ };
@@ -0,0 +1,331 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.segmentWordSpans = exports.segmentSentences = exports.countWords = exports.buildCharsByOffset = void 0;
7
+ function _createForOfIteratorHelper(r, e) { var t = "undefined" != typeof Symbol && r[Symbol.iterator] || r["@@iterator"]; if (!t) { if (Array.isArray(r) || (t = _unsupportedIterableToArray(r)) || e && r && "number" == typeof r.length) { t && (r = t); var _n = 0, F = function F() {}; return { s: F, n: function n() { return _n >= r.length ? { done: !0 } : { done: !1, value: r[_n++] }; }, e: function e(r) { throw r; }, f: F }; } throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); } var o, a = !0, u = !1; return { s: function s() { t = t.call(r); }, n: function n() { var r = t.next(); return a = r.done, r; }, e: function e(r) { u = !0, o = r; }, f: function f() { try { a || null == t.return || t.return(); } finally { if (u) throw o; } } }; }
8
+ function _unsupportedIterableToArray(r, a) { if (r) { if ("string" == typeof r) return _arrayLikeToArray(r, a); var t = {}.toString.call(r).slice(8, -1); return "Object" === t && r.constructor && (t = r.constructor.name), "Map" === t || "Set" === t ? Array.from(r) : "Arguments" === t || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(t) ? _arrayLikeToArray(r, a) : void 0; } }
9
+ function _arrayLikeToArray(r, a) { (null == a || a > r.length) && (a = r.length); for (var e = 0, n = Array(a); e < a; e++) n[e] = r[e]; return n; }
10
+ /**
11
+ * A half-open offset span `[from, to)` measured in *content offsets* relative to the
12
+ * start of a textblock (i.e. the same coordinate space as `buildCharsByOffset`).
13
+ */
14
+
15
+ /**
16
+ * Minimal local typing for `Intl.Segmenter`. The TS lib target configured for this
17
+ * package does not include the `Intl.Segmenter` declarations, so we declare just the
18
+ * surface we use and access it via a runtime-checked cast. Remove when the lib target is
19
+ * bumped to include `es2022.intl`/`esnext.intl`.
20
+ */
21
+
22
+ // A character is treated as part of a word unless it is whitespace or common punctuation.
23
+ // We deliberately avoid Unicode property escapes (`\p{...}`) and the `u` flag here: the `u`
24
+ // flag is only valid when targeting es6+, and the declaration-generation build config for
25
+ // this package targets an older lib, which rejects it with TS1501. This negated approach
26
+ // keeps word segmentation reasonable across scripts (any non-punctuation, non-space glyph
27
+ // counts as a word char) without needing Unicode-aware regex.
28
+ var NON_WORD_CHARS = ' \t\n\r\f\v.,;:!?…。!?、"\'`“”‘’()[]{}<>/\\|@#$%^&*-+=~';
29
+ var NON_WORD_CHAR_SET = new Set(NON_WORD_CHARS.split(''));
30
+ var isWordChar = function isWordChar(ch) {
31
+ return !NON_WORD_CHAR_SET.has(ch);
32
+ };
33
+ // Whitespace is checked via a Set rather than a regex to avoid both the `require-unicode-regexp`
34
+ // lint rule and the TS1501 error the `u` flag triggers under this package's build lib target.
35
+ var WHITESPACE_CHAR_SET = new Set([' ', '\t', '\n', '\r', '\f', '\v', "\xA0"]);
36
+ var isWhitespaceChar = function isWhitespaceChar(ch) {
37
+ return WHITESPACE_CHAR_SET.has(ch);
38
+ };
39
+ // Sentence terminators used by the regex fallback. We intentionally keep this small and
40
+ // conservative — the Intl.Segmenter path is the source of truth when available.
41
+ var SENTENCE_TERMINATORS = new Set(['.', '!', '?', '…', '。', '!', '?']);
42
+
43
+ /**
44
+ * Build a per-content-offset view of a textblock's characters.
45
+ *
46
+ * `chars[i]` is the character at content offset `i`, or `null` when that offset lies
47
+ * inside a non-text inline node (mention, date, emoji, hardBreak, …). A `null` acts as an
48
+ * *opaque single token*: it counts as one word and never terminates a sentence.
49
+ */
50
+ var buildCharsByOffset = exports.buildCharsByOffset = function buildCharsByOffset(parent) {
51
+ var chars = Array.from({
52
+ length: parent.content.size
53
+ }, function () {
54
+ return null;
55
+ });
56
+ parent.content.forEach(function (child, offset) {
57
+ var _child$text;
58
+ if (!child.isText) {
59
+ return;
60
+ }
61
+ var text = (_child$text = child.text) !== null && _child$text !== void 0 ? _child$text : '';
62
+ for (var i = 0; i < text.length; i++) {
63
+ chars[offset + i] = text[i];
64
+ }
65
+ });
66
+ return chars;
67
+ };
68
+ var getSegmenterCtor = function getSegmenterCtor() {
69
+ if (typeof Intl === 'undefined') {
70
+ return undefined;
71
+ }
72
+ var ctor = Intl.Segmenter;
73
+ return typeof ctor === 'function' ? ctor : undefined;
74
+ };
75
+ var cachedLocale;
76
+ var cachedSentenceSegmenter;
77
+ var cachedWordSegmenter;
78
+ var getSegmenters = function getSegmenters(locale) {
79
+ var Ctor = getSegmenterCtor();
80
+ if (!Ctor) {
81
+ return undefined;
82
+ }
83
+ if (cachedLocale !== locale || !cachedSentenceSegmenter || !cachedWordSegmenter) {
84
+ cachedLocale = locale;
85
+ cachedSentenceSegmenter = new Ctor(locale, {
86
+ granularity: 'sentence'
87
+ });
88
+ cachedWordSegmenter = new Ctor(locale, {
89
+ granularity: 'word'
90
+ });
91
+ }
92
+ return {
93
+ sentence: cachedSentenceSegmenter,
94
+ word: cachedWordSegmenter
95
+ };
96
+ };
97
+
98
+ /**
99
+ * Convert a char-view into a plain string plus an index map back to content offsets.
100
+ *
101
+ * Opaque inline tokens (`null`) are represented in the string by a single sentinel
102
+ * character (Unicode Object Replacement Character, U+FFFC) so that:
103
+ * - the segmenter treats them as a word-like glyph rather than whitespace/punctuation;
104
+ * - they never look like a sentence terminator.
105
+ * `indexMap[j]` gives the content offset of string index `j`.
106
+ */
107
+ var OBJECT_REPLACEMENT = "\uFFFC";
108
+ var flattenChars = function flattenChars(chars) {
109
+ var text = '';
110
+ var indexMap = [];
111
+ for (var offset = 0; offset < chars.length; offset++) {
112
+ var ch = chars[offset];
113
+ text += ch === null ? OBJECT_REPLACEMENT : ch;
114
+ indexMap.push(offset);
115
+ }
116
+ return {
117
+ text: text,
118
+ indexMap: indexMap
119
+ };
120
+ };
121
+ var stringIndexToOffset = function stringIndexToOffset(indexMap, strIdx, fallback) {
122
+ if (strIdx < 0) {
123
+ return fallback;
124
+ }
125
+ if (strIdx >= indexMap.length) {
126
+ // One-past-the-end maps to the offset after the last char.
127
+ return indexMap.length > 0 ? indexMap[indexMap.length - 1] + 1 : fallback;
128
+ }
129
+ return indexMap[strIdx];
130
+ };
131
+
132
+ /**
133
+ * Segment a textblock char-view into sentence spans (content-offset coordinates).
134
+ * Uses Intl.Segmenter when available, otherwise a conservative regex fallback.
135
+ */
136
+ var segmentSentences = exports.segmentSentences = function segmentSentences(chars, locale) {
137
+ if (chars.length === 0) {
138
+ return [];
139
+ }
140
+ var _flattenChars = flattenChars(chars),
141
+ text = _flattenChars.text,
142
+ indexMap = _flattenChars.indexMap;
143
+ var segmenters = getSegmenters(locale);
144
+ if (segmenters) {
145
+ var _spans = [];
146
+ var _iterator = _createForOfIteratorHelper(segmenters.sentence.segment(text)),
147
+ _step;
148
+ try {
149
+ for (_iterator.s(); !(_step = _iterator.n()).done;) {
150
+ var seg = _step.value;
151
+ var startOffset = stringIndexToOffset(indexMap, seg.index, 0);
152
+ var endOffset = stringIndexToOffset(indexMap, seg.index + seg.segment.length, chars.length);
153
+ if (endOffset > startOffset) {
154
+ _spans.push({
155
+ from: startOffset,
156
+ to: endOffset
157
+ });
158
+ }
159
+ }
160
+ } catch (err) {
161
+ _iterator.e(err);
162
+ } finally {
163
+ _iterator.f();
164
+ }
165
+ return _spans.length > 0 ? _spans : [{
166
+ from: 0,
167
+ to: chars.length
168
+ }];
169
+ }
170
+
171
+ // Regex fallback: split after a sentence terminator followed by whitespace/EOL.
172
+ // A `null` (OBJECT_REPLACEMENT) is never a terminator.
173
+ var spans = [];
174
+ var start = 0;
175
+ for (var i = 0; i < chars.length; i++) {
176
+ var ch = chars[i];
177
+ if (ch !== null && SENTENCE_TERMINATORS.has(ch)) {
178
+ // Consume trailing spaces as part of this sentence.
179
+ var end = i + 1;
180
+ while (end < chars.length && chars[end] !== null && isWhitespaceChar(chars[end])) {
181
+ end++;
182
+ }
183
+ spans.push({
184
+ from: start,
185
+ to: end
186
+ });
187
+ start = end;
188
+ }
189
+ }
190
+ if (start < chars.length) {
191
+ spans.push({
192
+ from: start,
193
+ to: chars.length
194
+ });
195
+ }
196
+ return spans.length > 0 ? spans : [{
197
+ from: 0,
198
+ to: chars.length
199
+ }];
200
+ };
201
+
202
+ /**
203
+ * Count word-like tokens within a content-offset range of the char-view.
204
+ * Each opaque inline token (`null`) counts as exactly one word.
205
+ */
206
+ var countWords = exports.countWords = function countWords(chars, span, locale) {
207
+ var slice = chars.slice(span.from, span.to);
208
+ if (slice.length === 0) {
209
+ return 0;
210
+ }
211
+ var segmenters = getSegmenters(locale);
212
+ var _flattenChars2 = flattenChars(slice),
213
+ text = _flattenChars2.text;
214
+ if (segmenters) {
215
+ var _count = 0;
216
+ var _iterator2 = _createForOfIteratorHelper(segmenters.word.segment(text)),
217
+ _step2;
218
+ try {
219
+ for (_iterator2.s(); !(_step2 = _iterator2.n()).done;) {
220
+ var seg = _step2.value;
221
+ // Object-replacement sentinel is word-like; count it.
222
+ if (seg.isWordLike || seg.segment.includes(OBJECT_REPLACEMENT)) {
223
+ _count++;
224
+ }
225
+ }
226
+ } catch (err) {
227
+ _iterator2.e(err);
228
+ } finally {
229
+ _iterator2.f();
230
+ }
231
+ return _count;
232
+ }
233
+
234
+ // Regex fallback: a run of word chars OR a single opaque token is one word.
235
+ var count = 0;
236
+ var inWord = false;
237
+ var _iterator3 = _createForOfIteratorHelper(slice),
238
+ _step3;
239
+ try {
240
+ for (_iterator3.s(); !(_step3 = _iterator3.n()).done;) {
241
+ var ch = _step3.value;
242
+ if (ch === null) {
243
+ count++;
244
+ inWord = false;
245
+ continue;
246
+ }
247
+ if (isWordChar(ch)) {
248
+ if (!inWord) {
249
+ count++;
250
+ inWord = true;
251
+ }
252
+ } else {
253
+ inWord = false;
254
+ }
255
+ }
256
+ } catch (err) {
257
+ _iterator3.e(err);
258
+ } finally {
259
+ _iterator3.f();
260
+ }
261
+ return count;
262
+ };
263
+
264
+ /**
265
+ * Return the word-token spans (content-offset coordinates) within a range. Used to decide
266
+ * which words a change overlaps. Each opaque inline token is its own span.
267
+ */
268
+ var segmentWordSpans = exports.segmentWordSpans = function segmentWordSpans(chars, span, locale) {
269
+ var segmenters = getSegmenters(locale);
270
+ var slice = chars.slice(span.from, span.to);
271
+ var _flattenChars3 = flattenChars(slice),
272
+ text = _flattenChars3.text,
273
+ indexMap = _flattenChars3.indexMap;
274
+ var spans = [];
275
+ if (segmenters) {
276
+ var _iterator4 = _createForOfIteratorHelper(segmenters.word.segment(text)),
277
+ _step4;
278
+ try {
279
+ for (_iterator4.s(); !(_step4 = _iterator4.n()).done;) {
280
+ var seg = _step4.value;
281
+ if (seg.isWordLike || seg.segment.includes(OBJECT_REPLACEMENT)) {
282
+ var from = span.from + stringIndexToOffset(indexMap, seg.index, 0);
283
+ var to = span.from + stringIndexToOffset(indexMap, seg.index + seg.segment.length, slice.length);
284
+ spans.push({
285
+ from: from,
286
+ to: to
287
+ });
288
+ }
289
+ }
290
+ } catch (err) {
291
+ _iterator4.e(err);
292
+ } finally {
293
+ _iterator4.f();
294
+ }
295
+ return spans;
296
+ }
297
+ var wordStart = -1;
298
+ for (var i = 0; i < slice.length; i++) {
299
+ var ch = slice[i];
300
+ if (ch === null) {
301
+ if (wordStart !== -1) {
302
+ spans.push({
303
+ from: span.from + wordStart,
304
+ to: span.from + i
305
+ });
306
+ wordStart = -1;
307
+ }
308
+ spans.push({
309
+ from: span.from + i,
310
+ to: span.from + i + 1
311
+ });
312
+ } else if (isWordChar(ch)) {
313
+ if (wordStart === -1) {
314
+ wordStart = i;
315
+ }
316
+ } else if (wordStart !== -1) {
317
+ spans.push({
318
+ from: span.from + wordStart,
319
+ to: span.from + i
320
+ });
321
+ wordStart = -1;
322
+ }
323
+ }
324
+ if (wordStart !== -1) {
325
+ spans.push({
326
+ from: span.from + wordStart,
327
+ to: span.from + slice.length
328
+ });
329
+ }
330
+ return spans;
331
+ };
@@ -0,0 +1,46 @@
1
+ "use strict";
2
+
3
+ var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
4
+ Object.defineProperty(exports, "__esModule", {
5
+ value: true
6
+ });
7
+ exports.resolveThresholds = exports.DEFAULT_SMART_THRESHOLDS = void 0;
8
+ var _defineProperty2 = _interopRequireDefault(require("@babel/runtime/helpers/defineProperty"));
9
+ 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; }
10
+ 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; }
11
+ /**
12
+ * Configurable thresholds for the `smart` diffType.
13
+ *
14
+ * See docs/smart-diff-design.md §3 and §6 for the exact semantics of each level.
15
+ */
16
+
17
+ var DEFAULT_SMART_THRESHOLDS = exports.DEFAULT_SMART_THRESHOLDS = {
18
+ sentence: {
19
+ ratio: 0.4,
20
+ minChanged: 2
21
+ },
22
+ paragraph: {
23
+ ratio: 0.4,
24
+ minChanged: 2
25
+ },
26
+ node: {
27
+ ratio: 0.6,
28
+ textBearingRatio: 0.6
29
+ }
30
+ };
31
+
32
+ /**
33
+ * Merge caller overrides onto the defaults. Overrides are shallow-merged per level so a
34
+ * caller can tune a single field (e.g. only `sentence.ratio`) without re-specifying the
35
+ * rest.
36
+ */
37
+ var resolveThresholds = exports.resolveThresholds = function resolveThresholds(overrides) {
38
+ if (!overrides) {
39
+ return DEFAULT_SMART_THRESHOLDS;
40
+ }
41
+ return {
42
+ sentence: _objectSpread(_objectSpread({}, DEFAULT_SMART_THRESHOLDS.sentence), overrides.sentence),
43
+ paragraph: _objectSpread(_objectSpread({}, DEFAULT_SMART_THRESHOLDS.paragraph), overrides.paragraph),
44
+ node: _objectSpread(_objectSpread({}, DEFAULT_SMART_THRESHOLDS.node), overrides.node)
45
+ };
46
+ };
@@ -8,7 +8,7 @@ exports.createBlockChangedDecoration = void 0;
8
8
  var _toConsumableArray2 = _interopRequireDefault(require("@babel/runtime/helpers/toConsumableArray"));
9
9
  var _lazyNodeView = require("@atlaskit/editor-common/lazy-node-view");
10
10
  var _view = require("@atlaskit/editor-prosemirror/view");
11
- var _expValEquals = require("@atlaskit/tmp-editor-statsig/exp-val-equals");
11
+ var _isExtendedEnabled = require("../isExtendedEnabled");
12
12
  var _standard = require("./colorSchemes/standard");
13
13
  var _traditional = require("./colorSchemes/traditional");
14
14
  var _createAnchorDecorationWidgets = require("./createAnchorDecorationWidgets");
@@ -30,7 +30,8 @@ var getBlockNodeStyle = function getBlockNodeStyle(_ref) {
30
30
  _ref$isInserted = _ref.isInserted,
31
31
  isInserted = _ref$isInserted === void 0 ? true : _ref$isInserted,
32
32
  _ref$isActive = _ref.isActive,
33
- isActive = _ref$isActive === void 0 ? false : _ref$isActive;
33
+ isActive = _ref$isActive === void 0 ? false : _ref$isActive,
34
+ diffType = _ref.diffType;
34
35
  var isTraditional = colorScheme === 'traditional';
35
36
  if (['mediaSingle', 'mediaGroup', 'table',
36
37
  // Handle table separately to avoid border issues
@@ -43,13 +44,13 @@ var getBlockNodeStyle = function getBlockNodeStyle(_ref) {
43
44
  // Media nodes inside mediaSingle should not get position:relative
44
45
  // as it shifts the image outside its parent container (e.g. panel)
45
46
  if (nodeName === 'media') {
46
- if (!isInserted && (0, _expValEquals.expValEquals)('platform_editor_diff_plugin_extended', 'isEnabled', true)) {
47
+ if (!isInserted && (0, _isExtendedEnabled.isExtendedEnabled)(diffType)) {
47
48
  return isTraditional ? (0, _traditional.getDeletedTraditionalInlineStyle)(false) : _standard.deletedDecorationMarkerVariable;
48
49
  }
49
50
  return isTraditional ? isActive ? _traditional.traditionalStyleNodeActive : _traditional.traditionalStyleNodeNew : _standard.editingStyleNode;
50
51
  }
51
52
  if (['tableCell', 'tableHeader'].includes(nodeName)) {
52
- if ((0, _expValEquals.expValEquals)('platform_editor_diff_plugin_extended', 'isEnabled', true)) {
53
+ if ((0, _isExtendedEnabled.isExtendedEnabled)(diffType)) {
53
54
  // This is used for positioning the cell overlay widget decorations
54
55
  return (0, _lazyNodeView.convertToInlineCss)({
55
56
  position: 'relative'
@@ -59,13 +60,13 @@ var getBlockNodeStyle = function getBlockNodeStyle(_ref) {
59
60
  return undefined;
60
61
  }
61
62
  if (nodeName === 'panel') {
62
- if (!isInserted && (0, _expValEquals.expValEquals)('platform_editor_diff_plugin_extended', 'isEnabled', true)) {
63
+ if (!isInserted && (0, _isExtendedEnabled.isExtendedEnabled)(diffType)) {
63
64
  return isTraditional ? (0, _traditional.getDeletedTraditionalInlineStyle)(false) : _standard.deletedDecorationMarkerVariable;
64
65
  }
65
66
  return isTraditional ? isActive ? _traditional.traditionalStyleNodeActive : _traditional.traditionalStyleNodeNew : _standard.editingStyleNode;
66
67
  }
67
68
  if (['extension', 'embedCard', 'listItem'].includes(nodeName)) {
68
- if ((0, _expValEquals.expValEquals)('platform_editor_diff_plugin_extended', 'isEnabled', true)) {
69
+ if ((0, _isExtendedEnabled.isExtendedEnabled)(diffType)) {
69
70
  if (isInserted) {
70
71
  return isTraditional && isActive ? _traditional.traditionalDecorationMarkerVariableActive : isTraditional ? _traditional.traditionalDecorationMarkerVariableNew : _standard.standardDecorationMarkerVariable;
71
72
  } else {
@@ -78,7 +79,7 @@ var getBlockNodeStyle = function getBlockNodeStyle(_ref) {
78
79
  return isTraditional && isActive ? _traditional.traditionalDecorationMarkerVariableActive : isTraditional ? _traditional.traditionalDecorationMarkerVariableNew : _standard.standardDecorationMarkerVariable;
79
80
  }
80
81
  if (nodeName === 'blockquote') {
81
- if ((0, _expValEquals.expValEquals)('platform_editor_diff_plugin_extended', 'isEnabled', true)) {
82
+ if ((0, _isExtendedEnabled.isExtendedEnabled)(diffType)) {
82
83
  if (isInserted) {
83
84
  return isTraditional ? isActive ? _traditional.traditionalStyleQuoteNodeActive : _traditional.traditionalStyleQuoteNodeNew : _standard.editingStyleQuoteNode;
84
85
  } else {
@@ -88,7 +89,7 @@ var getBlockNodeStyle = function getBlockNodeStyle(_ref) {
88
89
  return isTraditional ? isActive ? _traditional.traditionalStyleQuoteNodeActive : _traditional.traditionalStyleQuoteNodeNew : _standard.editingStyleQuoteNode;
89
90
  }
90
91
  if (nodeName === 'rule') {
91
- if ((0, _expValEquals.expValEquals)('platform_editor_diff_plugin_extended', 'isEnabled', true)) {
92
+ if ((0, _isExtendedEnabled.isExtendedEnabled)(diffType)) {
92
93
  if (isInserted) {
93
94
  return isTraditional ? isActive ? _traditional.traditionalStyleRuleNodeActive : _traditional.traditionalStyleRuleNodeNew : _standard.editingStyleRuleNode;
94
95
  } else {
@@ -98,7 +99,7 @@ var getBlockNodeStyle = function getBlockNodeStyle(_ref) {
98
99
  return isTraditional ? isActive ? _traditional.traditionalStyleRuleNodeActive : _traditional.traditionalStyleRuleNodeNew : _standard.editingStyleRuleNode;
99
100
  }
100
101
  if (nodeName === 'blockCard') {
101
- if ((0, _expValEquals.expValEquals)('platform_editor_diff_plugin_extended', 'isEnabled', true)) {
102
+ if ((0, _isExtendedEnabled.isExtendedEnabled)(diffType)) {
102
103
  if (isInserted) {
103
104
  return isTraditional ? isActive ? _traditional.traditionalStyleCardBlockNodeActive : _traditional.traditionalStyleCardBlockNodeNew : _standard.editingStyleCardBlockNode;
104
105
  } else {
@@ -107,7 +108,7 @@ var getBlockNodeStyle = function getBlockNodeStyle(_ref) {
107
108
  }
108
109
  return isTraditional ? isActive ? _traditional.traditionalStyleCardBlockNodeActive : _traditional.traditionalStyleCardBlockNodeNew : _standard.editingStyleCardBlockNode;
109
110
  }
110
- if ((0, _expValEquals.expValEquals)('platform_editor_diff_plugin_extended', 'isEnabled', true)) {
111
+ if ((0, _isExtendedEnabled.isExtendedEnabled)(diffType)) {
111
112
  if (isInserted) {
112
113
  return isTraditional ? isActive ? _traditional.traditionalStyleNodeActive : _traditional.traditionalStyleNodeNew : _standard.editingStyleNode;
113
114
  } else {
@@ -136,7 +137,8 @@ var createBlockChangedDecoration = exports.createBlockChangedDecoration = functi
136
137
  shouldHideDeleted = _ref2$shouldHideDelet === void 0 ? false : _ref2$shouldHideDelet,
137
138
  _ref2$showIndicators = _ref2.showIndicators,
138
139
  showIndicators = _ref2$showIndicators === void 0 ? false : _ref2$showIndicators,
139
- doc = _ref2.doc;
140
+ doc = _ref2.doc,
141
+ diffType = _ref2.diffType;
140
142
  var decorations = [];
141
143
  var diffId = crypto.randomUUID();
142
144
  if (shouldHideDeleted) {
@@ -146,11 +148,12 @@ var createBlockChangedDecoration = exports.createBlockChangedDecoration = functi
146
148
  decorationType: 'block',
147
149
  diffId: diffId,
148
150
  isActive: isActive,
149
- nodeName: change.name
151
+ nodeName: change.name,
152
+ diffType: diffType
150
153
  }))];
151
154
  }
152
155
  var style;
153
- if ((0, _expValEquals.expValEquals)('platform_editor_diff_plugin_extended', 'isEnabled', true) && ['tableCell', 'tableHeader'].includes(change.name)) {
156
+ if ((0, _isExtendedEnabled.isExtendedEnabled)(diffType) && ['tableCell', 'tableHeader'].includes(change.name)) {
154
157
  var cellOverlay = document.createElement('div');
155
158
  var cellOverlayStyle = isInserted ? colorScheme === 'traditional' ? _traditional.traditionalAddedCellOverlayStyle : _standard.addedCellOverlayStyle : colorScheme === 'traditional' ? _traditional.deletedTraditionalCellOverlayStyle : _standard.deletedCellOverlayStyle;
156
159
  cellOverlay.setAttribute('style', cellOverlayStyle);
@@ -162,18 +165,20 @@ var createBlockChangedDecoration = exports.createBlockChangedDecoration = functi
162
165
  key: 'cell-overlay-decoration'
163
166
  }));
164
167
  }
165
- if ((0, _expValEquals.expValEquals)('platform_editor_diff_plugin_extended', 'isEnabled', true)) {
168
+ if ((0, _isExtendedEnabled.isExtendedEnabled)(diffType)) {
166
169
  style = getBlockNodeStyle({
167
170
  nodeName: change.name,
168
171
  colorScheme: colorScheme,
169
172
  isInserted: isInserted,
170
- isActive: isActive
173
+ isActive: isActive,
174
+ diffType: diffType
171
175
  });
172
176
  } else {
173
177
  style = getBlockNodeStyle({
174
178
  nodeName: change.name,
175
179
  colorScheme: colorScheme,
176
- isActive: isActive
180
+ isActive: isActive,
181
+ diffType: diffType
177
182
  });
178
183
  }
179
184
  var className = getNodeClass(change.name);
@@ -186,10 +191,11 @@ var createBlockChangedDecoration = exports.createBlockChangedDecoration = functi
186
191
  decorationType: 'block',
187
192
  diffId: diffId,
188
193
  isActive: isActive,
189
- nodeName: change.name
194
+ nodeName: change.name,
195
+ diffType: diffType
190
196
  })));
191
197
  }
192
- if (decorations.length > 0 && showIndicators && doc && (0, _expValEquals.expValEquals)('platform_editor_diff_plugin_extended', 'isEnabled', true)) {
198
+ if (decorations.length > 0 && showIndicators && doc && (0, _isExtendedEnabled.isExtendedEnabled)(diffType)) {
193
199
  decorations.push.apply(decorations, (0, _toConsumableArray2.default)((0, _createAnchorDecorationWidgets.createBlockIndicatorAnchorWidgets)({
194
200
  doc: doc,
195
201
  from: change.from,