@atlaskit/editor-plugin-mentions 14.1.1 → 14.2.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,23 @@
1
1
  # @atlaskit/editor-plugin-mentions
2
2
 
3
+ ## 14.2.1
4
+
5
+ ### Patch Changes
6
+
7
+ - Updated dependencies
8
+
9
+ ## 14.2.0
10
+
11
+ ### Minor Changes
12
+
13
+ - [`cf7bf77da7d41`](https://bitbucket.org/atlassian/atlassian-frontend-monorepo/commits/cf7bf77da7d41) -
14
+ Delay typed agent mention trigger until editor inactivity or when selection leaves the parent
15
+ block
16
+
17
+ ### Patch Changes
18
+
19
+ - Updated dependencies
20
+
3
21
  ## 14.1.1
4
22
 
5
23
  ### Patch Changes
@@ -31,11 +31,15 @@ var ACTIONS = exports.ACTIONS = {
31
31
  };
32
32
 
33
33
  // 'AGENT' is not in the ADF schema UserType enum but is used at runtime.
34
+
34
35
  var AGENT_USER_TYPES = new Set(['APP', 'AGENT']);
36
+ var isAgentUserType = function isAgentUserType(userType) {
37
+ return typeof userType === 'string' && AGENT_USER_TYPES.has(userType);
38
+ };
35
39
  var AI_STREAMING_TRANSFORMATION_META_KEY = 'isAIStreamingTransformation';
36
40
  var AGENT_MENTION_INACTIVITY_MS = 3000;
37
41
  var PACKAGE_NAME = "@atlaskit/editor-plugin-mentions";
38
- var PACKAGE_VERSION = "14.1.0";
42
+ var PACKAGE_VERSION = "14.2.0";
39
43
  var setProvider = function setProvider(provider) {
40
44
  return function (state, dispatch) {
41
45
  if (dispatch) {
@@ -61,6 +65,14 @@ var isQualifyingLocalUserDocChange = function isQualifyingLocalUserDocChange(tr)
61
65
  var isAIStreaming = Boolean(tr.getMeta(AI_STREAMING_TRANSFORMATION_META_KEY));
62
66
  return tr.docChanged && !tr.getMeta('isRemote') && !tr.getMeta('replaceDocument') && !isAIStreaming;
63
67
  };
68
+ var isLocalSelectionChange = function isLocalSelectionChange(tr, hasPositionChanged) {
69
+ var isAIStreaming = Boolean(tr.getMeta(AI_STREAMING_TRANSFORMATION_META_KEY));
70
+
71
+ // Pressing Enter can move selection through a doc split without setting tr.selectionSet
72
+ // or changing from/to numerically, so local doc changes are checked against the
73
+ // pending mention's current parent before publishing.
74
+ return (hasPositionChanged || tr.docChanged) && !tr.getMeta('isRemote') && !tr.getMeta('replaceDocument') && !isAIStreaming;
75
+ };
64
76
 
65
77
  /**
66
78
  * Reads agent-mention details from a known document position without traversing
@@ -74,16 +86,18 @@ var getAgentMentionDetailsAtPos = function getAgentMentionDetailsAtPos(state, po
74
86
  }
75
87
  var node = state.doc.nodeAt(pos);
76
88
  var mentionSchema = state.schema.nodes.mention;
77
- if ((node === null || node === void 0 ? void 0 : node.type) !== mentionSchema || !AGENT_USER_TYPES.has(node.attrs.userType) || !matchesMention(node.attrs) || !node.attrs.id) {
89
+ if ((node === null || node === void 0 ? void 0 : node.type) !== mentionSchema || !isAgentUserType(node.attrs.userType) || !matchesMention(node.attrs) || !node.attrs.id) {
78
90
  return null;
79
91
  }
80
- var $mentionPos = state.doc.resolve(pos);
92
+ var $mentionPos = state.doc.resolve(Math.min(pos + node.nodeSize, state.doc.content.size));
81
93
  var parentNode = $mentionPos.node($mentionPos.depth);
82
94
  return {
83
95
  id: node.attrs.id,
84
96
  context: parentNode.textContent.trim() || null,
85
97
  nodeSize: node.nodeSize,
98
+ parentEnd: $mentionPos.end($mentionPos.depth),
86
99
  parentNodeType: (_parentNode$type$name = parentNode.type.name) !== null && _parentNode$type$name !== void 0 ? _parentNode$type$name : null,
100
+ parentStart: $mentionPos.start($mentionPos.depth),
87
101
  pos: pos
88
102
  };
89
103
  };
@@ -101,7 +115,7 @@ var getSurvivingAgentMentionDetails = function getSurvivingAgentMentionDetails(s
101
115
  if (((_result = result) === null || _result === void 0 ? void 0 : _result.id) === preferredId) {
102
116
  return false;
103
117
  }
104
- if (node.type !== mentionSchema || !AGENT_USER_TYPES.has(node.attrs.userType) || !node.attrs.id) {
118
+ if (node.type !== mentionSchema || !isAgentUserType(node.attrs.userType) || !node.attrs.id) {
105
119
  return true;
106
120
  }
107
121
  result = getAgentMentionDetailsAtPos(state, pos, function (attrs) {
@@ -135,6 +149,79 @@ var getPendingTypedAgentMentionAfterDocChange = function getPendingTypedAgentMen
135
149
  resetCount: resetCount
136
150
  } : null;
137
151
  };
152
+ var hasPendingMentionMovedToNewParent = function hasPendingMentionMovedToNewParent(oldState, tr, previousPendingTypedAgentMention, pendingMentionDetails) {
153
+ if (!previousPendingTypedAgentMention) {
154
+ return false;
155
+ }
156
+ var previousMentionDetails = getAgentMentionDetailsAtPos(oldState, previousPendingTypedAgentMention.pos, function (attrs) {
157
+ return attrs.localId === previousPendingTypedAgentMention.localId;
158
+ });
159
+
160
+ // Keep the previous parent boundary associated with the left side of an
161
+ // insertion at that boundary, so typing at the start of the parent does not
162
+ // look like the pending mention moved into a new parent.
163
+ var mappedPreviousParentStart = previousMentionDetails && tr.mapping.map(previousMentionDetails.parentStart, -1);
164
+ return Boolean(previousMentionDetails && mappedPreviousParentStart !== pendingMentionDetails.parentStart);
165
+ };
166
+ var isSelectionOutsideDirectParent = function isSelectionOutsideDirectParent(state, pendingMentionDetails) {
167
+ return state.selection.from < pendingMentionDetails.parentStart || state.selection.to > pendingMentionDetails.parentEnd;
168
+ };
169
+
170
+ /**
171
+ * Finalises a pending typed agent mention by copying its details into the
172
+ * public lastInserted* plugin state after the caller has already resolved the
173
+ * pending mention from the current document.
174
+ */
175
+ var commitResolvedPendingTypedAgentMention = function commitResolvedPendingTypedAgentMention(pluginState, pendingMentionDetails) {
176
+ var _pluginState$lastAgen;
177
+ return {
178
+ hasPublicPluginStateChanged: true,
179
+ pluginState: _objectSpread(_objectSpread({}, pluginState), {}, {
180
+ pendingTypedAgentMention: null,
181
+ lastInsertedAgentMentionId: pendingMentionDetails.id,
182
+ lastInsertedAgentMentionContext: pendingMentionDetails.context,
183
+ lastInsertedAgentMentionParentNodeType: pendingMentionDetails.parentNodeType,
184
+ lastAgentMentionInsertionCount: ((_pluginState$lastAgen = pluginState.lastAgentMentionInsertionCount) !== null && _pluginState$lastAgen !== void 0 ? _pluginState$lastAgen : 0) + 1
185
+ })
186
+ };
187
+ };
188
+
189
+ /**
190
+ * Resolves and finalises a pending typed agent mention. If the tracked mention
191
+ * no longer resolves, the stale pending state is cleared without dispatching a
192
+ * public update.
193
+ */
194
+ var commitPendingTypedAgentMention = function commitPendingTypedAgentMention(state, pluginState, pendingTypedAgentMention) {
195
+ var pendingMentionDetails = getAgentMentionDetailsAtPos(state, pendingTypedAgentMention.pos, function (attrs) {
196
+ return attrs.localId === pendingTypedAgentMention.localId;
197
+ });
198
+ if (!pendingMentionDetails) {
199
+ return {
200
+ hasPublicPluginStateChanged: false,
201
+ pluginState: _objectSpread(_objectSpread({}, pluginState), {}, {
202
+ pendingTypedAgentMention: null
203
+ })
204
+ };
205
+ }
206
+ return commitResolvedPendingTypedAgentMention(pluginState, pendingMentionDetails);
207
+ };
208
+ var hasTrackedAgentMentionState = function hasTrackedAgentMentionState(pluginState) {
209
+ return Boolean(pluginState.pendingTypedAgentMention) || pluginState.lastInsertedAgentMentionId != null || pluginState.lastInsertedAgentMentionContext != null || pluginState.lastInsertedAgentMentionParentNodeType != null;
210
+ };
211
+
212
+ /**
213
+ * Clears agent mention state that points at a specific document snapshot.
214
+ * replaceDocument swaps content wholesale, so pending typed mentions and
215
+ * lastInserted* details from the previous document must be cleared together.
216
+ */
217
+ var clearTrackedAgentMentionState = function clearTrackedAgentMentionState(pluginState) {
218
+ return _objectSpread(_objectSpread({}, pluginState), {}, {
219
+ pendingTypedAgentMention: null,
220
+ lastInsertedAgentMentionId: null,
221
+ lastInsertedAgentMentionContext: null,
222
+ lastInsertedAgentMentionParentNodeType: null
223
+ });
224
+ };
138
225
  function createMentionPlugin(_ref2) {
139
226
  var pmPluginFactoryParams = _ref2.pmPluginFactoryParams,
140
227
  fireEvent = _ref2.fireEvent,
@@ -184,30 +271,15 @@ function createMentionPlugin(_ref2) {
184
271
  switch (action) {
185
272
  case ACTIONS.COMMIT_PENDING_TYPED_AGENT_MENTION:
186
273
  {
187
- var _newPluginState$lastA;
188
274
  var pendingTypedAgentMention = newPluginState.pendingTypedAgentMention;
189
275
  // Ignore stale timer callbacks. The localId and resetCount must still match the
190
276
  // current pending mention so older timers cannot publish after later user edits.
191
277
  if (!isAgentMentionsExperimentEnabled || !pendingTypedAgentMention || pendingTypedAgentMention.localId !== (params === null || params === void 0 ? void 0 : params.localId) || pendingTypedAgentMention.resetCount !== (params === null || params === void 0 ? void 0 : params.resetCount)) {
192
278
  break;
193
279
  }
194
- var pendingMentionDetails = getAgentMentionDetailsAtPos(newState, pendingTypedAgentMention.pos, function (attrs) {
195
- return attrs.localId === pendingTypedAgentMention.localId;
196
- });
197
- if (!pendingMentionDetails) {
198
- newPluginState = _objectSpread(_objectSpread({}, newPluginState), {}, {
199
- pendingTypedAgentMention: null
200
- });
201
- break;
202
- }
203
- newPluginState = _objectSpread(_objectSpread({}, newPluginState), {}, {
204
- pendingTypedAgentMention: null,
205
- lastInsertedAgentMentionId: pendingMentionDetails.id,
206
- lastInsertedAgentMentionContext: pendingMentionDetails.context,
207
- lastInsertedAgentMentionParentNodeType: pendingMentionDetails.parentNodeType,
208
- lastAgentMentionInsertionCount: ((_newPluginState$lastA = newPluginState.lastAgentMentionInsertionCount) !== null && _newPluginState$lastA !== void 0 ? _newPluginState$lastA : 0) + 1
209
- });
210
- hasPublicPluginStateChanged = true;
280
+ var commitResult = commitPendingTypedAgentMention(newState, newPluginState, pendingTypedAgentMention);
281
+ newPluginState = commitResult.pluginState;
282
+ hasPublicPluginStateChanged = hasPublicPluginStateChanged || commitResult.hasPublicPluginStateChanged;
211
283
  break;
212
284
  }
213
285
  case ACTIONS.SET_PROVIDER:
@@ -224,15 +296,6 @@ function createMentionPlugin(_ref2) {
224
296
  if (hasPublicPluginStateChanged && !isAgentMentionsExperimentEnabled) {
225
297
  pmPluginFactoryParams.dispatch(_key.mentionPluginKey, newPluginState);
226
298
  }
227
- if (isAgentMentionsExperimentEnabled && tr.docChanged && tr.getMeta('replaceDocument')) {
228
- newPluginState = _objectSpread(_objectSpread({}, newPluginState), {}, {
229
- pendingTypedAgentMention: null,
230
- lastInsertedAgentMentionId: null,
231
- lastInsertedAgentMentionContext: null,
232
- lastInsertedAgentMentionParentNodeType: null
233
- });
234
- hasPublicPluginStateChanged = true;
235
- }
236
299
  if (options !== null && options !== void 0 && options.handleMentionsChanged && tr.docChanged) {
237
300
  var _insm$session, _insm$session2;
238
301
  (_insm$session = _insm.insm.session) === null || _insm$session === void 0 || _insm$session.startFeature('mentionDeletionDetection');
@@ -313,7 +376,7 @@ function createMentionPlugin(_ref2) {
313
376
  var clampedNewTo = Math.min(newTo, newState.doc.content.size);
314
377
  if (clampedNewFrom < clampedNewTo) {
315
378
  newState.doc.nodesBetween(clampedNewFrom, clampedNewTo, function (node) {
316
- if (node.type === _mentionSchema && AGENT_USER_TYPES.has(node.attrs.userType)) {
379
+ if (node.type === _mentionSchema && isAgentUserType(node.attrs.userType)) {
317
380
  found = true;
318
381
  }
319
382
  return !found;
@@ -341,14 +404,14 @@ function createMentionPlugin(_ref2) {
341
404
  });
342
405
  var shouldResolveAgentMentionState = stepsTouchMentions || Boolean(newPluginState.lastInsertedAgentMentionId);
343
406
  if (shouldResolveAgentMentionState) {
344
- var _newPluginState$lastA2, _newPluginState$lastI, _newPluginState$lastI2, _newPluginState$lastI3;
407
+ var _newPluginState$lastA, _newPluginState$lastI, _newPluginState$lastI2, _newPluginState$lastI3;
345
408
  var agentMentionId = null;
346
409
  var agentMentionContext = null;
347
410
  var agentMentionParentNodeType = null;
348
411
  var newCount = 0;
349
412
  var oldAgentMentionId = null;
350
413
  var oldCount = 0;
351
- var pendingTypedAgentMentionDetails = {};
414
+ var pendingTypedAgentMentionDetails = null;
352
415
  if (stepsTouchMentions) {
353
416
  var _iterator = _createForOfIteratorHelper(newDocRanges),
354
417
  _step;
@@ -360,15 +423,14 @@ function createMentionPlugin(_ref2) {
360
423
  var clampedTo = Math.min(to, newState.doc.content.size);
361
424
  if (from >= clampedTo) continue;
362
425
  newState.doc.nodesBetween(from, clampedTo, function (node, pos) {
363
- if (node.type !== _mentionSchema || !AGENT_USER_TYPES.has(node.attrs.userType)) {
426
+ if (node.type !== _mentionSchema || !isAgentUserType(node.attrs.userType)) {
364
427
  return true;
365
428
  }
366
429
  newCount++;
367
- if (pendingTypedAgentMentionDetails.current === undefined && action === ACTIONS.SET_PENDING_TYPED_AGENT_MENTION && node.attrs.localId === (params === null || params === void 0 ? void 0 : params.localId)) {
368
- var _getAgentMentionDetai;
369
- pendingTypedAgentMentionDetails.current = (_getAgentMentionDetai = getAgentMentionDetailsAtPos(newState, pos, function (attrs) {
430
+ if (pendingTypedAgentMentionDetails === null && action === ACTIONS.SET_PENDING_TYPED_AGENT_MENTION && node.attrs.localId === (params === null || params === void 0 ? void 0 : params.localId)) {
431
+ pendingTypedAgentMentionDetails = getAgentMentionDetailsAtPos(newState, pos, function (attrs) {
370
432
  return attrs.localId === params.localId;
371
- })) !== null && _getAgentMentionDetai !== void 0 ? _getAgentMentionDetai : undefined;
433
+ });
372
434
  }
373
435
  if (agentMentionId === null && node.attrs.id) {
374
436
  var agentMentionDetails = getAgentMentionDetailsAtPos(newState, pos, function (attrs) {
@@ -398,7 +460,7 @@ function createMentionPlugin(_ref2) {
398
460
  var clampedOldTo = Math.min(_to, oldState.doc.content.size);
399
461
  if (_from >= clampedOldTo) continue;
400
462
  oldState.doc.nodesBetween(_from, clampedOldTo, function (node) {
401
- if (node.type !== _mentionSchema || !AGENT_USER_TYPES.has(node.attrs.userType)) {
463
+ if (node.type !== _mentionSchema || !isAgentUserType(node.attrs.userType)) {
402
464
  return true;
403
465
  }
404
466
  oldCount++;
@@ -430,15 +492,16 @@ function createMentionPlugin(_ref2) {
430
492
  }
431
493
  var isNewInsertion = agentMentionId !== null && !resolvedFromFullDocFallback && (oldAgentMentionId !== agentMentionId || newCount > oldCount);
432
494
  var isPendingTypedAgentMentionInsertion = isNewInsertion && action === ACTIONS.SET_PENDING_TYPED_AGENT_MENTION && typeof (params === null || params === void 0 ? void 0 : params.localId) === 'string';
433
- var newInsertionCount = isNewInsertion ? ((_newPluginState$lastA2 = newPluginState.lastAgentMentionInsertionCount) !== null && _newPluginState$lastA2 !== void 0 ? _newPluginState$lastA2 : 0) + 1 : undefined;
434
- if (isPendingTypedAgentMentionInsertion && pendingTypedAgentMentionDetails.current) {
495
+ var newInsertionCount = isNewInsertion ? ((_newPluginState$lastA = newPluginState.lastAgentMentionInsertionCount) !== null && _newPluginState$lastA !== void 0 ? _newPluginState$lastA : 0) + 1 : undefined;
496
+ var pendingTypedAgentMentionDetailsForState = pendingTypedAgentMentionDetails;
497
+ if (isPendingTypedAgentMentionInsertion && pendingTypedAgentMentionDetailsForState) {
435
498
  var pendingTypedAgentMentionLocalId = params === null || params === void 0 ? void 0 : params.localId;
436
499
  newPluginState = _objectSpread(_objectSpread({}, newPluginState), {}, {
437
500
  pendingTypedAgentMention: {
438
- id: pendingTypedAgentMentionDetails.current.id,
501
+ id: pendingTypedAgentMentionDetailsForState.id,
439
502
  localId: pendingTypedAgentMentionLocalId,
440
- nodeSize: pendingTypedAgentMentionDetails.current.nodeSize,
441
- pos: pendingTypedAgentMentionDetails.current.pos,
503
+ nodeSize: pendingTypedAgentMentionDetailsForState.nodeSize,
504
+ pos: pendingTypedAgentMentionDetailsForState.pos,
442
505
  resetCount: 1
443
506
  }
444
507
  });
@@ -466,6 +529,10 @@ function createMentionPlugin(_ref2) {
466
529
  }
467
530
  }
468
531
  }
532
+ if (isAgentMentionsExperimentEnabled && tr.docChanged && tr.getMeta('replaceDocument') && hasTrackedAgentMentionState(newPluginState)) {
533
+ newPluginState = clearTrackedAgentMentionState(newPluginState);
534
+ hasPublicPluginStateChanged = true;
535
+ }
469
536
  if (isAgentMentionsExperimentEnabled && newPluginState.pendingTypedAgentMention && action !== ACTIONS.SET_PENDING_TYPED_AGENT_MENTION && action !== ACTIONS.COMMIT_PENDING_TYPED_AGENT_MENTION && tr.docChanged) {
470
537
  newPluginState = _objectSpread(_objectSpread({}, newPluginState), {}, {
471
538
  pendingTypedAgentMention: getPendingTypedAgentMentionAfterDocChange(newState, tr, newPluginState.pendingTypedAgentMention, {
@@ -473,6 +540,27 @@ function createMentionPlugin(_ref2) {
473
540
  })
474
541
  });
475
542
  }
543
+
544
+ // Typed agent mentions stay pending while the user is still editing around them,
545
+ // but leaving the mention's direct parent means they have moved on from that
546
+ // paragraph/block. Publish immediately in that case instead of waiting for the
547
+ // inactivity timer.
548
+ var shouldCheckPendingTypedAgentMentionParent = isLocalSelectionChange(tr, hasPositionChanged);
549
+ if (isAgentMentionsExperimentEnabled && newPluginState.pendingTypedAgentMention && action !== ACTIONS.SET_PENDING_TYPED_AGENT_MENTION && action !== ACTIONS.COMMIT_PENDING_TYPED_AGENT_MENTION && shouldCheckPendingTypedAgentMentionParent) {
550
+ var _pendingTypedAgentMention = newPluginState.pendingTypedAgentMention;
551
+ var pendingMentionDetails = getAgentMentionDetailsAtPos(newState, _pendingTypedAgentMention.pos, function (attrs) {
552
+ return attrs.localId === _pendingTypedAgentMention.localId;
553
+ });
554
+ if (!pendingMentionDetails) {
555
+ newPluginState = _objectSpread(_objectSpread({}, newPluginState), {}, {
556
+ pendingTypedAgentMention: null
557
+ });
558
+ } else if (hasPendingMentionMovedToNewParent(oldState, tr, pluginState.pendingTypedAgentMention, pendingMentionDetails) || isSelectionOutsideDirectParent(newState, pendingMentionDetails)) {
559
+ var _commitResult = commitResolvedPendingTypedAgentMention(newPluginState, pendingMentionDetails);
560
+ newPluginState = _commitResult.pluginState;
561
+ hasPublicPluginStateChanged = hasPublicPluginStateChanged || _commitResult.hasPublicPluginStateChanged;
562
+ }
563
+ }
476
564
  if (hasPublicPluginStateChanged && isAgentMentionsExperimentEnabled) {
477
565
  pmPluginFactoryParams.dispatch(_key.mentionPluginKey, newPluginState);
478
566
  }
@@ -16,11 +16,15 @@ export const ACTIONS = {
16
16
  };
17
17
 
18
18
  // 'AGENT' is not in the ADF schema UserType enum but is used at runtime.
19
+
19
20
  const AGENT_USER_TYPES = new Set(['APP', 'AGENT']);
21
+ const isAgentUserType = userType => {
22
+ return typeof userType === 'string' && AGENT_USER_TYPES.has(userType);
23
+ };
20
24
  const AI_STREAMING_TRANSFORMATION_META_KEY = 'isAIStreamingTransformation';
21
25
  const AGENT_MENTION_INACTIVITY_MS = 3000;
22
26
  const PACKAGE_NAME = "@atlaskit/editor-plugin-mentions";
23
- const PACKAGE_VERSION = "14.1.0";
27
+ const PACKAGE_VERSION = "14.2.0";
24
28
  const setProvider = provider => (state, dispatch) => {
25
29
  if (dispatch) {
26
30
  dispatch(state.tr.setMeta(mentionPluginKey, {
@@ -44,6 +48,14 @@ const isQualifyingLocalUserDocChange = tr => {
44
48
  const isAIStreaming = Boolean(tr.getMeta(AI_STREAMING_TRANSFORMATION_META_KEY));
45
49
  return tr.docChanged && !tr.getMeta('isRemote') && !tr.getMeta('replaceDocument') && !isAIStreaming;
46
50
  };
51
+ const isLocalSelectionChange = (tr, hasPositionChanged) => {
52
+ const isAIStreaming = Boolean(tr.getMeta(AI_STREAMING_TRANSFORMATION_META_KEY));
53
+
54
+ // Pressing Enter can move selection through a doc split without setting tr.selectionSet
55
+ // or changing from/to numerically, so local doc changes are checked against the
56
+ // pending mention's current parent before publishing.
57
+ return (hasPositionChanged || tr.docChanged) && !tr.getMeta('isRemote') && !tr.getMeta('replaceDocument') && !isAIStreaming;
58
+ };
47
59
 
48
60
  /**
49
61
  * Reads agent-mention details from a known document position without traversing
@@ -57,16 +69,18 @@ const getAgentMentionDetailsAtPos = (state, pos, matchesMention) => {
57
69
  }
58
70
  const node = state.doc.nodeAt(pos);
59
71
  const mentionSchema = state.schema.nodes.mention;
60
- if ((node === null || node === void 0 ? void 0 : node.type) !== mentionSchema || !AGENT_USER_TYPES.has(node.attrs.userType) || !matchesMention(node.attrs) || !node.attrs.id) {
72
+ if ((node === null || node === void 0 ? void 0 : node.type) !== mentionSchema || !isAgentUserType(node.attrs.userType) || !matchesMention(node.attrs) || !node.attrs.id) {
61
73
  return null;
62
74
  }
63
- const $mentionPos = state.doc.resolve(pos);
75
+ const $mentionPos = state.doc.resolve(Math.min(pos + node.nodeSize, state.doc.content.size));
64
76
  const parentNode = $mentionPos.node($mentionPos.depth);
65
77
  return {
66
78
  id: node.attrs.id,
67
79
  context: parentNode.textContent.trim() || null,
68
80
  nodeSize: node.nodeSize,
81
+ parentEnd: $mentionPos.end($mentionPos.depth),
69
82
  parentNodeType: (_parentNode$type$name = parentNode.type.name) !== null && _parentNode$type$name !== void 0 ? _parentNode$type$name : null,
83
+ parentStart: $mentionPos.start($mentionPos.depth),
70
84
  pos
71
85
  };
72
86
  };
@@ -84,7 +98,7 @@ const getSurvivingAgentMentionDetails = (state, preferredId) => {
84
98
  if (((_result = result) === null || _result === void 0 ? void 0 : _result.id) === preferredId) {
85
99
  return false;
86
100
  }
87
- if (node.type !== mentionSchema || !AGENT_USER_TYPES.has(node.attrs.userType) || !node.attrs.id) {
101
+ if (node.type !== mentionSchema || !isAgentUserType(node.attrs.userType) || !node.attrs.id) {
88
102
  return true;
89
103
  }
90
104
  result = getAgentMentionDetailsAtPos(state, pos, attrs => attrs.id === node.attrs.id);
@@ -115,6 +129,74 @@ const getPendingTypedAgentMentionAfterDocChange = (state, tr, pendingTypedAgentM
115
129
  resetCount
116
130
  } : null;
117
131
  };
132
+ const hasPendingMentionMovedToNewParent = (oldState, tr, previousPendingTypedAgentMention, pendingMentionDetails) => {
133
+ if (!previousPendingTypedAgentMention) {
134
+ return false;
135
+ }
136
+ const previousMentionDetails = getAgentMentionDetailsAtPos(oldState, previousPendingTypedAgentMention.pos, attrs => attrs.localId === previousPendingTypedAgentMention.localId);
137
+
138
+ // Keep the previous parent boundary associated with the left side of an
139
+ // insertion at that boundary, so typing at the start of the parent does not
140
+ // look like the pending mention moved into a new parent.
141
+ const mappedPreviousParentStart = previousMentionDetails && tr.mapping.map(previousMentionDetails.parentStart, -1);
142
+ return Boolean(previousMentionDetails && mappedPreviousParentStart !== pendingMentionDetails.parentStart);
143
+ };
144
+ const isSelectionOutsideDirectParent = (state, pendingMentionDetails) => {
145
+ return state.selection.from < pendingMentionDetails.parentStart || state.selection.to > pendingMentionDetails.parentEnd;
146
+ };
147
+
148
+ /**
149
+ * Finalises a pending typed agent mention by copying its details into the
150
+ * public lastInserted* plugin state after the caller has already resolved the
151
+ * pending mention from the current document.
152
+ */
153
+ const commitResolvedPendingTypedAgentMention = (pluginState, pendingMentionDetails) => {
154
+ var _pluginState$lastAgen;
155
+ return {
156
+ hasPublicPluginStateChanged: true,
157
+ pluginState: {
158
+ ...pluginState,
159
+ pendingTypedAgentMention: null,
160
+ lastInsertedAgentMentionId: pendingMentionDetails.id,
161
+ lastInsertedAgentMentionContext: pendingMentionDetails.context,
162
+ lastInsertedAgentMentionParentNodeType: pendingMentionDetails.parentNodeType,
163
+ lastAgentMentionInsertionCount: ((_pluginState$lastAgen = pluginState.lastAgentMentionInsertionCount) !== null && _pluginState$lastAgen !== void 0 ? _pluginState$lastAgen : 0) + 1
164
+ }
165
+ };
166
+ };
167
+
168
+ /**
169
+ * Resolves and finalises a pending typed agent mention. If the tracked mention
170
+ * no longer resolves, the stale pending state is cleared without dispatching a
171
+ * public update.
172
+ */
173
+ const commitPendingTypedAgentMention = (state, pluginState, pendingTypedAgentMention) => {
174
+ const pendingMentionDetails = getAgentMentionDetailsAtPos(state, pendingTypedAgentMention.pos, attrs => attrs.localId === pendingTypedAgentMention.localId);
175
+ if (!pendingMentionDetails) {
176
+ return {
177
+ hasPublicPluginStateChanged: false,
178
+ pluginState: {
179
+ ...pluginState,
180
+ pendingTypedAgentMention: null
181
+ }
182
+ };
183
+ }
184
+ return commitResolvedPendingTypedAgentMention(pluginState, pendingMentionDetails);
185
+ };
186
+ const hasTrackedAgentMentionState = pluginState => Boolean(pluginState.pendingTypedAgentMention) || pluginState.lastInsertedAgentMentionId != null || pluginState.lastInsertedAgentMentionContext != null || pluginState.lastInsertedAgentMentionParentNodeType != null;
187
+
188
+ /**
189
+ * Clears agent mention state that points at a specific document snapshot.
190
+ * replaceDocument swaps content wholesale, so pending typed mentions and
191
+ * lastInserted* details from the previous document must be cleared together.
192
+ */
193
+ const clearTrackedAgentMentionState = pluginState => ({
194
+ ...pluginState,
195
+ pendingTypedAgentMention: null,
196
+ lastInsertedAgentMentionId: null,
197
+ lastInsertedAgentMentionContext: null,
198
+ lastInsertedAgentMentionParentNodeType: null
199
+ });
118
200
  export function createMentionPlugin({
119
201
  pmPluginFactoryParams,
120
202
  fireEvent,
@@ -168,30 +250,15 @@ export function createMentionPlugin({
168
250
  switch (action) {
169
251
  case ACTIONS.COMMIT_PENDING_TYPED_AGENT_MENTION:
170
252
  {
171
- var _newPluginState$lastA;
172
253
  const pendingTypedAgentMention = newPluginState.pendingTypedAgentMention;
173
254
  // Ignore stale timer callbacks. The localId and resetCount must still match the
174
255
  // current pending mention so older timers cannot publish after later user edits.
175
256
  if (!isAgentMentionsExperimentEnabled || !pendingTypedAgentMention || pendingTypedAgentMention.localId !== (params === null || params === void 0 ? void 0 : params.localId) || pendingTypedAgentMention.resetCount !== (params === null || params === void 0 ? void 0 : params.resetCount)) {
176
257
  break;
177
258
  }
178
- const pendingMentionDetails = getAgentMentionDetailsAtPos(newState, pendingTypedAgentMention.pos, attrs => attrs.localId === pendingTypedAgentMention.localId);
179
- if (!pendingMentionDetails) {
180
- newPluginState = {
181
- ...newPluginState,
182
- pendingTypedAgentMention: null
183
- };
184
- break;
185
- }
186
- newPluginState = {
187
- ...newPluginState,
188
- pendingTypedAgentMention: null,
189
- lastInsertedAgentMentionId: pendingMentionDetails.id,
190
- lastInsertedAgentMentionContext: pendingMentionDetails.context,
191
- lastInsertedAgentMentionParentNodeType: pendingMentionDetails.parentNodeType,
192
- lastAgentMentionInsertionCount: ((_newPluginState$lastA = newPluginState.lastAgentMentionInsertionCount) !== null && _newPluginState$lastA !== void 0 ? _newPluginState$lastA : 0) + 1
193
- };
194
- hasPublicPluginStateChanged = true;
259
+ const commitResult = commitPendingTypedAgentMention(newState, newPluginState, pendingTypedAgentMention);
260
+ newPluginState = commitResult.pluginState;
261
+ hasPublicPluginStateChanged = hasPublicPluginStateChanged || commitResult.hasPublicPluginStateChanged;
195
262
  break;
196
263
  }
197
264
  case ACTIONS.SET_PROVIDER:
@@ -209,16 +276,6 @@ export function createMentionPlugin({
209
276
  if (hasPublicPluginStateChanged && !isAgentMentionsExperimentEnabled) {
210
277
  pmPluginFactoryParams.dispatch(mentionPluginKey, newPluginState);
211
278
  }
212
- if (isAgentMentionsExperimentEnabled && tr.docChanged && tr.getMeta('replaceDocument')) {
213
- newPluginState = {
214
- ...newPluginState,
215
- pendingTypedAgentMention: null,
216
- lastInsertedAgentMentionId: null,
217
- lastInsertedAgentMentionContext: null,
218
- lastInsertedAgentMentionParentNodeType: null
219
- };
220
- hasPublicPluginStateChanged = true;
221
- }
222
279
  if (options !== null && options !== void 0 && options.handleMentionsChanged && tr.docChanged) {
223
280
  var _insm$session, _insm$session2;
224
281
  (_insm$session = insm.session) === null || _insm$session === void 0 ? void 0 : _insm$session.startFeature('mentionDeletionDetection');
@@ -297,7 +354,7 @@ export function createMentionPlugin({
297
354
  const clampedNewTo = Math.min(newTo, newState.doc.content.size);
298
355
  if (clampedNewFrom < clampedNewTo) {
299
356
  newState.doc.nodesBetween(clampedNewFrom, clampedNewTo, node => {
300
- if (node.type === mentionSchema && AGENT_USER_TYPES.has(node.attrs.userType)) {
357
+ if (node.type === mentionSchema && isAgentUserType(node.attrs.userType)) {
301
358
  found = true;
302
359
  }
303
360
  return !found;
@@ -325,26 +382,25 @@ export function createMentionPlugin({
325
382
  });
326
383
  const shouldResolveAgentMentionState = stepsTouchMentions || Boolean(newPluginState.lastInsertedAgentMentionId);
327
384
  if (shouldResolveAgentMentionState) {
328
- var _newPluginState$lastA2, _newPluginState$lastI, _newPluginState$lastI2, _newPluginState$lastI3;
385
+ var _newPluginState$lastA, _newPluginState$lastI, _newPluginState$lastI2, _newPluginState$lastI3;
329
386
  let agentMentionId = null;
330
387
  let agentMentionContext = null;
331
388
  let agentMentionParentNodeType = null;
332
389
  let newCount = 0;
333
390
  let oldAgentMentionId = null;
334
391
  let oldCount = 0;
335
- const pendingTypedAgentMentionDetails = {};
392
+ let pendingTypedAgentMentionDetails = null;
336
393
  if (stepsTouchMentions) {
337
394
  for (const [from, to] of newDocRanges) {
338
395
  const clampedTo = Math.min(to, newState.doc.content.size);
339
396
  if (from >= clampedTo) continue;
340
397
  newState.doc.nodesBetween(from, clampedTo, (node, pos) => {
341
- if (node.type !== mentionSchema || !AGENT_USER_TYPES.has(node.attrs.userType)) {
398
+ if (node.type !== mentionSchema || !isAgentUserType(node.attrs.userType)) {
342
399
  return true;
343
400
  }
344
401
  newCount++;
345
- if (pendingTypedAgentMentionDetails.current === undefined && action === ACTIONS.SET_PENDING_TYPED_AGENT_MENTION && node.attrs.localId === (params === null || params === void 0 ? void 0 : params.localId)) {
346
- var _getAgentMentionDetai;
347
- pendingTypedAgentMentionDetails.current = (_getAgentMentionDetai = getAgentMentionDetailsAtPos(newState, pos, attrs => attrs.localId === params.localId)) !== null && _getAgentMentionDetai !== void 0 ? _getAgentMentionDetai : undefined;
402
+ if (pendingTypedAgentMentionDetails === null && action === ACTIONS.SET_PENDING_TYPED_AGENT_MENTION && node.attrs.localId === (params === null || params === void 0 ? void 0 : params.localId)) {
403
+ pendingTypedAgentMentionDetails = getAgentMentionDetailsAtPos(newState, pos, attrs => attrs.localId === params.localId);
348
404
  }
349
405
  if (agentMentionId === null && node.attrs.id) {
350
406
  const agentMentionDetails = getAgentMentionDetailsAtPos(newState, pos, attrs => attrs.id === node.attrs.id);
@@ -361,7 +417,7 @@ export function createMentionPlugin({
361
417
  const clampedOldTo = Math.min(to, oldState.doc.content.size);
362
418
  if (from >= clampedOldTo) continue;
363
419
  oldState.doc.nodesBetween(from, clampedOldTo, node => {
364
- if (node.type !== mentionSchema || !AGENT_USER_TYPES.has(node.attrs.userType)) {
420
+ if (node.type !== mentionSchema || !isAgentUserType(node.attrs.userType)) {
365
421
  return true;
366
422
  }
367
423
  oldCount++;
@@ -388,16 +444,17 @@ export function createMentionPlugin({
388
444
  }
389
445
  const isNewInsertion = agentMentionId !== null && !resolvedFromFullDocFallback && (oldAgentMentionId !== agentMentionId || newCount > oldCount);
390
446
  const isPendingTypedAgentMentionInsertion = isNewInsertion && action === ACTIONS.SET_PENDING_TYPED_AGENT_MENTION && typeof (params === null || params === void 0 ? void 0 : params.localId) === 'string';
391
- const newInsertionCount = isNewInsertion ? ((_newPluginState$lastA2 = newPluginState.lastAgentMentionInsertionCount) !== null && _newPluginState$lastA2 !== void 0 ? _newPluginState$lastA2 : 0) + 1 : undefined;
392
- if (isPendingTypedAgentMentionInsertion && pendingTypedAgentMentionDetails.current) {
447
+ const newInsertionCount = isNewInsertion ? ((_newPluginState$lastA = newPluginState.lastAgentMentionInsertionCount) !== null && _newPluginState$lastA !== void 0 ? _newPluginState$lastA : 0) + 1 : undefined;
448
+ const pendingTypedAgentMentionDetailsForState = pendingTypedAgentMentionDetails;
449
+ if (isPendingTypedAgentMentionInsertion && pendingTypedAgentMentionDetailsForState) {
393
450
  const pendingTypedAgentMentionLocalId = params === null || params === void 0 ? void 0 : params.localId;
394
451
  newPluginState = {
395
452
  ...newPluginState,
396
453
  pendingTypedAgentMention: {
397
- id: pendingTypedAgentMentionDetails.current.id,
454
+ id: pendingTypedAgentMentionDetailsForState.id,
398
455
  localId: pendingTypedAgentMentionLocalId,
399
- nodeSize: pendingTypedAgentMentionDetails.current.nodeSize,
400
- pos: pendingTypedAgentMentionDetails.current.pos,
456
+ nodeSize: pendingTypedAgentMentionDetailsForState.nodeSize,
457
+ pos: pendingTypedAgentMentionDetailsForState.pos,
401
458
  resetCount: 1
402
459
  }
403
460
  };
@@ -429,6 +486,10 @@ export function createMentionPlugin({
429
486
  }
430
487
  }
431
488
  }
489
+ if (isAgentMentionsExperimentEnabled && tr.docChanged && tr.getMeta('replaceDocument') && hasTrackedAgentMentionState(newPluginState)) {
490
+ newPluginState = clearTrackedAgentMentionState(newPluginState);
491
+ hasPublicPluginStateChanged = true;
492
+ }
432
493
  if (isAgentMentionsExperimentEnabled && newPluginState.pendingTypedAgentMention && action !== ACTIONS.SET_PENDING_TYPED_AGENT_MENTION && action !== ACTIONS.COMMIT_PENDING_TYPED_AGENT_MENTION && tr.docChanged) {
433
494
  newPluginState = {
434
495
  ...newPluginState,
@@ -437,6 +498,26 @@ export function createMentionPlugin({
437
498
  })
438
499
  };
439
500
  }
501
+
502
+ // Typed agent mentions stay pending while the user is still editing around them,
503
+ // but leaving the mention's direct parent means they have moved on from that
504
+ // paragraph/block. Publish immediately in that case instead of waiting for the
505
+ // inactivity timer.
506
+ const shouldCheckPendingTypedAgentMentionParent = isLocalSelectionChange(tr, hasPositionChanged);
507
+ if (isAgentMentionsExperimentEnabled && newPluginState.pendingTypedAgentMention && action !== ACTIONS.SET_PENDING_TYPED_AGENT_MENTION && action !== ACTIONS.COMMIT_PENDING_TYPED_AGENT_MENTION && shouldCheckPendingTypedAgentMentionParent) {
508
+ const pendingTypedAgentMention = newPluginState.pendingTypedAgentMention;
509
+ const pendingMentionDetails = getAgentMentionDetailsAtPos(newState, pendingTypedAgentMention.pos, attrs => attrs.localId === pendingTypedAgentMention.localId);
510
+ if (!pendingMentionDetails) {
511
+ newPluginState = {
512
+ ...newPluginState,
513
+ pendingTypedAgentMention: null
514
+ };
515
+ } else if (hasPendingMentionMovedToNewParent(oldState, tr, pluginState.pendingTypedAgentMention, pendingMentionDetails) || isSelectionOutsideDirectParent(newState, pendingMentionDetails)) {
516
+ const commitResult = commitResolvedPendingTypedAgentMention(newPluginState, pendingMentionDetails);
517
+ newPluginState = commitResult.pluginState;
518
+ hasPublicPluginStateChanged = hasPublicPluginStateChanged || commitResult.hasPublicPluginStateChanged;
519
+ }
520
+ }
440
521
  if (hasPublicPluginStateChanged && isAgentMentionsExperimentEnabled) {
441
522
  pmPluginFactoryParams.dispatch(mentionPluginKey, newPluginState);
442
523
  }
@@ -23,11 +23,15 @@ export var ACTIONS = {
23
23
  };
24
24
 
25
25
  // 'AGENT' is not in the ADF schema UserType enum but is used at runtime.
26
+
26
27
  var AGENT_USER_TYPES = new Set(['APP', 'AGENT']);
28
+ var isAgentUserType = function isAgentUserType(userType) {
29
+ return typeof userType === 'string' && AGENT_USER_TYPES.has(userType);
30
+ };
27
31
  var AI_STREAMING_TRANSFORMATION_META_KEY = 'isAIStreamingTransformation';
28
32
  var AGENT_MENTION_INACTIVITY_MS = 3000;
29
33
  var PACKAGE_NAME = "@atlaskit/editor-plugin-mentions";
30
- var PACKAGE_VERSION = "14.1.0";
34
+ var PACKAGE_VERSION = "14.2.0";
31
35
  var setProvider = function setProvider(provider) {
32
36
  return function (state, dispatch) {
33
37
  if (dispatch) {
@@ -53,6 +57,14 @@ var isQualifyingLocalUserDocChange = function isQualifyingLocalUserDocChange(tr)
53
57
  var isAIStreaming = Boolean(tr.getMeta(AI_STREAMING_TRANSFORMATION_META_KEY));
54
58
  return tr.docChanged && !tr.getMeta('isRemote') && !tr.getMeta('replaceDocument') && !isAIStreaming;
55
59
  };
60
+ var isLocalSelectionChange = function isLocalSelectionChange(tr, hasPositionChanged) {
61
+ var isAIStreaming = Boolean(tr.getMeta(AI_STREAMING_TRANSFORMATION_META_KEY));
62
+
63
+ // Pressing Enter can move selection through a doc split without setting tr.selectionSet
64
+ // or changing from/to numerically, so local doc changes are checked against the
65
+ // pending mention's current parent before publishing.
66
+ return (hasPositionChanged || tr.docChanged) && !tr.getMeta('isRemote') && !tr.getMeta('replaceDocument') && !isAIStreaming;
67
+ };
56
68
 
57
69
  /**
58
70
  * Reads agent-mention details from a known document position without traversing
@@ -66,16 +78,18 @@ var getAgentMentionDetailsAtPos = function getAgentMentionDetailsAtPos(state, po
66
78
  }
67
79
  var node = state.doc.nodeAt(pos);
68
80
  var mentionSchema = state.schema.nodes.mention;
69
- if ((node === null || node === void 0 ? void 0 : node.type) !== mentionSchema || !AGENT_USER_TYPES.has(node.attrs.userType) || !matchesMention(node.attrs) || !node.attrs.id) {
81
+ if ((node === null || node === void 0 ? void 0 : node.type) !== mentionSchema || !isAgentUserType(node.attrs.userType) || !matchesMention(node.attrs) || !node.attrs.id) {
70
82
  return null;
71
83
  }
72
- var $mentionPos = state.doc.resolve(pos);
84
+ var $mentionPos = state.doc.resolve(Math.min(pos + node.nodeSize, state.doc.content.size));
73
85
  var parentNode = $mentionPos.node($mentionPos.depth);
74
86
  return {
75
87
  id: node.attrs.id,
76
88
  context: parentNode.textContent.trim() || null,
77
89
  nodeSize: node.nodeSize,
90
+ parentEnd: $mentionPos.end($mentionPos.depth),
78
91
  parentNodeType: (_parentNode$type$name = parentNode.type.name) !== null && _parentNode$type$name !== void 0 ? _parentNode$type$name : null,
92
+ parentStart: $mentionPos.start($mentionPos.depth),
79
93
  pos: pos
80
94
  };
81
95
  };
@@ -93,7 +107,7 @@ var getSurvivingAgentMentionDetails = function getSurvivingAgentMentionDetails(s
93
107
  if (((_result = result) === null || _result === void 0 ? void 0 : _result.id) === preferredId) {
94
108
  return false;
95
109
  }
96
- if (node.type !== mentionSchema || !AGENT_USER_TYPES.has(node.attrs.userType) || !node.attrs.id) {
110
+ if (node.type !== mentionSchema || !isAgentUserType(node.attrs.userType) || !node.attrs.id) {
97
111
  return true;
98
112
  }
99
113
  result = getAgentMentionDetailsAtPos(state, pos, function (attrs) {
@@ -127,6 +141,79 @@ var getPendingTypedAgentMentionAfterDocChange = function getPendingTypedAgentMen
127
141
  resetCount: resetCount
128
142
  } : null;
129
143
  };
144
+ var hasPendingMentionMovedToNewParent = function hasPendingMentionMovedToNewParent(oldState, tr, previousPendingTypedAgentMention, pendingMentionDetails) {
145
+ if (!previousPendingTypedAgentMention) {
146
+ return false;
147
+ }
148
+ var previousMentionDetails = getAgentMentionDetailsAtPos(oldState, previousPendingTypedAgentMention.pos, function (attrs) {
149
+ return attrs.localId === previousPendingTypedAgentMention.localId;
150
+ });
151
+
152
+ // Keep the previous parent boundary associated with the left side of an
153
+ // insertion at that boundary, so typing at the start of the parent does not
154
+ // look like the pending mention moved into a new parent.
155
+ var mappedPreviousParentStart = previousMentionDetails && tr.mapping.map(previousMentionDetails.parentStart, -1);
156
+ return Boolean(previousMentionDetails && mappedPreviousParentStart !== pendingMentionDetails.parentStart);
157
+ };
158
+ var isSelectionOutsideDirectParent = function isSelectionOutsideDirectParent(state, pendingMentionDetails) {
159
+ return state.selection.from < pendingMentionDetails.parentStart || state.selection.to > pendingMentionDetails.parentEnd;
160
+ };
161
+
162
+ /**
163
+ * Finalises a pending typed agent mention by copying its details into the
164
+ * public lastInserted* plugin state after the caller has already resolved the
165
+ * pending mention from the current document.
166
+ */
167
+ var commitResolvedPendingTypedAgentMention = function commitResolvedPendingTypedAgentMention(pluginState, pendingMentionDetails) {
168
+ var _pluginState$lastAgen;
169
+ return {
170
+ hasPublicPluginStateChanged: true,
171
+ pluginState: _objectSpread(_objectSpread({}, pluginState), {}, {
172
+ pendingTypedAgentMention: null,
173
+ lastInsertedAgentMentionId: pendingMentionDetails.id,
174
+ lastInsertedAgentMentionContext: pendingMentionDetails.context,
175
+ lastInsertedAgentMentionParentNodeType: pendingMentionDetails.parentNodeType,
176
+ lastAgentMentionInsertionCount: ((_pluginState$lastAgen = pluginState.lastAgentMentionInsertionCount) !== null && _pluginState$lastAgen !== void 0 ? _pluginState$lastAgen : 0) + 1
177
+ })
178
+ };
179
+ };
180
+
181
+ /**
182
+ * Resolves and finalises a pending typed agent mention. If the tracked mention
183
+ * no longer resolves, the stale pending state is cleared without dispatching a
184
+ * public update.
185
+ */
186
+ var commitPendingTypedAgentMention = function commitPendingTypedAgentMention(state, pluginState, pendingTypedAgentMention) {
187
+ var pendingMentionDetails = getAgentMentionDetailsAtPos(state, pendingTypedAgentMention.pos, function (attrs) {
188
+ return attrs.localId === pendingTypedAgentMention.localId;
189
+ });
190
+ if (!pendingMentionDetails) {
191
+ return {
192
+ hasPublicPluginStateChanged: false,
193
+ pluginState: _objectSpread(_objectSpread({}, pluginState), {}, {
194
+ pendingTypedAgentMention: null
195
+ })
196
+ };
197
+ }
198
+ return commitResolvedPendingTypedAgentMention(pluginState, pendingMentionDetails);
199
+ };
200
+ var hasTrackedAgentMentionState = function hasTrackedAgentMentionState(pluginState) {
201
+ return Boolean(pluginState.pendingTypedAgentMention) || pluginState.lastInsertedAgentMentionId != null || pluginState.lastInsertedAgentMentionContext != null || pluginState.lastInsertedAgentMentionParentNodeType != null;
202
+ };
203
+
204
+ /**
205
+ * Clears agent mention state that points at a specific document snapshot.
206
+ * replaceDocument swaps content wholesale, so pending typed mentions and
207
+ * lastInserted* details from the previous document must be cleared together.
208
+ */
209
+ var clearTrackedAgentMentionState = function clearTrackedAgentMentionState(pluginState) {
210
+ return _objectSpread(_objectSpread({}, pluginState), {}, {
211
+ pendingTypedAgentMention: null,
212
+ lastInsertedAgentMentionId: null,
213
+ lastInsertedAgentMentionContext: null,
214
+ lastInsertedAgentMentionParentNodeType: null
215
+ });
216
+ };
130
217
  export function createMentionPlugin(_ref2) {
131
218
  var pmPluginFactoryParams = _ref2.pmPluginFactoryParams,
132
219
  fireEvent = _ref2.fireEvent,
@@ -176,30 +263,15 @@ export function createMentionPlugin(_ref2) {
176
263
  switch (action) {
177
264
  case ACTIONS.COMMIT_PENDING_TYPED_AGENT_MENTION:
178
265
  {
179
- var _newPluginState$lastA;
180
266
  var pendingTypedAgentMention = newPluginState.pendingTypedAgentMention;
181
267
  // Ignore stale timer callbacks. The localId and resetCount must still match the
182
268
  // current pending mention so older timers cannot publish after later user edits.
183
269
  if (!isAgentMentionsExperimentEnabled || !pendingTypedAgentMention || pendingTypedAgentMention.localId !== (params === null || params === void 0 ? void 0 : params.localId) || pendingTypedAgentMention.resetCount !== (params === null || params === void 0 ? void 0 : params.resetCount)) {
184
270
  break;
185
271
  }
186
- var pendingMentionDetails = getAgentMentionDetailsAtPos(newState, pendingTypedAgentMention.pos, function (attrs) {
187
- return attrs.localId === pendingTypedAgentMention.localId;
188
- });
189
- if (!pendingMentionDetails) {
190
- newPluginState = _objectSpread(_objectSpread({}, newPluginState), {}, {
191
- pendingTypedAgentMention: null
192
- });
193
- break;
194
- }
195
- newPluginState = _objectSpread(_objectSpread({}, newPluginState), {}, {
196
- pendingTypedAgentMention: null,
197
- lastInsertedAgentMentionId: pendingMentionDetails.id,
198
- lastInsertedAgentMentionContext: pendingMentionDetails.context,
199
- lastInsertedAgentMentionParentNodeType: pendingMentionDetails.parentNodeType,
200
- lastAgentMentionInsertionCount: ((_newPluginState$lastA = newPluginState.lastAgentMentionInsertionCount) !== null && _newPluginState$lastA !== void 0 ? _newPluginState$lastA : 0) + 1
201
- });
202
- hasPublicPluginStateChanged = true;
272
+ var commitResult = commitPendingTypedAgentMention(newState, newPluginState, pendingTypedAgentMention);
273
+ newPluginState = commitResult.pluginState;
274
+ hasPublicPluginStateChanged = hasPublicPluginStateChanged || commitResult.hasPublicPluginStateChanged;
203
275
  break;
204
276
  }
205
277
  case ACTIONS.SET_PROVIDER:
@@ -216,15 +288,6 @@ export function createMentionPlugin(_ref2) {
216
288
  if (hasPublicPluginStateChanged && !isAgentMentionsExperimentEnabled) {
217
289
  pmPluginFactoryParams.dispatch(mentionPluginKey, newPluginState);
218
290
  }
219
- if (isAgentMentionsExperimentEnabled && tr.docChanged && tr.getMeta('replaceDocument')) {
220
- newPluginState = _objectSpread(_objectSpread({}, newPluginState), {}, {
221
- pendingTypedAgentMention: null,
222
- lastInsertedAgentMentionId: null,
223
- lastInsertedAgentMentionContext: null,
224
- lastInsertedAgentMentionParentNodeType: null
225
- });
226
- hasPublicPluginStateChanged = true;
227
- }
228
291
  if (options !== null && options !== void 0 && options.handleMentionsChanged && tr.docChanged) {
229
292
  var _insm$session, _insm$session2;
230
293
  (_insm$session = insm.session) === null || _insm$session === void 0 || _insm$session.startFeature('mentionDeletionDetection');
@@ -305,7 +368,7 @@ export function createMentionPlugin(_ref2) {
305
368
  var clampedNewTo = Math.min(newTo, newState.doc.content.size);
306
369
  if (clampedNewFrom < clampedNewTo) {
307
370
  newState.doc.nodesBetween(clampedNewFrom, clampedNewTo, function (node) {
308
- if (node.type === _mentionSchema && AGENT_USER_TYPES.has(node.attrs.userType)) {
371
+ if (node.type === _mentionSchema && isAgentUserType(node.attrs.userType)) {
309
372
  found = true;
310
373
  }
311
374
  return !found;
@@ -333,14 +396,14 @@ export function createMentionPlugin(_ref2) {
333
396
  });
334
397
  var shouldResolveAgentMentionState = stepsTouchMentions || Boolean(newPluginState.lastInsertedAgentMentionId);
335
398
  if (shouldResolveAgentMentionState) {
336
- var _newPluginState$lastA2, _newPluginState$lastI, _newPluginState$lastI2, _newPluginState$lastI3;
399
+ var _newPluginState$lastA, _newPluginState$lastI, _newPluginState$lastI2, _newPluginState$lastI3;
337
400
  var agentMentionId = null;
338
401
  var agentMentionContext = null;
339
402
  var agentMentionParentNodeType = null;
340
403
  var newCount = 0;
341
404
  var oldAgentMentionId = null;
342
405
  var oldCount = 0;
343
- var pendingTypedAgentMentionDetails = {};
406
+ var pendingTypedAgentMentionDetails = null;
344
407
  if (stepsTouchMentions) {
345
408
  var _iterator = _createForOfIteratorHelper(newDocRanges),
346
409
  _step;
@@ -352,15 +415,14 @@ export function createMentionPlugin(_ref2) {
352
415
  var clampedTo = Math.min(to, newState.doc.content.size);
353
416
  if (from >= clampedTo) continue;
354
417
  newState.doc.nodesBetween(from, clampedTo, function (node, pos) {
355
- if (node.type !== _mentionSchema || !AGENT_USER_TYPES.has(node.attrs.userType)) {
418
+ if (node.type !== _mentionSchema || !isAgentUserType(node.attrs.userType)) {
356
419
  return true;
357
420
  }
358
421
  newCount++;
359
- if (pendingTypedAgentMentionDetails.current === undefined && action === ACTIONS.SET_PENDING_TYPED_AGENT_MENTION && node.attrs.localId === (params === null || params === void 0 ? void 0 : params.localId)) {
360
- var _getAgentMentionDetai;
361
- pendingTypedAgentMentionDetails.current = (_getAgentMentionDetai = getAgentMentionDetailsAtPos(newState, pos, function (attrs) {
422
+ if (pendingTypedAgentMentionDetails === null && action === ACTIONS.SET_PENDING_TYPED_AGENT_MENTION && node.attrs.localId === (params === null || params === void 0 ? void 0 : params.localId)) {
423
+ pendingTypedAgentMentionDetails = getAgentMentionDetailsAtPos(newState, pos, function (attrs) {
362
424
  return attrs.localId === params.localId;
363
- })) !== null && _getAgentMentionDetai !== void 0 ? _getAgentMentionDetai : undefined;
425
+ });
364
426
  }
365
427
  if (agentMentionId === null && node.attrs.id) {
366
428
  var agentMentionDetails = getAgentMentionDetailsAtPos(newState, pos, function (attrs) {
@@ -390,7 +452,7 @@ export function createMentionPlugin(_ref2) {
390
452
  var clampedOldTo = Math.min(_to, oldState.doc.content.size);
391
453
  if (_from >= clampedOldTo) continue;
392
454
  oldState.doc.nodesBetween(_from, clampedOldTo, function (node) {
393
- if (node.type !== _mentionSchema || !AGENT_USER_TYPES.has(node.attrs.userType)) {
455
+ if (node.type !== _mentionSchema || !isAgentUserType(node.attrs.userType)) {
394
456
  return true;
395
457
  }
396
458
  oldCount++;
@@ -422,15 +484,16 @@ export function createMentionPlugin(_ref2) {
422
484
  }
423
485
  var isNewInsertion = agentMentionId !== null && !resolvedFromFullDocFallback && (oldAgentMentionId !== agentMentionId || newCount > oldCount);
424
486
  var isPendingTypedAgentMentionInsertion = isNewInsertion && action === ACTIONS.SET_PENDING_TYPED_AGENT_MENTION && typeof (params === null || params === void 0 ? void 0 : params.localId) === 'string';
425
- var newInsertionCount = isNewInsertion ? ((_newPluginState$lastA2 = newPluginState.lastAgentMentionInsertionCount) !== null && _newPluginState$lastA2 !== void 0 ? _newPluginState$lastA2 : 0) + 1 : undefined;
426
- if (isPendingTypedAgentMentionInsertion && pendingTypedAgentMentionDetails.current) {
487
+ var newInsertionCount = isNewInsertion ? ((_newPluginState$lastA = newPluginState.lastAgentMentionInsertionCount) !== null && _newPluginState$lastA !== void 0 ? _newPluginState$lastA : 0) + 1 : undefined;
488
+ var pendingTypedAgentMentionDetailsForState = pendingTypedAgentMentionDetails;
489
+ if (isPendingTypedAgentMentionInsertion && pendingTypedAgentMentionDetailsForState) {
427
490
  var pendingTypedAgentMentionLocalId = params === null || params === void 0 ? void 0 : params.localId;
428
491
  newPluginState = _objectSpread(_objectSpread({}, newPluginState), {}, {
429
492
  pendingTypedAgentMention: {
430
- id: pendingTypedAgentMentionDetails.current.id,
493
+ id: pendingTypedAgentMentionDetailsForState.id,
431
494
  localId: pendingTypedAgentMentionLocalId,
432
- nodeSize: pendingTypedAgentMentionDetails.current.nodeSize,
433
- pos: pendingTypedAgentMentionDetails.current.pos,
495
+ nodeSize: pendingTypedAgentMentionDetailsForState.nodeSize,
496
+ pos: pendingTypedAgentMentionDetailsForState.pos,
434
497
  resetCount: 1
435
498
  }
436
499
  });
@@ -458,6 +521,10 @@ export function createMentionPlugin(_ref2) {
458
521
  }
459
522
  }
460
523
  }
524
+ if (isAgentMentionsExperimentEnabled && tr.docChanged && tr.getMeta('replaceDocument') && hasTrackedAgentMentionState(newPluginState)) {
525
+ newPluginState = clearTrackedAgentMentionState(newPluginState);
526
+ hasPublicPluginStateChanged = true;
527
+ }
461
528
  if (isAgentMentionsExperimentEnabled && newPluginState.pendingTypedAgentMention && action !== ACTIONS.SET_PENDING_TYPED_AGENT_MENTION && action !== ACTIONS.COMMIT_PENDING_TYPED_AGENT_MENTION && tr.docChanged) {
462
529
  newPluginState = _objectSpread(_objectSpread({}, newPluginState), {}, {
463
530
  pendingTypedAgentMention: getPendingTypedAgentMentionAfterDocChange(newState, tr, newPluginState.pendingTypedAgentMention, {
@@ -465,6 +532,27 @@ export function createMentionPlugin(_ref2) {
465
532
  })
466
533
  });
467
534
  }
535
+
536
+ // Typed agent mentions stay pending while the user is still editing around them,
537
+ // but leaving the mention's direct parent means they have moved on from that
538
+ // paragraph/block. Publish immediately in that case instead of waiting for the
539
+ // inactivity timer.
540
+ var shouldCheckPendingTypedAgentMentionParent = isLocalSelectionChange(tr, hasPositionChanged);
541
+ if (isAgentMentionsExperimentEnabled && newPluginState.pendingTypedAgentMention && action !== ACTIONS.SET_PENDING_TYPED_AGENT_MENTION && action !== ACTIONS.COMMIT_PENDING_TYPED_AGENT_MENTION && shouldCheckPendingTypedAgentMentionParent) {
542
+ var _pendingTypedAgentMention = newPluginState.pendingTypedAgentMention;
543
+ var pendingMentionDetails = getAgentMentionDetailsAtPos(newState, _pendingTypedAgentMention.pos, function (attrs) {
544
+ return attrs.localId === _pendingTypedAgentMention.localId;
545
+ });
546
+ if (!pendingMentionDetails) {
547
+ newPluginState = _objectSpread(_objectSpread({}, newPluginState), {}, {
548
+ pendingTypedAgentMention: null
549
+ });
550
+ } else if (hasPendingMentionMovedToNewParent(oldState, tr, pluginState.pendingTypedAgentMention, pendingMentionDetails) || isSelectionOutsideDirectParent(newState, pendingMentionDetails)) {
551
+ var _commitResult = commitResolvedPendingTypedAgentMention(newPluginState, pendingMentionDetails);
552
+ newPluginState = _commitResult.pluginState;
553
+ hasPublicPluginStateChanged = hasPublicPluginStateChanged || _commitResult.hasPublicPluginStateChanged;
554
+ }
555
+ }
468
556
  if (hasPublicPluginStateChanged && isAgentMentionsExperimentEnabled) {
469
557
  pmPluginFactoryParams.dispatch(mentionPluginKey, newPluginState);
470
558
  }
@@ -44,31 +44,22 @@ export interface MentionsPluginOptions extends MentionPluginConfig {
44
44
  */
45
45
  export type MentionPluginOptions = MentionsPluginOptions;
46
46
  export type AgentMentionDetails = {
47
- id: string;
48
47
  context: string | null;
48
+ id: string;
49
49
  nodeSize: number;
50
+ /**
51
+ * @internal ProseMirror position used for parent-boundary checks.
52
+ */
53
+ parentEnd: number;
50
54
  parentNodeType: string | null;
55
+ /**
56
+ * @internal ProseMirror position used for parent-boundary checks.
57
+ */
58
+ parentStart: number;
51
59
  pos: number;
52
60
  };
53
61
  export type MentionPluginState = {
54
62
  canInsertMention?: boolean;
55
- /**
56
- * @internal Tracks a typed agent mention while waiting for the platform-side
57
- * ready-to-fire trigger. Consumers should continue to react only to
58
- * lastInsertedAgentMention* fields.
59
- */
60
- pendingTypedAgentMention?: {
61
- id: string;
62
- localId: string;
63
- nodeSize: number;
64
- pos: number;
65
- /**
66
- * Generation value for the inactivity timer. This changes when local edits
67
- * reset the pending mention window, so stale timer callbacks for the same
68
- * localId cannot publish before the latest inactivity period has elapsed.
69
- */
70
- resetCount: number;
71
- } | null;
72
63
  /**
73
64
  * Increments on each new agent mention insertion (including re-mentions of the same agent).
74
65
  * Used to trigger re-renders when the same agent is mentioned again.
@@ -91,6 +82,23 @@ export type MentionPluginState = {
91
82
  lastInsertedAgentMentionParentNodeType?: string | null;
92
83
  mentionProvider?: MentionProvider;
93
84
  mentions?: Array<MentionDescription>;
85
+ /**
86
+ * @internal Tracks a typed agent mention while waiting for the platform-side
87
+ * ready-to-fire trigger. Consumers should continue to react only to
88
+ * lastInsertedAgentMention* fields.
89
+ */
90
+ pendingTypedAgentMention?: {
91
+ id: string;
92
+ localId: string;
93
+ nodeSize: number;
94
+ pos: number;
95
+ /**
96
+ * Generation value for the inactivity timer. This changes when local edits
97
+ * reset the pending mention window, so stale timer callbacks for the same
98
+ * localId cannot publish before the latest inactivity period has elapsed.
99
+ */
100
+ resetCount: number;
101
+ } | null;
94
102
  };
95
103
  export type FireElementsChannelEvent = (payload: AnalyticsEventPayload, channel?: string) => void;
96
104
  export type MentionSharedState = MentionPluginState & {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@atlaskit/editor-plugin-mentions",
3
- "version": "14.1.1",
3
+ "version": "14.2.1",
4
4
  "description": "Mentions plugin for @atlaskit/editor-core",
5
5
  "author": "Atlassian Pty Ltd",
6
6
  "license": "Apache-2.0",
@@ -21,7 +21,7 @@
21
21
  ],
22
22
  "atlaskit:src": "src/index.ts",
23
23
  "dependencies": {
24
- "@atlaskit/adf-schema": "^55.0.0",
24
+ "@atlaskit/adf-schema": "^55.2.0",
25
25
  "@atlaskit/css": "^1.0.0",
26
26
  "@atlaskit/editor-plugin-analytics": "^12.0.0",
27
27
  "@atlaskit/editor-plugin-base": "^13.0.0",
@@ -32,18 +32,18 @@
32
32
  "@atlaskit/icon": "^36.0.0",
33
33
  "@atlaskit/insm": "^1.0.0",
34
34
  "@atlaskit/link": "^4.0.0",
35
- "@atlaskit/mention": "^27.0.0",
35
+ "@atlaskit/mention": "^27.1.0",
36
36
  "@atlaskit/platform-feature-flags": "^2.0.0",
37
37
  "@atlaskit/popper": "^8.0.0",
38
38
  "@atlaskit/portal": "^6.0.0",
39
39
  "@atlaskit/primitives": "^20.0.0",
40
- "@atlaskit/profilecard": "^26.0.0",
40
+ "@atlaskit/profilecard": "^26.1.0",
41
41
  "@atlaskit/teams-app-config": "^2.0.0",
42
42
  "@atlaskit/theme": "^26.0.0",
43
- "@atlaskit/tmp-editor-statsig": "^106.0.0",
43
+ "@atlaskit/tmp-editor-statsig": "^108.0.0",
44
44
  "@atlaskit/tokens": "^14.0.0",
45
45
  "@atlaskit/tooltip": "^23.0.0",
46
- "@atlaskit/user-picker": "^13.0.0",
46
+ "@atlaskit/user-picker": "^13.1.0",
47
47
  "@babel/runtime": "^7.0.0",
48
48
  "@compiled/react": "^0.20.0",
49
49
  "bind-event-listener": "^3.0.0",
@@ -52,7 +52,7 @@
52
52
  "uuid": "^3.1.0"
53
53
  },
54
54
  "peerDependencies": {
55
- "@atlaskit/editor-common": "^116.2.0",
55
+ "@atlaskit/editor-common": "^116.3.0",
56
56
  "react": "^18.2.0",
57
57
  "react-dom": "^18.2.0",
58
58
  "react-intl": "^5.25.1 || ^6.0.0 || ^7.0.0"