@atlaskit/editor-common 119.4.0 → 119.5.1

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 CHANGED
@@ -1,5 +1,31 @@
1
1
  # @atlaskit/editor-common
2
2
 
3
+ ## 119.5.1
4
+
5
+ ### Patch Changes
6
+
7
+ - Updated dependencies
8
+
9
+ ## 119.5.0
10
+
11
+ ### Minor Changes
12
+
13
+ - [`b5b6cb8261481`](https://bitbucket.org/atlassian/atlassian-frontend-monorepo/commits/b5b6cb8261481) -
14
+ [CCI-18813] Fix the Post Stream Review modal anchoring far from the real edit (e.g. at an
15
+ unrelated panel) during backend AI streaming, when the only change to that node was a layout
16
+ `breakout` mark (e.g. a width change). The collab-edit segment producer recorded it as a
17
+ reviewable segment while the AI-plugin filter dropped it, leaving the modal anchored to a node
18
+ with no visible diff.
19
+
20
+ The layout-mark-aware slice comparison is now shared from `@atlaskit/editor-common` (new
21
+ `./collab-agent-review-slice-compare` entry point exporting `slicesEqualIgnoringLocalId`) and used
22
+ by both sides, so a `localId`-only or `breakout`-only difference is consistently treated as no
23
+ change. Real content and formatting edits are still recorded.
24
+
25
+ ### Patch Changes
26
+
27
+ - Updated dependencies
28
+
3
29
  ## 119.4.0
4
30
 
5
31
  ### Minor Changes
@@ -0,0 +1,10 @@
1
+ {
2
+ "name": "@atlaskit/editor-common/collab-agent-review-slice-compare",
3
+ "main": "../dist/cjs/collab/agent-review-slice-compare.js",
4
+ "module": "../dist/esm/collab/agent-review-slice-compare.js",
5
+ "module:es2019": "../dist/es2019/collab/agent-review-slice-compare.js",
6
+ "sideEffects": [
7
+ "**/*.compiled.css"
8
+ ],
9
+ "types": "../dist/types/collab/agent-review-slice-compare.d.ts"
10
+ }
@@ -0,0 +1,99 @@
1
+ "use strict";
2
+
3
+ var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
4
+ Object.defineProperty(exports, "__esModule", {
5
+ value: true
6
+ });
7
+ exports.slicesEqualIgnoringLocalId = void 0;
8
+ var _slicedToArray2 = _interopRequireDefault(require("@babel/runtime/helpers/slicedToArray"));
9
+ var _typeof2 = _interopRequireDefault(require("@babel/runtime/helpers/typeof"));
10
+ /**
11
+ * ADF mark types ignored when deciding whether agent-edited content changed:
12
+ *
13
+ * - `breakout` — a layout-width mark (`mode: wide | full-width`) that a
14
+ * whole-node/whole-doc replace (e.g. `replaceDoc`) can drop, re-add or
15
+ * re-value without the reviewable *content* changing.
16
+ *
17
+ * Module-private (not exported) so this file has a single public runtime export
18
+ * (Volt strict-mode rule), and so callers cannot diverge from the one comparison
19
+ * definition below.
20
+ */
21
+ var IGNORED_MARK_TYPES = new Set(['breakout']);
22
+
23
+ /**
24
+ * Deep-normalise ADF JSON for change comparison so two structures that differ
25
+ * ONLY by ignorable metadata compare equal. Specifically it:
26
+ *
27
+ * - strips every `localId` (at any nesting): an ephemeral, per-node identity
28
+ * stamp that lives both as a node attribute (`attrs.localId`) and as a
29
+ * `fragment` mark attribute. A whole-node/whole-doc replace (e.g.
30
+ * `replaceDoc`) re-emits content with FRESH localIds, so content that is
31
+ * otherwise identical legitimately carries different localIds.
32
+ * - drops ignorable marks (`IGNORED_MARK_TYPES`, e.g. `breakout`) from every
33
+ * `marks` array, and removes a `marks` key that becomes empty so `marks: []`
34
+ * and an absent `marks` compare equal.
35
+ *
36
+ * Removing these regardless of nesting handles all carriers without needing to
37
+ * know the schema.
38
+ */
39
+ var _normaliseForChangeCompare = function normaliseForChangeCompare(value) {
40
+ if (Array.isArray(value)) {
41
+ return value.map(_normaliseForChangeCompare);
42
+ }
43
+ if (value && (0, _typeof2.default)(value) === 'object') {
44
+ var result = {};
45
+ for (var _i = 0, _Object$entries = Object.entries(value); _i < _Object$entries.length; _i++) {
46
+ var _Object$entries$_i = (0, _slicedToArray2.default)(_Object$entries[_i], 2),
47
+ key = _Object$entries$_i[0],
48
+ child = _Object$entries$_i[1];
49
+ if (key === 'localId') {
50
+ continue;
51
+ }
52
+ if (key === 'marks' && Array.isArray(child)) {
53
+ var kept = child.filter(function (mark) {
54
+ return !(mark && (0, _typeof2.default)(mark) === 'object' && IGNORED_MARK_TYPES.has(mark.type));
55
+ });
56
+ // Drop an empty `marks` array so `marks: []` == absent `marks`.
57
+ if (kept.length > 0) {
58
+ result[key] = kept.map(_normaliseForChangeCompare);
59
+ }
60
+ continue;
61
+ }
62
+ result[key] = _normaliseForChangeCompare(child);
63
+ }
64
+ return result;
65
+ }
66
+ return value;
67
+ };
68
+
69
+ /**
70
+ * We cannot use `Slice.eq` here: it delegates to `Node.eq` → `Node.sameMarkup`,
71
+ * which `compareDeep`s ALL attributes and marks — so two paragraphs with
72
+ * identical text but different `localId`s (or a dropped/re-valued `breakout`) are
73
+ * reported as different. That would leak unchanged segments into the review
74
+ * whenever the AI re-stamps localIds or re-flows layout (the `replaceDoc` case).
75
+ * We instead compare the normalised JSON, plus the open depths (`openStart` /
76
+ * `openEnd`) that `Slice.eq` also considers.
77
+ *
78
+ * Shared here in `@atlaskit/editor-common` so the two sides of the Review
79
+ * pipeline agree on what counts as a change: the collab-edit COARSE segment
80
+ * producer (`getAgentEditSegments`) and the AI-plugin FINE filter
81
+ * (`entryHasNoChange`). If they disagreed, an edit whose only delta is a
82
+ * `breakout` change could be recorded as a segment (anchoring the modal) yet
83
+ * dropped by the filter (no diff). A breakout-only difference is not reviewable;
84
+ * both sides consume this to honour that consistently.
85
+ */
86
+ var slicesEqualIgnoringLocalId = exports.slicesEqualIgnoringLocalId = function slicesEqualIgnoringLocalId(a, b) {
87
+ var _a$content$toJSON, _b$content$toJSON;
88
+ if (a === b) {
89
+ return true;
90
+ }
91
+ if (a.openStart !== b.openStart || a.openEnd !== b.openEnd) {
92
+ return false;
93
+ }
94
+ // `Fragment.toJSON()` returns `null` for empty content; normalise so two
95
+ // empty slices (whatever their provenance) still compare equal.
96
+ var aContent = _normaliseForChangeCompare((_a$content$toJSON = a.content.toJSON()) !== null && _a$content$toJSON !== void 0 ? _a$content$toJSON : null);
97
+ var bContent = _normaliseForChangeCompare((_b$content$toJSON = b.content.toJSON()) !== null && _b$content$toJSON !== void 0 ? _b$content$toJSON : null);
98
+ return JSON.stringify(aContent) === JSON.stringify(bContent);
99
+ };
@@ -28,7 +28,7 @@ var NETWORK_FAILURE_REGEX = /^network failure/i;
28
28
  var RESIZE_OBSERVER_LOOP_REGEX = /ResizeObserver loop completed with undelivered notifications/;
29
29
  var SENTRY_DSN = 'https://0b10c8e02fb44d8796c047b102c9bee8@o55978.ingest.sentry.io/4505129224110080';
30
30
  var packageName = 'editor-common'; // Sentry doesn't accept '/' in its releases https://docs.sentry.io/platforms/javascript/configuration/releases/
31
- var packageVersion = "119.3.0";
31
+ var packageVersion = "119.5.0";
32
32
  var sanitiseSentryEvents = function sanitiseSentryEvents(data, _hint) {
33
33
  // Remove URL as it has UGC
34
34
  // Ignored via go/ees007
@@ -24,7 +24,7 @@ function _isNativeReflectConstruct() { try { var t = !Boolean.prototype.valueOf.
24
24
  * @jsx jsx
25
25
  */ // eslint-disable-next-line @atlaskit/ui-styling-standard/use-compiled -- Ignored via go/DSP-18766
26
26
  var packageName = "@atlaskit/editor-common";
27
- var packageVersion = "119.3.0";
27
+ var packageVersion = "119.5.0";
28
28
  var halfFocusRing = 1;
29
29
  var dropOffset = '0, 8';
30
30
  var fadeIn = (0, _react2.keyframes)({
@@ -0,0 +1,85 @@
1
+ /**
2
+ * ADF mark types ignored when deciding whether agent-edited content changed:
3
+ *
4
+ * - `breakout` — a layout-width mark (`mode: wide | full-width`) that a
5
+ * whole-node/whole-doc replace (e.g. `replaceDoc`) can drop, re-add or
6
+ * re-value without the reviewable *content* changing.
7
+ *
8
+ * Module-private (not exported) so this file has a single public runtime export
9
+ * (Volt strict-mode rule), and so callers cannot diverge from the one comparison
10
+ * definition below.
11
+ */
12
+ const IGNORED_MARK_TYPES = new Set(['breakout']);
13
+
14
+ /**
15
+ * Deep-normalise ADF JSON for change comparison so two structures that differ
16
+ * ONLY by ignorable metadata compare equal. Specifically it:
17
+ *
18
+ * - strips every `localId` (at any nesting): an ephemeral, per-node identity
19
+ * stamp that lives both as a node attribute (`attrs.localId`) and as a
20
+ * `fragment` mark attribute. A whole-node/whole-doc replace (e.g.
21
+ * `replaceDoc`) re-emits content with FRESH localIds, so content that is
22
+ * otherwise identical legitimately carries different localIds.
23
+ * - drops ignorable marks (`IGNORED_MARK_TYPES`, e.g. `breakout`) from every
24
+ * `marks` array, and removes a `marks` key that becomes empty so `marks: []`
25
+ * and an absent `marks` compare equal.
26
+ *
27
+ * Removing these regardless of nesting handles all carriers without needing to
28
+ * know the schema.
29
+ */
30
+ const normaliseForChangeCompare = value => {
31
+ if (Array.isArray(value)) {
32
+ return value.map(normaliseForChangeCompare);
33
+ }
34
+ if (value && typeof value === 'object') {
35
+ const result = {};
36
+ for (const [key, child] of Object.entries(value)) {
37
+ if (key === 'localId') {
38
+ continue;
39
+ }
40
+ if (key === 'marks' && Array.isArray(child)) {
41
+ const kept = child.filter(mark => !(mark && typeof mark === 'object' && IGNORED_MARK_TYPES.has(mark.type)));
42
+ // Drop an empty `marks` array so `marks: []` == absent `marks`.
43
+ if (kept.length > 0) {
44
+ result[key] = kept.map(normaliseForChangeCompare);
45
+ }
46
+ continue;
47
+ }
48
+ result[key] = normaliseForChangeCompare(child);
49
+ }
50
+ return result;
51
+ }
52
+ return value;
53
+ };
54
+
55
+ /**
56
+ * We cannot use `Slice.eq` here: it delegates to `Node.eq` → `Node.sameMarkup`,
57
+ * which `compareDeep`s ALL attributes and marks — so two paragraphs with
58
+ * identical text but different `localId`s (or a dropped/re-valued `breakout`) are
59
+ * reported as different. That would leak unchanged segments into the review
60
+ * whenever the AI re-stamps localIds or re-flows layout (the `replaceDoc` case).
61
+ * We instead compare the normalised JSON, plus the open depths (`openStart` /
62
+ * `openEnd`) that `Slice.eq` also considers.
63
+ *
64
+ * Shared here in `@atlaskit/editor-common` so the two sides of the Review
65
+ * pipeline agree on what counts as a change: the collab-edit COARSE segment
66
+ * producer (`getAgentEditSegments`) and the AI-plugin FINE filter
67
+ * (`entryHasNoChange`). If they disagreed, an edit whose only delta is a
68
+ * `breakout` change could be recorded as a segment (anchoring the modal) yet
69
+ * dropped by the filter (no diff). A breakout-only difference is not reviewable;
70
+ * both sides consume this to honour that consistently.
71
+ */
72
+ export const slicesEqualIgnoringLocalId = (a, b) => {
73
+ var _a$content$toJSON, _b$content$toJSON;
74
+ if (a === b) {
75
+ return true;
76
+ }
77
+ if (a.openStart !== b.openStart || a.openEnd !== b.openEnd) {
78
+ return false;
79
+ }
80
+ // `Fragment.toJSON()` returns `null` for empty content; normalise so two
81
+ // empty slices (whatever their provenance) still compare equal.
82
+ const aContent = normaliseForChangeCompare((_a$content$toJSON = a.content.toJSON()) !== null && _a$content$toJSON !== void 0 ? _a$content$toJSON : null);
83
+ const bContent = normaliseForChangeCompare((_b$content$toJSON = b.content.toJSON()) !== null && _b$content$toJSON !== void 0 ? _b$content$toJSON : null);
84
+ return JSON.stringify(aContent) === JSON.stringify(bContent);
85
+ };
@@ -14,7 +14,7 @@ const NETWORK_FAILURE_REGEX = /^network failure/i;
14
14
  const RESIZE_OBSERVER_LOOP_REGEX = /ResizeObserver loop completed with undelivered notifications/;
15
15
  const SENTRY_DSN = 'https://0b10c8e02fb44d8796c047b102c9bee8@o55978.ingest.sentry.io/4505129224110080';
16
16
  const packageName = 'editor-common'; // Sentry doesn't accept '/' in its releases https://docs.sentry.io/platforms/javascript/configuration/releases/
17
- const packageVersion = "119.3.0";
17
+ const packageVersion = "119.5.0";
18
18
  const sanitiseSentryEvents = (data, _hint) => {
19
19
  // Remove URL as it has UGC
20
20
  // Ignored via go/ees007
@@ -14,7 +14,7 @@ import withAnalyticsEvents from '@atlaskit/analytics-next/withAnalyticsEvents';
14
14
  import { fg } from '@atlaskit/platform-feature-flags';
15
15
  import Layer from '../Layer';
16
16
  const packageName = "@atlaskit/editor-common";
17
- const packageVersion = "119.3.0";
17
+ const packageVersion = "119.5.0";
18
18
  const halfFocusRing = 1;
19
19
  const dropOffset = '0, 8';
20
20
  const fadeIn = keyframes({
@@ -0,0 +1,92 @@
1
+ import _slicedToArray from "@babel/runtime/helpers/slicedToArray";
2
+ import _typeof from "@babel/runtime/helpers/typeof";
3
+ /**
4
+ * ADF mark types ignored when deciding whether agent-edited content changed:
5
+ *
6
+ * - `breakout` — a layout-width mark (`mode: wide | full-width`) that a
7
+ * whole-node/whole-doc replace (e.g. `replaceDoc`) can drop, re-add or
8
+ * re-value without the reviewable *content* changing.
9
+ *
10
+ * Module-private (not exported) so this file has a single public runtime export
11
+ * (Volt strict-mode rule), and so callers cannot diverge from the one comparison
12
+ * definition below.
13
+ */
14
+ var IGNORED_MARK_TYPES = new Set(['breakout']);
15
+
16
+ /**
17
+ * Deep-normalise ADF JSON for change comparison so two structures that differ
18
+ * ONLY by ignorable metadata compare equal. Specifically it:
19
+ *
20
+ * - strips every `localId` (at any nesting): an ephemeral, per-node identity
21
+ * stamp that lives both as a node attribute (`attrs.localId`) and as a
22
+ * `fragment` mark attribute. A whole-node/whole-doc replace (e.g.
23
+ * `replaceDoc`) re-emits content with FRESH localIds, so content that is
24
+ * otherwise identical legitimately carries different localIds.
25
+ * - drops ignorable marks (`IGNORED_MARK_TYPES`, e.g. `breakout`) from every
26
+ * `marks` array, and removes a `marks` key that becomes empty so `marks: []`
27
+ * and an absent `marks` compare equal.
28
+ *
29
+ * Removing these regardless of nesting handles all carriers without needing to
30
+ * know the schema.
31
+ */
32
+ var _normaliseForChangeCompare = function normaliseForChangeCompare(value) {
33
+ if (Array.isArray(value)) {
34
+ return value.map(_normaliseForChangeCompare);
35
+ }
36
+ if (value && _typeof(value) === 'object') {
37
+ var result = {};
38
+ for (var _i = 0, _Object$entries = Object.entries(value); _i < _Object$entries.length; _i++) {
39
+ var _Object$entries$_i = _slicedToArray(_Object$entries[_i], 2),
40
+ key = _Object$entries$_i[0],
41
+ child = _Object$entries$_i[1];
42
+ if (key === 'localId') {
43
+ continue;
44
+ }
45
+ if (key === 'marks' && Array.isArray(child)) {
46
+ var kept = child.filter(function (mark) {
47
+ return !(mark && _typeof(mark) === 'object' && IGNORED_MARK_TYPES.has(mark.type));
48
+ });
49
+ // Drop an empty `marks` array so `marks: []` == absent `marks`.
50
+ if (kept.length > 0) {
51
+ result[key] = kept.map(_normaliseForChangeCompare);
52
+ }
53
+ continue;
54
+ }
55
+ result[key] = _normaliseForChangeCompare(child);
56
+ }
57
+ return result;
58
+ }
59
+ return value;
60
+ };
61
+
62
+ /**
63
+ * We cannot use `Slice.eq` here: it delegates to `Node.eq` → `Node.sameMarkup`,
64
+ * which `compareDeep`s ALL attributes and marks — so two paragraphs with
65
+ * identical text but different `localId`s (or a dropped/re-valued `breakout`) are
66
+ * reported as different. That would leak unchanged segments into the review
67
+ * whenever the AI re-stamps localIds or re-flows layout (the `replaceDoc` case).
68
+ * We instead compare the normalised JSON, plus the open depths (`openStart` /
69
+ * `openEnd`) that `Slice.eq` also considers.
70
+ *
71
+ * Shared here in `@atlaskit/editor-common` so the two sides of the Review
72
+ * pipeline agree on what counts as a change: the collab-edit COARSE segment
73
+ * producer (`getAgentEditSegments`) and the AI-plugin FINE filter
74
+ * (`entryHasNoChange`). If they disagreed, an edit whose only delta is a
75
+ * `breakout` change could be recorded as a segment (anchoring the modal) yet
76
+ * dropped by the filter (no diff). A breakout-only difference is not reviewable;
77
+ * both sides consume this to honour that consistently.
78
+ */
79
+ export var slicesEqualIgnoringLocalId = function slicesEqualIgnoringLocalId(a, b) {
80
+ var _a$content$toJSON, _b$content$toJSON;
81
+ if (a === b) {
82
+ return true;
83
+ }
84
+ if (a.openStart !== b.openStart || a.openEnd !== b.openEnd) {
85
+ return false;
86
+ }
87
+ // `Fragment.toJSON()` returns `null` for empty content; normalise so two
88
+ // empty slices (whatever their provenance) still compare equal.
89
+ var aContent = _normaliseForChangeCompare((_a$content$toJSON = a.content.toJSON()) !== null && _a$content$toJSON !== void 0 ? _a$content$toJSON : null);
90
+ var bContent = _normaliseForChangeCompare((_b$content$toJSON = b.content.toJSON()) !== null && _b$content$toJSON !== void 0 ? _b$content$toJSON : null);
91
+ return JSON.stringify(aContent) === JSON.stringify(bContent);
92
+ };
@@ -20,7 +20,7 @@ var NETWORK_FAILURE_REGEX = /^network failure/i;
20
20
  var RESIZE_OBSERVER_LOOP_REGEX = /ResizeObserver loop completed with undelivered notifications/;
21
21
  var SENTRY_DSN = 'https://0b10c8e02fb44d8796c047b102c9bee8@o55978.ingest.sentry.io/4505129224110080';
22
22
  var packageName = 'editor-common'; // Sentry doesn't accept '/' in its releases https://docs.sentry.io/platforms/javascript/configuration/releases/
23
- var packageVersion = "119.3.0";
23
+ var packageVersion = "119.5.0";
24
24
  var sanitiseSentryEvents = function sanitiseSentryEvents(data, _hint) {
25
25
  // Remove URL as it has UGC
26
26
  // Ignored via go/ees007
@@ -21,7 +21,7 @@ import withAnalyticsEvents from '@atlaskit/analytics-next/withAnalyticsEvents';
21
21
  import { fg } from '@atlaskit/platform-feature-flags';
22
22
  import Layer from '../Layer';
23
23
  var packageName = "@atlaskit/editor-common";
24
- var packageVersion = "119.3.0";
24
+ var packageVersion = "119.5.0";
25
25
  var halfFocusRing = 1;
26
26
  var dropOffset = '0, 8';
27
27
  var fadeIn = keyframes({
@@ -0,0 +1,19 @@
1
+ import type { Slice } from '@atlaskit/editor-prosemirror/model';
2
+ /**
3
+ * We cannot use `Slice.eq` here: it delegates to `Node.eq` → `Node.sameMarkup`,
4
+ * which `compareDeep`s ALL attributes and marks — so two paragraphs with
5
+ * identical text but different `localId`s (or a dropped/re-valued `breakout`) are
6
+ * reported as different. That would leak unchanged segments into the review
7
+ * whenever the AI re-stamps localIds or re-flows layout (the `replaceDoc` case).
8
+ * We instead compare the normalised JSON, plus the open depths (`openStart` /
9
+ * `openEnd`) that `Slice.eq` also considers.
10
+ *
11
+ * Shared here in `@atlaskit/editor-common` so the two sides of the Review
12
+ * pipeline agree on what counts as a change: the collab-edit COARSE segment
13
+ * producer (`getAgentEditSegments`) and the AI-plugin FINE filter
14
+ * (`entryHasNoChange`). If they disagreed, an edit whose only delta is a
15
+ * `breakout` change could be recorded as a segment (anchoring the modal) yet
16
+ * dropped by the filter (no diff). A breakout-only difference is not reviewable;
17
+ * both sides consume this to honour that consistently.
18
+ */
19
+ export declare const slicesEqualIgnoringLocalId: (a: Slice, b: Slice) => boolean;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@atlaskit/editor-common",
3
- "version": "119.4.0",
3
+ "version": "119.5.1",
4
4
  "description": "A package that contains common classes and components for editor and renderer",
5
5
  "publishConfig": {
6
6
  "registry": "https://registry.npmjs.org/"
@@ -49,8 +49,8 @@
49
49
  "@atlaskit/editor-tables": "^3.0.0",
50
50
  "@atlaskit/editor-toolbar": "^2.4.0",
51
51
  "@atlaskit/editor-toolbar-model": "^1.2.0",
52
- "@atlaskit/editor-ui-control-model": "^2.6.0",
53
- "@atlaskit/emoji": "^71.17.0",
52
+ "@atlaskit/editor-ui-control-model": "^2.7.0",
53
+ "@atlaskit/emoji": "^72.0.0",
54
54
  "@atlaskit/feature-gate-js-client": "^6.0.0",
55
55
  "@atlaskit/icon": "^37.3.0",
56
56
  "@atlaskit/link": "^5.0.0",
@@ -84,7 +84,7 @@
84
84
  "@atlaskit/task-decision": "^21.8.0",
85
85
  "@atlaskit/teams-app-config": "^2.2.0",
86
86
  "@atlaskit/textfield": "^10.0.0",
87
- "@atlaskit/tmp-editor-statsig": "^147.1.0",
87
+ "@atlaskit/tmp-editor-statsig": "^148.0.0",
88
88
  "@atlaskit/tokens": "^16.7.0",
89
89
  "@atlaskit/tooltip": "^24.0.0",
90
90
  "@atlaskit/width-detector": "^7.0.0",