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