@atlaskit/editor-plugin-show-diff 13.0.7 → 13.0.9
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 +20 -0
- package/dist/cjs/pm-plugins/calculateDiff/calculateDiffDecorations.js +40 -3
- package/dist/cjs/pm-plugins/main.js +2 -2
- package/dist/cjs/pm-plugins/scrollToDiff.js +5 -21
- package/dist/es2019/pm-plugins/calculateDiff/calculateDiffDecorations.js +39 -3
- package/dist/es2019/pm-plugins/main.js +3 -3
- package/dist/es2019/pm-plugins/scrollToDiff.js +3 -18
- package/dist/esm/pm-plugins/calculateDiff/calculateDiffDecorations.js +40 -3
- package/dist/esm/pm-plugins/main.js +3 -3
- package/dist/esm/pm-plugins/scrollToDiff.js +4 -20
- package/dist/types/pm-plugins/scrollToDiff.d.ts +3 -17
- package/package.json +4 -4
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,25 @@
|
|
|
1
1
|
# @atlaskit/editor-plugin-show-diff
|
|
2
2
|
|
|
3
|
+
## 13.0.9
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- [`0123de9acf8b1`](https://bitbucket.org/atlassian/atlassian-frontend-monorepo/commits/0123de9acf8b1) -
|
|
8
|
+
[CCI-18588] Fix multi-second browser freeze when opening the AI Review moment on a large generated
|
|
9
|
+
table. For big inserted tables the diff previously emitted one decoration per cell (O(cells)) plus
|
|
10
|
+
redundant inner-block decorations, forcing a full synchronous table relayout. It now skips the
|
|
11
|
+
per-cell inline and inner-block (table/row/paragraph) decorations while keeping the cell overlay
|
|
12
|
+
highlight. Gated behind `platform_editor_ai_new_aifc_editor_experience`; behaviour is unchanged
|
|
13
|
+
for small tables and when the experiment is off.
|
|
14
|
+
|
|
15
|
+
## 13.0.8
|
|
16
|
+
|
|
17
|
+
### Patch Changes
|
|
18
|
+
|
|
19
|
+
- [`d4fbe7dee465e`](https://bitbucket.org/atlassian/atlassian-frontend-monorepo/commits/d4fbe7dee465e) -
|
|
20
|
+
Consolidate diff decoration scrolling into a single internal helper.
|
|
21
|
+
- Updated dependencies
|
|
22
|
+
|
|
3
23
|
## 13.0.7
|
|
4
24
|
|
|
5
25
|
### Patch Changes
|
|
@@ -95,6 +95,26 @@ var leafTextblockRanges = function leafTextblockRanges(doc, from, to) {
|
|
|
95
95
|
});
|
|
96
96
|
return ranges;
|
|
97
97
|
};
|
|
98
|
+
|
|
99
|
+
// A large inserted table is treated with a coarse decoration path (see below)
|
|
100
|
+
// once it has more leaf textblocks (~cells) than this threshold. Small tables
|
|
101
|
+
// keep the precise per-cell path.
|
|
102
|
+
var LARGE_TABLE_LEAF_THRESHOLD = 40;
|
|
103
|
+
var isLargeInsertedTableRange = function isLargeInsertedTableRange(doc, from, to) {
|
|
104
|
+
var containsTable = false;
|
|
105
|
+
var leafCount = 0;
|
|
106
|
+
doc.nodesBetween(from, to, function (node) {
|
|
107
|
+
if (node.type.name === 'table') {
|
|
108
|
+
containsTable = true;
|
|
109
|
+
}
|
|
110
|
+
if (node.isTextblock) {
|
|
111
|
+
leafCount += 1;
|
|
112
|
+
return false;
|
|
113
|
+
}
|
|
114
|
+
return true;
|
|
115
|
+
});
|
|
116
|
+
return containsTable && leafCount > LARGE_TABLE_LEAF_THRESHOLD;
|
|
117
|
+
};
|
|
98
118
|
var calculateNodesForBlockDecoration = function calculateNodesForBlockDecoration(_ref2) {
|
|
99
119
|
var doc = _ref2.doc,
|
|
100
120
|
from = _ref2.from,
|
|
@@ -107,10 +127,20 @@ var calculateNodesForBlockDecoration = function calculateNodesForBlockDecoration
|
|
|
107
127
|
shouldHideDeleted = _ref2$shouldHideDelet === void 0 ? false : _ref2$shouldHideDelet,
|
|
108
128
|
_ref2$showIndicators = _ref2.showIndicators,
|
|
109
129
|
showIndicators = _ref2$showIndicators === void 0 ? false : _ref2$showIndicators,
|
|
110
|
-
diffType = _ref2.diffType
|
|
130
|
+
diffType = _ref2.diffType,
|
|
131
|
+
_ref2$coarseTableCell = _ref2.coarseTableCellsOnly,
|
|
132
|
+
coarseTableCellsOnly = _ref2$coarseTableCell === void 0 ? false : _ref2$coarseTableCell;
|
|
111
133
|
var decorations = [];
|
|
112
134
|
// Iterate over the document nodes within the range
|
|
113
135
|
doc.nodesBetween(from, to, function (node, pos) {
|
|
136
|
+
// Coarse path: only cell/header overlays are visible; table/row/paragraph
|
|
137
|
+
// decorations are redundant and cause expensive per-cell re-render.
|
|
138
|
+
if (coarseTableCellsOnly) {
|
|
139
|
+
var name = node.type.name;
|
|
140
|
+
if (name !== 'tableCell' && name !== 'tableHeader') {
|
|
141
|
+
return;
|
|
142
|
+
}
|
|
143
|
+
}
|
|
114
144
|
if (node.isBlock && (!(0, _isExtendedEnabled.isExtendedEnabled)(diffType) || pos + node.nodeSize <= to)) {
|
|
115
145
|
var nodeEnd = pos + node.nodeSize;
|
|
116
146
|
var isActive = activeIndexPos && pos === activeIndexPos.from && nodeEnd === activeIndexPos.to;
|
|
@@ -290,12 +320,18 @@ var calculateDiffDecorationsInner = function calculateDiffDecorationsInner(_ref4
|
|
|
290
320
|
// emit ONE inline decoration per leaf text-bearing block within the range — the text
|
|
291
321
|
// gets highlighted, and structural gaps never do.
|
|
292
322
|
var isSmartNodeLevel = diffType === 'smart' && (0, _platformFeatureFlags.fg)('platform_editor_ai_smart_diff') && (0, _helpers.smartChangeLevel)(change) === 'node';
|
|
323
|
+
// For a large inserted table, skip the per-cell inline decorations that
|
|
324
|
+
// freeze the browser on re-render. Only active in the AIFC experience.
|
|
325
|
+
var useCoarseTableDecoration = isSmartNodeLevel && (0, _expValEquals.expValEquals)('platform_editor_ai_new_aifc_editor_experience', 'isEnabled', true) && isLargeInsertedTableRange(tr.doc, change.fromB, change.toB);
|
|
293
326
|
// Whether the deleted-content widget will actually be rendered for this
|
|
294
327
|
// change. Used to decide if indicator anchor positions should be adjusted
|
|
295
328
|
// inward — when the widget is present the anchor must stay at the block
|
|
296
329
|
// boundary to keep the indicator bar continuous with the deleted content.
|
|
297
330
|
var willRenderDeletedWidget = change.deleted.length > 0 && !((0, _isExtendedEnabled.isExtendedEnabled)(diffType) && !isInverted && hideDeletedDiffs && change.inserted.length > 0);
|
|
298
|
-
if (
|
|
331
|
+
if (useCoarseTableDecoration) {
|
|
332
|
+
// Skip the O(cells) inline decorations; the cell overlays (added below)
|
|
333
|
+
// provide the highlight without the expensive content re-render.
|
|
334
|
+
} else if (isSmartNodeLevel) {
|
|
299
335
|
var _iterator2 = _createForOfIteratorHelper(leafTextblockRanges(tr.doc, change.fromB, change.toB)),
|
|
300
336
|
_step2;
|
|
301
337
|
try {
|
|
@@ -354,7 +390,8 @@ var calculateDiffDecorationsInner = function calculateDiffDecorationsInner(_ref4
|
|
|
354
390
|
}), {}, {
|
|
355
391
|
activeIndexPos: activeIndexPos,
|
|
356
392
|
intl: intl,
|
|
357
|
-
diffType: diffType
|
|
393
|
+
diffType: diffType,
|
|
394
|
+
coarseTableCellsOnly: useCoarseTableDecoration
|
|
358
395
|
}))));
|
|
359
396
|
}
|
|
360
397
|
if (change.deleted.length > 0) {
|
|
@@ -187,7 +187,7 @@ var createPlugin = exports.createPlugin = function createPlugin(config, getIntl,
|
|
|
187
187
|
if (pluginState !== null && pluginState !== void 0 && pluginState.scrollIntoView && (0, _isExtendedEnabled.isExtendedEnabled)(pluginState === null || pluginState === void 0 ? void 0 : pluginState.diffType)) {
|
|
188
188
|
var _cancelPendingScrollT;
|
|
189
189
|
(_cancelPendingScrollT = cancelPendingScrollToDecoration) === null || _cancelPendingScrollT === void 0 || _cancelPendingScrollT();
|
|
190
|
-
cancelPendingScrollToDecoration = (0, _scrollToDiff.
|
|
190
|
+
cancelPendingScrollToDecoration = (0, _scrollToDiff.scrollToDecoration)(view, (0, _getScrollableDecorations.getScrollableDecorations)(pluginState.decorations, view.state.doc, pluginState === null || pluginState === void 0 ? void 0 : pluginState.diffType));
|
|
191
191
|
|
|
192
192
|
// Reset the flag so we don't scroll again on subsequent updates
|
|
193
193
|
view.dispatch(view.state.tr.setMeta(showDiffPluginKey, {
|
|
@@ -208,7 +208,7 @@ var createPlugin = exports.createPlugin = function createPlugin(config, getIntl,
|
|
|
208
208
|
// avoid a circular dependency; expand picks up the meta if loaded (no-op if not).
|
|
209
209
|
api === null || api === void 0 || api.core.actions.execute((0, _expand.toggleExpandRange)(activeDecoration.from, activeDecoration.to, true));
|
|
210
210
|
}
|
|
211
|
-
cancelPendingScrollToDecoration = (0, _scrollToDiff.
|
|
211
|
+
cancelPendingScrollToDecoration = (0, _scrollToDiff.scrollToDecoration)(view, scrollableDecorations, pluginState.activeIndex);
|
|
212
212
|
}
|
|
213
213
|
},
|
|
214
214
|
destroy: function destroy() {
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
Object.defineProperty(exports, "__esModule", {
|
|
4
4
|
value: true
|
|
5
5
|
});
|
|
6
|
-
exports.
|
|
6
|
+
exports.scrollToDecoration = void 0;
|
|
7
7
|
var _decorationKeys = require("./decorations/decorationKeys");
|
|
8
8
|
/**
|
|
9
9
|
* Extra space above the scrolled-to element so it does not sit flush under the
|
|
@@ -40,29 +40,13 @@ function scrollToSelection(node) {
|
|
|
40
40
|
}
|
|
41
41
|
|
|
42
42
|
/**
|
|
43
|
-
* Schedules scrolling to the
|
|
44
|
-
*
|
|
45
|
-
* it simply scrolls to bring the first decoration into view.
|
|
46
|
-
*
|
|
47
|
-
* The caller must pass the list of scrollable decorations as produced by
|
|
48
|
-
* `getScrollableDecorations`, which is filtered (non-scrollable decorations
|
|
49
|
-
* removed) and sorted by document position. This guarantees "first" means the
|
|
50
|
-
* topmost scrollable diff in the document rather than an arbitrary decoration
|
|
51
|
-
* from the raw `DecorationSet` ordering. This is delegated to
|
|
52
|
-
* `scrollToActiveDecoration` at index 0 so both scroll paths behave identically.
|
|
53
|
-
*
|
|
54
|
-
* @returns A function that cancels the scheduled `requestAnimationFrame` if it has not run yet.
|
|
55
|
-
*/
|
|
56
|
-
var scrollToFirstDecoration = exports.scrollToFirstDecoration = function scrollToFirstDecoration(view, scrollableDecorations) {
|
|
57
|
-
return scrollToActiveDecoration(view, scrollableDecorations, 0);
|
|
58
|
-
};
|
|
59
|
-
|
|
60
|
-
/**
|
|
61
|
-
* Schedules scrolling to the decoration at the given index after the next frame.
|
|
43
|
+
* Schedules scrolling to the decoration at the given index after the next frame. Defaults to the
|
|
44
|
+
* first decoration when no index is provided.
|
|
62
45
|
*
|
|
63
46
|
* @returns A function that cancels the scheduled `requestAnimationFrame` if it has not run yet.
|
|
64
47
|
*/
|
|
65
|
-
var
|
|
48
|
+
var scrollToDecoration = exports.scrollToDecoration = function scrollToDecoration(view, decorations) {
|
|
49
|
+
var activeIndex = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : 0;
|
|
66
50
|
var decoration = decorations[activeIndex];
|
|
67
51
|
if (!decoration) {
|
|
68
52
|
return function () {};
|
|
@@ -81,6 +81,26 @@ const leafTextblockRanges = (doc, from, to) => {
|
|
|
81
81
|
});
|
|
82
82
|
return ranges;
|
|
83
83
|
};
|
|
84
|
+
|
|
85
|
+
// A large inserted table is treated with a coarse decoration path (see below)
|
|
86
|
+
// once it has more leaf textblocks (~cells) than this threshold. Small tables
|
|
87
|
+
// keep the precise per-cell path.
|
|
88
|
+
const LARGE_TABLE_LEAF_THRESHOLD = 40;
|
|
89
|
+
const isLargeInsertedTableRange = (doc, from, to) => {
|
|
90
|
+
let containsTable = false;
|
|
91
|
+
let leafCount = 0;
|
|
92
|
+
doc.nodesBetween(from, to, node => {
|
|
93
|
+
if (node.type.name === 'table') {
|
|
94
|
+
containsTable = true;
|
|
95
|
+
}
|
|
96
|
+
if (node.isTextblock) {
|
|
97
|
+
leafCount += 1;
|
|
98
|
+
return false;
|
|
99
|
+
}
|
|
100
|
+
return true;
|
|
101
|
+
});
|
|
102
|
+
return containsTable && leafCount > LARGE_TABLE_LEAF_THRESHOLD;
|
|
103
|
+
};
|
|
84
104
|
const calculateNodesForBlockDecoration = ({
|
|
85
105
|
doc,
|
|
86
106
|
from,
|
|
@@ -90,11 +110,20 @@ const calculateNodesForBlockDecoration = ({
|
|
|
90
110
|
activeIndexPos,
|
|
91
111
|
shouldHideDeleted = false,
|
|
92
112
|
showIndicators = false,
|
|
93
|
-
diffType
|
|
113
|
+
diffType,
|
|
114
|
+
coarseTableCellsOnly = false
|
|
94
115
|
}) => {
|
|
95
116
|
const decorations = [];
|
|
96
117
|
// Iterate over the document nodes within the range
|
|
97
118
|
doc.nodesBetween(from, to, (node, pos) => {
|
|
119
|
+
// Coarse path: only cell/header overlays are visible; table/row/paragraph
|
|
120
|
+
// decorations are redundant and cause expensive per-cell re-render.
|
|
121
|
+
if (coarseTableCellsOnly) {
|
|
122
|
+
const name = node.type.name;
|
|
123
|
+
if (name !== 'tableCell' && name !== 'tableHeader') {
|
|
124
|
+
return;
|
|
125
|
+
}
|
|
126
|
+
}
|
|
98
127
|
if (node.isBlock && (!isExtendedEnabled(diffType) || pos + node.nodeSize <= to)) {
|
|
99
128
|
const nodeEnd = pos + node.nodeSize;
|
|
100
129
|
const isActive = activeIndexPos && pos === activeIndexPos.from && nodeEnd === activeIndexPos.to;
|
|
@@ -264,12 +293,18 @@ const calculateDiffDecorationsInner = ({
|
|
|
264
293
|
// emit ONE inline decoration per leaf text-bearing block within the range — the text
|
|
265
294
|
// gets highlighted, and structural gaps never do.
|
|
266
295
|
const isSmartNodeLevel = diffType === 'smart' && fg('platform_editor_ai_smart_diff') && smartChangeLevel(change) === 'node';
|
|
296
|
+
// For a large inserted table, skip the per-cell inline decorations that
|
|
297
|
+
// freeze the browser on re-render. Only active in the AIFC experience.
|
|
298
|
+
const useCoarseTableDecoration = isSmartNodeLevel && expValEquals('platform_editor_ai_new_aifc_editor_experience', 'isEnabled', true) && isLargeInsertedTableRange(tr.doc, change.fromB, change.toB);
|
|
267
299
|
// Whether the deleted-content widget will actually be rendered for this
|
|
268
300
|
// change. Used to decide if indicator anchor positions should be adjusted
|
|
269
301
|
// inward — when the widget is present the anchor must stay at the block
|
|
270
302
|
// boundary to keep the indicator bar continuous with the deleted content.
|
|
271
303
|
const willRenderDeletedWidget = change.deleted.length > 0 && !(isExtendedEnabled(diffType) && !isInverted && hideDeletedDiffs && change.inserted.length > 0);
|
|
272
|
-
if (
|
|
304
|
+
if (useCoarseTableDecoration) {
|
|
305
|
+
// Skip the O(cells) inline decorations; the cell overlays (added below)
|
|
306
|
+
// provide the highlight without the expensive content re-render.
|
|
307
|
+
} else if (isSmartNodeLevel) {
|
|
273
308
|
for (const [from, to] of leafTextblockRanges(tr.doc, change.fromB, change.toB)) {
|
|
274
309
|
decorations.push(...createInlineChangedDecoration({
|
|
275
310
|
change: {
|
|
@@ -319,7 +354,8 @@ const calculateDiffDecorationsInner = ({
|
|
|
319
354
|
}),
|
|
320
355
|
activeIndexPos,
|
|
321
356
|
intl,
|
|
322
|
-
diffType
|
|
357
|
+
diffType,
|
|
358
|
+
coarseTableCellsOnly: useCoarseTableDecoration
|
|
323
359
|
}));
|
|
324
360
|
}
|
|
325
361
|
if (change.deleted.length > 0) {
|
|
@@ -9,7 +9,7 @@ import { enforceCustomStepRegisters } from './enforceCustomStepRegisters';
|
|
|
9
9
|
import { getScrollableDecorations } from './getScrollableDecorations';
|
|
10
10
|
import { isExtendedEnabled } from './isExtendedEnabled';
|
|
11
11
|
import { NodeViewSerializer } from './NodeViewSerializer';
|
|
12
|
-
import {
|
|
12
|
+
import { scrollToDecoration } from './scrollToDiff';
|
|
13
13
|
export const showDiffPluginKey = new PluginKey('showDiffPlugin');
|
|
14
14
|
export const createPlugin = (config, getIntl, api, onEditorView) => {
|
|
15
15
|
enforceCustomStepRegisters();
|
|
@@ -194,7 +194,7 @@ export const createPlugin = (config, getIntl, api, onEditorView) => {
|
|
|
194
194
|
if (pluginState !== null && pluginState !== void 0 && pluginState.scrollIntoView && isExtendedEnabled(pluginState === null || pluginState === void 0 ? void 0 : pluginState.diffType)) {
|
|
195
195
|
var _cancelPendingScrollT;
|
|
196
196
|
(_cancelPendingScrollT = cancelPendingScrollToDecoration) === null || _cancelPendingScrollT === void 0 ? void 0 : _cancelPendingScrollT();
|
|
197
|
-
cancelPendingScrollToDecoration =
|
|
197
|
+
cancelPendingScrollToDecoration = scrollToDecoration(view, getScrollableDecorations(pluginState.decorations, view.state.doc, pluginState === null || pluginState === void 0 ? void 0 : pluginState.diffType));
|
|
198
198
|
|
|
199
199
|
// Reset the flag so we don't scroll again on subsequent updates
|
|
200
200
|
view.dispatch(view.state.tr.setMeta(showDiffPluginKey, {
|
|
@@ -215,7 +215,7 @@ export const createPlugin = (config, getIntl, api, onEditorView) => {
|
|
|
215
215
|
// avoid a circular dependency; expand picks up the meta if loaded (no-op if not).
|
|
216
216
|
api === null || api === void 0 ? void 0 : api.core.actions.execute(toggleExpandRange(activeDecoration.from, activeDecoration.to, true));
|
|
217
217
|
}
|
|
218
|
-
cancelPendingScrollToDecoration =
|
|
218
|
+
cancelPendingScrollToDecoration = scrollToDecoration(view, scrollableDecorations, pluginState.activeIndex);
|
|
219
219
|
}
|
|
220
220
|
},
|
|
221
221
|
destroy() {
|
|
@@ -35,27 +35,12 @@ function scrollToSelection(node) {
|
|
|
35
35
|
}
|
|
36
36
|
|
|
37
37
|
/**
|
|
38
|
-
* Schedules scrolling to the
|
|
39
|
-
*
|
|
40
|
-
* it simply scrolls to bring the first decoration into view.
|
|
41
|
-
*
|
|
42
|
-
* The caller must pass the list of scrollable decorations as produced by
|
|
43
|
-
* `getScrollableDecorations`, which is filtered (non-scrollable decorations
|
|
44
|
-
* removed) and sorted by document position. This guarantees "first" means the
|
|
45
|
-
* topmost scrollable diff in the document rather than an arbitrary decoration
|
|
46
|
-
* from the raw `DecorationSet` ordering. This is delegated to
|
|
47
|
-
* `scrollToActiveDecoration` at index 0 so both scroll paths behave identically.
|
|
48
|
-
*
|
|
49
|
-
* @returns A function that cancels the scheduled `requestAnimationFrame` if it has not run yet.
|
|
50
|
-
*/
|
|
51
|
-
export const scrollToFirstDecoration = (view, scrollableDecorations) => scrollToActiveDecoration(view, scrollableDecorations, 0);
|
|
52
|
-
|
|
53
|
-
/**
|
|
54
|
-
* Schedules scrolling to the decoration at the given index after the next frame.
|
|
38
|
+
* Schedules scrolling to the decoration at the given index after the next frame. Defaults to the
|
|
39
|
+
* first decoration when no index is provided.
|
|
55
40
|
*
|
|
56
41
|
* @returns A function that cancels the scheduled `requestAnimationFrame` if it has not run yet.
|
|
57
42
|
*/
|
|
58
|
-
export const
|
|
43
|
+
export const scrollToDecoration = (view, decorations, activeIndex = 0) => {
|
|
59
44
|
const decoration = decorations[activeIndex];
|
|
60
45
|
if (!decoration) {
|
|
61
46
|
return () => {};
|
|
@@ -88,6 +88,26 @@ var leafTextblockRanges = function leafTextblockRanges(doc, from, to) {
|
|
|
88
88
|
});
|
|
89
89
|
return ranges;
|
|
90
90
|
};
|
|
91
|
+
|
|
92
|
+
// A large inserted table is treated with a coarse decoration path (see below)
|
|
93
|
+
// once it has more leaf textblocks (~cells) than this threshold. Small tables
|
|
94
|
+
// keep the precise per-cell path.
|
|
95
|
+
var LARGE_TABLE_LEAF_THRESHOLD = 40;
|
|
96
|
+
var isLargeInsertedTableRange = function isLargeInsertedTableRange(doc, from, to) {
|
|
97
|
+
var containsTable = false;
|
|
98
|
+
var leafCount = 0;
|
|
99
|
+
doc.nodesBetween(from, to, function (node) {
|
|
100
|
+
if (node.type.name === 'table') {
|
|
101
|
+
containsTable = true;
|
|
102
|
+
}
|
|
103
|
+
if (node.isTextblock) {
|
|
104
|
+
leafCount += 1;
|
|
105
|
+
return false;
|
|
106
|
+
}
|
|
107
|
+
return true;
|
|
108
|
+
});
|
|
109
|
+
return containsTable && leafCount > LARGE_TABLE_LEAF_THRESHOLD;
|
|
110
|
+
};
|
|
91
111
|
var calculateNodesForBlockDecoration = function calculateNodesForBlockDecoration(_ref2) {
|
|
92
112
|
var doc = _ref2.doc,
|
|
93
113
|
from = _ref2.from,
|
|
@@ -100,10 +120,20 @@ var calculateNodesForBlockDecoration = function calculateNodesForBlockDecoration
|
|
|
100
120
|
shouldHideDeleted = _ref2$shouldHideDelet === void 0 ? false : _ref2$shouldHideDelet,
|
|
101
121
|
_ref2$showIndicators = _ref2.showIndicators,
|
|
102
122
|
showIndicators = _ref2$showIndicators === void 0 ? false : _ref2$showIndicators,
|
|
103
|
-
diffType = _ref2.diffType
|
|
123
|
+
diffType = _ref2.diffType,
|
|
124
|
+
_ref2$coarseTableCell = _ref2.coarseTableCellsOnly,
|
|
125
|
+
coarseTableCellsOnly = _ref2$coarseTableCell === void 0 ? false : _ref2$coarseTableCell;
|
|
104
126
|
var decorations = [];
|
|
105
127
|
// Iterate over the document nodes within the range
|
|
106
128
|
doc.nodesBetween(from, to, function (node, pos) {
|
|
129
|
+
// Coarse path: only cell/header overlays are visible; table/row/paragraph
|
|
130
|
+
// decorations are redundant and cause expensive per-cell re-render.
|
|
131
|
+
if (coarseTableCellsOnly) {
|
|
132
|
+
var name = node.type.name;
|
|
133
|
+
if (name !== 'tableCell' && name !== 'tableHeader') {
|
|
134
|
+
return;
|
|
135
|
+
}
|
|
136
|
+
}
|
|
107
137
|
if (node.isBlock && (!isExtendedEnabled(diffType) || pos + node.nodeSize <= to)) {
|
|
108
138
|
var nodeEnd = pos + node.nodeSize;
|
|
109
139
|
var isActive = activeIndexPos && pos === activeIndexPos.from && nodeEnd === activeIndexPos.to;
|
|
@@ -283,12 +313,18 @@ var calculateDiffDecorationsInner = function calculateDiffDecorationsInner(_ref4
|
|
|
283
313
|
// emit ONE inline decoration per leaf text-bearing block within the range — the text
|
|
284
314
|
// gets highlighted, and structural gaps never do.
|
|
285
315
|
var isSmartNodeLevel = diffType === 'smart' && fg('platform_editor_ai_smart_diff') && smartChangeLevel(change) === 'node';
|
|
316
|
+
// For a large inserted table, skip the per-cell inline decorations that
|
|
317
|
+
// freeze the browser on re-render. Only active in the AIFC experience.
|
|
318
|
+
var useCoarseTableDecoration = isSmartNodeLevel && expValEquals('platform_editor_ai_new_aifc_editor_experience', 'isEnabled', true) && isLargeInsertedTableRange(tr.doc, change.fromB, change.toB);
|
|
286
319
|
// Whether the deleted-content widget will actually be rendered for this
|
|
287
320
|
// change. Used to decide if indicator anchor positions should be adjusted
|
|
288
321
|
// inward — when the widget is present the anchor must stay at the block
|
|
289
322
|
// boundary to keep the indicator bar continuous with the deleted content.
|
|
290
323
|
var willRenderDeletedWidget = change.deleted.length > 0 && !(isExtendedEnabled(diffType) && !isInverted && hideDeletedDiffs && change.inserted.length > 0);
|
|
291
|
-
if (
|
|
324
|
+
if (useCoarseTableDecoration) {
|
|
325
|
+
// Skip the O(cells) inline decorations; the cell overlays (added below)
|
|
326
|
+
// provide the highlight without the expensive content re-render.
|
|
327
|
+
} else if (isSmartNodeLevel) {
|
|
292
328
|
var _iterator2 = _createForOfIteratorHelper(leafTextblockRanges(tr.doc, change.fromB, change.toB)),
|
|
293
329
|
_step2;
|
|
294
330
|
try {
|
|
@@ -347,7 +383,8 @@ var calculateDiffDecorationsInner = function calculateDiffDecorationsInner(_ref4
|
|
|
347
383
|
}), {}, {
|
|
348
384
|
activeIndexPos: activeIndexPos,
|
|
349
385
|
intl: intl,
|
|
350
|
-
diffType: diffType
|
|
386
|
+
diffType: diffType,
|
|
387
|
+
coarseTableCellsOnly: useCoarseTableDecoration
|
|
351
388
|
}))));
|
|
352
389
|
}
|
|
353
390
|
if (change.deleted.length > 0) {
|
|
@@ -12,7 +12,7 @@ import { enforceCustomStepRegisters } from './enforceCustomStepRegisters';
|
|
|
12
12
|
import { getScrollableDecorations } from './getScrollableDecorations';
|
|
13
13
|
import { isExtendedEnabled } from './isExtendedEnabled';
|
|
14
14
|
import { NodeViewSerializer } from './NodeViewSerializer';
|
|
15
|
-
import {
|
|
15
|
+
import { scrollToDecoration } from './scrollToDiff';
|
|
16
16
|
export var showDiffPluginKey = new PluginKey('showDiffPlugin');
|
|
17
17
|
export var createPlugin = function createPlugin(config, getIntl, api, onEditorView) {
|
|
18
18
|
enforceCustomStepRegisters();
|
|
@@ -180,7 +180,7 @@ export var createPlugin = function createPlugin(config, getIntl, api, onEditorVi
|
|
|
180
180
|
if (pluginState !== null && pluginState !== void 0 && pluginState.scrollIntoView && isExtendedEnabled(pluginState === null || pluginState === void 0 ? void 0 : pluginState.diffType)) {
|
|
181
181
|
var _cancelPendingScrollT;
|
|
182
182
|
(_cancelPendingScrollT = cancelPendingScrollToDecoration) === null || _cancelPendingScrollT === void 0 || _cancelPendingScrollT();
|
|
183
|
-
cancelPendingScrollToDecoration =
|
|
183
|
+
cancelPendingScrollToDecoration = scrollToDecoration(view, getScrollableDecorations(pluginState.decorations, view.state.doc, pluginState === null || pluginState === void 0 ? void 0 : pluginState.diffType));
|
|
184
184
|
|
|
185
185
|
// Reset the flag so we don't scroll again on subsequent updates
|
|
186
186
|
view.dispatch(view.state.tr.setMeta(showDiffPluginKey, {
|
|
@@ -201,7 +201,7 @@ export var createPlugin = function createPlugin(config, getIntl, api, onEditorVi
|
|
|
201
201
|
// avoid a circular dependency; expand picks up the meta if loaded (no-op if not).
|
|
202
202
|
api === null || api === void 0 || api.core.actions.execute(toggleExpandRange(activeDecoration.from, activeDecoration.to, true));
|
|
203
203
|
}
|
|
204
|
-
cancelPendingScrollToDecoration =
|
|
204
|
+
cancelPendingScrollToDecoration = scrollToDecoration(view, scrollableDecorations, pluginState.activeIndex);
|
|
205
205
|
}
|
|
206
206
|
},
|
|
207
207
|
destroy: function destroy() {
|
|
@@ -35,29 +35,13 @@ function scrollToSelection(node) {
|
|
|
35
35
|
}
|
|
36
36
|
|
|
37
37
|
/**
|
|
38
|
-
* Schedules scrolling to the
|
|
39
|
-
*
|
|
40
|
-
* it simply scrolls to bring the first decoration into view.
|
|
41
|
-
*
|
|
42
|
-
* The caller must pass the list of scrollable decorations as produced by
|
|
43
|
-
* `getScrollableDecorations`, which is filtered (non-scrollable decorations
|
|
44
|
-
* removed) and sorted by document position. This guarantees "first" means the
|
|
45
|
-
* topmost scrollable diff in the document rather than an arbitrary decoration
|
|
46
|
-
* from the raw `DecorationSet` ordering. This is delegated to
|
|
47
|
-
* `scrollToActiveDecoration` at index 0 so both scroll paths behave identically.
|
|
48
|
-
*
|
|
49
|
-
* @returns A function that cancels the scheduled `requestAnimationFrame` if it has not run yet.
|
|
50
|
-
*/
|
|
51
|
-
export var scrollToFirstDecoration = function scrollToFirstDecoration(view, scrollableDecorations) {
|
|
52
|
-
return scrollToActiveDecoration(view, scrollableDecorations, 0);
|
|
53
|
-
};
|
|
54
|
-
|
|
55
|
-
/**
|
|
56
|
-
* Schedules scrolling to the decoration at the given index after the next frame.
|
|
38
|
+
* Schedules scrolling to the decoration at the given index after the next frame. Defaults to the
|
|
39
|
+
* first decoration when no index is provided.
|
|
57
40
|
*
|
|
58
41
|
* @returns A function that cancels the scheduled `requestAnimationFrame` if it has not run yet.
|
|
59
42
|
*/
|
|
60
|
-
export var
|
|
43
|
+
export var scrollToDecoration = function scrollToDecoration(view, decorations) {
|
|
44
|
+
var activeIndex = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : 0;
|
|
61
45
|
var decoration = decorations[activeIndex];
|
|
62
46
|
if (!decoration) {
|
|
63
47
|
return function () {};
|
|
@@ -1,22 +1,8 @@
|
|
|
1
1
|
import type { EditorView, Decoration } from '@atlaskit/editor-prosemirror/view';
|
|
2
2
|
/**
|
|
3
|
-
* Schedules scrolling to the
|
|
4
|
-
*
|
|
5
|
-
* it simply scrolls to bring the first decoration into view.
|
|
6
|
-
*
|
|
7
|
-
* The caller must pass the list of scrollable decorations as produced by
|
|
8
|
-
* `getScrollableDecorations`, which is filtered (non-scrollable decorations
|
|
9
|
-
* removed) and sorted by document position. This guarantees "first" means the
|
|
10
|
-
* topmost scrollable diff in the document rather than an arbitrary decoration
|
|
11
|
-
* from the raw `DecorationSet` ordering. This is delegated to
|
|
12
|
-
* `scrollToActiveDecoration` at index 0 so both scroll paths behave identically.
|
|
13
|
-
*
|
|
14
|
-
* @returns A function that cancels the scheduled `requestAnimationFrame` if it has not run yet.
|
|
15
|
-
*/
|
|
16
|
-
export declare const scrollToFirstDecoration: (view: EditorView, scrollableDecorations: Decoration[]) => (() => void);
|
|
17
|
-
/**
|
|
18
|
-
* Schedules scrolling to the decoration at the given index after the next frame.
|
|
3
|
+
* Schedules scrolling to the decoration at the given index after the next frame. Defaults to the
|
|
4
|
+
* first decoration when no index is provided.
|
|
19
5
|
*
|
|
20
6
|
* @returns A function that cancels the scheduled `requestAnimationFrame` if it has not run yet.
|
|
21
7
|
*/
|
|
22
|
-
export declare const
|
|
8
|
+
export declare const scrollToDecoration: (view: EditorView, decorations: Decoration[], activeIndex?: number) => (() => void);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@atlaskit/editor-plugin-show-diff",
|
|
3
|
-
"version": "13.0.
|
|
3
|
+
"version": "13.0.9",
|
|
4
4
|
"description": "ShowDiff plugin for @atlaskit/editor-core",
|
|
5
5
|
"author": "Atlassian Pty Ltd",
|
|
6
6
|
"license": "Apache-2.0",
|
|
@@ -36,8 +36,8 @@
|
|
|
36
36
|
"prosemirror-changeset": "^2.3.1"
|
|
37
37
|
},
|
|
38
38
|
"devDependencies": {
|
|
39
|
-
"@atlaskit/adf-utils": "^20.
|
|
40
|
-
"@atlaskit/button": "^25.
|
|
39
|
+
"@atlaskit/adf-utils": "^20.7.0",
|
|
40
|
+
"@atlaskit/button": "^25.1.0",
|
|
41
41
|
"@atlaskit/css": "^1.0.0",
|
|
42
42
|
"@atlaskit/dropdown-menu": "^18.0.0",
|
|
43
43
|
"@atlaskit/editor-core": "^224.1.0",
|
|
@@ -53,7 +53,7 @@
|
|
|
53
53
|
"react-intl": "^7.0.0"
|
|
54
54
|
},
|
|
55
55
|
"peerDependencies": {
|
|
56
|
-
"@atlaskit/editor-common": "^119.
|
|
56
|
+
"@atlaskit/editor-common": "^119.9.0",
|
|
57
57
|
"react": "^18.2.0 || ^19.2.0"
|
|
58
58
|
},
|
|
59
59
|
"techstack": {
|