@atlaskit/editor-plugin-show-diff 20.0.11 → 21.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (24) hide show
  1. package/CHANGELOG.md +17 -0
  2. package/dist/cjs/pm-plugins/NodeViewSerializer.js +71 -7
  3. package/dist/cjs/pm-plugins/createEditorProxy.js +84 -0
  4. package/dist/cjs/pm-plugins/decorations/createContributorTagWidget.js +25 -6
  5. package/dist/cjs/pm-plugins/decorations/createNodeChangedDecorationWidget.js +35 -11
  6. package/dist/cjs/pm-plugins/decorations/utils/wrapBlockNodeView.js +38 -2
  7. package/dist/cjs/pm-plugins/markViews.js +65 -0
  8. package/dist/es2019/pm-plugins/NodeViewSerializer.js +64 -8
  9. package/dist/es2019/pm-plugins/createEditorProxy.js +76 -0
  10. package/dist/es2019/pm-plugins/decorations/createContributorTagWidget.js +26 -8
  11. package/dist/es2019/pm-plugins/decorations/createNodeChangedDecorationWidget.js +35 -12
  12. package/dist/es2019/pm-plugins/decorations/utils/wrapBlockNodeView.js +36 -2
  13. package/dist/es2019/pm-plugins/markViews.js +61 -0
  14. package/dist/esm/pm-plugins/NodeViewSerializer.js +71 -7
  15. package/dist/esm/pm-plugins/createEditorProxy.js +78 -0
  16. package/dist/esm/pm-plugins/decorations/createContributorTagWidget.js +27 -8
  17. package/dist/esm/pm-plugins/decorations/createNodeChangedDecorationWidget.js +37 -12
  18. package/dist/esm/pm-plugins/decorations/utils/wrapBlockNodeView.js +38 -2
  19. package/dist/esm/pm-plugins/markViews.js +60 -0
  20. package/dist/types/pm-plugins/NodeViewSerializer.d.ts +7 -2
  21. package/dist/types/pm-plugins/createEditorProxy.d.ts +18 -0
  22. package/dist/types/pm-plugins/decorations/createContributorTagWidget.d.ts +2 -1
  23. package/dist/types/pm-plugins/markViews.d.ts +14 -0
  24. package/package.json +10 -10
@@ -5,7 +5,7 @@ import { Decoration } from '@atlaskit/editor-prosemirror/view';
5
5
  import { fg } from '@atlaskit/platform-feature-flags/fg';
6
6
  import { countEmptyTextBlockOnlySlice } from '../utils/emptyTextBlocks';
7
7
  import { isEmptyParagraphSlice } from '../utils/isEmptyParagraphSlice';
8
- import { clampAnchorPosIntoCell, createLeftAnchorWidget } from './createAnchorDecorationWidgets';
8
+ import { clampAnchorPosIntoCell, createAnchorNameSpan, createLeftAnchorWidget } from './createAnchorDecorationWidgets';
9
9
  import { createChangedRowDecorationWidgets } from './createChangedRowDecorationWidgets';
10
10
  import { createContributorTagHost, resolveHoistedCodeBlockAnchor, unmountContributorTag } from './createContributorTagWidget';
11
11
  import { AnchorTypeKey, buildContributorTagDecorationSpec, buildDiffDecorationSpec, buildAnchorDecorationKey, scrollMarginTopValue } from './decorationKeys';
@@ -17,6 +17,22 @@ import { findSafeInsertPos } from './utils/findSafeInsertPos';
17
17
  import { safeResolve } from './utils/safeResolve';
18
18
  import { getTableDiffMode } from './utils/tableDiffMode';
19
19
  import { wrapBlockNodeView, createRemovedLozenge, injectInnerWrapper, createContentWrapper } from './utils/wrapBlockNodeView';
20
+
21
+ /**
22
+ * Where a slice's `index`th child sits in `doc`: the children belong to the ancestor `openStart`
23
+ * levels above the change. `null` if out of range — `posAtIndex` throws past the last child.
24
+ */
25
+ var getSourcePos = function getSourcePos(doc, $changeFrom, openStart, index) {
26
+ var depth = $changeFrom ? $changeFrom.depth - openStart : -1;
27
+ if (!$changeFrom || depth < 0) {
28
+ return null;
29
+ }
30
+ var childIndex = $changeFrom.index(depth) + index;
31
+ if (childIndex >= $changeFrom.node(depth).childCount) {
32
+ return null;
33
+ }
34
+ return safeResolve(doc, $changeFrom.posAtIndex(childIndex, depth));
35
+ };
20
36
  var isHeadingLevel = function isHeadingLevel(level) {
21
37
  return typeof level === 'number' && level >= 1 && level <= 6;
22
38
  };
@@ -284,7 +300,7 @@ export var createNodeChangedDecorationWidget = function createNodeChangedDecorat
284
300
  * and if it's the first or last content, we go in however many the sliced Open
285
301
  * or sliced End depth is and match only the entire node.
286
302
  */
287
- slice.content.forEach(function (node) {
303
+ slice.content.forEach(function (node, _offset, index) {
288
304
  var isFirst = firstReplacedNode === node;
289
305
  var isLast = lastReplacedNode === node;
290
306
  var isOpenAtSliceBoundary = isFirst && slice.openStart > 0 || isLast && slice.openEnd > 0;
@@ -355,7 +371,8 @@ export var createNodeChangedDecorationWidget = function createNodeChangedDecorat
355
371
  }
356
372
 
357
373
  // Try to create node view, fallback to serialization
358
- var nodeView = serializer.tryCreateNodeView(node);
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);
359
376
  if (nodeView) {
360
377
  if (node.isInline) {
361
378
  var wrapper = createContentWrapper(colorScheme, isActive, isInserted, reveal);
@@ -458,18 +475,27 @@ export var createNodeChangedDecorationWidget = function createNodeChangedDecorat
458
475
  });
459
476
  }
460
477
 
461
- // Needed even when the indicator bar is off, because a contributor tag also anchors against the
462
- // widget.
478
+ // Needed even with the indicator bar off, since it anchors its top/bottom to the widget.
463
479
  if (showIndicators || showContributorTags) {
464
480
  dom.style.setProperty('anchor-name', "--".concat(buildAnchorDecorationKey({
465
481
  diffId: diffId
466
482
  })));
467
- // The tag built below only anchors via `anchor()` if `contributorTagAnchorName` is set; without
468
- // it the tag falls back to a non-anchored position (EDITOR-9045).
469
- if (fg('confluence_ncs_step_diffing_version_history')) {
470
- contributorTagAnchorName !== null && contributorTagAnchorName !== void 0 ? contributorTagAnchorName : contributorTagAnchorName = buildAnchorDecorationKey({
471
- diffId: diffId
483
+ // `dom` is inline, so its box fragments across a line wrap and `anchor()` resolves to the
484
+ // wrong edge. The tag anchors to its own point marker instead (EDITOR-9286).
485
+ if (fg('confluence_ncs_step_diffing_version_history') && !contributorTagAnchorName) {
486
+ var tagPointAnchorName = buildAnchorDecorationKey({
487
+ diffId: diffId,
488
+ anchorType: AnchorTypeKey.tag
472
489
  });
490
+ contributorTagAnchorName = tagPointAnchorName;
491
+ decorations.push(Decoration.widget(safeInsertPos, function () {
492
+ return createAnchorNameSpan(tagPointAnchorName);
493
+ }, {
494
+ key: "contributor-tag-anchor-".concat(diffId),
495
+ side: -1,
496
+ marks: [],
497
+ ignoreSelection: true
498
+ }));
473
499
  }
474
500
  }
475
501
 
@@ -504,8 +530,7 @@ export var createNodeChangedDecorationWidget = function createNodeChangedDecorat
504
530
  }
505
531
  })));
506
532
  } else {
507
- // Hosted at the start of the deleted run rather than on `dom` itself: `dom` is inline, so once
508
- // the deleted content wraps its box is the union of its line fragments.
533
+ // Anchored to the point marker pushed above, not to `dom` (EDITOR-9286).
509
534
  var tagHost = createContributorTagHost({
510
535
  anchorName: contributorTagAnchorName,
511
536
  diffId: diffId,
@@ -314,6 +314,21 @@ var applyTextLikeBlockNodeStyles = function applyTextLikeBlockNodeStyles(_ref8)
314
314
  /**
315
315
  * Creates a content wrapper with deleted styles for a block node
316
316
  */
317
+ /**
318
+ * The element that spans breakout width, for nodes carrying a `breakout` mark. The wrapper divs
319
+ * stay at editor-column width, so anything that should align to the node's visible edges —
320
+ * outlines, absolutely positioned lozenges — has to attach here instead.
321
+ */
322
+ var findBreakoutElement = function findBreakoutElement(targetNode, nodeView) {
323
+ var _targetNode$marks;
324
+ var hasBreakoutMark = (_targetNode$marks = targetNode.marks) === null || _targetNode$marks === void 0 ? void 0 : _targetNode$marks.some(function (mark) {
325
+ return mark.type.name === 'breakout';
326
+ });
327
+ if (!hasBreakoutMark || !(nodeView instanceof HTMLElement)) {
328
+ return null;
329
+ }
330
+ return nodeView.matches('[data-node-type]') ? nodeView : nodeView.querySelector('[data-node-type]');
331
+ };
317
332
  var createBlockNodeContentWrapper = function createBlockNodeContentWrapper(_ref9) {
318
333
  var nodeView = _ref9.nodeView,
319
334
  targetNode = _ref9.targetNode,
@@ -331,7 +346,16 @@ var createBlockNodeContentWrapper = function createBlockNodeContentWrapper(_ref9
331
346
  // should not also get the inline content style (borderBottom underline) on their container.
332
347
  var shouldSkipContentStyle = isInserted && nodeStyle !== undefined;
333
348
  var contentStyle = shouldSkipContentStyle ? '' : getChangedContentStyle(colorScheme, isActive, isInserted, hideAddedDiffsUnderline);
334
- contentWrapper.setAttribute('style', "".concat(contentStyle).concat(nodeStyle || ''));
349
+
350
+ // A `breakout` node renders wider than the editor column while the wrapper divs
351
+ // stay at column width, so paint the outline on the node element that spans that width.
352
+ var breakoutTarget = nodeStyle ? findBreakoutElement(targetNode, nodeView) : null;
353
+ if (fg('platform_editor_ai_show_diff_patch_2') && breakoutTarget) {
354
+ appendStyleToElement(breakoutTarget, nodeStyle !== null && nodeStyle !== void 0 ? nodeStyle : '');
355
+ contentWrapper.setAttribute('style', contentStyle || '');
356
+ } else {
357
+ contentWrapper.setAttribute('style', "".concat(contentStyle).concat(nodeStyle || ''));
358
+ }
335
359
  contentWrapper.append(nodeView);
336
360
  return contentWrapper;
337
361
  };
@@ -461,7 +485,19 @@ var wrapBlockNode = function wrapBlockNode(_ref10) {
461
485
  })) {
462
486
  return;
463
487
  }
464
- blockWrapper.append(lozenge);
488
+
489
+ // `lozenge` is absolutely positioned, so it lands on the nearest positioned ancestor. For a
490
+ // breakout node that is the column-width wrapper, leaving the pill short of the node's
491
+ // right edge — host it on the breakout element, which must establish a containing block.
492
+ var breakoutHost = fg('platform_editor_ai_show_diff_patch_2') ? findBreakoutElement(targetNode, nodeView) : null;
493
+ if (breakoutHost) {
494
+ appendStyleToElement(breakoutHost, convertToInlineCss({
495
+ position: 'relative'
496
+ }));
497
+ breakoutHost.append(lozenge);
498
+ } else {
499
+ blockWrapper.append(lozenge);
500
+ }
465
501
  }
466
502
  var contentWrapper = createBlockNodeContentWrapper({
467
503
  nodeView: nodeView,
@@ -0,0 +1,60 @@
1
+ import { DOMSerializer } from '@atlaskit/editor-prosemirror/model';
2
+ import { fg } from '@atlaskit/platform-feature-flags/fg';
3
+
4
+ /**
5
+ * `@atlaskit/editor-prosemirror/view` doesn't re-export prosemirror-view's `MarkViewConstructor`,
6
+ * so it's typed locally, as `EditorSSRRenderer` does.
7
+ */
8
+
9
+ /** Where to look up a mark's renderer, and the view to render it against. */
10
+
11
+ /**
12
+ * Renders a mark as ProseMirror does: `view.nodeViews` also holds mark views, keyed by mark name,
13
+ * so `breakout` resolves whether it's registered under `markViews` or `nodeViews`. Falls back to
14
+ * `toDOM` — they can differ: `breakout` renders `.fabric-editor-breakout-mark-dom`, which the width
15
+ * CSS targets.
16
+ */
17
+ var renderMarkView = function renderMarkView(mark, inline, _ref) {
18
+ var nodeViews = _ref.nodeViews,
19
+ view = _ref.view;
20
+ try {
21
+ var markView = nodeViews === null || nodeViews === void 0 ? void 0 : nodeViews[mark.type.name];
22
+ if (markView && view) {
23
+ var rendered = markView(mark, view, inline);
24
+ return {
25
+ dom: rendered.dom,
26
+ contentDOM: rendered.contentDOM
27
+ };
28
+ }
29
+ var toDOM = mark.type.spec.toDOM;
30
+ if (!toDOM) {
31
+ return null;
32
+ }
33
+ return DOMSerializer.renderSpec(document, toDOM(mark, inline));
34
+ } catch (_unused) {
35
+ return null;
36
+ }
37
+ };
38
+
39
+ /**
40
+ * Wraps rendered DOM in its marks, as ProseMirror does for the live document. Without this a
41
+ * block node loses `breakout`, whose wrapper supplies the width. Marks nest
42
+ * outside-in, so `marks[0]` is outermost.
43
+ */
44
+ export var wrapInMarkViews = function wrapInMarkViews(targetNode, nodeDom, sources) {
45
+ if (targetNode.marks.length === 0 || !fg('platform_editor_ai_show_diff_patch_2')) {
46
+ return nodeDom;
47
+ }
48
+ var wrapped = nodeDom;
49
+ for (var index = targetNode.marks.length - 1; index >= 0; index--) {
50
+ var _mark = targetNode.marks[index];
51
+ var rendered = renderMarkView(_mark, targetNode.isInline, sources);
52
+ // No content hole means nowhere to nest the node, so skip the mark, not the node.
53
+ if (!(rendered !== null && rendered !== void 0 && rendered.contentDOM)) {
54
+ continue;
55
+ }
56
+ rendered.contentDOM.appendChild(wrapped);
57
+ wrapped = rendered.dom;
58
+ }
59
+ return wrapped;
60
+ };
@@ -1,5 +1,5 @@
1
1
  import type { NodeViewConstructor } from '@atlaskit/editor-common/lazy-node-view';
2
- import type { Node as PMNode, Fragment } from '@atlaskit/editor-prosemirror/model';
2
+ import type { Node as PMNode, Fragment, ResolvedPos } from '@atlaskit/editor-prosemirror/model';
3
3
  import type { EditorView } from '@atlaskit/editor-prosemirror/view';
4
4
  /**
5
5
  * Utilities for working with ProseMirror node views and DOM serialization within the
@@ -58,8 +58,13 @@ export declare class NodeViewSerializer {
58
58
  *
59
59
  * Returns `null` when there is no `EditorView`, no constructor for the node type,
60
60
  * or the node type is blocklisted. Otherwise returns the constructed node view instance.
61
+ *
62
+ * `$sourcePos` is where the node sits in the doc it came from, so nested tables keep their depth.
61
63
  */
62
- tryCreateNodeView(targetNode: PMNode): Node | null;
64
+ tryCreateNodeView(targetNode: PMNode, basePos?: number, $sourcePos?: ResolvedPos): Node | null;
65
+ private tryCreateNodeViewInner;
66
+ /** Wraps rendered DOM in its marks, rendered against the editor proxy when there is one. */
67
+ private withMarkViews;
63
68
  /**
64
69
  * Serializes a node to a DOM `Node` using the schema's `DOMSerializer`.
65
70
  */
@@ -0,0 +1,18 @@
1
+ import type { Node as PMNode, ResolvedPos } from '@atlaskit/editor-prosemirror/model';
2
+ import type { EditorView } from '@atlaskit/editor-prosemirror/view';
3
+ /**
4
+ * A preview is rendered by the live view's own node views, which read `getPos()` against
5
+ * `view.state.doc` for both layout (`isTableNested`) and writes (`scaleTable` builds attribute
6
+ * steps at `getPos() + 1`). Handing them live positions makes the layout correct but points the
7
+ * writes at unrelated live nodes, so give them a view whose document *is* the preview instead.
8
+ *
9
+ * With `$sourcePos` (the node's position in the doc it came from) the node is swapped into a copy
10
+ * of its real top-level ancestor, so a table inside an expand still resolves as nested.
11
+ *
12
+ * Returns `null` when the node cannot be a top-level child (e.g. a bare `tableCell`), in which
13
+ * case the caller falls back to the live view and the previous stubbed positions.
14
+ */
15
+ export declare function createEditorProxy(liveView: EditorView, rootNode: PMNode, $sourcePos?: ResolvedPos): {
16
+ editorProxy: EditorView;
17
+ rootPos: number;
18
+ } | null;
@@ -68,7 +68,8 @@ export declare const resolveHoistedCodeBlockAnchor: (doc: PMNode, pos: number, d
68
68
  * highlights — or, with `anchorAtRangeStart`, at `from` itself. A host inside a code block is
69
69
  * hoisted in front of it instead, anchored via `resolveHoistedCodeBlockAnchor` (EDITOR-9045).
70
70
  *
71
- * Returns one decoration normally, two (marker + tag) when hoisted out of a code block.
71
+ * Returns one decoration when a caller-supplied `anchorName` is used, two (marker + tag)
72
+ * otherwise — hoisted out of a code block, or anchored to its own point marker.
72
73
  *
73
74
  * The tag is mounted in `toDOM` and taken down in `destroy`, so it lives exactly as long as the host
74
75
  * element ProseMirror drew it into — see `mountContributorTag`.
@@ -0,0 +1,14 @@
1
+ import type { NodeViewConstructor } from '@atlaskit/editor-common/lazy-node-view';
2
+ import type { Node as PMNode } from '@atlaskit/editor-prosemirror/model';
3
+ import type { EditorView } from '@atlaskit/editor-prosemirror/view';
4
+ /** Where to look up a mark's renderer, and the view to render it against. */
5
+ export type MarkViewSources = {
6
+ nodeViews?: Record<string, NodeViewConstructor>;
7
+ view?: EditorView;
8
+ };
9
+ /**
10
+ * Wraps rendered DOM in its marks, as ProseMirror does for the live document. Without this a
11
+ * block node loses `breakout`, whose wrapper supplies the width. Marks nest
12
+ * outside-in, so `marks[0]` is outermost.
13
+ */
14
+ export declare const wrapInMarkViews: (targetNode: PMNode, nodeDom: Node, sources: MarkViewSources) => Node;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@atlaskit/editor-plugin-show-diff",
3
- "version": "20.0.11",
3
+ "version": "21.0.0",
4
4
  "description": "ShowDiff plugin for @atlaskit/editor-core",
5
5
  "author": "Atlassian Pty Ltd",
6
6
  "license": "Apache-2.0",
@@ -29,17 +29,17 @@
29
29
  "@atlaskit/agent-color": "^1.0.0",
30
30
  "@atlaskit/avatar": "^30.0.0",
31
31
  "@atlaskit/custom-steps": "^1.1.0",
32
- "@atlaskit/editor-plugin-accessibility-utils": "^22.0.0",
33
- "@atlaskit/editor-plugin-analytics": "^22.0.0",
34
- "@atlaskit/editor-plugin-limited-mode": "^19.0.0",
35
- "@atlaskit/editor-plugin-user-intent": "^20.0.0",
32
+ "@atlaskit/editor-plugin-accessibility-utils": "^23.0.0",
33
+ "@atlaskit/editor-plugin-analytics": "^23.0.0",
34
+ "@atlaskit/editor-plugin-limited-mode": "^20.0.0",
35
+ "@atlaskit/editor-plugin-user-intent": "^21.0.0",
36
36
  "@atlaskit/editor-prosemirror": "^9.0.0",
37
37
  "@atlaskit/editor-shared-styles": "^4.3.0",
38
38
  "@atlaskit/editor-tables": "^3.2.0",
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": "^218.0.0",
42
+ "@atlaskit/tmp-editor-statsig": "^219.0.0",
43
43
  "@atlaskit/tokens": "^20.1.0",
44
44
  "@babel/runtime": "^7.0.0",
45
45
  "@compiled/react": "^1.0.2",
@@ -53,7 +53,7 @@
53
53
  "@atlaskit/button": "^25.4.0",
54
54
  "@atlaskit/css": "^1.1.0",
55
55
  "@atlaskit/dropdown-menu": "^20.0.0",
56
- "@atlaskit/editor-core": "^232.0.0",
56
+ "@atlaskit/editor-core": "^233.0.0",
57
57
  "@atlaskit/editor-json-transformer": "^10.0.0",
58
58
  "@atlaskit/editor-plugins": "^16.1.0",
59
59
  "@atlaskit/form": "^18.0.0",
@@ -63,15 +63,15 @@
63
63
  "@atlaskit/util-data-test": "^19.1.0",
64
64
  "@atlassian/confluence-presets": "workspace:^",
65
65
  "@atlassian/content-reconciliation": "^0.1.3506",
66
- "@atlassian/editor-ai-injected-editors": "^36.0.0",
67
- "@atlassian/editor-plugin-ai": "^76.1.0",
66
+ "@atlassian/editor-ai-injected-editors": "^37.0.0",
67
+ "@atlassian/editor-plugin-ai": "^77.0.0",
68
68
  "@atlassian/structured-docs-types": "workspace:^",
69
69
  "react": "^19.2.0",
70
70
  "react-dom": "^19.2.0",
71
71
  "react-intl": "^7.0.0"
72
72
  },
73
73
  "peerDependencies": {
74
- "@atlaskit/editor-common": "^126.4.0",
74
+ "@atlaskit/editor-common": "^127.0.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"