@atlaskit/editor-plugin-show-diff 21.0.1 → 21.0.3
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 +25 -0
- package/dist/cjs/pm-plugins/NodeViewSerializer.js +33 -4
- package/dist/cjs/pm-plugins/calculateDiff/calculateDiffDecorations.js +51 -13
- package/dist/cjs/pm-plugins/calculateDiff/markAwareTokenEncoder.js +2 -1
- package/dist/cjs/pm-plugins/calculateDiff/selectTokenEncoder.js +1 -1
- package/dist/cjs/pm-plugins/decorations/createChangedRowDecorationWidgets.js +1 -1
- package/dist/cjs/pm-plugins/decorations/createNodeChangedDecorationWidget.js +3 -3
- package/dist/cjs/pm-plugins/decorations/utils/diffableAttrs.js +5 -1
- package/dist/cjs/pm-plugins/decorations/utils/getAttrChangeRanges.js +10 -7
- package/dist/cjs/pm-plugins/decorations/utils/wrapBlockNodeViewStyles.js +4 -0
- package/dist/cjs/pm-plugins/decorations/utils/wrapBlockNodeViewStyles.legacy.js +6 -0
- package/dist/es2019/pm-plugins/NodeViewSerializer.js +31 -7
- package/dist/es2019/pm-plugins/calculateDiff/calculateDiffDecorations.js +38 -11
- package/dist/es2019/pm-plugins/calculateDiff/markAwareTokenEncoder.js +2 -1
- package/dist/es2019/pm-plugins/calculateDiff/selectTokenEncoder.js +1 -1
- package/dist/es2019/pm-plugins/decorations/createChangedRowDecorationWidgets.js +1 -1
- package/dist/es2019/pm-plugins/decorations/createNodeChangedDecorationWidget.js +3 -3
- package/dist/es2019/pm-plugins/decorations/utils/diffableAttrs.js +6 -1
- package/dist/es2019/pm-plugins/decorations/utils/getAttrChangeRanges.js +4 -3
- package/dist/es2019/pm-plugins/decorations/utils/wrapBlockNodeViewStyles.js +4 -0
- package/dist/es2019/pm-plugins/decorations/utils/wrapBlockNodeViewStyles.legacy.js +6 -0
- package/dist/esm/pm-plugins/NodeViewSerializer.js +33 -4
- package/dist/esm/pm-plugins/calculateDiff/calculateDiffDecorations.js +51 -13
- package/dist/esm/pm-plugins/calculateDiff/markAwareTokenEncoder.js +2 -1
- package/dist/esm/pm-plugins/calculateDiff/selectTokenEncoder.js +1 -1
- package/dist/esm/pm-plugins/decorations/createChangedRowDecorationWidgets.js +1 -1
- package/dist/esm/pm-plugins/decorations/createNodeChangedDecorationWidget.js +3 -3
- package/dist/esm/pm-plugins/decorations/utils/diffableAttrs.js +6 -1
- package/dist/esm/pm-plugins/decorations/utils/getAttrChangeRanges.js +10 -7
- package/dist/esm/pm-plugins/decorations/utils/wrapBlockNodeViewStyles.js +4 -0
- package/dist/esm/pm-plugins/decorations/utils/wrapBlockNodeViewStyles.legacy.js +6 -0
- package/dist/types/pm-plugins/NodeViewSerializer.d.ts +2 -1
- package/dist/types/pm-plugins/calculateDiff/markAwareTokenEncoder.d.ts +2 -1
- package/dist/types/pm-plugins/calculateDiff/selectTokenEncoder.d.ts +1 -1
- package/package.json +3 -3
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,30 @@
|
|
|
1
1
|
# @atlaskit/editor-plugin-show-diff
|
|
2
2
|
|
|
3
|
+
## 21.0.3
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- [`bb99ff596a192`](https://bitbucket.org/atlassian/atlassian-frontend-monorepo/commits/bb99ff596a192) -
|
|
8
|
+
Fix show-diff rendering a bordered box around fully-replaced paragraphs, a
|
|
9
|
+
platform_editor_ai_show_diff_patch_2 regression caused by paragraph falling through to the generic
|
|
10
|
+
block-container border treatment intended for real block containers (panels, blockquotes). Gated
|
|
11
|
+
behind platform_editor_ai_show_diff_patch_2, the same gate that caused the regression.
|
|
12
|
+
|
|
13
|
+
Also give an inserted atomic inline leaf (date/emoji/mention/status) the same box-shadow highlight
|
|
14
|
+
an in-place attribute change already gets, when it renders through `NodeViewSerializer`'s
|
|
15
|
+
blocklisted-container recursion under platform_editor_show_diff_deleted_nodeview_content — that
|
|
16
|
+
recursion gives the node its real node view instead of a lossy `toDOM()`, but previously carried
|
|
17
|
+
no diff styling of its own.
|
|
18
|
+
|
|
19
|
+
- Updated dependencies
|
|
20
|
+
|
|
21
|
+
## 21.0.2
|
|
22
|
+
|
|
23
|
+
### Patch Changes
|
|
24
|
+
|
|
25
|
+
- [`1647c1098fca5`](https://bitbucket.org/atlassian/atlassian-frontend-monorepo/commits/1647c1098fca5) -
|
|
26
|
+
Highlight changed inline nodes and the edited portion of link display text
|
|
27
|
+
|
|
3
28
|
## 21.0.1
|
|
4
29
|
|
|
5
30
|
### Patch Changes
|
|
@@ -14,6 +14,8 @@ var _utils = require("@atlaskit/editor-prosemirror/utils");
|
|
|
14
14
|
var _isExperimentEnabled = require("@atlaskit/platform-feature-experiments/is-experiment-enabled");
|
|
15
15
|
var _fg = require("@atlaskit/platform-feature-flags/fg");
|
|
16
16
|
var _createEditorProxy = require("./createEditorProxy");
|
|
17
|
+
var _createInlineChangedDecoration = require("./decorations/createInlineChangedDecoration");
|
|
18
|
+
var _getAttrChangeRanges = require("./decorations/utils/getAttrChangeRanges");
|
|
17
19
|
var _markViews = require("./markViews");
|
|
18
20
|
/**
|
|
19
21
|
* Utilities for working with ProseMirror node views and DOM serialization within the
|
|
@@ -45,6 +47,14 @@ function isEditorViewWithNodeViews(view) {
|
|
|
45
47
|
/** Expand-family nodes whose expanded state lives in a node-keyed WeakMap. */
|
|
46
48
|
var EXPAND_TYPES = new Set(['expand', 'nestedExpand']);
|
|
47
49
|
|
|
50
|
+
/** Merges an inline style string onto an element's existing `style` attribute. */
|
|
51
|
+
var appendInlineStyle = function appendInlineStyle(element, style) {
|
|
52
|
+
var _element$getAttribute;
|
|
53
|
+
var currentStyle = (_element$getAttribute = element.getAttribute('style')) !== null && _element$getAttribute !== void 0 ? _element$getAttribute : '';
|
|
54
|
+
var separator = currentStyle && style && !currentStyle.trimEnd().endsWith(';') ? '; ' : '';
|
|
55
|
+
element.setAttribute('style', "".concat(currentStyle).concat(separator).concat(style));
|
|
56
|
+
};
|
|
57
|
+
|
|
48
58
|
/** Tables are the only node views that read and write positions, so only they need a preview. */
|
|
49
59
|
var containsTable = function containsTable(node) {
|
|
50
60
|
var table = node.type.schema.nodes.table;
|
|
@@ -99,13 +109,28 @@ var NodeViewSerializer = exports.NodeViewSerializer = /*#__PURE__*/function () {
|
|
|
99
109
|
var _this = this;
|
|
100
110
|
var basePos = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : 0;
|
|
101
111
|
var editorProxy = arguments.length > 3 ? arguments[3] : undefined;
|
|
112
|
+
var isInserted = arguments.length > 4 ? arguments[4] : undefined;
|
|
113
|
+
var colorScheme = arguments.length > 5 ? arguments[5] : undefined;
|
|
102
114
|
// A node's first child sits one position inside it, and each subsequent
|
|
103
115
|
// child is offset by the previous child's `nodeSize`. Tracking this lets nested node
|
|
104
116
|
// views resolve to their real depth instead of every one of them seeing depth 0.
|
|
105
117
|
var childPos = basePos + 1;
|
|
106
118
|
children.forEach(function (child) {
|
|
107
|
-
var childNode = _this.tryCreateNodeViewInner(child, childPos, editorProxy) || _this.serializeNode(child);
|
|
119
|
+
var childNode = _this.tryCreateNodeViewInner(child, childPos, editorProxy, isInserted, colorScheme) || _this.serializeNode(child);
|
|
108
120
|
if (childNode) {
|
|
121
|
+
// Give inserted atomic children the same box-shadow
|
|
122
|
+
// highlight an in-place attribute change already gets (`getAtomicInlineChangedAttrs`).
|
|
123
|
+
if (isInserted && (0, _isExperimentEnabled.isExperimentEnabled)('platform_editor_show_diff_deleted_nodeview_content') && (0, _getAttrChangeRanges.isInlineAttrChangeNodeName)(child.type.name) && childNode instanceof HTMLElement) {
|
|
124
|
+
var _childNode$getAttribu;
|
|
125
|
+
var _getAtomicInlineChang = (0, _createInlineChangedDecoration.getAtomicInlineChangedAttrs)(child.type.name, colorScheme),
|
|
126
|
+
className = _getAtomicInlineChang.className,
|
|
127
|
+
styleSuffix = _getAtomicInlineChang.styleSuffix;
|
|
128
|
+
var existingClassName = (_childNode$getAttribu = childNode.getAttribute('class')) !== null && _childNode$getAttribu !== void 0 ? _childNode$getAttribu : '';
|
|
129
|
+
childNode.setAttribute('class', existingClassName ? "".concat(existingClassName, " ").concat(className) : className);
|
|
130
|
+
if (styleSuffix) {
|
|
131
|
+
appendInlineStyle(childNode, styleSuffix);
|
|
132
|
+
}
|
|
133
|
+
}
|
|
109
134
|
contentDOM === null || contentDOM === void 0 || contentDOM.append(childNode);
|
|
110
135
|
}
|
|
111
136
|
childPos += child.nodeSize;
|
|
@@ -126,13 +151,15 @@ var NodeViewSerializer = exports.NodeViewSerializer = /*#__PURE__*/function () {
|
|
|
126
151
|
var _preview$rootPos;
|
|
127
152
|
var basePos = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 0;
|
|
128
153
|
var $sourcePos = arguments.length > 2 ? arguments[2] : undefined;
|
|
154
|
+
var isInserted = arguments.length > 3 ? arguments[3] : undefined;
|
|
155
|
+
var colorScheme = arguments.length > 4 ? arguments[4] : undefined;
|
|
129
156
|
if (!this.editorView) {
|
|
130
157
|
return null;
|
|
131
158
|
}
|
|
132
159
|
// The preview becomes the document every nested node view resolves its position against,
|
|
133
160
|
// so positions stay internally consistent.
|
|
134
161
|
var preview = (0, _fg.fg)('platform_editor_ai_show_diff_patch_2') && containsTable(targetNode) ? (0, _createEditorProxy.createEditorProxy)(this.editorView, targetNode, $sourcePos) : null;
|
|
135
|
-
return this.tryCreateNodeViewInner(targetNode, (_preview$rootPos = preview === null || preview === void 0 ? void 0 : preview.rootPos) !== null && _preview$rootPos !== void 0 ? _preview$rootPos : basePos, preview === null || preview === void 0 ? void 0 : preview.editorProxy);
|
|
162
|
+
return this.tryCreateNodeViewInner(targetNode, (_preview$rootPos = preview === null || preview === void 0 ? void 0 : preview.rootPos) !== null && _preview$rootPos !== void 0 ? _preview$rootPos : basePos, preview === null || preview === void 0 ? void 0 : preview.editorProxy, isInserted, colorScheme);
|
|
136
163
|
}
|
|
137
164
|
}, {
|
|
138
165
|
key: "tryCreateNodeViewInner",
|
|
@@ -140,6 +167,8 @@ var NodeViewSerializer = exports.NodeViewSerializer = /*#__PURE__*/function () {
|
|
|
140
167
|
var _this$nodeViews;
|
|
141
168
|
var basePos = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 0;
|
|
142
169
|
var editorProxy = arguments.length > 2 ? arguments[2] : undefined;
|
|
170
|
+
var isInserted = arguments.length > 3 ? arguments[3] : undefined;
|
|
171
|
+
var colorScheme = arguments.length > 4 ? arguments[4] : undefined;
|
|
143
172
|
if (!this.editorView) {
|
|
144
173
|
return null;
|
|
145
174
|
}
|
|
@@ -185,7 +214,7 @@ var NodeViewSerializer = exports.NodeViewSerializer = /*#__PURE__*/function () {
|
|
|
185
214
|
if (!(0, _isExperimentEnabled.isExperimentEnabled)('platform_editor_show_diff_deleted_nodeview_content') && targetNode.type.name === 'paragraph' && targetNode.children.length === 1) {
|
|
186
215
|
return this.serializeFragment(targetNode.content);
|
|
187
216
|
}
|
|
188
|
-
this.appendChildNodes(targetNode.children, _contentDOM, basePos, editorProxy);
|
|
217
|
+
this.appendChildNodes(targetNode.children, _contentDOM, basePos, editorProxy, isInserted, colorScheme);
|
|
189
218
|
}
|
|
190
219
|
return this.withMarkViews(targetNode, _dom, editorProxy);
|
|
191
220
|
}
|
|
@@ -209,7 +238,7 @@ var NodeViewSerializer = exports.NodeViewSerializer = /*#__PURE__*/function () {
|
|
|
209
238
|
dom = _constructor.dom,
|
|
210
239
|
contentDOM = _constructor.contentDOM;
|
|
211
240
|
// Iteratively populate children
|
|
212
|
-
this.appendChildNodes(targetNode.children, contentDOM, basePos, editorProxy);
|
|
241
|
+
this.appendChildNodes(targetNode.children, contentDOM, basePos, editorProxy, isInserted, colorScheme);
|
|
213
242
|
return this.withMarkViews(targetNode, dom, editorProxy);
|
|
214
243
|
} catch (_unused) {
|
|
215
244
|
return null;
|
|
@@ -464,11 +464,14 @@ var calculateDiffDecorationsInner = function calculateDiffDecorationsInner(_ref8
|
|
|
464
464
|
};
|
|
465
465
|
}
|
|
466
466
|
}
|
|
467
|
-
// The attribute-aware encoder is
|
|
468
|
-
|
|
467
|
+
// The attribute-aware encoder is needed by the smart classifier and is gated with it.
|
|
468
|
+
// The NCS version-history path also uses it so the main diff calculation detects attribute-only
|
|
469
|
+
// node replacements, e.g. changing an emoji or mention without changing the node type.
|
|
470
|
+
var _selectTokenEncoder = (0, _selectTokenEncoder2.selectTokenEncoder)(diffType === 'smart' && (0, _fg.fg)('platform_editor_ai_smart_diff') || (0, _fg.fg)('confluence_ncs_step_diffing_version_history')),
|
|
469
471
|
tokenEncoder = _selectTokenEncoder.tokenEncoder,
|
|
470
472
|
shouldHideMarkOnlyDeletions = _selectTokenEncoder.shouldHideMarkOnlyDeletions;
|
|
471
473
|
var changes;
|
|
474
|
+
var contentChanges;
|
|
472
475
|
var attributedChanges = [];
|
|
473
476
|
var attributionColors;
|
|
474
477
|
if (attributedChangesetEnabled) {
|
|
@@ -482,6 +485,7 @@ var calculateDiffDecorationsInner = function calculateDiffDecorationsInner(_ref8
|
|
|
482
485
|
stepIndex: stepIndex
|
|
483
486
|
});
|
|
484
487
|
}));
|
|
488
|
+
contentChanges = changeset.changes;
|
|
485
489
|
attributedChanges = diffType === 'step' ? (0, _diffBySteps.diffBySteps)(originalDoc, simplifiedSteps, simplifiedStepAttributions) : (0, _toConsumableArray2.default)(changeset.changes);
|
|
486
490
|
// The attributed pipeline gives no guarantee that its output ranges are disjoint — see
|
|
487
491
|
// `collapseOverlappingChanges`.
|
|
@@ -501,6 +505,7 @@ var calculateDiffDecorationsInner = function calculateDiffDecorationsInner(_ref8
|
|
|
501
505
|
}
|
|
502
506
|
} else {
|
|
503
507
|
var _changeset = _prosemirrorChangeset.ChangeSet.create(originalDoc, undefined, tokenEncoder).addSteps(steppedDoc, stepMaps, tr.doc);
|
|
508
|
+
contentChanges = _changeset.changes;
|
|
504
509
|
changes = getChanges({
|
|
505
510
|
changeset: _changeset,
|
|
506
511
|
originalDoc: originalDoc,
|
|
@@ -760,20 +765,52 @@ var calculateDiffDecorationsInner = function calculateDiffDecorationsInner(_ref8
|
|
|
760
765
|
changes.forEach(function (change) {
|
|
761
766
|
createDecorationsForChange(change);
|
|
762
767
|
});
|
|
763
|
-
//
|
|
764
|
-
//
|
|
765
|
-
//
|
|
766
|
-
|
|
767
|
-
(0, _getMarkChangeRanges.getMarkChangeRanges)(simplifiedSteps, simplifiedStepAttributions, (0, _fg.fg)('confluence_ncs_step_diffing_version_history') ? stepMaps : undefined).filter(function (_ref10) {
|
|
768
|
+
// Compare the content outside replacement ranges at matching positions in both documents.
|
|
769
|
+
// This preserves real formatting edits without highlighting an unchanged link mark reapplied
|
|
770
|
+
// while editing its display text.
|
|
771
|
+
var hasUnhighlightedMarkChanges = function hasUnhighlightedMarkChanges(_ref10) {
|
|
768
772
|
var fromB = _ref10.fromB,
|
|
769
773
|
toB = _ref10.toB;
|
|
770
|
-
|
|
771
|
-
|
|
774
|
+
var from = fromB;
|
|
775
|
+
var offset = 0;
|
|
776
|
+
var differs = function differs(to) {
|
|
777
|
+
return from < to && !originalDoc.slice(from + offset, to + offset).content.eq(tr.doc.slice(from, to).content);
|
|
778
|
+
};
|
|
779
|
+
var _iterator5 = _createForOfIteratorHelper(contentChanges),
|
|
780
|
+
_step5;
|
|
781
|
+
try {
|
|
782
|
+
for (_iterator5.s(); !(_step5 = _iterator5.n()).done;) {
|
|
783
|
+
var change = _step5.value;
|
|
784
|
+
if (change.fromB >= toB) {
|
|
785
|
+
break;
|
|
786
|
+
}
|
|
787
|
+
if (change.toB > from) {
|
|
788
|
+
if (differs(change.fromB)) {
|
|
789
|
+
return true;
|
|
790
|
+
}
|
|
791
|
+
from = change.toB;
|
|
792
|
+
}
|
|
793
|
+
offset = change.toA - change.toB;
|
|
794
|
+
}
|
|
795
|
+
} catch (err) {
|
|
796
|
+
_iterator5.e(err);
|
|
797
|
+
} finally {
|
|
798
|
+
_iterator5.f();
|
|
799
|
+
}
|
|
800
|
+
return differs(toB);
|
|
801
|
+
};
|
|
802
|
+
|
|
803
|
+
// The attribute-aware token encoder already reports mark-only changes as regular changes in the
|
|
804
|
+
// smart path and the NCS version-history path. For NCS version history, only skip the step-range
|
|
805
|
+
// highlight if the main diff covers it or the remaining content has unchanged formatting.
|
|
806
|
+
(0, _getMarkChangeRanges.getMarkChangeRanges)(simplifiedSteps, simplifiedStepAttributions, (0, _fg.fg)('confluence_ncs_step_diffing_version_history') ? stepMaps : undefined).filter(function (_ref11) {
|
|
772
807
|
var fromB = _ref11.fromB,
|
|
773
808
|
toB = _ref11.toB;
|
|
809
|
+
return !isHandledByTable(fromB, toB);
|
|
810
|
+
}).filter(function (range) {
|
|
774
811
|
return !(0, _fg.fg)('confluence_ncs_step_diffing_version_history') || !changes.some(function (change) {
|
|
775
|
-
return change.fromB
|
|
776
|
-
});
|
|
812
|
+
return change.fromB <= range.fromB && change.toB >= range.toB;
|
|
813
|
+
}) && hasUnhighlightedMarkChanges(range);
|
|
777
814
|
}).forEach(function (change) {
|
|
778
815
|
var _attributionColors$ge, _attributionColors;
|
|
779
816
|
var changeColorScheme = change.attributionKey ? (_attributionColors$ge = (_attributionColors = attributionColors) === null || _attributionColors === void 0 ? void 0 : _attributionColors.get(change.attributionKey)) !== null && _attributionColors$ge !== void 0 ? _attributionColors$ge : colorScheme : colorScheme;
|
|
@@ -797,8 +834,9 @@ var calculateDiffDecorationsInner = function calculateDiffDecorationsInner(_ref8
|
|
|
797
834
|
return !isHandledByTable(fromB, toB);
|
|
798
835
|
}).filter(function (_ref13) {
|
|
799
836
|
var fromB = _ref13.fromB,
|
|
800
|
-
toB = _ref13.toB
|
|
801
|
-
|
|
837
|
+
toB = _ref13.toB,
|
|
838
|
+
isInline = _ref13.isInline;
|
|
839
|
+
return !(0, _fg.fg)('confluence_ncs_step_diffing_version_history') || !isInline || !changes.some(function (change) {
|
|
802
840
|
return change.fromB === fromB && change.toB === toB;
|
|
803
841
|
});
|
|
804
842
|
}).forEach(function (change) {
|
|
@@ -20,7 +20,8 @@ var typeID = function typeID(type) {
|
|
|
20
20
|
*
|
|
21
21
|
* Deliberately narrower than `attrAwareTokenEncoder`: node-start tokens keep the library default
|
|
22
22
|
* and do not fold in diffable attributes, which `getAttrChangeRanges` already reports for every
|
|
23
|
-
* diff type.
|
|
23
|
+
* diff type. The NCS version-history path also uses `attrAwareTokenEncoder` so main diff calculation
|
|
24
|
+
* detects attribute-only node replacements.
|
|
24
25
|
*/
|
|
25
26
|
var markAwareTokenEncoder = exports.markAwareTokenEncoder = {
|
|
26
27
|
encodeCharacter: function encodeCharacter(char, marks) {
|
|
@@ -11,7 +11,7 @@ var _markAwareTokenEncoder = require("./markAwareTokenEncoder");
|
|
|
11
11
|
* `computeDiffChanges` so the two cannot drift.
|
|
12
12
|
*
|
|
13
13
|
* @param useAttrAwareEncoder Caller is running the smart classifier, which needs node attrs folded
|
|
14
|
-
* into the token.
|
|
14
|
+
* into the token. Also used by the NCS version-history path to detect attribute-only node changes.
|
|
15
15
|
*/
|
|
16
16
|
var selectTokenEncoder = exports.selectTokenEncoder = function selectTokenEncoder(useAttrAwareEncoder) {
|
|
17
17
|
if (useAttrAwareEncoder) {
|
|
@@ -182,7 +182,7 @@ var createChangedRowDOM = function createChangedRowDOM(_ref3) {
|
|
|
182
182
|
var cellIndex = 0;
|
|
183
183
|
rowNode.content.forEach(function (cellNode) {
|
|
184
184
|
if (cellNode.type.name === 'tableCell' || cellNode.type.name === 'tableHeader') {
|
|
185
|
-
var nodeView = nodeViewSerializer.tryCreateNodeView(cellNode);
|
|
185
|
+
var nodeView = nodeViewSerializer.tryCreateNodeView(cellNode, 0, undefined, isInserted, colorScheme);
|
|
186
186
|
if (nodeView) {
|
|
187
187
|
if (nodeView instanceof HTMLElement) {
|
|
188
188
|
(0, _tableCellEdgeAttrs.applyCellEdgeAttrs)(nodeView, cellEdgeAttrs === null || cellEdgeAttrs === void 0 ? void 0 : cellEdgeAttrs[cellIndex]);
|
|
@@ -82,7 +82,7 @@ var createTableCellContentWidgets = function createTableCellContentWidgets(_ref)
|
|
|
82
82
|
// A cell contains block content (paragraphs); serialize the cell's content
|
|
83
83
|
// so every block inside the cell is rendered.
|
|
84
84
|
cellNode.content.forEach(function (blockNode) {
|
|
85
|
-
var nodeView = nodeViewSerializer.tryCreateNodeView(blockNode);
|
|
85
|
+
var nodeView = nodeViewSerializer.tryCreateNodeView(blockNode, 0, undefined, isInserted, colorScheme);
|
|
86
86
|
var wrapper = (0, _wrapBlockNodeView.createContentWrapper)(colorScheme, false, isInserted);
|
|
87
87
|
if (nodeView) {
|
|
88
88
|
wrapper.append(nodeView);
|
|
@@ -322,7 +322,7 @@ var createNodeChangedDecorationWidget = exports.createNodeChangedDecorationWidge
|
|
|
322
322
|
var handleMultipleChildNodes = function handleMultipleChildNodes(node) {
|
|
323
323
|
if (!shouldPreserveCompleteMultiInlineBlock && !shouldRenderAsBlockNode && node.content.childCount > 1 && node.type.inlineContent) {
|
|
324
324
|
node.content.forEach(function (childNode) {
|
|
325
|
-
var childNodeView = serializer.tryCreateNodeView(childNode);
|
|
325
|
+
var childNodeView = serializer.tryCreateNodeView(childNode, 0, undefined, isInserted, colorScheme);
|
|
326
326
|
if (childNodeView) {
|
|
327
327
|
var lineBreak = document.createElement('br');
|
|
328
328
|
dom.append(lineBreak);
|
|
@@ -378,7 +378,7 @@ var createNodeChangedDecorationWidget = exports.createNodeChangedDecorationWidge
|
|
|
378
378
|
|
|
379
379
|
// Try to create node view, fallback to serialization
|
|
380
380
|
var $sourcePos = (0, _fg.fg)('platform_editor_ai_show_diff_patch_2') ? getSourcePos(doc, $changeFromA, slice.openStart, index) : null;
|
|
381
|
-
var nodeView = serializer.tryCreateNodeView(node, 0, $sourcePos !== null && $sourcePos !== void 0 ? $sourcePos : undefined);
|
|
381
|
+
var nodeView = serializer.tryCreateNodeView(node, 0, $sourcePos !== null && $sourcePos !== void 0 ? $sourcePos : undefined, isInserted, colorScheme);
|
|
382
382
|
if (nodeView) {
|
|
383
383
|
if (node.isInline) {
|
|
384
384
|
var wrapper = (0, _wrapBlockNodeView.createContentWrapper)(colorScheme, isActive, isInserted, reveal);
|
|
@@ -4,6 +4,7 @@ Object.defineProperty(exports, "__esModule", {
|
|
|
4
4
|
value: true
|
|
5
5
|
});
|
|
6
6
|
exports.isDiffableAttr = exports.getRequiredIncludedDiffableAttrs = exports.getIncludedDiffableAttrs = exports.getDiffableAttrNames = exports.DIFFABLE_ATTRS_BY_NODE_TYPE = void 0;
|
|
7
|
+
var _fg = require("@atlaskit/platform-feature-flags/fg");
|
|
7
8
|
/**
|
|
8
9
|
* Single source of truth for which attributes of a node type represent a
|
|
9
10
|
* meaningful change. Shared by `attrAwareTokenEncoder` (changeset detection)
|
|
@@ -94,10 +95,13 @@ var getRequiredIncludedDiffableAttrs = exports.getRequiredIncludedDiffableAttrs
|
|
|
94
95
|
* order is not stable across the two diffed documents.
|
|
95
96
|
*/
|
|
96
97
|
var getDiffableAttrNames = exports.getDiffableAttrNames = function getDiffableAttrNames(nodeTypeName, attrs) {
|
|
98
|
+
// Adding a column updates existing widths, ignore this attribute change. Move this into
|
|
99
|
+
// DIFFABLE_ATTRS_BY_NODE_TYPE when cleaning up confluence_ncs_step_diffing_version_history
|
|
100
|
+
var ignoreColumnWidth = nodeTypeName === 'layoutColumn' && (0, _fg.fg)('confluence_ncs_step_diffing_version_history');
|
|
97
101
|
var rule = DIFFABLE_ATTRS_BY_NODE_TYPE[nodeTypeName];
|
|
98
102
|
if (!rule) {
|
|
99
103
|
return Object.keys(attrs).filter(function (name) {
|
|
100
|
-
return !DEFAULT_EXCLUDED_ATTRS.includes(name);
|
|
104
|
+
return !DEFAULT_EXCLUDED_ATTRS.includes(name) && !(ignoreColumnWidth && name === 'width');
|
|
101
105
|
}).sort();
|
|
102
106
|
}
|
|
103
107
|
if ('include' in rule) {
|
|
@@ -90,6 +90,11 @@ var getAttrChangeRanges = exports.getAttrChangeRanges = function getAttrChangeRa
|
|
|
90
90
|
var $pos = doc.resolve(finalPos);
|
|
91
91
|
var nodeAtPos = doc.nodeAt(finalPos);
|
|
92
92
|
var originalNodeAtPos = originalPos === undefined ? null : originalDoc.nodeAt(originalPos);
|
|
93
|
+
var hasChangedAttr = function hasChangedAttr(watchedAttrs) {
|
|
94
|
+
return attrsToCheck.some(function (attr) {
|
|
95
|
+
return watchedAttrs.includes(attr) && (!(0, _fg.fg)('confluence_ncs_step_diffing_version_history') || !originalNodeAtPos || originalNodeAtPos.type !== (nodeAtPos === null || nodeAtPos === void 0 ? void 0 : nodeAtPos.type) || !(0, _isEqual.default)(originalNodeAtPos.attrs[attr], nodeAtPos === null || nodeAtPos === void 0 ? void 0 : nodeAtPos.attrs[attr]));
|
|
96
|
+
});
|
|
97
|
+
};
|
|
93
98
|
|
|
94
99
|
// The changeset path (createDecorationsForChange) handles the deletion widget via
|
|
95
100
|
// prosemirror-changeset; we only need to add the inline insertion highlight here.
|
|
@@ -97,9 +102,7 @@ var getAttrChangeRanges = exports.getAttrChangeRanges = function getAttrChangeRa
|
|
|
97
102
|
var nodeName = nodeAtPos.type.name;
|
|
98
103
|
if (isInlineAttrChangeNodeName(nodeName)) {
|
|
99
104
|
var watchedAttrs = inlineNodeAttrMap[nodeName];
|
|
100
|
-
if (
|
|
101
|
-
return watchedAttrs.includes(v);
|
|
102
|
-
})) {
|
|
105
|
+
if (hasChangedAttr(watchedAttrs)) {
|
|
103
106
|
return _objectSpread(_objectSpread({}, attributionKey ? {
|
|
104
107
|
attributionKey: attributionKey
|
|
105
108
|
} : {}), {}, {
|
|
@@ -131,9 +134,7 @@ var getAttrChangeRanges = exports.getAttrChangeRanges = function getAttrChangeRa
|
|
|
131
134
|
// (e.g. note -> warning) — highlight the new panel and, when the original node
|
|
132
135
|
// is resolvable, expose its range (fromA/toA) so the caller can render the old
|
|
133
136
|
// panel as a "deleted" widget for a before/after comparison.
|
|
134
|
-
if (
|
|
135
|
-
return panelAttrs.includes(v);
|
|
136
|
-
}) && nodeAtPos && (0, _nodeTypeUtils.getBaseNodeTypeName)(nodeAtPos.type) === 'panel') {
|
|
137
|
+
if (hasChangedAttr(panelAttrs) && nodeAtPos && (0, _nodeTypeUtils.getBaseNodeTypeName)(nodeAtPos.type) === 'panel') {
|
|
137
138
|
return _objectSpread(_objectSpread({}, attributionKey ? {
|
|
138
139
|
attributionKey: attributionKey
|
|
139
140
|
} : {}), {}, {
|
|
@@ -181,7 +182,9 @@ var getAttrChangeRanges = exports.getAttrChangeRanges = function getAttrChangeRa
|
|
|
181
182
|
// (e.g. setNodeAttribute(pos, 'text', ...) + setNodeAttribute(pos, 'color', ...))
|
|
182
183
|
// should produce only one decoration, not one per step.
|
|
183
184
|
.filter(function (range, i, arr) {
|
|
184
|
-
return arr.
|
|
185
|
+
return (0, _fg.fg)('confluence_ncs_step_diffing_version_history') ? !arr.some(function (r, nextIndex) {
|
|
186
|
+
return nextIndex > i && r.fromB === range.fromB;
|
|
187
|
+
}) : arr.findIndex(function (r) {
|
|
185
188
|
return r.fromB === range.fromB;
|
|
186
189
|
}) === i;
|
|
187
190
|
});
|
|
@@ -41,6 +41,10 @@ var isMultiContainerBlockNode = exports.isMultiContainerBlockNode = function isM
|
|
|
41
41
|
return ['decisionList', 'layoutSection'].includes(nodeName);
|
|
42
42
|
};
|
|
43
43
|
var isTextLikeBlockNode = exports.isTextLikeBlockNode = function isTextLikeBlockNode(nodeName) {
|
|
44
|
+
// When cleaning up platform_editor_ai_show_diff_patch_2 just put paragraph into the list
|
|
45
|
+
if (nodeName === 'paragraph') {
|
|
46
|
+
return (0, _fg.fg)('platform_editor_ai_show_diff_patch_2');
|
|
47
|
+
}
|
|
44
48
|
return ['heading', 'bulletList', 'orderedList', 'listItem', 'taskList', 'blockquote'].includes(nodeName);
|
|
45
49
|
};
|
|
46
50
|
var getChangedContentStyleNext = function getChangedContentStyleNext(colorScheme) {
|
|
@@ -78,6 +78,12 @@ var isMultiContainerBlockNode = function isMultiContainerBlockNode(nodeName) {
|
|
|
78
78
|
return ['decisionList', 'layoutSection'].includes(nodeName);
|
|
79
79
|
};
|
|
80
80
|
var isTextLikeBlockNode = function isTextLikeBlockNode(nodeName) {
|
|
81
|
+
// `paragraph` only reaches this classification via patch_2's `shouldRenderAsBlockNode` routing
|
|
82
|
+
// (createNodeChangedDecorationWidget.ts), which fully replaced paragraphs never hit otherwise —
|
|
83
|
+
// gating it here keeps that dependency explicit rather than implicit.
|
|
84
|
+
if (nodeName === 'paragraph') {
|
|
85
|
+
return (0, _fg.fg)('platform_editor_ai_show_diff_patch_2');
|
|
86
|
+
}
|
|
81
87
|
return ['heading', 'bulletList', 'orderedList', 'listItem', 'taskList', 'blockquote'].includes(nodeName);
|
|
82
88
|
};
|
|
83
89
|
|
|
@@ -4,6 +4,8 @@ import { contains } from '@atlaskit/editor-prosemirror/utils';
|
|
|
4
4
|
import { isExperimentEnabled } from '@atlaskit/platform-feature-experiments/is-experiment-enabled';
|
|
5
5
|
import { fg } from '@atlaskit/platform-feature-flags/fg';
|
|
6
6
|
import { createEditorProxy } from './createEditorProxy';
|
|
7
|
+
import { getAtomicInlineChangedAttrs } from './decorations/createInlineChangedDecoration';
|
|
8
|
+
import { isInlineAttrChangeNodeName } from './decorations/utils/getAttrChangeRanges';
|
|
7
9
|
import { wrapInMarkViews } from './markViews';
|
|
8
10
|
|
|
9
11
|
/**
|
|
@@ -36,6 +38,14 @@ export function isEditorViewWithNodeViews(view) {
|
|
|
36
38
|
/** Expand-family nodes whose expanded state lives in a node-keyed WeakMap. */
|
|
37
39
|
const EXPAND_TYPES = new Set(['expand', 'nestedExpand']);
|
|
38
40
|
|
|
41
|
+
/** Merges an inline style string onto an element's existing `style` attribute. */
|
|
42
|
+
const appendInlineStyle = (element, style) => {
|
|
43
|
+
var _element$getAttribute;
|
|
44
|
+
const currentStyle = (_element$getAttribute = element.getAttribute('style')) !== null && _element$getAttribute !== void 0 ? _element$getAttribute : '';
|
|
45
|
+
const separator = currentStyle && style && !currentStyle.trimEnd().endsWith(';') ? '; ' : '';
|
|
46
|
+
element.setAttribute('style', `${currentStyle}${separator}${style}`);
|
|
47
|
+
};
|
|
48
|
+
|
|
39
49
|
/** Tables are the only node views that read and write positions, so only they need a preview. */
|
|
40
50
|
const containsTable = node => {
|
|
41
51
|
const {
|
|
@@ -83,14 +93,28 @@ export class NodeViewSerializer {
|
|
|
83
93
|
/**
|
|
84
94
|
* Appends serialized child nodes to the given contentDOM element.
|
|
85
95
|
*/
|
|
86
|
-
appendChildNodes(children, contentDOM, basePos = 0, editorProxy) {
|
|
96
|
+
appendChildNodes(children, contentDOM, basePos = 0, editorProxy, isInserted, colorScheme) {
|
|
87
97
|
// A node's first child sits one position inside it, and each subsequent
|
|
88
98
|
// child is offset by the previous child's `nodeSize`. Tracking this lets nested node
|
|
89
99
|
// views resolve to their real depth instead of every one of them seeing depth 0.
|
|
90
100
|
let childPos = basePos + 1;
|
|
91
101
|
children.forEach(child => {
|
|
92
|
-
const childNode = this.tryCreateNodeViewInner(child, childPos, editorProxy) || this.serializeNode(child);
|
|
102
|
+
const childNode = this.tryCreateNodeViewInner(child, childPos, editorProxy, isInserted, colorScheme) || this.serializeNode(child);
|
|
93
103
|
if (childNode) {
|
|
104
|
+
// Give inserted atomic children the same box-shadow
|
|
105
|
+
// highlight an in-place attribute change already gets (`getAtomicInlineChangedAttrs`).
|
|
106
|
+
if (isInserted && isExperimentEnabled('platform_editor_show_diff_deleted_nodeview_content') && isInlineAttrChangeNodeName(child.type.name) && childNode instanceof HTMLElement) {
|
|
107
|
+
var _childNode$getAttribu;
|
|
108
|
+
const {
|
|
109
|
+
className,
|
|
110
|
+
styleSuffix
|
|
111
|
+
} = getAtomicInlineChangedAttrs(child.type.name, colorScheme);
|
|
112
|
+
const existingClassName = (_childNode$getAttribu = childNode.getAttribute('class')) !== null && _childNode$getAttribu !== void 0 ? _childNode$getAttribu : '';
|
|
113
|
+
childNode.setAttribute('class', existingClassName ? `${existingClassName} ${className}` : className);
|
|
114
|
+
if (styleSuffix) {
|
|
115
|
+
appendInlineStyle(childNode, styleSuffix);
|
|
116
|
+
}
|
|
117
|
+
}
|
|
94
118
|
contentDOM === null || contentDOM === void 0 ? void 0 : contentDOM.append(childNode);
|
|
95
119
|
}
|
|
96
120
|
childPos += child.nodeSize;
|
|
@@ -105,7 +129,7 @@ export class NodeViewSerializer {
|
|
|
105
129
|
*
|
|
106
130
|
* `$sourcePos` is where the node sits in the doc it came from, so nested tables keep their depth.
|
|
107
131
|
*/
|
|
108
|
-
tryCreateNodeView(targetNode, basePos = 0, $sourcePos) {
|
|
132
|
+
tryCreateNodeView(targetNode, basePos = 0, $sourcePos, isInserted, colorScheme) {
|
|
109
133
|
var _preview$rootPos;
|
|
110
134
|
if (!this.editorView) {
|
|
111
135
|
return null;
|
|
@@ -113,9 +137,9 @@ export class NodeViewSerializer {
|
|
|
113
137
|
// The preview becomes the document every nested node view resolves its position against,
|
|
114
138
|
// so positions stay internally consistent.
|
|
115
139
|
const preview = fg('platform_editor_ai_show_diff_patch_2') && containsTable(targetNode) ? createEditorProxy(this.editorView, targetNode, $sourcePos) : null;
|
|
116
|
-
return this.tryCreateNodeViewInner(targetNode, (_preview$rootPos = preview === null || preview === void 0 ? void 0 : preview.rootPos) !== null && _preview$rootPos !== void 0 ? _preview$rootPos : basePos, preview === null || preview === void 0 ? void 0 : preview.editorProxy);
|
|
140
|
+
return this.tryCreateNodeViewInner(targetNode, (_preview$rootPos = preview === null || preview === void 0 ? void 0 : preview.rootPos) !== null && _preview$rootPos !== void 0 ? _preview$rootPos : basePos, preview === null || preview === void 0 ? void 0 : preview.editorProxy, isInserted, colorScheme);
|
|
117
141
|
}
|
|
118
|
-
tryCreateNodeViewInner(targetNode, basePos = 0, editorProxy) {
|
|
142
|
+
tryCreateNodeViewInner(targetNode, basePos = 0, editorProxy, isInserted, colorScheme) {
|
|
119
143
|
var _this$nodeViews;
|
|
120
144
|
if (!this.editorView) {
|
|
121
145
|
return null;
|
|
@@ -163,7 +187,7 @@ export class NodeViewSerializer {
|
|
|
163
187
|
if (!isExperimentEnabled('platform_editor_show_diff_deleted_nodeview_content') && targetNode.type.name === 'paragraph' && targetNode.children.length === 1) {
|
|
164
188
|
return this.serializeFragment(targetNode.content);
|
|
165
189
|
}
|
|
166
|
-
this.appendChildNodes(targetNode.children, contentDOM, basePos, editorProxy);
|
|
190
|
+
this.appendChildNodes(targetNode.children, contentDOM, basePos, editorProxy, isInserted, colorScheme);
|
|
167
191
|
}
|
|
168
192
|
return this.withMarkViews(targetNode, dom, editorProxy);
|
|
169
193
|
}
|
|
@@ -186,7 +210,7 @@ export class NodeViewSerializer {
|
|
|
186
210
|
contentDOM
|
|
187
211
|
} = constructor(targetNode, view, () => resolvedPos, [], {});
|
|
188
212
|
// Iteratively populate children
|
|
189
|
-
this.appendChildNodes(targetNode.children, contentDOM, basePos, editorProxy);
|
|
213
|
+
this.appendChildNodes(targetNode.children, contentDOM, basePos, editorProxy, isInserted, colorScheme);
|
|
190
214
|
return this.withMarkViews(targetNode, dom, editorProxy);
|
|
191
215
|
} catch {
|
|
192
216
|
return null;
|
|
@@ -418,12 +418,15 @@ const calculateDiffDecorationsInner = ({
|
|
|
418
418
|
};
|
|
419
419
|
}
|
|
420
420
|
}
|
|
421
|
-
// The attribute-aware encoder is
|
|
421
|
+
// The attribute-aware encoder is needed by the smart classifier and is gated with it.
|
|
422
|
+
// The NCS version-history path also uses it so the main diff calculation detects attribute-only
|
|
423
|
+
// node replacements, e.g. changing an emoji or mention without changing the node type.
|
|
422
424
|
const {
|
|
423
425
|
tokenEncoder,
|
|
424
426
|
shouldHideMarkOnlyDeletions
|
|
425
|
-
} = selectTokenEncoder(diffType === 'smart' && fg('platform_editor_ai_smart_diff'));
|
|
427
|
+
} = selectTokenEncoder(diffType === 'smart' && fg('platform_editor_ai_smart_diff') || fg('confluence_ncs_step_diffing_version_history'));
|
|
426
428
|
let changes;
|
|
429
|
+
let contentChanges;
|
|
427
430
|
let attributedChanges = [];
|
|
428
431
|
let attributionColors;
|
|
429
432
|
if (attributedChangesetEnabled) {
|
|
@@ -434,6 +437,7 @@ const calculateDiffDecorationsInner = ({
|
|
|
434
437
|
...attribution,
|
|
435
438
|
stepIndex
|
|
436
439
|
})));
|
|
440
|
+
contentChanges = changeset.changes;
|
|
437
441
|
attributedChanges = diffType === 'step' ? diffBySteps(originalDoc, simplifiedSteps, simplifiedStepAttributions) : [...changeset.changes];
|
|
438
442
|
// The attributed pipeline gives no guarantee that its output ranges are disjoint — see
|
|
439
443
|
// `collapseOverlappingChanges`.
|
|
@@ -453,6 +457,7 @@ const calculateDiffDecorationsInner = ({
|
|
|
453
457
|
}
|
|
454
458
|
} else {
|
|
455
459
|
const changeset = ChangeSet.create(originalDoc, undefined, tokenEncoder).addSteps(steppedDoc, stepMaps, tr.doc);
|
|
460
|
+
contentChanges = changeset.changes;
|
|
456
461
|
changes = getChanges({
|
|
457
462
|
changeset,
|
|
458
463
|
originalDoc,
|
|
@@ -686,17 +691,38 @@ const calculateDiffDecorationsInner = ({
|
|
|
686
691
|
changes.forEach(change => {
|
|
687
692
|
createDecorationsForChange(change);
|
|
688
693
|
});
|
|
689
|
-
//
|
|
690
|
-
//
|
|
691
|
-
//
|
|
692
|
-
|
|
693
|
-
getMarkChangeRanges(simplifiedSteps, simplifiedStepAttributions, fg('confluence_ncs_step_diffing_version_history') ? stepMaps : undefined).filter(({
|
|
694
|
+
// Compare the content outside replacement ranges at matching positions in both documents.
|
|
695
|
+
// This preserves real formatting edits without highlighting an unchanged link mark reapplied
|
|
696
|
+
// while editing its display text.
|
|
697
|
+
const hasUnhighlightedMarkChanges = ({
|
|
694
698
|
fromB,
|
|
695
699
|
toB
|
|
696
|
-
}) =>
|
|
700
|
+
}) => {
|
|
701
|
+
let from = fromB;
|
|
702
|
+
let offset = 0;
|
|
703
|
+
const differs = to => from < to && !originalDoc.slice(from + offset, to + offset).content.eq(tr.doc.slice(from, to).content);
|
|
704
|
+
for (const change of contentChanges) {
|
|
705
|
+
if (change.fromB >= toB) {
|
|
706
|
+
break;
|
|
707
|
+
}
|
|
708
|
+
if (change.toB > from) {
|
|
709
|
+
if (differs(change.fromB)) {
|
|
710
|
+
return true;
|
|
711
|
+
}
|
|
712
|
+
from = change.toB;
|
|
713
|
+
}
|
|
714
|
+
offset = change.toA - change.toB;
|
|
715
|
+
}
|
|
716
|
+
return differs(toB);
|
|
717
|
+
};
|
|
718
|
+
|
|
719
|
+
// The attribute-aware token encoder already reports mark-only changes as regular changes in the
|
|
720
|
+
// smart path and the NCS version-history path. For NCS version history, only skip the step-range
|
|
721
|
+
// highlight if the main diff covers it or the remaining content has unchanged formatting.
|
|
722
|
+
getMarkChangeRanges(simplifiedSteps, simplifiedStepAttributions, fg('confluence_ncs_step_diffing_version_history') ? stepMaps : undefined).filter(({
|
|
697
723
|
fromB,
|
|
698
724
|
toB
|
|
699
|
-
}) => !fg('confluence_ncs_step_diffing_version_history') || !changes.some(change => change.fromB
|
|
725
|
+
}) => !isHandledByTable(fromB, toB)).filter(range => !fg('confluence_ncs_step_diffing_version_history') || !changes.some(change => change.fromB <= range.fromB && change.toB >= range.toB) && hasUnhighlightedMarkChanges(range)).forEach(change => {
|
|
700
726
|
var _attributionColors$ge, _attributionColors;
|
|
701
727
|
const changeColorScheme = change.attributionKey ? (_attributionColors$ge = (_attributionColors = attributionColors) === null || _attributionColors === void 0 ? void 0 : _attributionColors.get(change.attributionKey)) !== null && _attributionColors$ge !== void 0 ? _attributionColors$ge : colorScheme : colorScheme;
|
|
702
728
|
const attributionKey = showContributorTags ? change.attributionKey : undefined;
|
|
@@ -718,8 +744,9 @@ const calculateDiffDecorationsInner = ({
|
|
|
718
744
|
toB
|
|
719
745
|
}) => !isHandledByTable(fromB, toB)).filter(({
|
|
720
746
|
fromB,
|
|
721
|
-
toB
|
|
722
|
-
|
|
747
|
+
toB,
|
|
748
|
+
isInline
|
|
749
|
+
}) => !fg('confluence_ncs_step_diffing_version_history') || !isInline || !changes.some(change => change.fromB === fromB && change.toB === toB)).forEach(change => {
|
|
723
750
|
var _attributionColors$ge2, _attributionColors2;
|
|
724
751
|
const changeColorScheme = change.attributionKey ? (_attributionColors$ge2 = (_attributionColors2 = attributionColors) === null || _attributionColors2 === void 0 ? void 0 : _attributionColors2.get(change.attributionKey)) !== null && _attributionColors$ge2 !== void 0 ? _attributionColors$ge2 : colorScheme : colorScheme;
|
|
725
752
|
const attributionKey = showContributorTags ? change.attributionKey : undefined;
|
|
@@ -15,7 +15,8 @@ const typeID = type => {
|
|
|
15
15
|
*
|
|
16
16
|
* Deliberately narrower than `attrAwareTokenEncoder`: node-start tokens keep the library default
|
|
17
17
|
* and do not fold in diffable attributes, which `getAttrChangeRanges` already reports for every
|
|
18
|
-
* diff type.
|
|
18
|
+
* diff type. The NCS version-history path also uses `attrAwareTokenEncoder` so main diff calculation
|
|
19
|
+
* detects attribute-only node replacements.
|
|
19
20
|
*/
|
|
20
21
|
export const markAwareTokenEncoder = {
|
|
21
22
|
encodeCharacter: (char, marks) => encodeCharacterWithMarks(char, marks),
|
|
@@ -5,7 +5,7 @@ import { markAwareTokenEncoder } from './markAwareTokenEncoder';
|
|
|
5
5
|
* `computeDiffChanges` so the two cannot drift.
|
|
6
6
|
*
|
|
7
7
|
* @param useAttrAwareEncoder Caller is running the smart classifier, which needs node attrs folded
|
|
8
|
-
* into the token.
|
|
8
|
+
* into the token. Also used by the NCS version-history path to detect attribute-only node changes.
|
|
9
9
|
*/
|
|
10
10
|
export const selectTokenEncoder = useAttrAwareEncoder => {
|
|
11
11
|
if (useAttrAwareEncoder) {
|
|
@@ -157,7 +157,7 @@ const createChangedRowDOM = ({
|
|
|
157
157
|
let cellIndex = 0;
|
|
158
158
|
rowNode.content.forEach(cellNode => {
|
|
159
159
|
if (cellNode.type.name === 'tableCell' || cellNode.type.name === 'tableHeader') {
|
|
160
|
-
const nodeView = nodeViewSerializer.tryCreateNodeView(cellNode);
|
|
160
|
+
const nodeView = nodeViewSerializer.tryCreateNodeView(cellNode, 0, undefined, isInserted, colorScheme);
|
|
161
161
|
if (nodeView) {
|
|
162
162
|
if (nodeView instanceof HTMLElement) {
|
|
163
163
|
applyCellEdgeAttrs(nodeView, cellEdgeAttrs === null || cellEdgeAttrs === void 0 ? void 0 : cellEdgeAttrs[cellIndex]);
|
|
@@ -72,7 +72,7 @@ const createTableCellContentWidgets = ({
|
|
|
72
72
|
// A cell contains block content (paragraphs); serialize the cell's content
|
|
73
73
|
// so every block inside the cell is rendered.
|
|
74
74
|
cellNode.content.forEach(blockNode => {
|
|
75
|
-
const nodeView = nodeViewSerializer.tryCreateNodeView(blockNode);
|
|
75
|
+
const nodeView = nodeViewSerializer.tryCreateNodeView(blockNode, 0, undefined, isInserted, colorScheme);
|
|
76
76
|
const wrapper = createContentWrapper(colorScheme, false, isInserted);
|
|
77
77
|
if (nodeView) {
|
|
78
78
|
wrapper.append(nodeView);
|
|
@@ -314,7 +314,7 @@ export const createNodeChangedDecorationWidget = ({
|
|
|
314
314
|
const handleMultipleChildNodes = node => {
|
|
315
315
|
if (!shouldPreserveCompleteMultiInlineBlock && !shouldRenderAsBlockNode && node.content.childCount > 1 && node.type.inlineContent) {
|
|
316
316
|
node.content.forEach(childNode => {
|
|
317
|
-
const childNodeView = serializer.tryCreateNodeView(childNode);
|
|
317
|
+
const childNodeView = serializer.tryCreateNodeView(childNode, 0, undefined, isInserted, colorScheme);
|
|
318
318
|
if (childNodeView) {
|
|
319
319
|
const lineBreak = document.createElement('br');
|
|
320
320
|
dom.append(lineBreak);
|
|
@@ -364,7 +364,7 @@ export const createNodeChangedDecorationWidget = ({
|
|
|
364
364
|
|
|
365
365
|
// Try to create node view, fallback to serialization
|
|
366
366
|
const $sourcePos = fg('platform_editor_ai_show_diff_patch_2') ? getSourcePos(doc, $changeFromA, slice.openStart, index) : null;
|
|
367
|
-
const nodeView = serializer.tryCreateNodeView(node, 0, $sourcePos !== null && $sourcePos !== void 0 ? $sourcePos : undefined);
|
|
367
|
+
const nodeView = serializer.tryCreateNodeView(node, 0, $sourcePos !== null && $sourcePos !== void 0 ? $sourcePos : undefined, isInserted, colorScheme);
|
|
368
368
|
if (nodeView) {
|
|
369
369
|
if (node.isInline) {
|
|
370
370
|
const wrapper = createContentWrapper(colorScheme, isActive, isInserted, reveal);
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { fg } from '@atlaskit/platform-feature-flags/fg';
|
|
2
|
+
|
|
1
3
|
/**
|
|
2
4
|
* Single source of truth for which attributes of a node type represent a
|
|
3
5
|
* meaningful change. Shared by `attrAwareTokenEncoder` (changeset detection)
|
|
@@ -88,9 +90,12 @@ export const getRequiredIncludedDiffableAttrs = nodeTypeName => {
|
|
|
88
90
|
* order is not stable across the two diffed documents.
|
|
89
91
|
*/
|
|
90
92
|
export const getDiffableAttrNames = (nodeTypeName, attrs) => {
|
|
93
|
+
// Adding a column updates existing widths, ignore this attribute change. Move this into
|
|
94
|
+
// DIFFABLE_ATTRS_BY_NODE_TYPE when cleaning up confluence_ncs_step_diffing_version_history
|
|
95
|
+
const ignoreColumnWidth = nodeTypeName === 'layoutColumn' && fg('confluence_ncs_step_diffing_version_history');
|
|
91
96
|
const rule = DIFFABLE_ATTRS_BY_NODE_TYPE[nodeTypeName];
|
|
92
97
|
if (!rule) {
|
|
93
|
-
return Object.keys(attrs).filter(name => !DEFAULT_EXCLUDED_ATTRS.includes(name)).sort();
|
|
98
|
+
return Object.keys(attrs).filter(name => !DEFAULT_EXCLUDED_ATTRS.includes(name) && !(ignoreColumnWidth && name === 'width')).sort();
|
|
94
99
|
}
|
|
95
100
|
if ('include' in rule) {
|
|
96
101
|
return rule.include;
|
|
@@ -74,6 +74,7 @@ export const getAttrChangeRanges = (doc, attrStepContexts, originalDoc) => {
|
|
|
74
74
|
const $pos = doc.resolve(finalPos);
|
|
75
75
|
const nodeAtPos = doc.nodeAt(finalPos);
|
|
76
76
|
const originalNodeAtPos = originalPos === undefined ? null : originalDoc.nodeAt(originalPos);
|
|
77
|
+
const hasChangedAttr = watchedAttrs => attrsToCheck.some(attr => watchedAttrs.includes(attr) && (!fg('confluence_ncs_step_diffing_version_history') || !originalNodeAtPos || originalNodeAtPos.type !== (nodeAtPos === null || nodeAtPos === void 0 ? void 0 : nodeAtPos.type) || !isEqual(originalNodeAtPos.attrs[attr], nodeAtPos === null || nodeAtPos === void 0 ? void 0 : nodeAtPos.attrs[attr])));
|
|
77
78
|
|
|
78
79
|
// The changeset path (createDecorationsForChange) handles the deletion widget via
|
|
79
80
|
// prosemirror-changeset; we only need to add the inline insertion highlight here.
|
|
@@ -81,7 +82,7 @@ export const getAttrChangeRanges = (doc, attrStepContexts, originalDoc) => {
|
|
|
81
82
|
const nodeName = nodeAtPos.type.name;
|
|
82
83
|
if (isInlineAttrChangeNodeName(nodeName)) {
|
|
83
84
|
const watchedAttrs = inlineNodeAttrMap[nodeName];
|
|
84
|
-
if (
|
|
85
|
+
if (hasChangedAttr(watchedAttrs)) {
|
|
85
86
|
return {
|
|
86
87
|
...(attributionKey ? {
|
|
87
88
|
attributionKey
|
|
@@ -114,7 +115,7 @@ export const getAttrChangeRanges = (doc, attrStepContexts, originalDoc) => {
|
|
|
114
115
|
// (e.g. note -> warning) — highlight the new panel and, when the original node
|
|
115
116
|
// is resolvable, expose its range (fromA/toA) so the caller can render the old
|
|
116
117
|
// panel as a "deleted" widget for a before/after comparison.
|
|
117
|
-
if (
|
|
118
|
+
if (hasChangedAttr(panelAttrs) && nodeAtPos && getBaseNodeTypeName(nodeAtPos.type) === 'panel') {
|
|
118
119
|
return {
|
|
119
120
|
...(attributionKey ? {
|
|
120
121
|
attributionKey
|
|
@@ -161,7 +162,7 @@ export const getAttrChangeRanges = (doc, attrStepContexts, originalDoc) => {
|
|
|
161
162
|
// Deduplicate by node position: multiple AttrSteps on the same node
|
|
162
163
|
// (e.g. setNodeAttribute(pos, 'text', ...) + setNodeAttribute(pos, 'color', ...))
|
|
163
164
|
// should produce only one decoration, not one per step.
|
|
164
|
-
.filter((range, i, arr) => arr.findIndex(r => r.fromB === range.fromB) === i);
|
|
165
|
+
.filter((range, i, arr) => fg('confluence_ncs_step_diffing_version_history') ? !arr.some((r, nextIndex) => nextIndex > i && r.fromB === range.fromB) : arr.findIndex(r => r.fromB === range.fromB) === i);
|
|
165
166
|
};
|
|
166
167
|
|
|
167
168
|
/**
|
|
@@ -30,6 +30,10 @@ export const isMultiContainerBlockNode = nodeName => {
|
|
|
30
30
|
return ['decisionList', 'layoutSection'].includes(nodeName);
|
|
31
31
|
};
|
|
32
32
|
export const isTextLikeBlockNode = nodeName => {
|
|
33
|
+
// When cleaning up platform_editor_ai_show_diff_patch_2 just put paragraph into the list
|
|
34
|
+
if (nodeName === 'paragraph') {
|
|
35
|
+
return fg('platform_editor_ai_show_diff_patch_2');
|
|
36
|
+
}
|
|
33
37
|
return ['heading', 'bulletList', 'orderedList', 'listItem', 'taskList', 'blockquote'].includes(nodeName);
|
|
34
38
|
};
|
|
35
39
|
const getChangedContentStyleNext = (colorScheme, isActive = false, isInserted = false, hideAddedDiffsUnderline = false) => {
|
|
@@ -71,6 +71,12 @@ const isMultiContainerBlockNode = nodeName => {
|
|
|
71
71
|
return ['decisionList', 'layoutSection'].includes(nodeName);
|
|
72
72
|
};
|
|
73
73
|
const isTextLikeBlockNode = nodeName => {
|
|
74
|
+
// `paragraph` only reaches this classification via patch_2's `shouldRenderAsBlockNode` routing
|
|
75
|
+
// (createNodeChangedDecorationWidget.ts), which fully replaced paragraphs never hit otherwise —
|
|
76
|
+
// gating it here keeps that dependency explicit rather than implicit.
|
|
77
|
+
if (nodeName === 'paragraph') {
|
|
78
|
+
return fg('platform_editor_ai_show_diff_patch_2');
|
|
79
|
+
}
|
|
74
80
|
return ['heading', 'bulletList', 'orderedList', 'listItem', 'taskList', 'blockquote'].includes(nodeName);
|
|
75
81
|
};
|
|
76
82
|
|
|
@@ -6,6 +6,8 @@ import { contains } from '@atlaskit/editor-prosemirror/utils';
|
|
|
6
6
|
import { isExperimentEnabled } from '@atlaskit/platform-feature-experiments/is-experiment-enabled';
|
|
7
7
|
import { fg } from '@atlaskit/platform-feature-flags/fg';
|
|
8
8
|
import { createEditorProxy } from './createEditorProxy';
|
|
9
|
+
import { getAtomicInlineChangedAttrs } from './decorations/createInlineChangedDecoration';
|
|
10
|
+
import { isInlineAttrChangeNodeName } from './decorations/utils/getAttrChangeRanges';
|
|
9
11
|
import { wrapInMarkViews } from './markViews';
|
|
10
12
|
|
|
11
13
|
/**
|
|
@@ -38,6 +40,14 @@ export function isEditorViewWithNodeViews(view) {
|
|
|
38
40
|
/** Expand-family nodes whose expanded state lives in a node-keyed WeakMap. */
|
|
39
41
|
var EXPAND_TYPES = new Set(['expand', 'nestedExpand']);
|
|
40
42
|
|
|
43
|
+
/** Merges an inline style string onto an element's existing `style` attribute. */
|
|
44
|
+
var appendInlineStyle = function appendInlineStyle(element, style) {
|
|
45
|
+
var _element$getAttribute;
|
|
46
|
+
var currentStyle = (_element$getAttribute = element.getAttribute('style')) !== null && _element$getAttribute !== void 0 ? _element$getAttribute : '';
|
|
47
|
+
var separator = currentStyle && style && !currentStyle.trimEnd().endsWith(';') ? '; ' : '';
|
|
48
|
+
element.setAttribute('style', "".concat(currentStyle).concat(separator).concat(style));
|
|
49
|
+
};
|
|
50
|
+
|
|
41
51
|
/** Tables are the only node views that read and write positions, so only they need a preview. */
|
|
42
52
|
var containsTable = function containsTable(node) {
|
|
43
53
|
var table = node.type.schema.nodes.table;
|
|
@@ -92,13 +102,28 @@ export var NodeViewSerializer = /*#__PURE__*/function () {
|
|
|
92
102
|
var _this = this;
|
|
93
103
|
var basePos = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : 0;
|
|
94
104
|
var editorProxy = arguments.length > 3 ? arguments[3] : undefined;
|
|
105
|
+
var isInserted = arguments.length > 4 ? arguments[4] : undefined;
|
|
106
|
+
var colorScheme = arguments.length > 5 ? arguments[5] : undefined;
|
|
95
107
|
// A node's first child sits one position inside it, and each subsequent
|
|
96
108
|
// child is offset by the previous child's `nodeSize`. Tracking this lets nested node
|
|
97
109
|
// views resolve to their real depth instead of every one of them seeing depth 0.
|
|
98
110
|
var childPos = basePos + 1;
|
|
99
111
|
children.forEach(function (child) {
|
|
100
|
-
var childNode = _this.tryCreateNodeViewInner(child, childPos, editorProxy) || _this.serializeNode(child);
|
|
112
|
+
var childNode = _this.tryCreateNodeViewInner(child, childPos, editorProxy, isInserted, colorScheme) || _this.serializeNode(child);
|
|
101
113
|
if (childNode) {
|
|
114
|
+
// Give inserted atomic children the same box-shadow
|
|
115
|
+
// highlight an in-place attribute change already gets (`getAtomicInlineChangedAttrs`).
|
|
116
|
+
if (isInserted && isExperimentEnabled('platform_editor_show_diff_deleted_nodeview_content') && isInlineAttrChangeNodeName(child.type.name) && childNode instanceof HTMLElement) {
|
|
117
|
+
var _childNode$getAttribu;
|
|
118
|
+
var _getAtomicInlineChang = getAtomicInlineChangedAttrs(child.type.name, colorScheme),
|
|
119
|
+
className = _getAtomicInlineChang.className,
|
|
120
|
+
styleSuffix = _getAtomicInlineChang.styleSuffix;
|
|
121
|
+
var existingClassName = (_childNode$getAttribu = childNode.getAttribute('class')) !== null && _childNode$getAttribu !== void 0 ? _childNode$getAttribu : '';
|
|
122
|
+
childNode.setAttribute('class', existingClassName ? "".concat(existingClassName, " ").concat(className) : className);
|
|
123
|
+
if (styleSuffix) {
|
|
124
|
+
appendInlineStyle(childNode, styleSuffix);
|
|
125
|
+
}
|
|
126
|
+
}
|
|
102
127
|
contentDOM === null || contentDOM === void 0 || contentDOM.append(childNode);
|
|
103
128
|
}
|
|
104
129
|
childPos += child.nodeSize;
|
|
@@ -119,13 +144,15 @@ export var NodeViewSerializer = /*#__PURE__*/function () {
|
|
|
119
144
|
var _preview$rootPos;
|
|
120
145
|
var basePos = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 0;
|
|
121
146
|
var $sourcePos = arguments.length > 2 ? arguments[2] : undefined;
|
|
147
|
+
var isInserted = arguments.length > 3 ? arguments[3] : undefined;
|
|
148
|
+
var colorScheme = arguments.length > 4 ? arguments[4] : undefined;
|
|
122
149
|
if (!this.editorView) {
|
|
123
150
|
return null;
|
|
124
151
|
}
|
|
125
152
|
// The preview becomes the document every nested node view resolves its position against,
|
|
126
153
|
// so positions stay internally consistent.
|
|
127
154
|
var preview = fg('platform_editor_ai_show_diff_patch_2') && containsTable(targetNode) ? createEditorProxy(this.editorView, targetNode, $sourcePos) : null;
|
|
128
|
-
return this.tryCreateNodeViewInner(targetNode, (_preview$rootPos = preview === null || preview === void 0 ? void 0 : preview.rootPos) !== null && _preview$rootPos !== void 0 ? _preview$rootPos : basePos, preview === null || preview === void 0 ? void 0 : preview.editorProxy);
|
|
155
|
+
return this.tryCreateNodeViewInner(targetNode, (_preview$rootPos = preview === null || preview === void 0 ? void 0 : preview.rootPos) !== null && _preview$rootPos !== void 0 ? _preview$rootPos : basePos, preview === null || preview === void 0 ? void 0 : preview.editorProxy, isInserted, colorScheme);
|
|
129
156
|
}
|
|
130
157
|
}, {
|
|
131
158
|
key: "tryCreateNodeViewInner",
|
|
@@ -133,6 +160,8 @@ export var NodeViewSerializer = /*#__PURE__*/function () {
|
|
|
133
160
|
var _this$nodeViews;
|
|
134
161
|
var basePos = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 0;
|
|
135
162
|
var editorProxy = arguments.length > 2 ? arguments[2] : undefined;
|
|
163
|
+
var isInserted = arguments.length > 3 ? arguments[3] : undefined;
|
|
164
|
+
var colorScheme = arguments.length > 4 ? arguments[4] : undefined;
|
|
136
165
|
if (!this.editorView) {
|
|
137
166
|
return null;
|
|
138
167
|
}
|
|
@@ -178,7 +207,7 @@ export var NodeViewSerializer = /*#__PURE__*/function () {
|
|
|
178
207
|
if (!isExperimentEnabled('platform_editor_show_diff_deleted_nodeview_content') && targetNode.type.name === 'paragraph' && targetNode.children.length === 1) {
|
|
179
208
|
return this.serializeFragment(targetNode.content);
|
|
180
209
|
}
|
|
181
|
-
this.appendChildNodes(targetNode.children, _contentDOM, basePos, editorProxy);
|
|
210
|
+
this.appendChildNodes(targetNode.children, _contentDOM, basePos, editorProxy, isInserted, colorScheme);
|
|
182
211
|
}
|
|
183
212
|
return this.withMarkViews(targetNode, _dom, editorProxy);
|
|
184
213
|
}
|
|
@@ -202,7 +231,7 @@ export var NodeViewSerializer = /*#__PURE__*/function () {
|
|
|
202
231
|
dom = _constructor.dom,
|
|
203
232
|
contentDOM = _constructor.contentDOM;
|
|
204
233
|
// Iteratively populate children
|
|
205
|
-
this.appendChildNodes(targetNode.children, contentDOM, basePos, editorProxy);
|
|
234
|
+
this.appendChildNodes(targetNode.children, contentDOM, basePos, editorProxy, isInserted, colorScheme);
|
|
206
235
|
return this.withMarkViews(targetNode, dom, editorProxy);
|
|
207
236
|
} catch (_unused) {
|
|
208
237
|
return null;
|
|
@@ -457,11 +457,14 @@ var calculateDiffDecorationsInner = function calculateDiffDecorationsInner(_ref8
|
|
|
457
457
|
};
|
|
458
458
|
}
|
|
459
459
|
}
|
|
460
|
-
// The attribute-aware encoder is
|
|
461
|
-
|
|
460
|
+
// The attribute-aware encoder is needed by the smart classifier and is gated with it.
|
|
461
|
+
// The NCS version-history path also uses it so the main diff calculation detects attribute-only
|
|
462
|
+
// node replacements, e.g. changing an emoji or mention without changing the node type.
|
|
463
|
+
var _selectTokenEncoder = selectTokenEncoder(diffType === 'smart' && fg('platform_editor_ai_smart_diff') || fg('confluence_ncs_step_diffing_version_history')),
|
|
462
464
|
tokenEncoder = _selectTokenEncoder.tokenEncoder,
|
|
463
465
|
shouldHideMarkOnlyDeletions = _selectTokenEncoder.shouldHideMarkOnlyDeletions;
|
|
464
466
|
var changes;
|
|
467
|
+
var contentChanges;
|
|
465
468
|
var attributedChanges = [];
|
|
466
469
|
var attributionColors;
|
|
467
470
|
if (attributedChangesetEnabled) {
|
|
@@ -475,6 +478,7 @@ var calculateDiffDecorationsInner = function calculateDiffDecorationsInner(_ref8
|
|
|
475
478
|
stepIndex: stepIndex
|
|
476
479
|
});
|
|
477
480
|
}));
|
|
481
|
+
contentChanges = changeset.changes;
|
|
478
482
|
attributedChanges = diffType === 'step' ? diffBySteps(originalDoc, simplifiedSteps, simplifiedStepAttributions) : _toConsumableArray(changeset.changes);
|
|
479
483
|
// The attributed pipeline gives no guarantee that its output ranges are disjoint — see
|
|
480
484
|
// `collapseOverlappingChanges`.
|
|
@@ -494,6 +498,7 @@ var calculateDiffDecorationsInner = function calculateDiffDecorationsInner(_ref8
|
|
|
494
498
|
}
|
|
495
499
|
} else {
|
|
496
500
|
var _changeset = ChangeSet.create(originalDoc, undefined, tokenEncoder).addSteps(steppedDoc, stepMaps, tr.doc);
|
|
501
|
+
contentChanges = _changeset.changes;
|
|
497
502
|
changes = getChanges({
|
|
498
503
|
changeset: _changeset,
|
|
499
504
|
originalDoc: originalDoc,
|
|
@@ -753,20 +758,52 @@ var calculateDiffDecorationsInner = function calculateDiffDecorationsInner(_ref8
|
|
|
753
758
|
changes.forEach(function (change) {
|
|
754
759
|
createDecorationsForChange(change);
|
|
755
760
|
});
|
|
756
|
-
//
|
|
757
|
-
//
|
|
758
|
-
//
|
|
759
|
-
|
|
760
|
-
getMarkChangeRanges(simplifiedSteps, simplifiedStepAttributions, fg('confluence_ncs_step_diffing_version_history') ? stepMaps : undefined).filter(function (_ref10) {
|
|
761
|
+
// Compare the content outside replacement ranges at matching positions in both documents.
|
|
762
|
+
// This preserves real formatting edits without highlighting an unchanged link mark reapplied
|
|
763
|
+
// while editing its display text.
|
|
764
|
+
var hasUnhighlightedMarkChanges = function hasUnhighlightedMarkChanges(_ref10) {
|
|
761
765
|
var fromB = _ref10.fromB,
|
|
762
766
|
toB = _ref10.toB;
|
|
763
|
-
|
|
764
|
-
|
|
767
|
+
var from = fromB;
|
|
768
|
+
var offset = 0;
|
|
769
|
+
var differs = function differs(to) {
|
|
770
|
+
return from < to && !originalDoc.slice(from + offset, to + offset).content.eq(tr.doc.slice(from, to).content);
|
|
771
|
+
};
|
|
772
|
+
var _iterator5 = _createForOfIteratorHelper(contentChanges),
|
|
773
|
+
_step5;
|
|
774
|
+
try {
|
|
775
|
+
for (_iterator5.s(); !(_step5 = _iterator5.n()).done;) {
|
|
776
|
+
var change = _step5.value;
|
|
777
|
+
if (change.fromB >= toB) {
|
|
778
|
+
break;
|
|
779
|
+
}
|
|
780
|
+
if (change.toB > from) {
|
|
781
|
+
if (differs(change.fromB)) {
|
|
782
|
+
return true;
|
|
783
|
+
}
|
|
784
|
+
from = change.toB;
|
|
785
|
+
}
|
|
786
|
+
offset = change.toA - change.toB;
|
|
787
|
+
}
|
|
788
|
+
} catch (err) {
|
|
789
|
+
_iterator5.e(err);
|
|
790
|
+
} finally {
|
|
791
|
+
_iterator5.f();
|
|
792
|
+
}
|
|
793
|
+
return differs(toB);
|
|
794
|
+
};
|
|
795
|
+
|
|
796
|
+
// The attribute-aware token encoder already reports mark-only changes as regular changes in the
|
|
797
|
+
// smart path and the NCS version-history path. For NCS version history, only skip the step-range
|
|
798
|
+
// highlight if the main diff covers it or the remaining content has unchanged formatting.
|
|
799
|
+
getMarkChangeRanges(simplifiedSteps, simplifiedStepAttributions, fg('confluence_ncs_step_diffing_version_history') ? stepMaps : undefined).filter(function (_ref11) {
|
|
765
800
|
var fromB = _ref11.fromB,
|
|
766
801
|
toB = _ref11.toB;
|
|
802
|
+
return !isHandledByTable(fromB, toB);
|
|
803
|
+
}).filter(function (range) {
|
|
767
804
|
return !fg('confluence_ncs_step_diffing_version_history') || !changes.some(function (change) {
|
|
768
|
-
return change.fromB
|
|
769
|
-
});
|
|
805
|
+
return change.fromB <= range.fromB && change.toB >= range.toB;
|
|
806
|
+
}) && hasUnhighlightedMarkChanges(range);
|
|
770
807
|
}).forEach(function (change) {
|
|
771
808
|
var _attributionColors$ge, _attributionColors;
|
|
772
809
|
var changeColorScheme = change.attributionKey ? (_attributionColors$ge = (_attributionColors = attributionColors) === null || _attributionColors === void 0 ? void 0 : _attributionColors.get(change.attributionKey)) !== null && _attributionColors$ge !== void 0 ? _attributionColors$ge : colorScheme : colorScheme;
|
|
@@ -790,8 +827,9 @@ var calculateDiffDecorationsInner = function calculateDiffDecorationsInner(_ref8
|
|
|
790
827
|
return !isHandledByTable(fromB, toB);
|
|
791
828
|
}).filter(function (_ref13) {
|
|
792
829
|
var fromB = _ref13.fromB,
|
|
793
|
-
toB = _ref13.toB
|
|
794
|
-
|
|
830
|
+
toB = _ref13.toB,
|
|
831
|
+
isInline = _ref13.isInline;
|
|
832
|
+
return !fg('confluence_ncs_step_diffing_version_history') || !isInline || !changes.some(function (change) {
|
|
795
833
|
return change.fromB === fromB && change.toB === toB;
|
|
796
834
|
});
|
|
797
835
|
}).forEach(function (change) {
|
|
@@ -15,7 +15,8 @@ var typeID = function typeID(type) {
|
|
|
15
15
|
*
|
|
16
16
|
* Deliberately narrower than `attrAwareTokenEncoder`: node-start tokens keep the library default
|
|
17
17
|
* and do not fold in diffable attributes, which `getAttrChangeRanges` already reports for every
|
|
18
|
-
* diff type.
|
|
18
|
+
* diff type. The NCS version-history path also uses `attrAwareTokenEncoder` so main diff calculation
|
|
19
|
+
* detects attribute-only node replacements.
|
|
19
20
|
*/
|
|
20
21
|
export var markAwareTokenEncoder = {
|
|
21
22
|
encodeCharacter: function encodeCharacter(char, marks) {
|
|
@@ -5,7 +5,7 @@ import { markAwareTokenEncoder } from './markAwareTokenEncoder';
|
|
|
5
5
|
* `computeDiffChanges` so the two cannot drift.
|
|
6
6
|
*
|
|
7
7
|
* @param useAttrAwareEncoder Caller is running the smart classifier, which needs node attrs folded
|
|
8
|
-
* into the token.
|
|
8
|
+
* into the token. Also used by the NCS version-history path to detect attribute-only node changes.
|
|
9
9
|
*/
|
|
10
10
|
export var selectTokenEncoder = function selectTokenEncoder(useAttrAwareEncoder) {
|
|
11
11
|
if (useAttrAwareEncoder) {
|
|
@@ -175,7 +175,7 @@ var createChangedRowDOM = function createChangedRowDOM(_ref3) {
|
|
|
175
175
|
var cellIndex = 0;
|
|
176
176
|
rowNode.content.forEach(function (cellNode) {
|
|
177
177
|
if (cellNode.type.name === 'tableCell' || cellNode.type.name === 'tableHeader') {
|
|
178
|
-
var nodeView = nodeViewSerializer.tryCreateNodeView(cellNode);
|
|
178
|
+
var nodeView = nodeViewSerializer.tryCreateNodeView(cellNode, 0, undefined, isInserted, colorScheme);
|
|
179
179
|
if (nodeView) {
|
|
180
180
|
if (nodeView instanceof HTMLElement) {
|
|
181
181
|
applyCellEdgeAttrs(nodeView, cellEdgeAttrs === null || cellEdgeAttrs === void 0 ? void 0 : cellEdgeAttrs[cellIndex]);
|
|
@@ -76,7 +76,7 @@ var createTableCellContentWidgets = function createTableCellContentWidgets(_ref)
|
|
|
76
76
|
// A cell contains block content (paragraphs); serialize the cell's content
|
|
77
77
|
// so every block inside the cell is rendered.
|
|
78
78
|
cellNode.content.forEach(function (blockNode) {
|
|
79
|
-
var nodeView = nodeViewSerializer.tryCreateNodeView(blockNode);
|
|
79
|
+
var nodeView = nodeViewSerializer.tryCreateNodeView(blockNode, 0, undefined, isInserted, colorScheme);
|
|
80
80
|
var wrapper = createContentWrapper(colorScheme, false, isInserted);
|
|
81
81
|
if (nodeView) {
|
|
82
82
|
wrapper.append(nodeView);
|
|
@@ -316,7 +316,7 @@ export var createNodeChangedDecorationWidget = function createNodeChangedDecorat
|
|
|
316
316
|
var handleMultipleChildNodes = function handleMultipleChildNodes(node) {
|
|
317
317
|
if (!shouldPreserveCompleteMultiInlineBlock && !shouldRenderAsBlockNode && node.content.childCount > 1 && node.type.inlineContent) {
|
|
318
318
|
node.content.forEach(function (childNode) {
|
|
319
|
-
var childNodeView = serializer.tryCreateNodeView(childNode);
|
|
319
|
+
var childNodeView = serializer.tryCreateNodeView(childNode, 0, undefined, isInserted, colorScheme);
|
|
320
320
|
if (childNodeView) {
|
|
321
321
|
var lineBreak = document.createElement('br');
|
|
322
322
|
dom.append(lineBreak);
|
|
@@ -372,7 +372,7 @@ export var createNodeChangedDecorationWidget = function createNodeChangedDecorat
|
|
|
372
372
|
|
|
373
373
|
// Try to create node view, fallback to serialization
|
|
374
374
|
var $sourcePos = fg('platform_editor_ai_show_diff_patch_2') ? getSourcePos(doc, $changeFromA, slice.openStart, index) : null;
|
|
375
|
-
var nodeView = serializer.tryCreateNodeView(node, 0, $sourcePos !== null && $sourcePos !== void 0 ? $sourcePos : undefined);
|
|
375
|
+
var nodeView = serializer.tryCreateNodeView(node, 0, $sourcePos !== null && $sourcePos !== void 0 ? $sourcePos : undefined, isInserted, colorScheme);
|
|
376
376
|
if (nodeView) {
|
|
377
377
|
if (node.isInline) {
|
|
378
378
|
var wrapper = createContentWrapper(colorScheme, isActive, isInserted, reveal);
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { fg } from '@atlaskit/platform-feature-flags/fg';
|
|
2
|
+
|
|
1
3
|
/**
|
|
2
4
|
* Single source of truth for which attributes of a node type represent a
|
|
3
5
|
* meaningful change. Shared by `attrAwareTokenEncoder` (changeset detection)
|
|
@@ -88,10 +90,13 @@ export var getRequiredIncludedDiffableAttrs = function getRequiredIncludedDiffab
|
|
|
88
90
|
* order is not stable across the two diffed documents.
|
|
89
91
|
*/
|
|
90
92
|
export var getDiffableAttrNames = function getDiffableAttrNames(nodeTypeName, attrs) {
|
|
93
|
+
// Adding a column updates existing widths, ignore this attribute change. Move this into
|
|
94
|
+
// DIFFABLE_ATTRS_BY_NODE_TYPE when cleaning up confluence_ncs_step_diffing_version_history
|
|
95
|
+
var ignoreColumnWidth = nodeTypeName === 'layoutColumn' && fg('confluence_ncs_step_diffing_version_history');
|
|
91
96
|
var rule = DIFFABLE_ATTRS_BY_NODE_TYPE[nodeTypeName];
|
|
92
97
|
if (!rule) {
|
|
93
98
|
return Object.keys(attrs).filter(function (name) {
|
|
94
|
-
return !DEFAULT_EXCLUDED_ATTRS.includes(name);
|
|
99
|
+
return !DEFAULT_EXCLUDED_ATTRS.includes(name) && !(ignoreColumnWidth && name === 'width');
|
|
95
100
|
}).sort();
|
|
96
101
|
}
|
|
97
102
|
if ('include' in rule) {
|
|
@@ -83,6 +83,11 @@ export var getAttrChangeRanges = function getAttrChangeRanges(doc, attrStepConte
|
|
|
83
83
|
var $pos = doc.resolve(finalPos);
|
|
84
84
|
var nodeAtPos = doc.nodeAt(finalPos);
|
|
85
85
|
var originalNodeAtPos = originalPos === undefined ? null : originalDoc.nodeAt(originalPos);
|
|
86
|
+
var hasChangedAttr = function hasChangedAttr(watchedAttrs) {
|
|
87
|
+
return attrsToCheck.some(function (attr) {
|
|
88
|
+
return watchedAttrs.includes(attr) && (!fg('confluence_ncs_step_diffing_version_history') || !originalNodeAtPos || originalNodeAtPos.type !== (nodeAtPos === null || nodeAtPos === void 0 ? void 0 : nodeAtPos.type) || !isEqual(originalNodeAtPos.attrs[attr], nodeAtPos === null || nodeAtPos === void 0 ? void 0 : nodeAtPos.attrs[attr]));
|
|
89
|
+
});
|
|
90
|
+
};
|
|
86
91
|
|
|
87
92
|
// The changeset path (createDecorationsForChange) handles the deletion widget via
|
|
88
93
|
// prosemirror-changeset; we only need to add the inline insertion highlight here.
|
|
@@ -90,9 +95,7 @@ export var getAttrChangeRanges = function getAttrChangeRanges(doc, attrStepConte
|
|
|
90
95
|
var nodeName = nodeAtPos.type.name;
|
|
91
96
|
if (isInlineAttrChangeNodeName(nodeName)) {
|
|
92
97
|
var watchedAttrs = inlineNodeAttrMap[nodeName];
|
|
93
|
-
if (
|
|
94
|
-
return watchedAttrs.includes(v);
|
|
95
|
-
})) {
|
|
98
|
+
if (hasChangedAttr(watchedAttrs)) {
|
|
96
99
|
return _objectSpread(_objectSpread({}, attributionKey ? {
|
|
97
100
|
attributionKey: attributionKey
|
|
98
101
|
} : {}), {}, {
|
|
@@ -124,9 +127,7 @@ export var getAttrChangeRanges = function getAttrChangeRanges(doc, attrStepConte
|
|
|
124
127
|
// (e.g. note -> warning) — highlight the new panel and, when the original node
|
|
125
128
|
// is resolvable, expose its range (fromA/toA) so the caller can render the old
|
|
126
129
|
// panel as a "deleted" widget for a before/after comparison.
|
|
127
|
-
if (
|
|
128
|
-
return panelAttrs.includes(v);
|
|
129
|
-
}) && nodeAtPos && getBaseNodeTypeName(nodeAtPos.type) === 'panel') {
|
|
130
|
+
if (hasChangedAttr(panelAttrs) && nodeAtPos && getBaseNodeTypeName(nodeAtPos.type) === 'panel') {
|
|
130
131
|
return _objectSpread(_objectSpread({}, attributionKey ? {
|
|
131
132
|
attributionKey: attributionKey
|
|
132
133
|
} : {}), {}, {
|
|
@@ -174,7 +175,9 @@ export var getAttrChangeRanges = function getAttrChangeRanges(doc, attrStepConte
|
|
|
174
175
|
// (e.g. setNodeAttribute(pos, 'text', ...) + setNodeAttribute(pos, 'color', ...))
|
|
175
176
|
// should produce only one decoration, not one per step.
|
|
176
177
|
.filter(function (range, i, arr) {
|
|
177
|
-
return arr.
|
|
178
|
+
return fg('confluence_ncs_step_diffing_version_history') ? !arr.some(function (r, nextIndex) {
|
|
179
|
+
return nextIndex > i && r.fromB === range.fromB;
|
|
180
|
+
}) : arr.findIndex(function (r) {
|
|
178
181
|
return r.fromB === range.fromB;
|
|
179
182
|
}) === i;
|
|
180
183
|
});
|
|
@@ -35,6 +35,10 @@ export var isMultiContainerBlockNode = function isMultiContainerBlockNode(nodeNa
|
|
|
35
35
|
return ['decisionList', 'layoutSection'].includes(nodeName);
|
|
36
36
|
};
|
|
37
37
|
export var isTextLikeBlockNode = function isTextLikeBlockNode(nodeName) {
|
|
38
|
+
// When cleaning up platform_editor_ai_show_diff_patch_2 just put paragraph into the list
|
|
39
|
+
if (nodeName === 'paragraph') {
|
|
40
|
+
return fg('platform_editor_ai_show_diff_patch_2');
|
|
41
|
+
}
|
|
38
42
|
return ['heading', 'bulletList', 'orderedList', 'listItem', 'taskList', 'blockquote'].includes(nodeName);
|
|
39
43
|
};
|
|
40
44
|
var getChangedContentStyleNext = function getChangedContentStyleNext(colorScheme) {
|
|
@@ -71,6 +71,12 @@ var isMultiContainerBlockNode = function isMultiContainerBlockNode(nodeName) {
|
|
|
71
71
|
return ['decisionList', 'layoutSection'].includes(nodeName);
|
|
72
72
|
};
|
|
73
73
|
var isTextLikeBlockNode = function isTextLikeBlockNode(nodeName) {
|
|
74
|
+
// `paragraph` only reaches this classification via patch_2's `shouldRenderAsBlockNode` routing
|
|
75
|
+
// (createNodeChangedDecorationWidget.ts), which fully replaced paragraphs never hit otherwise —
|
|
76
|
+
// gating it here keeps that dependency explicit rather than implicit.
|
|
77
|
+
if (nodeName === 'paragraph') {
|
|
78
|
+
return fg('platform_editor_ai_show_diff_patch_2');
|
|
79
|
+
}
|
|
74
80
|
return ['heading', 'bulletList', 'orderedList', 'listItem', 'taskList', 'blockquote'].includes(nodeName);
|
|
75
81
|
};
|
|
76
82
|
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import type { NodeViewConstructor } from '@atlaskit/editor-common/lazy-node-view';
|
|
2
2
|
import type { Node as PMNode, Fragment, ResolvedPos } from '@atlaskit/editor-prosemirror/model';
|
|
3
3
|
import type { EditorView } from '@atlaskit/editor-prosemirror/view';
|
|
4
|
+
import type { ColorScheme } from './decorations/colorSchemes/types';
|
|
4
5
|
/**
|
|
5
6
|
* Utilities for working with ProseMirror node views and DOM serialization within the
|
|
6
7
|
* Show Diff editor plugin.
|
|
@@ -61,7 +62,7 @@ export declare class NodeViewSerializer {
|
|
|
61
62
|
*
|
|
62
63
|
* `$sourcePos` is where the node sits in the doc it came from, so nested tables keep their depth.
|
|
63
64
|
*/
|
|
64
|
-
tryCreateNodeView(targetNode: PMNode, basePos?: number, $sourcePos?: ResolvedPos): Node | null;
|
|
65
|
+
tryCreateNodeView(targetNode: PMNode, basePos?: number, $sourcePos?: ResolvedPos, isInserted?: boolean, colorScheme?: ColorScheme): Node | null;
|
|
65
66
|
private tryCreateNodeViewInner;
|
|
66
67
|
/** Wraps rendered DOM in its marks, rendered against the editor proxy when there is one. */
|
|
67
68
|
private withMarkViews;
|
|
@@ -4,6 +4,7 @@ import type { TokenEncoder } from 'prosemirror-changeset';
|
|
|
4
4
|
*
|
|
5
5
|
* Deliberately narrower than `attrAwareTokenEncoder`: node-start tokens keep the library default
|
|
6
6
|
* and do not fold in diffable attributes, which `getAttrChangeRanges` already reports for every
|
|
7
|
-
* diff type.
|
|
7
|
+
* diff type. The NCS version-history path also uses `attrAwareTokenEncoder` so main diff calculation
|
|
8
|
+
* detects attribute-only node replacements.
|
|
8
9
|
*/
|
|
9
10
|
export declare const markAwareTokenEncoder: TokenEncoder<string | number>;
|
|
@@ -10,6 +10,6 @@ export type TokenEncoderSelection = {
|
|
|
10
10
|
* `computeDiffChanges` so the two cannot drift.
|
|
11
11
|
*
|
|
12
12
|
* @param useAttrAwareEncoder Caller is running the smart classifier, which needs node attrs folded
|
|
13
|
-
* into the token.
|
|
13
|
+
* into the token. Also used by the NCS version-history path to detect attribute-only node changes.
|
|
14
14
|
*/
|
|
15
15
|
export declare const selectTokenEncoder: (useAttrAwareEncoder: boolean) => TokenEncoderSelection;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@atlaskit/editor-plugin-show-diff",
|
|
3
|
-
"version": "21.0.
|
|
3
|
+
"version": "21.0.3",
|
|
4
4
|
"description": "ShowDiff plugin for @atlaskit/editor-core",
|
|
5
5
|
"author": "Atlassian Pty Ltd",
|
|
6
6
|
"license": "Apache-2.0",
|
|
@@ -39,7 +39,7 @@
|
|
|
39
39
|
"@atlaskit/platform-feature-experiments": "^0.3.0",
|
|
40
40
|
"@atlaskit/platform-feature-flags": "^2.2.0",
|
|
41
41
|
"@atlaskit/primitives": "^22.5.0",
|
|
42
|
-
"@atlaskit/tmp-editor-statsig": "^
|
|
42
|
+
"@atlaskit/tmp-editor-statsig": "^220.0.0",
|
|
43
43
|
"@atlaskit/tokens": "^20.1.0",
|
|
44
44
|
"@babel/runtime": "^7.0.0",
|
|
45
45
|
"@compiled/react": "^1.0.2",
|
|
@@ -71,7 +71,7 @@
|
|
|
71
71
|
"react-intl": "^7.0.0"
|
|
72
72
|
},
|
|
73
73
|
"peerDependencies": {
|
|
74
|
-
"@atlaskit/editor-common": "^127.
|
|
74
|
+
"@atlaskit/editor-common": "^127.2.0",
|
|
75
75
|
"react": "^18.2.0 || ^19.2.0",
|
|
76
76
|
"react-dom": "^18.2.0 || ^19.2.0",
|
|
77
77
|
"react-intl": "^5.25.1 || ^6.0.0 || ^7.0.0"
|