@atlaskit/editor-plugin-local-id 8.1.2 → 8.3.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.
- package/CHANGELOG.md +28 -0
- package/afm-cc/tsconfig.json +3 -0
- package/dist/cjs/pm-plugins/main.js +101 -58
- package/dist/es2019/pm-plugins/main.js +58 -46
- package/dist/esm/pm-plugins/main.js +100 -57
- package/dist/types/pm-plugins/main.d.ts +0 -9
- package/dist/types-ts4.5/pm-plugins/main.d.ts +0 -9
- package/package.json +4 -5
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,33 @@
|
|
|
1
1
|
# @atlaskit/editor-plugin-local-id
|
|
2
2
|
|
|
3
|
+
## 8.3.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- [`1c5e23accfe24`](https://bitbucket.org/atlassian/atlassian-frontend-monorepo/commits/1c5e23accfe24) -
|
|
8
|
+
Cleanup feature gate platform_editor_localid_improvements. Updates are now always batched into a
|
|
9
|
+
single BatchAttrsStep. Removes the unused addLocalIdToNode export.
|
|
10
|
+
|
|
11
|
+
### Patch Changes
|
|
12
|
+
|
|
13
|
+
- Updated dependencies
|
|
14
|
+
|
|
15
|
+
## 8.2.0
|
|
16
|
+
|
|
17
|
+
### Minor Changes
|
|
18
|
+
|
|
19
|
+
- [`b284d7fc05568`](https://bitbucket.org/atlassian/atlassian-frontend-monorepo/commits/b284d7fc05568) -
|
|
20
|
+
Fix tableCell/tableHeader nodes in middle rows of newly-inserted tables missing localId. The
|
|
21
|
+
localId plugin's appendTransaction was keyed by PMNode identity, which incorrectly overwrote
|
|
22
|
+
positions when the same node reference legitimately occupied multiple document positions (as
|
|
23
|
+
createTable from prosemirror-utils does by reusing cell node objects across non-header rows). The
|
|
24
|
+
plugin now tracks all positions per node identity and assigns a unique localId per position. Gated
|
|
25
|
+
behind the `platform_editor_ai_tablecell_localids` experiment.
|
|
26
|
+
|
|
27
|
+
### Patch Changes
|
|
28
|
+
|
|
29
|
+
- Updated dependencies
|
|
30
|
+
|
|
3
31
|
## 8.1.2
|
|
4
32
|
|
|
5
33
|
### Patch Changes
|
package/afm-cc/tsconfig.json
CHANGED
|
@@ -4,14 +4,16 @@ var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefau
|
|
|
4
4
|
Object.defineProperty(exports, "__esModule", {
|
|
5
5
|
value: true
|
|
6
6
|
});
|
|
7
|
-
exports.localIdPluginKey = exports.createPlugin = exports.batchAddLocalIdToNodes =
|
|
7
|
+
exports.localIdPluginKey = exports.createPlugin = exports.batchAddLocalIdToNodes = void 0;
|
|
8
8
|
var _slicedToArray2 = _interopRequireDefault(require("@babel/runtime/helpers/slicedToArray"));
|
|
9
|
+
var _toArray2 = _interopRequireDefault(require("@babel/runtime/helpers/toArray"));
|
|
9
10
|
var _steps = require("@atlaskit/adf-schema/steps");
|
|
10
11
|
var _collab = require("@atlaskit/editor-common/collab");
|
|
11
12
|
var _safePlugin = require("@atlaskit/editor-common/safe-plugin");
|
|
12
13
|
var _utils = require("@atlaskit/editor-common/utils");
|
|
13
14
|
var _state = require("@atlaskit/editor-prosemirror/state");
|
|
14
15
|
var _platformFeatureFlags = require("@atlaskit/platform-feature-flags");
|
|
16
|
+
var _expValEquals = require("@atlaskit/tmp-editor-statsig/exp-val-equals");
|
|
15
17
|
var _generateShortUUID = require("./generateShortUUID");
|
|
16
18
|
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
19
|
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; } }
|
|
@@ -48,7 +50,6 @@ var createPlugin = exports.createPlugin = function createPlugin(api) {
|
|
|
48
50
|
}
|
|
49
51
|
requestIdleCallbackWithFallback(function () {
|
|
50
52
|
var tr = editorView.state.tr;
|
|
51
|
-
var localIdWasAdded = false;
|
|
52
53
|
var nodesToUpdate = new Map(); // position -> localId
|
|
53
54
|
|
|
54
55
|
var _editorView$state$sch = editorView.state.schema.nodes,
|
|
@@ -61,23 +62,12 @@ var createPlugin = exports.createPlugin = function createPlugin(api) {
|
|
|
61
62
|
editorView.state.doc.descendants(function (node, pos) {
|
|
62
63
|
var _node$type$spec$attrs;
|
|
63
64
|
if (!ignoredNodeTypes.includes(node.type.name) && !node.attrs.localId && !!((_node$type$spec$attrs = node.type.spec.attrs) !== null && _node$type$spec$attrs !== void 0 && _node$type$spec$attrs.localId)) {
|
|
64
|
-
|
|
65
|
-
nodesToUpdate.set(pos, generateUUID());
|
|
66
|
-
} else {
|
|
67
|
-
localIdWasAdded = true;
|
|
68
|
-
addLocalIdToNode(pos, tr);
|
|
69
|
-
}
|
|
65
|
+
nodesToUpdate.set(pos, generateUUID());
|
|
70
66
|
}
|
|
71
67
|
return true; // Continue traversing
|
|
72
68
|
});
|
|
73
|
-
if (
|
|
74
|
-
|
|
75
|
-
batchAddLocalIdToNodes(nodesToUpdate, tr);
|
|
76
|
-
(0, _collab.tintDirtyTransaction)(tr);
|
|
77
|
-
editorView.dispatch(tr);
|
|
78
|
-
}
|
|
79
|
-
} else if (localIdWasAdded) {
|
|
80
|
-
tr.setMeta('addToHistory', false);
|
|
69
|
+
if (nodesToUpdate.size > 0) {
|
|
70
|
+
batchAddLocalIdToNodes(nodesToUpdate, tr);
|
|
81
71
|
(0, _collab.tintDirtyTransaction)(tr);
|
|
82
72
|
editorView.dispatch(tr);
|
|
83
73
|
}
|
|
@@ -105,6 +95,11 @@ var createPlugin = exports.createPlugin = function createPlugin(api) {
|
|
|
105
95
|
// https://bitbucket.org/atlassian/adf-schema/src/fb2236147a0c2bc9c8efbdb75fd8f8c411df44ba/packages/adf-schema/src/next-schema/nodes/mediaGroup.ts#lines-12
|
|
106
96
|
var ignoredNodeTypes = [text === null || text === void 0 ? void 0 : text.name, hardBreak === null || hardBreak === void 0 ? void 0 : hardBreak.name, mediaGroup === null || mediaGroup === void 0 ? void 0 : mediaGroup.name];
|
|
107
97
|
var addedNodes = new Set();
|
|
98
|
+
// A single PMNode reference can appear at multiple positions in the doc (e.g.
|
|
99
|
+
// `createTable` from `prosemirror-utils` reuses cell node objects across
|
|
100
|
+
// non-header rows), so the new code path tracks every position per node identity.
|
|
101
|
+
// The legacy path retains the single-position-per-node map for compatibility.
|
|
102
|
+
var positionsByNode = new Map();
|
|
108
103
|
var addedNodePos = new Map();
|
|
109
104
|
var localIds = new Set();
|
|
110
105
|
var nodesToUpdate = new Map(); // position -> localId
|
|
@@ -141,15 +136,17 @@ var createPlugin = exports.createPlugin = function createPlugin(api) {
|
|
|
141
136
|
if ((0, _platformFeatureFlags.fg)('platform_editor_use_localid_dedupe')) {
|
|
142
137
|
// Always add to addedNodes for duplicate prevention
|
|
143
138
|
addedNodes.add(node);
|
|
144
|
-
|
|
139
|
+
if ((0, _expValEquals.expValEquals)('platform_editor_ai_tablecell_localids', 'isEnabled', true)) {
|
|
140
|
+
var _positionsByNode$get;
|
|
141
|
+
var positions = (_positionsByNode$get = positionsByNode.get(node)) !== null && _positionsByNode$get !== void 0 ? _positionsByNode$get : new Set();
|
|
142
|
+
positions.add(pos);
|
|
143
|
+
positionsByNode.set(node, positions);
|
|
144
|
+
} else {
|
|
145
|
+
addedNodePos.set(node, pos);
|
|
146
|
+
}
|
|
145
147
|
} else {
|
|
146
148
|
if (!(node !== null && node !== void 0 && node.attrs.localId)) {
|
|
147
|
-
|
|
148
|
-
nodesToUpdate.set(pos, generateUUID());
|
|
149
|
-
} else {
|
|
150
|
-
// Legacy behavior - individual steps
|
|
151
|
-
addLocalIdToNode(pos, tr);
|
|
152
|
-
}
|
|
149
|
+
nodesToUpdate.set(pos, generateUUID());
|
|
153
150
|
}
|
|
154
151
|
}
|
|
155
152
|
return true;
|
|
@@ -173,57 +170,103 @@ var createPlugin = exports.createPlugin = function createPlugin(api) {
|
|
|
173
170
|
hasInitializedExistingUUIDs = true;
|
|
174
171
|
// Also ensure the added have no duplicates
|
|
175
172
|
var seenIds = new Set();
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
var
|
|
183
|
-
if (
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
173
|
+
if ((0, _expValEquals.expValEquals)('platform_editor_ai_tablecell_localids', 'isEnabled', true)) {
|
|
174
|
+
var _iterator = _createForOfIteratorHelper(addedNodes),
|
|
175
|
+
_step;
|
|
176
|
+
try {
|
|
177
|
+
for (_iterator.s(); !(_step = _iterator.n()).done;) {
|
|
178
|
+
var node = _step.value;
|
|
179
|
+
var positions = positionsByNode.get(node);
|
|
180
|
+
if (!positions || positions.size === 0) {
|
|
181
|
+
continue;
|
|
182
|
+
}
|
|
183
|
+
var existingId = node.attrs.localId;
|
|
184
|
+
var needsNewIds = !existingId || localIds.has(existingId) || seenIds.has(existingId);
|
|
185
|
+
if (needsNewIds) {
|
|
186
|
+
// No usable localId: assign a fresh unique one to every position.
|
|
187
|
+
var _iterator2 = _createForOfIteratorHelper(positions),
|
|
188
|
+
_step2;
|
|
189
|
+
try {
|
|
190
|
+
for (_iterator2.s(); !(_step2 = _iterator2.n()).done;) {
|
|
191
|
+
var pos = _step2.value;
|
|
192
|
+
var newId = generateUUID();
|
|
193
|
+
nodesToUpdate.set(pos, newId);
|
|
194
|
+
seenIds.add(newId);
|
|
195
|
+
modified = true;
|
|
196
|
+
}
|
|
197
|
+
} catch (err) {
|
|
198
|
+
_iterator2.e(err);
|
|
199
|
+
} finally {
|
|
200
|
+
_iterator2.f();
|
|
201
|
+
}
|
|
202
|
+
} else if (positions.size > 1) {
|
|
203
|
+
// Shared node reference: keep the existing id at the first position,
|
|
204
|
+
// assign fresh ones to the rest so they don't share the same localId.
|
|
205
|
+
seenIds.add(existingId);
|
|
206
|
+
var _Array$from = Array.from(positions),
|
|
207
|
+
_Array$from2 = (0, _toArray2.default)(_Array$from),
|
|
208
|
+
_first = _Array$from2[0],
|
|
209
|
+
rest = _Array$from2.slice(1);
|
|
210
|
+
var _iterator3 = _createForOfIteratorHelper(rest),
|
|
211
|
+
_step3;
|
|
212
|
+
try {
|
|
213
|
+
for (_iterator3.s(); !(_step3 = _iterator3.n()).done;) {
|
|
214
|
+
var _pos = _step3.value;
|
|
215
|
+
var _newId = generateUUID();
|
|
216
|
+
nodesToUpdate.set(_pos, _newId);
|
|
217
|
+
seenIds.add(_newId);
|
|
218
|
+
modified = true;
|
|
219
|
+
}
|
|
220
|
+
} catch (err) {
|
|
221
|
+
_iterator3.e(err);
|
|
222
|
+
} finally {
|
|
223
|
+
_iterator3.f();
|
|
190
224
|
}
|
|
191
|
-
|
|
225
|
+
} else if (existingId) {
|
|
226
|
+
seenIds.add(existingId);
|
|
192
227
|
}
|
|
193
228
|
}
|
|
194
|
-
|
|
195
|
-
|
|
229
|
+
} catch (err) {
|
|
230
|
+
_iterator.e(err);
|
|
231
|
+
} finally {
|
|
232
|
+
_iterator.f();
|
|
233
|
+
}
|
|
234
|
+
} else {
|
|
235
|
+
var _iterator4 = _createForOfIteratorHelper(addedNodes),
|
|
236
|
+
_step4;
|
|
237
|
+
try {
|
|
238
|
+
for (_iterator4.s(); !(_step4 = _iterator4.n()).done;) {
|
|
239
|
+
var _node = _step4.value;
|
|
240
|
+
if (!_node.attrs.localId || localIds.has(_node.attrs.localId) || seenIds.has(_node.attrs.localId)) {
|
|
241
|
+
var _pos2 = addedNodePos.get(_node);
|
|
242
|
+
if (_pos2 !== undefined) {
|
|
243
|
+
var _newId2 = generateUUID();
|
|
244
|
+
nodesToUpdate.set(_pos2, _newId2);
|
|
245
|
+
seenIds.add(_newId2);
|
|
246
|
+
modified = true;
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
if (_node.attrs.localId) {
|
|
250
|
+
seenIds.add(_node.attrs.localId);
|
|
251
|
+
}
|
|
196
252
|
}
|
|
253
|
+
} catch (err) {
|
|
254
|
+
_iterator4.e(err);
|
|
255
|
+
} finally {
|
|
256
|
+
_iterator4.f();
|
|
197
257
|
}
|
|
198
|
-
} catch (err) {
|
|
199
|
-
_iterator.e(err);
|
|
200
|
-
} finally {
|
|
201
|
-
_iterator.f();
|
|
202
258
|
}
|
|
203
259
|
}
|
|
204
260
|
// Apply local ID updates based on the improvements feature flag:
|
|
205
261
|
// - When enabled: Batch all updates into a single BatchAttrsStep
|
|
206
262
|
// - When disabled: Individual steps were already applied above during node processing
|
|
207
|
-
if (modified && nodesToUpdate.size > 0
|
|
263
|
+
if (modified && nodesToUpdate.size > 0) {
|
|
208
264
|
batchAddLocalIdToNodes(nodesToUpdate, tr);
|
|
209
265
|
}
|
|
210
266
|
return modified ? tr : undefined;
|
|
211
267
|
}
|
|
212
268
|
});
|
|
213
269
|
};
|
|
214
|
-
/**
|
|
215
|
-
* Adds a local ID to a ProseMirror node
|
|
216
|
-
*
|
|
217
|
-
* This utility function updates a node's attributes to include a unique local ID.
|
|
218
|
-
*
|
|
219
|
-
* @param pos - The position of the node in the document
|
|
220
|
-
* @param tr - The transaction to apply the change to
|
|
221
|
-
*/
|
|
222
|
-
var addLocalIdToNode = exports.addLocalIdToNode = function addLocalIdToNode(pos, tr) {
|
|
223
|
-
tr.setNodeAttribute(pos, 'localId', generateUUID());
|
|
224
|
-
tr.setMeta('addToHistory', false);
|
|
225
|
-
};
|
|
226
|
-
|
|
227
270
|
/**
|
|
228
271
|
* Batch adds local IDs to nodes using a BatchAttrsStep
|
|
229
272
|
* @param nodesToUpdate Map of position -> localId for nodes that need updates
|
|
@@ -4,6 +4,7 @@ import { SafePlugin } from '@atlaskit/editor-common/safe-plugin';
|
|
|
4
4
|
import { stepHasSlice } from '@atlaskit/editor-common/utils';
|
|
5
5
|
import { PluginKey } from '@atlaskit/editor-prosemirror/state';
|
|
6
6
|
import { fg } from '@atlaskit/platform-feature-flags';
|
|
7
|
+
import { expValEquals } from '@atlaskit/tmp-editor-statsig/exp-val-equals';
|
|
7
8
|
import { generateShortUUID, generatedShortUUIDs } from './generateShortUUID';
|
|
8
9
|
export const localIdPluginKey = new PluginKey('localIdPlugin');
|
|
9
10
|
const generateUUID = () => {
|
|
@@ -37,7 +38,6 @@ export const createPlugin = api => {
|
|
|
37
38
|
}
|
|
38
39
|
requestIdleCallbackWithFallback(() => {
|
|
39
40
|
const tr = editorView.state.tr;
|
|
40
|
-
let localIdWasAdded = false;
|
|
41
41
|
const nodesToUpdate = new Map(); // position -> localId
|
|
42
42
|
|
|
43
43
|
const {
|
|
@@ -51,23 +51,12 @@ export const createPlugin = api => {
|
|
|
51
51
|
editorView.state.doc.descendants((node, pos) => {
|
|
52
52
|
var _node$type$spec$attrs;
|
|
53
53
|
if (!ignoredNodeTypes.includes(node.type.name) && !node.attrs.localId && !!((_node$type$spec$attrs = node.type.spec.attrs) !== null && _node$type$spec$attrs !== void 0 && _node$type$spec$attrs.localId)) {
|
|
54
|
-
|
|
55
|
-
nodesToUpdate.set(pos, generateUUID());
|
|
56
|
-
} else {
|
|
57
|
-
localIdWasAdded = true;
|
|
58
|
-
addLocalIdToNode(pos, tr);
|
|
59
|
-
}
|
|
54
|
+
nodesToUpdate.set(pos, generateUUID());
|
|
60
55
|
}
|
|
61
56
|
return true; // Continue traversing
|
|
62
57
|
});
|
|
63
|
-
if (
|
|
64
|
-
|
|
65
|
-
batchAddLocalIdToNodes(nodesToUpdate, tr);
|
|
66
|
-
tintDirtyTransaction(tr);
|
|
67
|
-
editorView.dispatch(tr);
|
|
68
|
-
}
|
|
69
|
-
} else if (localIdWasAdded) {
|
|
70
|
-
tr.setMeta('addToHistory', false);
|
|
58
|
+
if (nodesToUpdate.size > 0) {
|
|
59
|
+
batchAddLocalIdToNodes(nodesToUpdate, tr);
|
|
71
60
|
tintDirtyTransaction(tr);
|
|
72
61
|
editorView.dispatch(tr);
|
|
73
62
|
}
|
|
@@ -96,6 +85,11 @@ export const createPlugin = api => {
|
|
|
96
85
|
// https://bitbucket.org/atlassian/adf-schema/src/fb2236147a0c2bc9c8efbdb75fd8f8c411df44ba/packages/adf-schema/src/next-schema/nodes/mediaGroup.ts#lines-12
|
|
97
86
|
const ignoredNodeTypes = [text === null || text === void 0 ? void 0 : text.name, hardBreak === null || hardBreak === void 0 ? void 0 : hardBreak.name, mediaGroup === null || mediaGroup === void 0 ? void 0 : mediaGroup.name];
|
|
98
87
|
const addedNodes = new Set();
|
|
88
|
+
// A single PMNode reference can appear at multiple positions in the doc (e.g.
|
|
89
|
+
// `createTable` from `prosemirror-utils` reuses cell node objects across
|
|
90
|
+
// non-header rows), so the new code path tracks every position per node identity.
|
|
91
|
+
// The legacy path retains the single-position-per-node map for compatibility.
|
|
92
|
+
const positionsByNode = new Map();
|
|
99
93
|
const addedNodePos = new Map();
|
|
100
94
|
const localIds = new Set();
|
|
101
95
|
const nodesToUpdate = new Map(); // position -> localId
|
|
@@ -132,15 +126,17 @@ export const createPlugin = api => {
|
|
|
132
126
|
if (fg('platform_editor_use_localid_dedupe')) {
|
|
133
127
|
// Always add to addedNodes for duplicate prevention
|
|
134
128
|
addedNodes.add(node);
|
|
135
|
-
|
|
129
|
+
if (expValEquals('platform_editor_ai_tablecell_localids', 'isEnabled', true)) {
|
|
130
|
+
var _positionsByNode$get;
|
|
131
|
+
const positions = (_positionsByNode$get = positionsByNode.get(node)) !== null && _positionsByNode$get !== void 0 ? _positionsByNode$get : new Set();
|
|
132
|
+
positions.add(pos);
|
|
133
|
+
positionsByNode.set(node, positions);
|
|
134
|
+
} else {
|
|
135
|
+
addedNodePos.set(node, pos);
|
|
136
|
+
}
|
|
136
137
|
} else {
|
|
137
138
|
if (!(node !== null && node !== void 0 && node.attrs.localId)) {
|
|
138
|
-
|
|
139
|
-
nodesToUpdate.set(pos, generateUUID());
|
|
140
|
-
} else {
|
|
141
|
-
// Legacy behavior - individual steps
|
|
142
|
-
addLocalIdToNode(pos, tr);
|
|
143
|
-
}
|
|
139
|
+
nodesToUpdate.set(pos, generateUUID());
|
|
144
140
|
}
|
|
145
141
|
}
|
|
146
142
|
return true;
|
|
@@ -164,48 +160,64 @@ export const createPlugin = api => {
|
|
|
164
160
|
hasInitializedExistingUUIDs = true;
|
|
165
161
|
// Also ensure the added have no duplicates
|
|
166
162
|
const seenIds = new Set();
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
const
|
|
170
|
-
if (
|
|
171
|
-
|
|
163
|
+
if (expValEquals('platform_editor_ai_tablecell_localids', 'isEnabled', true)) {
|
|
164
|
+
for (const node of addedNodes) {
|
|
165
|
+
const positions = positionsByNode.get(node);
|
|
166
|
+
if (!positions || positions.size === 0) {
|
|
167
|
+
continue;
|
|
168
|
+
}
|
|
169
|
+
const existingId = node.attrs.localId;
|
|
170
|
+
const needsNewIds = !existingId || localIds.has(existingId) || seenIds.has(existingId);
|
|
171
|
+
if (needsNewIds) {
|
|
172
|
+
// No usable localId: assign a fresh unique one to every position.
|
|
173
|
+
for (const pos of positions) {
|
|
172
174
|
const newId = generateUUID();
|
|
173
175
|
nodesToUpdate.set(pos, newId);
|
|
174
176
|
seenIds.add(newId);
|
|
175
|
-
|
|
176
|
-
addLocalIdToNode(pos, tr);
|
|
177
|
+
modified = true;
|
|
177
178
|
}
|
|
178
|
-
|
|
179
|
+
} else if (positions.size > 1) {
|
|
180
|
+
// Shared node reference: keep the existing id at the first position,
|
|
181
|
+
// assign fresh ones to the rest so they don't share the same localId.
|
|
182
|
+
seenIds.add(existingId);
|
|
183
|
+
const [_first, ...rest] = Array.from(positions);
|
|
184
|
+
for (const pos of rest) {
|
|
185
|
+
const newId = generateUUID();
|
|
186
|
+
nodesToUpdate.set(pos, newId);
|
|
187
|
+
seenIds.add(newId);
|
|
188
|
+
modified = true;
|
|
189
|
+
}
|
|
190
|
+
} else if (existingId) {
|
|
191
|
+
seenIds.add(existingId);
|
|
179
192
|
}
|
|
180
193
|
}
|
|
181
|
-
|
|
182
|
-
|
|
194
|
+
} else {
|
|
195
|
+
for (const node of addedNodes) {
|
|
196
|
+
if (!node.attrs.localId || localIds.has(node.attrs.localId) || seenIds.has(node.attrs.localId)) {
|
|
197
|
+
const pos = addedNodePos.get(node);
|
|
198
|
+
if (pos !== undefined) {
|
|
199
|
+
const newId = generateUUID();
|
|
200
|
+
nodesToUpdate.set(pos, newId);
|
|
201
|
+
seenIds.add(newId);
|
|
202
|
+
modified = true;
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
if (node.attrs.localId) {
|
|
206
|
+
seenIds.add(node.attrs.localId);
|
|
207
|
+
}
|
|
183
208
|
}
|
|
184
209
|
}
|
|
185
210
|
}
|
|
186
211
|
// Apply local ID updates based on the improvements feature flag:
|
|
187
212
|
// - When enabled: Batch all updates into a single BatchAttrsStep
|
|
188
213
|
// - When disabled: Individual steps were already applied above during node processing
|
|
189
|
-
if (modified && nodesToUpdate.size > 0
|
|
214
|
+
if (modified && nodesToUpdate.size > 0) {
|
|
190
215
|
batchAddLocalIdToNodes(nodesToUpdate, tr);
|
|
191
216
|
}
|
|
192
217
|
return modified ? tr : undefined;
|
|
193
218
|
}
|
|
194
219
|
});
|
|
195
220
|
};
|
|
196
|
-
/**
|
|
197
|
-
* Adds a local ID to a ProseMirror node
|
|
198
|
-
*
|
|
199
|
-
* This utility function updates a node's attributes to include a unique local ID.
|
|
200
|
-
*
|
|
201
|
-
* @param pos - The position of the node in the document
|
|
202
|
-
* @param tr - The transaction to apply the change to
|
|
203
|
-
*/
|
|
204
|
-
export const addLocalIdToNode = (pos, tr) => {
|
|
205
|
-
tr.setNodeAttribute(pos, 'localId', generateUUID());
|
|
206
|
-
tr.setMeta('addToHistory', false);
|
|
207
|
-
};
|
|
208
|
-
|
|
209
221
|
/**
|
|
210
222
|
* Batch adds local IDs to nodes using a BatchAttrsStep
|
|
211
223
|
* @param nodesToUpdate Map of position -> localId for nodes that need updates
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import _slicedToArray from "@babel/runtime/helpers/slicedToArray";
|
|
2
|
+
import _toArray from "@babel/runtime/helpers/toArray";
|
|
2
3
|
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; } } }; }
|
|
3
4
|
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; } }
|
|
4
5
|
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; }
|
|
@@ -8,6 +9,7 @@ import { SafePlugin } from '@atlaskit/editor-common/safe-plugin';
|
|
|
8
9
|
import { stepHasSlice } from '@atlaskit/editor-common/utils';
|
|
9
10
|
import { PluginKey } from '@atlaskit/editor-prosemirror/state';
|
|
10
11
|
import { fg } from '@atlaskit/platform-feature-flags';
|
|
12
|
+
import { expValEquals } from '@atlaskit/tmp-editor-statsig/exp-val-equals';
|
|
11
13
|
import { generateShortUUID, generatedShortUUIDs } from './generateShortUUID';
|
|
12
14
|
export var localIdPluginKey = new PluginKey('localIdPlugin');
|
|
13
15
|
var generateUUID = function generateUUID() {
|
|
@@ -41,7 +43,6 @@ export var createPlugin = function createPlugin(api) {
|
|
|
41
43
|
}
|
|
42
44
|
requestIdleCallbackWithFallback(function () {
|
|
43
45
|
var tr = editorView.state.tr;
|
|
44
|
-
var localIdWasAdded = false;
|
|
45
46
|
var nodesToUpdate = new Map(); // position -> localId
|
|
46
47
|
|
|
47
48
|
var _editorView$state$sch = editorView.state.schema.nodes,
|
|
@@ -54,23 +55,12 @@ export var createPlugin = function createPlugin(api) {
|
|
|
54
55
|
editorView.state.doc.descendants(function (node, pos) {
|
|
55
56
|
var _node$type$spec$attrs;
|
|
56
57
|
if (!ignoredNodeTypes.includes(node.type.name) && !node.attrs.localId && !!((_node$type$spec$attrs = node.type.spec.attrs) !== null && _node$type$spec$attrs !== void 0 && _node$type$spec$attrs.localId)) {
|
|
57
|
-
|
|
58
|
-
nodesToUpdate.set(pos, generateUUID());
|
|
59
|
-
} else {
|
|
60
|
-
localIdWasAdded = true;
|
|
61
|
-
addLocalIdToNode(pos, tr);
|
|
62
|
-
}
|
|
58
|
+
nodesToUpdate.set(pos, generateUUID());
|
|
63
59
|
}
|
|
64
60
|
return true; // Continue traversing
|
|
65
61
|
});
|
|
66
|
-
if (
|
|
67
|
-
|
|
68
|
-
batchAddLocalIdToNodes(nodesToUpdate, tr);
|
|
69
|
-
tintDirtyTransaction(tr);
|
|
70
|
-
editorView.dispatch(tr);
|
|
71
|
-
}
|
|
72
|
-
} else if (localIdWasAdded) {
|
|
73
|
-
tr.setMeta('addToHistory', false);
|
|
62
|
+
if (nodesToUpdate.size > 0) {
|
|
63
|
+
batchAddLocalIdToNodes(nodesToUpdate, tr);
|
|
74
64
|
tintDirtyTransaction(tr);
|
|
75
65
|
editorView.dispatch(tr);
|
|
76
66
|
}
|
|
@@ -98,6 +88,11 @@ export var createPlugin = function createPlugin(api) {
|
|
|
98
88
|
// https://bitbucket.org/atlassian/adf-schema/src/fb2236147a0c2bc9c8efbdb75fd8f8c411df44ba/packages/adf-schema/src/next-schema/nodes/mediaGroup.ts#lines-12
|
|
99
89
|
var ignoredNodeTypes = [text === null || text === void 0 ? void 0 : text.name, hardBreak === null || hardBreak === void 0 ? void 0 : hardBreak.name, mediaGroup === null || mediaGroup === void 0 ? void 0 : mediaGroup.name];
|
|
100
90
|
var addedNodes = new Set();
|
|
91
|
+
// A single PMNode reference can appear at multiple positions in the doc (e.g.
|
|
92
|
+
// `createTable` from `prosemirror-utils` reuses cell node objects across
|
|
93
|
+
// non-header rows), so the new code path tracks every position per node identity.
|
|
94
|
+
// The legacy path retains the single-position-per-node map for compatibility.
|
|
95
|
+
var positionsByNode = new Map();
|
|
101
96
|
var addedNodePos = new Map();
|
|
102
97
|
var localIds = new Set();
|
|
103
98
|
var nodesToUpdate = new Map(); // position -> localId
|
|
@@ -134,15 +129,17 @@ export var createPlugin = function createPlugin(api) {
|
|
|
134
129
|
if (fg('platform_editor_use_localid_dedupe')) {
|
|
135
130
|
// Always add to addedNodes for duplicate prevention
|
|
136
131
|
addedNodes.add(node);
|
|
137
|
-
|
|
132
|
+
if (expValEquals('platform_editor_ai_tablecell_localids', 'isEnabled', true)) {
|
|
133
|
+
var _positionsByNode$get;
|
|
134
|
+
var positions = (_positionsByNode$get = positionsByNode.get(node)) !== null && _positionsByNode$get !== void 0 ? _positionsByNode$get : new Set();
|
|
135
|
+
positions.add(pos);
|
|
136
|
+
positionsByNode.set(node, positions);
|
|
137
|
+
} else {
|
|
138
|
+
addedNodePos.set(node, pos);
|
|
139
|
+
}
|
|
138
140
|
} else {
|
|
139
141
|
if (!(node !== null && node !== void 0 && node.attrs.localId)) {
|
|
140
|
-
|
|
141
|
-
nodesToUpdate.set(pos, generateUUID());
|
|
142
|
-
} else {
|
|
143
|
-
// Legacy behavior - individual steps
|
|
144
|
-
addLocalIdToNode(pos, tr);
|
|
145
|
-
}
|
|
142
|
+
nodesToUpdate.set(pos, generateUUID());
|
|
146
143
|
}
|
|
147
144
|
}
|
|
148
145
|
return true;
|
|
@@ -166,57 +163,103 @@ export var createPlugin = function createPlugin(api) {
|
|
|
166
163
|
hasInitializedExistingUUIDs = true;
|
|
167
164
|
// Also ensure the added have no duplicates
|
|
168
165
|
var seenIds = new Set();
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
var
|
|
176
|
-
if (
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
166
|
+
if (expValEquals('platform_editor_ai_tablecell_localids', 'isEnabled', true)) {
|
|
167
|
+
var _iterator = _createForOfIteratorHelper(addedNodes),
|
|
168
|
+
_step;
|
|
169
|
+
try {
|
|
170
|
+
for (_iterator.s(); !(_step = _iterator.n()).done;) {
|
|
171
|
+
var node = _step.value;
|
|
172
|
+
var positions = positionsByNode.get(node);
|
|
173
|
+
if (!positions || positions.size === 0) {
|
|
174
|
+
continue;
|
|
175
|
+
}
|
|
176
|
+
var existingId = node.attrs.localId;
|
|
177
|
+
var needsNewIds = !existingId || localIds.has(existingId) || seenIds.has(existingId);
|
|
178
|
+
if (needsNewIds) {
|
|
179
|
+
// No usable localId: assign a fresh unique one to every position.
|
|
180
|
+
var _iterator2 = _createForOfIteratorHelper(positions),
|
|
181
|
+
_step2;
|
|
182
|
+
try {
|
|
183
|
+
for (_iterator2.s(); !(_step2 = _iterator2.n()).done;) {
|
|
184
|
+
var pos = _step2.value;
|
|
185
|
+
var newId = generateUUID();
|
|
186
|
+
nodesToUpdate.set(pos, newId);
|
|
187
|
+
seenIds.add(newId);
|
|
188
|
+
modified = true;
|
|
189
|
+
}
|
|
190
|
+
} catch (err) {
|
|
191
|
+
_iterator2.e(err);
|
|
192
|
+
} finally {
|
|
193
|
+
_iterator2.f();
|
|
194
|
+
}
|
|
195
|
+
} else if (positions.size > 1) {
|
|
196
|
+
// Shared node reference: keep the existing id at the first position,
|
|
197
|
+
// assign fresh ones to the rest so they don't share the same localId.
|
|
198
|
+
seenIds.add(existingId);
|
|
199
|
+
var _Array$from = Array.from(positions),
|
|
200
|
+
_Array$from2 = _toArray(_Array$from),
|
|
201
|
+
_first = _Array$from2[0],
|
|
202
|
+
rest = _Array$from2.slice(1);
|
|
203
|
+
var _iterator3 = _createForOfIteratorHelper(rest),
|
|
204
|
+
_step3;
|
|
205
|
+
try {
|
|
206
|
+
for (_iterator3.s(); !(_step3 = _iterator3.n()).done;) {
|
|
207
|
+
var _pos = _step3.value;
|
|
208
|
+
var _newId = generateUUID();
|
|
209
|
+
nodesToUpdate.set(_pos, _newId);
|
|
210
|
+
seenIds.add(_newId);
|
|
211
|
+
modified = true;
|
|
212
|
+
}
|
|
213
|
+
} catch (err) {
|
|
214
|
+
_iterator3.e(err);
|
|
215
|
+
} finally {
|
|
216
|
+
_iterator3.f();
|
|
183
217
|
}
|
|
184
|
-
|
|
218
|
+
} else if (existingId) {
|
|
219
|
+
seenIds.add(existingId);
|
|
185
220
|
}
|
|
186
221
|
}
|
|
187
|
-
|
|
188
|
-
|
|
222
|
+
} catch (err) {
|
|
223
|
+
_iterator.e(err);
|
|
224
|
+
} finally {
|
|
225
|
+
_iterator.f();
|
|
226
|
+
}
|
|
227
|
+
} else {
|
|
228
|
+
var _iterator4 = _createForOfIteratorHelper(addedNodes),
|
|
229
|
+
_step4;
|
|
230
|
+
try {
|
|
231
|
+
for (_iterator4.s(); !(_step4 = _iterator4.n()).done;) {
|
|
232
|
+
var _node = _step4.value;
|
|
233
|
+
if (!_node.attrs.localId || localIds.has(_node.attrs.localId) || seenIds.has(_node.attrs.localId)) {
|
|
234
|
+
var _pos2 = addedNodePos.get(_node);
|
|
235
|
+
if (_pos2 !== undefined) {
|
|
236
|
+
var _newId2 = generateUUID();
|
|
237
|
+
nodesToUpdate.set(_pos2, _newId2);
|
|
238
|
+
seenIds.add(_newId2);
|
|
239
|
+
modified = true;
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
if (_node.attrs.localId) {
|
|
243
|
+
seenIds.add(_node.attrs.localId);
|
|
244
|
+
}
|
|
189
245
|
}
|
|
246
|
+
} catch (err) {
|
|
247
|
+
_iterator4.e(err);
|
|
248
|
+
} finally {
|
|
249
|
+
_iterator4.f();
|
|
190
250
|
}
|
|
191
|
-
} catch (err) {
|
|
192
|
-
_iterator.e(err);
|
|
193
|
-
} finally {
|
|
194
|
-
_iterator.f();
|
|
195
251
|
}
|
|
196
252
|
}
|
|
197
253
|
// Apply local ID updates based on the improvements feature flag:
|
|
198
254
|
// - When enabled: Batch all updates into a single BatchAttrsStep
|
|
199
255
|
// - When disabled: Individual steps were already applied above during node processing
|
|
200
|
-
if (modified && nodesToUpdate.size > 0
|
|
256
|
+
if (modified && nodesToUpdate.size > 0) {
|
|
201
257
|
batchAddLocalIdToNodes(nodesToUpdate, tr);
|
|
202
258
|
}
|
|
203
259
|
return modified ? tr : undefined;
|
|
204
260
|
}
|
|
205
261
|
});
|
|
206
262
|
};
|
|
207
|
-
/**
|
|
208
|
-
* Adds a local ID to a ProseMirror node
|
|
209
|
-
*
|
|
210
|
-
* This utility function updates a node's attributes to include a unique local ID.
|
|
211
|
-
*
|
|
212
|
-
* @param pos - The position of the node in the document
|
|
213
|
-
* @param tr - The transaction to apply the change to
|
|
214
|
-
*/
|
|
215
|
-
export var addLocalIdToNode = function addLocalIdToNode(pos, tr) {
|
|
216
|
-
tr.setNodeAttribute(pos, 'localId', generateUUID());
|
|
217
|
-
tr.setMeta('addToHistory', false);
|
|
218
|
-
};
|
|
219
|
-
|
|
220
263
|
/**
|
|
221
264
|
* Batch adds local IDs to nodes using a BatchAttrsStep
|
|
222
265
|
* @param nodesToUpdate Map of position -> localId for nodes that need updates
|
|
@@ -4,15 +4,6 @@ import { PluginKey, type Transaction } from '@atlaskit/editor-prosemirror/state'
|
|
|
4
4
|
import type { LocalIdPlugin } from '../localIdPluginType';
|
|
5
5
|
export declare const localIdPluginKey: PluginKey;
|
|
6
6
|
export declare const createPlugin: (api: ExtractInjectionAPI<LocalIdPlugin> | undefined) => SafePlugin;
|
|
7
|
-
/**
|
|
8
|
-
* Adds a local ID to a ProseMirror node
|
|
9
|
-
*
|
|
10
|
-
* This utility function updates a node's attributes to include a unique local ID.
|
|
11
|
-
*
|
|
12
|
-
* @param pos - The position of the node in the document
|
|
13
|
-
* @param tr - The transaction to apply the change to
|
|
14
|
-
*/
|
|
15
|
-
export declare const addLocalIdToNode: (pos: number, tr: Transaction) => void;
|
|
16
7
|
/**
|
|
17
8
|
* Batch adds local IDs to nodes using a BatchAttrsStep
|
|
18
9
|
* @param nodesToUpdate Map of position -> localId for nodes that need updates
|
|
@@ -4,15 +4,6 @@ import { PluginKey, type Transaction } from '@atlaskit/editor-prosemirror/state'
|
|
|
4
4
|
import type { LocalIdPlugin } from '../localIdPluginType';
|
|
5
5
|
export declare const localIdPluginKey: PluginKey;
|
|
6
6
|
export declare const createPlugin: (api: ExtractInjectionAPI<LocalIdPlugin> | undefined) => SafePlugin;
|
|
7
|
-
/**
|
|
8
|
-
* Adds a local ID to a ProseMirror node
|
|
9
|
-
*
|
|
10
|
-
* This utility function updates a node's attributes to include a unique local ID.
|
|
11
|
-
*
|
|
12
|
-
* @param pos - The position of the node in the document
|
|
13
|
-
* @param tr - The transaction to apply the change to
|
|
14
|
-
*/
|
|
15
|
-
export declare const addLocalIdToNode: (pos: number, tr: Transaction) => void;
|
|
16
7
|
/**
|
|
17
8
|
* Batch adds local IDs to nodes using a BatchAttrsStep
|
|
18
9
|
* @param nodesToUpdate Map of position -> localId for nodes that need updates
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@atlaskit/editor-plugin-local-id",
|
|
3
|
-
"version": "8.
|
|
3
|
+
"version": "8.3.0",
|
|
4
4
|
"description": "LocalId plugin for @atlaskit/editor-core",
|
|
5
5
|
"author": "Atlassian Pty Ltd",
|
|
6
6
|
"license": "Apache-2.0",
|
|
@@ -34,14 +34,16 @@
|
|
|
34
34
|
"@atlaskit/editor-plugin-limited-mode": "^7.2.0",
|
|
35
35
|
"@atlaskit/editor-prosemirror": "^7.3.0",
|
|
36
36
|
"@atlaskit/platform-feature-flags": "^1.1.0",
|
|
37
|
+
"@atlaskit/tmp-editor-statsig": "^82.3.0",
|
|
37
38
|
"@babel/runtime": "^7.0.0",
|
|
38
39
|
"raf-schd": "^4.0.3"
|
|
39
40
|
},
|
|
40
41
|
"peerDependencies": {
|
|
41
|
-
"@atlaskit/editor-common": "^114.
|
|
42
|
+
"@atlaskit/editor-common": "^114.41.0",
|
|
42
43
|
"react": "^18.2.0"
|
|
43
44
|
},
|
|
44
45
|
"devDependencies": {
|
|
46
|
+
"react": "^18.2.0",
|
|
45
47
|
"typescript": "5.9.2"
|
|
46
48
|
},
|
|
47
49
|
"techstack": {
|
|
@@ -84,9 +86,6 @@
|
|
|
84
86
|
"platform_editor_use_localid_dedupe": {
|
|
85
87
|
"type": "boolean"
|
|
86
88
|
},
|
|
87
|
-
"platform_editor_localid_improvements": {
|
|
88
|
-
"type": "boolean"
|
|
89
|
-
},
|
|
90
89
|
"platform_editor_ai_aifc_localid_error_reporting": {
|
|
91
90
|
"type": "boolean"
|
|
92
91
|
}
|