@atlaskit/editor-plugin-local-id 1.0.0 → 1.0.2

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,21 @@
1
1
  # @atlaskit/editor-plugin-local-id
2
2
 
3
+ ## 1.0.2
4
+
5
+ ### Patch Changes
6
+
7
+ - [`57b19274b9fdd`](https://bitbucket.org/atlassian/atlassian-frontend-monorepo/commits/57b19274b9fdd) -
8
+ EDITOR-1373 Bump adf-schema version
9
+ - Updated dependencies
10
+
11
+ ## 1.0.1
12
+
13
+ ### Patch Changes
14
+
15
+ - [`a53f6c9834696`](https://bitbucket.org/atlassian/atlassian-frontend-monorepo/commits/a53f6c9834696) -
16
+ EDITOR-1340 Adds plugin option to use duplicate id detection and fix.
17
+ - Updated dependencies
18
+
3
19
  ## 1.0.0
4
20
 
5
21
  ### Major Changes
package/README.md CHANGED
@@ -2,29 +2,38 @@
2
2
 
3
3
  ## 🧠 Overview
4
4
 
5
- This plugin ensures that all non-ignored nodes in the document have unique `localId` attributes. It performs a **one-time scan** of the document when the editor is initialized to add missing local IDs to existing nodes.
5
+ This plugin ensures that all non-ignored nodes in the document have unique `localId` attributes. It combines **two approaches** for optimal performance:
6
6
 
7
- The plugin is designed to run only once per editor instance to avoid performance issues and prevent duplicate ID generation. It's optimized by using idle time scheduling and avoiding unnecessary transactions.
7
+ 1. **One-time initialization scan** - Adds missing local IDs to existing nodes when the editor starts
8
+ 2. **Incremental transaction processing** - Adds local IDs to new nodes as they're created
8
9
 
9
10
  ---
10
11
 
11
12
  ## ⚙️ How It Works
12
13
 
13
- ### Core Mechanism
14
+ ### Initialization Phase
14
15
  - **Trigger**: Uses the editor view's initialization lifecycle to schedule the scan
15
16
  - **Execution**: Performs a single document traversal during browser idle time
16
17
  - **Scheduling**: Uses `requestIdleCallback` with `requestAnimationFrame` fallback for Safari
18
+ - **Purpose**: Ensures all existing nodes have local IDs
19
+
20
+ ### Transaction Processing Phase
21
+ - **Trigger**: Activates on every document-changing transaction
22
+ - **Execution**: Processes only the new/changed nodes from transaction steps
23
+ - **Performance**: Only scans changed ranges, not the entire document
24
+ - **Purpose**: Adds local IDs to newly created nodes
17
25
 
18
26
  ### Node Processing
19
- For each node in the document:
20
- - **Ignored nodes**: Always skipped (no local IDs needed)
27
+ For each node (both during initialization and transaction processing):
28
+ - **Ignored nodes**: Always skipped (text, hardBreak)
21
29
  - **Nodes with existing localId**: Left unchanged
22
30
  - **Nodes without localId**: New UUID generated and assigned
23
31
 
24
32
  ### Transaction Handling
25
- - All node updates are batched into a single transaction (only when needed)
26
- - Transaction is marked with `addToHistory: false` to prevent undo/redo issues
27
- - No transactions are dispatched if no local IDs need to be added
33
+ - **Incremental updates**: Only processes nodes that actually changed
34
+ - **Efficient scanning**: Uses `step.getMap()` to identify changed ranges
35
+ - **Position accuracy**: Gets precise document positions for all changed nodes
36
+ - **Batch processing**: All updates applied in a single transaction
28
37
 
29
38
  ---
30
39
 
@@ -32,8 +41,12 @@ For each node in the document:
32
41
 
33
42
  | Case | Action Taken | Transaction Impact |
34
43
  |------|-------------|-------------------|
35
- | Node has no `localId` and is not ignored | `localId` is added | Single transaction with all changes |
44
+ | Existing node without `localId` (initialization) | `localId` is added | Single transaction with all changes |
45
+ | New node added without `localId` (transaction) | `localId` is added | Single transaction with all changes |
36
46
  | Node already has a `localId` | Left unchanged | No transaction |
37
- | Plugin has already run | No action taken | No transaction |
38
- | Document has no applicable nodes | No action taken | No transaction |
47
+ | Only selection changes | No action taken | No transaction |
48
+ | Cut operations | Skipped to avoid conflicts | No transaction |
49
+ | Document unchanged | Early exit | No transaction |
50
+
51
+ ---
39
52
 
@@ -17,6 +17,9 @@
17
17
  "../src/**/examples.*"
18
18
  ],
19
19
  "references": [
20
+ {
21
+ "path": "../../../platform/feature-flags/afm-cc/tsconfig.json"
22
+ },
20
23
  {
21
24
  "path": "../../editor-common/afm-cc/tsconfig.json"
22
25
  }
@@ -17,6 +17,9 @@
17
17
  "../src/**/examples.*"
18
18
  ],
19
19
  "references": [
20
+ {
21
+ "path": "../../../platform/feature-flags/afm-dev-agents/tsconfig.json"
22
+ },
20
23
  {
21
24
  "path": "../../editor-common/afm-dev-agents/tsconfig.json"
22
25
  }
@@ -17,6 +17,9 @@
17
17
  "../src/**/examples.*"
18
18
  ],
19
19
  "references": [
20
+ {
21
+ "path": "../../../platform/feature-flags/afm-jira/tsconfig.json"
22
+ },
20
23
  {
21
24
  "path": "../../editor-common/afm-jira/tsconfig.json"
22
25
  }
@@ -0,0 +1,27 @@
1
+ {
2
+ "extends": "../../../../tsconfig.entry-points.passionfruit.json",
3
+ "compilerOptions": {
4
+ "target": "es5",
5
+ "outDir": "../../../../../passionfruit/tsDist/@atlaskit__editor-plugin-local-id/app",
6
+ "rootDir": "../",
7
+ "composite": true
8
+ },
9
+ "include": [
10
+ "../src/**/*.ts",
11
+ "../src/**/*.tsx"
12
+ ],
13
+ "exclude": [
14
+ "../src/**/__tests__/*",
15
+ "../src/**/*.test.*",
16
+ "../src/**/test.*",
17
+ "../src/**/examples.*"
18
+ ],
19
+ "references": [
20
+ {
21
+ "path": "../../../platform/feature-flags/afm-passionfruit/tsconfig.json"
22
+ },
23
+ {
24
+ "path": "../../editor-common/afm-passionfruit/tsconfig.json"
25
+ }
26
+ ]
27
+ }
@@ -17,6 +17,9 @@
17
17
  "../src/**/examples.*"
18
18
  ],
19
19
  "references": [
20
+ {
21
+ "path": "../../../platform/feature-flags/afm-post-office/tsconfig.json"
22
+ },
20
23
  {
21
24
  "path": "../../editor-common/afm-post-office/tsconfig.json"
22
25
  }
@@ -17,6 +17,9 @@
17
17
  "../src/**/examples.*"
18
18
  ],
19
19
  "references": [
20
+ {
21
+ "path": "../../../platform/feature-flags/afm-rovo-extension/tsconfig.json"
22
+ },
20
23
  {
21
24
  "path": "../../editor-common/afm-rovo-extension/tsconfig.json"
22
25
  }
@@ -17,6 +17,9 @@
17
17
  "../src/**/examples.*"
18
18
  ],
19
19
  "references": [
20
+ {
21
+ "path": "../../../platform/feature-flags/afm-townsquare/tsconfig.json"
22
+ },
20
23
  {
21
24
  "path": "../../editor-common/afm-townsquare/tsconfig.json"
22
25
  }
@@ -0,0 +1,24 @@
1
+ {
2
+ "extends": "../../../../tsconfig.entry-points.volt.json",
3
+ "compilerOptions": {
4
+ "target": "es5",
5
+ "outDir": "../../../../../volt/tsDist/@atlaskit__editor-plugin-local-id/app",
6
+ "rootDir": "../",
7
+ "composite": true
8
+ },
9
+ "include": [
10
+ "../src/**/*.ts",
11
+ "../src/**/*.tsx"
12
+ ],
13
+ "exclude": [
14
+ "../src/**/__tests__/*",
15
+ "../src/**/*.test.*",
16
+ "../src/**/test.*",
17
+ "../src/**/examples.*"
18
+ ],
19
+ "references": [
20
+ {
21
+ "path": "../../editor-common/afm-volt/tsconfig.json"
22
+ }
23
+ ]
24
+ }
@@ -11,7 +11,9 @@ var localIdPlugin = exports.localIdPlugin = function localIdPlugin() {
11
11
  pmPlugins: function pmPlugins() {
12
12
  return [{
13
13
  name: 'localIdPlugin',
14
- plugin: _main.createPlugin
14
+ plugin: function plugin() {
15
+ return (0, _main.createPlugin)();
16
+ }
15
17
  }];
16
18
  }
17
19
  };
@@ -8,9 +8,14 @@ exports.localIdPluginKey = exports.createPlugin = exports.addLocalIdToNode = voi
8
8
  var _defineProperty2 = _interopRequireDefault(require("@babel/runtime/helpers/defineProperty"));
9
9
  var _adfSchema = require("@atlaskit/adf-schema");
10
10
  var _safePlugin = require("@atlaskit/editor-common/safe-plugin");
11
+ var _utils = require("@atlaskit/editor-common/utils");
11
12
  var _state = require("@atlaskit/editor-prosemirror/state");
13
+ var _platformFeatureFlags = require("@atlaskit/platform-feature-flags");
12
14
  function ownKeys(e, r) { var t = Object.keys(e); if (Object.getOwnPropertySymbols) { var o = Object.getOwnPropertySymbols(e); r && (o = o.filter(function (r) { return Object.getOwnPropertyDescriptor(e, r).enumerable; })), t.push.apply(t, o); } return t; }
13
15
  function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? ownKeys(Object(t), !0).forEach(function (r) { (0, _defineProperty2.default)(e, r, t[r]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function (r) { Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r)); }); } return e; }
16
+ function _createForOfIteratorHelper(r, e) { var t = "undefined" != typeof Symbol && r[Symbol.iterator] || r["@@iterator"]; if (!t) { if (Array.isArray(r) || (t = _unsupportedIterableToArray(r)) || e && r && "number" == typeof r.length) { t && (r = t); var _n = 0, F = function F() {}; return { s: F, n: function n() { return _n >= r.length ? { done: !0 } : { done: !1, value: r[_n++] }; }, e: function e(r) { throw r; }, f: F }; } throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); } var o, a = !0, u = !1; return { s: function s() { t = t.call(r); }, n: function n() { var r = t.next(); return a = r.done, r; }, e: function e(r) { u = !0, o = r; }, f: function f() { try { a || null == t.return || t.return(); } finally { if (u) throw o; } } }; }
17
+ function _unsupportedIterableToArray(r, a) { if (r) { if ("string" == typeof r) return _arrayLikeToArray(r, a); var t = {}.toString.call(r).slice(8, -1); return "Object" === t && r.constructor && (t = r.constructor.name), "Map" === t || "Set" === t ? Array.from(r) : "Arguments" === t || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(t) ? _arrayLikeToArray(r, a) : void 0; } }
18
+ function _arrayLikeToArray(r, a) { (null == a || a > r.length) && (a = r.length); for (var e = 0, n = Array(a); e < a; e++) n[e] = r[e]; return n; }
14
19
  var localIdPluginKey = exports.localIdPluginKey = new _state.PluginKey('localIdPlugin');
15
20
 
16
21
  // Fallback for Safari which doesn't support requestIdleCallback
@@ -56,10 +61,85 @@ var createPlugin = exports.createPlugin = function createPlugin() {
56
61
  return {
57
62
  update: function update() {}
58
63
  };
64
+ },
65
+ /**
66
+ * Handles adding local IDs to new nodes that are created and have the localId attribute
67
+ * This ensures uniqueness of localIds on nodes being created or edited
68
+ */
69
+ appendTransaction: function appendTransaction(transactions, _oldState, newState) {
70
+ var modified = false;
71
+ var tr = newState.tr;
72
+ var _newState$schema$node = newState.schema.nodes,
73
+ text = _newState$schema$node.text,
74
+ hardBreak = _newState$schema$node.hardBreak;
75
+ var ignoredNodeTypes = [text === null || text === void 0 ? void 0 : text.name, hardBreak === null || hardBreak === void 0 ? void 0 : hardBreak.name];
76
+ var addedNodes = new Set();
77
+ var addedNodePos = new Map();
78
+ var localIds = new Set();
79
+ // Process only the nodes added in the transactions
80
+ transactions.forEach(function (transaction) {
81
+ if (!transaction.docChanged) {
82
+ return;
83
+ }
84
+ if (transaction.getMeta('uiEvent') === 'cut') {
85
+ return;
86
+ }
87
+ transaction.steps.forEach(function (step) {
88
+ if (!(0, _utils.stepHasSlice)(step)) {
89
+ return;
90
+ }
91
+ step.getMap().forEach(function (oldStart, oldEnd, newStart, newEnd) {
92
+ // Scan the changed range to find all nodes
93
+ tr.doc.nodesBetween(newStart, Math.min(newEnd, tr.doc.content.size), function (node, pos) {
94
+ if (ignoredNodeTypes.includes(node.type.name)) {
95
+ return true;
96
+ }
97
+ modified = true;
98
+ if ((0, _platformFeatureFlags.fg)('platform_editor_use_localid_dedupe')) {
99
+ // Always add to addedNodes for duplicate prevention
100
+ addedNodes.add(node);
101
+ addedNodePos.set(node, pos);
102
+ } else {
103
+ if (!(node !== null && node !== void 0 && node.attrs.localId)) {
104
+ addLocalIdToNode(node, pos, tr);
105
+ }
106
+ }
107
+ return true;
108
+ });
109
+ });
110
+ });
111
+ });
112
+ if (addedNodes.size > 0 && (0, _platformFeatureFlags.fg)('platform_editor_use_localid_dedupe')) {
113
+ newState.doc.descendants(function (node, pos) {
114
+ if (addedNodes.has(node)) {
115
+ return true;
116
+ }
117
+ localIds.add(node.attrs.localId);
118
+ return true;
119
+ });
120
+ var _iterator = _createForOfIteratorHelper(addedNodes),
121
+ _step;
122
+ try {
123
+ for (_iterator.s(); !(_step = _iterator.n()).done;) {
124
+ var node = _step.value;
125
+ if (!node.attrs.localId || localIds.has(node.attrs.localId)) {
126
+ var pos = addedNodePos.get(node);
127
+ if (pos !== undefined) {
128
+ addLocalIdToNode(node, pos, tr);
129
+ modified = true;
130
+ }
131
+ }
132
+ }
133
+ } catch (err) {
134
+ _iterator.e(err);
135
+ } finally {
136
+ _iterator.f();
137
+ }
138
+ }
139
+ return modified ? tr : undefined;
59
140
  }
60
141
  });
61
142
  };
62
-
63
143
  /**
64
144
  * Adds a local ID to a ProseMirror node
65
145
  *
@@ -72,7 +152,7 @@ var createPlugin = exports.createPlugin = function createPlugin() {
72
152
  * @returns The updated transaction with the node markup change
73
153
  */
74
154
  var addLocalIdToNode = exports.addLocalIdToNode = function addLocalIdToNode(node, pos, tr) {
75
- tr = tr.setNodeMarkup(pos, node.type, _objectSpread(_objectSpread({}, node.attrs), {}, {
155
+ tr.setNodeMarkup(pos, node.type, _objectSpread(_objectSpread({}, node.attrs), {}, {
76
156
  localId: _adfSchema.uuid.generate()
77
157
  }), node.marks);
78
158
  };
@@ -4,7 +4,7 @@ export const localIdPlugin = () => ({
4
4
  pmPlugins() {
5
5
  return [{
6
6
  name: 'localIdPlugin',
7
- plugin: createPlugin
7
+ plugin: () => createPlugin()
8
8
  }];
9
9
  }
10
10
  });
@@ -1,6 +1,8 @@
1
1
  import { uuid } from '@atlaskit/adf-schema';
2
2
  import { SafePlugin } from '@atlaskit/editor-common/safe-plugin';
3
+ import { stepHasSlice } from '@atlaskit/editor-common/utils';
3
4
  import { PluginKey } from '@atlaskit/editor-prosemirror/state';
5
+ import { fg } from '@atlaskit/platform-feature-flags';
4
6
  export const localIdPluginKey = new PluginKey('localIdPlugin');
5
7
 
6
8
  // Fallback for Safari which doesn't support requestIdleCallback
@@ -47,10 +49,77 @@ export const createPlugin = () => {
47
49
  return {
48
50
  update: () => {}
49
51
  };
52
+ },
53
+ /**
54
+ * Handles adding local IDs to new nodes that are created and have the localId attribute
55
+ * This ensures uniqueness of localIds on nodes being created or edited
56
+ */
57
+ appendTransaction: (transactions, _oldState, newState) => {
58
+ let modified = false;
59
+ const tr = newState.tr;
60
+ const {
61
+ text,
62
+ hardBreak
63
+ } = newState.schema.nodes;
64
+ const ignoredNodeTypes = [text === null || text === void 0 ? void 0 : text.name, hardBreak === null || hardBreak === void 0 ? void 0 : hardBreak.name];
65
+ const addedNodes = new Set();
66
+ const addedNodePos = new Map();
67
+ const localIds = new Set();
68
+ // Process only the nodes added in the transactions
69
+ transactions.forEach(transaction => {
70
+ if (!transaction.docChanged) {
71
+ return;
72
+ }
73
+ if (transaction.getMeta('uiEvent') === 'cut') {
74
+ return;
75
+ }
76
+ transaction.steps.forEach(step => {
77
+ if (!stepHasSlice(step)) {
78
+ return;
79
+ }
80
+ step.getMap().forEach((oldStart, oldEnd, newStart, newEnd) => {
81
+ // Scan the changed range to find all nodes
82
+ tr.doc.nodesBetween(newStart, Math.min(newEnd, tr.doc.content.size), (node, pos) => {
83
+ if (ignoredNodeTypes.includes(node.type.name)) {
84
+ return true;
85
+ }
86
+ modified = true;
87
+ if (fg('platform_editor_use_localid_dedupe')) {
88
+ // Always add to addedNodes for duplicate prevention
89
+ addedNodes.add(node);
90
+ addedNodePos.set(node, pos);
91
+ } else {
92
+ if (!(node !== null && node !== void 0 && node.attrs.localId)) {
93
+ addLocalIdToNode(node, pos, tr);
94
+ }
95
+ }
96
+ return true;
97
+ });
98
+ });
99
+ });
100
+ });
101
+ if (addedNodes.size > 0 && fg('platform_editor_use_localid_dedupe')) {
102
+ newState.doc.descendants((node, pos) => {
103
+ if (addedNodes.has(node)) {
104
+ return true;
105
+ }
106
+ localIds.add(node.attrs.localId);
107
+ return true;
108
+ });
109
+ for (const node of addedNodes) {
110
+ if (!node.attrs.localId || localIds.has(node.attrs.localId)) {
111
+ const pos = addedNodePos.get(node);
112
+ if (pos !== undefined) {
113
+ addLocalIdToNode(node, pos, tr);
114
+ modified = true;
115
+ }
116
+ }
117
+ }
118
+ }
119
+ return modified ? tr : undefined;
50
120
  }
51
121
  });
52
122
  };
53
-
54
123
  /**
55
124
  * Adds a local ID to a ProseMirror node
56
125
  *
@@ -63,7 +132,7 @@ export const createPlugin = () => {
63
132
  * @returns The updated transaction with the node markup change
64
133
  */
65
134
  export const addLocalIdToNode = (node, pos, tr) => {
66
- tr = tr.setNodeMarkup(pos, node.type, {
135
+ tr.setNodeMarkup(pos, node.type, {
67
136
  ...node.attrs,
68
137
  localId: uuid.generate()
69
138
  }, node.marks);
@@ -5,7 +5,9 @@ export var localIdPlugin = function localIdPlugin() {
5
5
  pmPlugins: function pmPlugins() {
6
6
  return [{
7
7
  name: 'localIdPlugin',
8
- plugin: createPlugin
8
+ plugin: function plugin() {
9
+ return createPlugin();
10
+ }
9
11
  }];
10
12
  }
11
13
  };
@@ -1,9 +1,14 @@
1
1
  import _defineProperty from "@babel/runtime/helpers/defineProperty";
2
2
  function ownKeys(e, r) { var t = Object.keys(e); if (Object.getOwnPropertySymbols) { var o = Object.getOwnPropertySymbols(e); r && (o = o.filter(function (r) { return Object.getOwnPropertyDescriptor(e, r).enumerable; })), t.push.apply(t, o); } return t; }
3
3
  function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? ownKeys(Object(t), !0).forEach(function (r) { _defineProperty(e, r, t[r]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function (r) { Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r)); }); } return e; }
4
+ function _createForOfIteratorHelper(r, e) { var t = "undefined" != typeof Symbol && r[Symbol.iterator] || r["@@iterator"]; if (!t) { if (Array.isArray(r) || (t = _unsupportedIterableToArray(r)) || e && r && "number" == typeof r.length) { t && (r = t); var _n = 0, F = function F() {}; return { s: F, n: function n() { return _n >= r.length ? { done: !0 } : { done: !1, value: r[_n++] }; }, e: function e(r) { throw r; }, f: F }; } throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); } var o, a = !0, u = !1; return { s: function s() { t = t.call(r); }, n: function n() { var r = t.next(); return a = r.done, r; }, e: function e(r) { u = !0, o = r; }, f: function f() { try { a || null == t.return || t.return(); } finally { if (u) throw o; } } }; }
5
+ function _unsupportedIterableToArray(r, a) { if (r) { if ("string" == typeof r) return _arrayLikeToArray(r, a); var t = {}.toString.call(r).slice(8, -1); return "Object" === t && r.constructor && (t = r.constructor.name), "Map" === t || "Set" === t ? Array.from(r) : "Arguments" === t || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(t) ? _arrayLikeToArray(r, a) : void 0; } }
6
+ function _arrayLikeToArray(r, a) { (null == a || a > r.length) && (a = r.length); for (var e = 0, n = Array(a); e < a; e++) n[e] = r[e]; return n; }
4
7
  import { uuid } from '@atlaskit/adf-schema';
5
8
  import { SafePlugin } from '@atlaskit/editor-common/safe-plugin';
9
+ import { stepHasSlice } from '@atlaskit/editor-common/utils';
6
10
  import { PluginKey } from '@atlaskit/editor-prosemirror/state';
11
+ import { fg } from '@atlaskit/platform-feature-flags';
7
12
  export var localIdPluginKey = new PluginKey('localIdPlugin');
8
13
 
9
14
  // Fallback for Safari which doesn't support requestIdleCallback
@@ -49,10 +54,85 @@ export var createPlugin = function createPlugin() {
49
54
  return {
50
55
  update: function update() {}
51
56
  };
57
+ },
58
+ /**
59
+ * Handles adding local IDs to new nodes that are created and have the localId attribute
60
+ * This ensures uniqueness of localIds on nodes being created or edited
61
+ */
62
+ appendTransaction: function appendTransaction(transactions, _oldState, newState) {
63
+ var modified = false;
64
+ var tr = newState.tr;
65
+ var _newState$schema$node = newState.schema.nodes,
66
+ text = _newState$schema$node.text,
67
+ hardBreak = _newState$schema$node.hardBreak;
68
+ var ignoredNodeTypes = [text === null || text === void 0 ? void 0 : text.name, hardBreak === null || hardBreak === void 0 ? void 0 : hardBreak.name];
69
+ var addedNodes = new Set();
70
+ var addedNodePos = new Map();
71
+ var localIds = new Set();
72
+ // Process only the nodes added in the transactions
73
+ transactions.forEach(function (transaction) {
74
+ if (!transaction.docChanged) {
75
+ return;
76
+ }
77
+ if (transaction.getMeta('uiEvent') === 'cut') {
78
+ return;
79
+ }
80
+ transaction.steps.forEach(function (step) {
81
+ if (!stepHasSlice(step)) {
82
+ return;
83
+ }
84
+ step.getMap().forEach(function (oldStart, oldEnd, newStart, newEnd) {
85
+ // Scan the changed range to find all nodes
86
+ tr.doc.nodesBetween(newStart, Math.min(newEnd, tr.doc.content.size), function (node, pos) {
87
+ if (ignoredNodeTypes.includes(node.type.name)) {
88
+ return true;
89
+ }
90
+ modified = true;
91
+ if (fg('platform_editor_use_localid_dedupe')) {
92
+ // Always add to addedNodes for duplicate prevention
93
+ addedNodes.add(node);
94
+ addedNodePos.set(node, pos);
95
+ } else {
96
+ if (!(node !== null && node !== void 0 && node.attrs.localId)) {
97
+ addLocalIdToNode(node, pos, tr);
98
+ }
99
+ }
100
+ return true;
101
+ });
102
+ });
103
+ });
104
+ });
105
+ if (addedNodes.size > 0 && fg('platform_editor_use_localid_dedupe')) {
106
+ newState.doc.descendants(function (node, pos) {
107
+ if (addedNodes.has(node)) {
108
+ return true;
109
+ }
110
+ localIds.add(node.attrs.localId);
111
+ return true;
112
+ });
113
+ var _iterator = _createForOfIteratorHelper(addedNodes),
114
+ _step;
115
+ try {
116
+ for (_iterator.s(); !(_step = _iterator.n()).done;) {
117
+ var node = _step.value;
118
+ if (!node.attrs.localId || localIds.has(node.attrs.localId)) {
119
+ var pos = addedNodePos.get(node);
120
+ if (pos !== undefined) {
121
+ addLocalIdToNode(node, pos, tr);
122
+ modified = true;
123
+ }
124
+ }
125
+ }
126
+ } catch (err) {
127
+ _iterator.e(err);
128
+ } finally {
129
+ _iterator.f();
130
+ }
131
+ }
132
+ return modified ? tr : undefined;
52
133
  }
53
134
  });
54
135
  };
55
-
56
136
  /**
57
137
  * Adds a local ID to a ProseMirror node
58
138
  *
@@ -65,7 +145,7 @@ export var createPlugin = function createPlugin() {
65
145
  * @returns The updated transaction with the node markup change
66
146
  */
67
147
  export var addLocalIdToNode = function addLocalIdToNode(node, pos, tr) {
68
- tr = tr.setNodeMarkup(pos, node.type, _objectSpread(_objectSpread({}, node.attrs), {}, {
148
+ tr.setNodeMarkup(pos, node.type, _objectSpread(_objectSpread({}, node.attrs), {}, {
69
149
  localId: uuid.generate()
70
150
  }), node.marks);
71
151
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@atlaskit/editor-plugin-local-id",
3
- "version": "1.0.0",
3
+ "version": "1.0.2",
4
4
  "description": "LocalId plugin for @atlaskit/editor-core",
5
5
  "author": "Atlassian Pty Ltd",
6
6
  "license": "Apache-2.0",
@@ -31,17 +31,18 @@
31
31
  ".": "./src/index.ts"
32
32
  },
33
33
  "dependencies": {
34
- "@atlaskit/adf-schema": "^50.2.0",
34
+ "@atlaskit/adf-schema": "^50.2.1",
35
35
  "@atlaskit/editor-prosemirror": "7.0.0",
36
+ "@atlaskit/platform-feature-flags": "^1.1.0",
36
37
  "@babel/runtime": "^7.0.0",
37
38
  "raf-schd": "^4.0.3"
38
39
  },
39
40
  "peerDependencies": {
40
- "@atlaskit/editor-common": "^107.18.0",
41
+ "@atlaskit/editor-common": "^107.26.0",
41
42
  "react": "^18.2.0"
42
43
  },
43
44
  "devDependencies": {
44
- "typescript": "~5.4.2"
45
+ "typescript": "5.9.2"
45
46
  },
46
47
  "techstack": {
47
48
  "@atlassian/frontend": {
@@ -78,5 +79,10 @@
78
79
  "import-no-extraneous-disable-for-examples-and-docs"
79
80
  ]
80
81
  }
82
+ },
83
+ "platform-feature-flags": {
84
+ "platform_editor_use_localid_dedupe": {
85
+ "type": "boolean"
86
+ }
81
87
  }
82
88
  }