@atlaskit/editor-plugin-mentions 13.3.15 → 14.1.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.
@@ -22,6 +22,8 @@ var _platformFeatureFlags = require("@atlaskit/platform-feature-flags");
22
22
  var _experiments = require("@atlaskit/tmp-editor-statsig/experiments");
23
23
  var _expVal = require("@atlaskit/tmp-editor-statsig/expVal");
24
24
  var _editorCommands = require("../../editor-commands");
25
+ var _key = require("../../pm-plugins/key");
26
+ var _main = require("../../pm-plugins/main");
25
27
  var _mentionPlaceholder = require("../../pm-plugins/mentionPlaceholder");
26
28
  var _utils2 = require("../../pm-plugins/utils");
27
29
  var _InviteItem = _interopRequireWildcard(require("../InviteItem"));
@@ -40,6 +42,9 @@ var isAgentUserType = function isAgentUserType(userType) {
40
42
  var isAgentMention = function isAgentMention(mention) {
41
43
  return isAgentUserType(mention.userType) || mention.appType === 'agent';
42
44
  };
45
+ var isAgentTypeAheadItem = function isAgentTypeAheadItem(item) {
46
+ return item.mention ? isAgentMention(item.mention) : false;
47
+ };
43
48
  var createInviteItem = function createInviteItem(_ref) {
44
49
  var mentionProvider = _ref.mentionProvider,
45
50
  onInviteItemMount = _ref.onInviteItemMount,
@@ -326,23 +331,31 @@ var createTypeAheadConfig = exports.createTypeAheadConfig = function createTypeA
326
331
  },
327
332
  getSections: function getSections(_ref9) {
328
333
  var intl = _ref9.intl;
329
- return [{
334
+ return [_objectSpread({
330
335
  id: 'people',
331
336
  title: intl.formatMessage(_messages.mentionMessages.typeAheadSectionPeople),
332
337
  filter: function filter(item) {
333
- var _item$mention;
334
- return !isAgentUserType(((_item$mention = item.mention) === null || _item$mention === void 0 ? void 0 : _item$mention.userType) || '');
338
+ return !isAgentTypeAheadItem(item);
335
339
  },
336
340
  limit: (0, _expVal.expVal)('platform_editor_agent_mentions', 'isEnabled', false) ? 5 : 6
337
- }, {
341
+ }, (0, _expVal.expVal)('platform_editor_agent_mentions', 'isEnabled', false) ? {
342
+ sectionTitleDisplay: {
343
+ showWhenQueryPresent: false,
344
+ showWhenOnlySection: true
345
+ }
346
+ } : {}), _objectSpread({
338
347
  id: 'agents',
339
348
  title: intl.formatMessage(_messages.mentionMessages.typeAheadSectionAgents),
340
349
  filter: function filter(item) {
341
- var _item$mention2;
342
- return isAgentUserType(((_item$mention2 = item.mention) === null || _item$mention2 === void 0 ? void 0 : _item$mention2.userType) || '');
350
+ return isAgentTypeAheadItem(item);
343
351
  },
344
352
  limit: (0, _expVal.expVal)('platform_editor_agent_mentions', 'isEnabled', false) ? 5 : undefined
345
- }];
353
+ }, (0, _expVal.expVal)('platform_editor_agent_mentions', 'isEnabled', false) ? {
354
+ sectionTitleDisplay: {
355
+ showWhenQueryPresent: false,
356
+ showWhenOnlySection: true
357
+ }
358
+ } : {})];
346
359
  },
347
360
  onOpen: function onOpen() {
348
361
  firstQueryWithoutResults = null;
@@ -356,23 +369,23 @@ var createTypeAheadConfig = exports.createTypeAheadConfig = function createTypeA
356
369
  var schema = state.schema;
357
370
  var pluginState = (0, _utils2.getMentionPluginState)(state);
358
371
  var mentionProvider = pluginState.mentionProvider;
359
- var _item$mention3 = item.mention,
360
- id = _item$mention3.id,
361
- name = _item$mention3.name,
362
- nickname = _item$mention3.nickname,
363
- accessLevel = _item$mention3.accessLevel,
364
- userType = _item$mention3.userType,
365
- isXProductUser = _item$mention3.isXProductUser;
372
+ var _item$mention = item.mention,
373
+ id = _item$mention.id,
374
+ name = _item$mention.name,
375
+ nickname = _item$mention.nickname,
376
+ accessLevel = _item$mention.accessLevel,
377
+ userType = _item$mention.userType,
378
+ isXProductUser = _item$mention.isXProductUser;
366
379
  var _ref1 = (_api$contextIdentifie3 = api === null || api === void 0 || (_api$contextIdentifie4 = api.contextIdentifier) === null || _api$contextIdentifie4 === void 0 ? void 0 : _api$contextIdentifie4.sharedState.currentState()) !== null && _api$contextIdentifie3 !== void 0 ? _api$contextIdentifie3 : {},
367
380
  contextIdentifierProvider = _ref1.contextIdentifierProvider;
368
381
  var mentionContext = _objectSpread(_objectSpread({}, contextIdentifierProvider), {}, {
369
382
  sessionId: sessionId
370
383
  });
371
384
  var isAgentMentionsExperimentEnabled = (0, _expVal.expVal)('platform_editor_agent_mentions', 'isEnabled', false);
372
- var shouldSuppressInviteForAgentMention = isAgentMentionsExperimentEnabled && isAgentMention(item.mention);
385
+ var isAgentMentionInsertion = isAgentMentionsExperimentEnabled && isAgentMention(item.mention);
373
386
  // userType can be missing for provider-only agent mentions. Copy/paste cannot
374
387
  // see appType, so persist APP only when there is no explicit userType.
375
- var persistedUserType = isAgentMentionsExperimentEnabled && isAgentMention(item.mention) && userType == null ? 'APP' : userType;
388
+ var persistedUserType = isAgentMentionInsertion && userType == null ? 'APP' : userType;
376
389
  if (mentionProvider && !(0, _utils3.isInviteItem)(item.mention)) {
377
390
  mentionProvider.recordMentionSelection(item.mention, mentionContext);
378
391
  }
@@ -397,12 +410,12 @@ var createTypeAheadConfig = exports.createTypeAheadConfig = function createTypeA
397
410
  // If query already includes @, use it as is
398
411
  if (email && mentionProvider.showInlineInviteRecaptcha) {
399
412
  mentionProvider.showInlineInviteRecaptcha(email);
400
- var tr = state.tr;
401
- tr.setMeta(_mentionPlaceholder.mentionPlaceholderPluginKey, {
413
+ var _tr = state.tr;
414
+ _tr.setMeta(_mentionPlaceholder.mentionPlaceholderPluginKey, {
402
415
  action: _mentionPlaceholder.MENTION_PLACEHOLDER_ACTIONS.SHOW_PLACEHOLDER,
403
416
  placeholder: "@".concat(query)
404
417
  });
405
- return tr;
418
+ return _tr;
406
419
  }
407
420
  } else if (mentionProvider.onInviteItemClick) {
408
421
  // Fallback to old behavior for backward compatibility
@@ -429,7 +442,7 @@ var createTypeAheadConfig = exports.createTypeAheadConfig = function createTypeA
429
442
  localId: mentionLocalId,
430
443
  method: 'typed',
431
444
  type: 'added'
432
- }, shouldSuppressInviteForAgentMention ? {
445
+ }, isAgentMentionInsertion ? {
433
446
  shouldSuppressMentionNotification: true
434
447
  } : {});
435
448
  if (taskItemId) {
@@ -442,22 +455,22 @@ var createTypeAheadConfig = exports.createTypeAheadConfig = function createTypeA
442
455
  }
443
456
  fireEvent((0, _analytics.buildTypeAheadInsertedPayload)(pickerElapsedTime, stats.keyCount.arrowUp, stats.keyCount.arrowDown, sessionId, mode, item.mention, mentionLocalId, sourceListItem.map(function (x) {
444
457
  return x.mention;
445
- }), query, contextIdentifierProvider, taskListId, taskItemId));
458
+ }), query, contextIdentifierProvider, taskListId, taskItemId, isAgentMentionInsertion));
446
459
 
447
460
  // eslint-disable-next-line @atlaskit/platform/prefer-crypto-random-uuid -- Use crypto.randomUUID instead
448
461
  sessionId = (0, _uuid.default)();
449
462
  if (mentionProvider && (0, _utils3.isTeamType)(userType)) {
450
463
  return insert(buildNodesForTeamMention(schema, item.mention, mentionProvider, sanitizePrivateContent));
451
464
  }
452
- if (!shouldSuppressInviteForAgentMention && isXProductUser && mentionProvider && mentionProvider.inviteXProductUser) {
465
+ if (!isAgentMentionInsertion && isXProductUser && mentionProvider && mentionProvider.inviteXProductUser) {
453
466
  mentionProvider.inviteXProductUser(id, name);
454
467
  }
455
- return insert((0, _editorCommands.createSingleMentionFragment)({
468
+ var tr = insert((0, _editorCommands.createSingleMentionFragment)({
456
469
  mentionProvider: mentionProvider,
457
470
  mentionInsertDisplayName: mentionInsertDisplayName,
458
471
  tr: state.tr,
459
472
  sanitizePrivateContent: sanitizePrivateContent,
460
- suppressInviteXProductUser: shouldSuppressInviteForAgentMention
473
+ suppressInviteXProductUser: isAgentMentionInsertion
461
474
  })({
462
475
  name: name,
463
476
  id: id,
@@ -467,6 +480,15 @@ var createTypeAheadConfig = exports.createTypeAheadConfig = function createTypeA
467
480
  accessLevel: accessLevel,
468
481
  isXProductUser: isXProductUser
469
482
  }));
483
+ if (isAgentMentionInsertion) {
484
+ tr.setMeta(_key.mentionPluginKey, {
485
+ action: _main.ACTIONS.SET_PENDING_TYPED_AGENT_MENTION,
486
+ params: {
487
+ localId: mentionLocalId
488
+ }
489
+ });
490
+ }
491
+ return tr;
470
492
  },
471
493
  dismiss: function dismiss(_ref10) {
472
494
  var editorState = _ref10.editorState,
@@ -10,14 +10,17 @@ import { MENTION_PROVIDER_REJECTED, MENTION_PROVIDER_UNDEFINED } from '../types'
10
10
  import { mentionPluginKey } from './key';
11
11
  import { canMentionBeCreatedInRange } from './utils';
12
12
  export const ACTIONS = {
13
+ COMMIT_PENDING_TYPED_AGENT_MENTION: 'COMMIT_PENDING_TYPED_AGENT_MENTION',
14
+ SET_PENDING_TYPED_AGENT_MENTION: 'SET_PENDING_TYPED_AGENT_MENTION',
13
15
  SET_PROVIDER: 'SET_PROVIDER'
14
16
  };
15
17
 
16
18
  // 'AGENT' is not in the ADF schema UserType enum but is used at runtime.
17
19
  const AGENT_USER_TYPES = new Set(['APP', 'AGENT']);
18
20
  const AI_STREAMING_TRANSFORMATION_META_KEY = 'isAIStreamingTransformation';
21
+ const AGENT_MENTION_INACTIVITY_MS = 3000;
19
22
  const PACKAGE_NAME = "@atlaskit/editor-plugin-mentions";
20
- const PACKAGE_VERSION = "13.3.14";
23
+ const PACKAGE_VERSION = "14.0.0";
21
24
  const setProvider = provider => (state, dispatch) => {
22
25
  if (dispatch) {
23
26
  dispatch(state.tr.setMeta(mentionPluginKey, {
@@ -29,6 +32,89 @@ const setProvider = provider => (state, dispatch) => {
29
32
  }
30
33
  return true;
31
34
  };
35
+
36
+ /**
37
+ * Returns true when a transaction represents a local user document edit that
38
+ * should restart pending agent-mention inactivity tracking.
39
+ *
40
+ * Remote/collab updates, replace-document transactions, AI streaming transforms,
41
+ * selection-only movements, and metadata-only transactions are intentionally ignored.
42
+ */
43
+ const isQualifyingLocalUserDocChange = tr => {
44
+ const isAIStreaming = Boolean(tr.getMeta(AI_STREAMING_TRANSFORMATION_META_KEY));
45
+ return tr.docChanged && !tr.getMeta('isRemote') && !tr.getMeta('replaceDocument') && !isAIStreaming;
46
+ };
47
+
48
+ /**
49
+ * Reads agent-mention details from a known document position without traversing
50
+ * the document. Callers pass a matcher so mapped positions are only accepted
51
+ * when they still point at the same pending/tracked mention.
52
+ */
53
+ const getAgentMentionDetailsAtPos = (state, pos, matchesMention) => {
54
+ var _parentNode$type$name;
55
+ if (pos < 0 || pos > state.doc.content.size) {
56
+ return null;
57
+ }
58
+ const node = state.doc.nodeAt(pos);
59
+ 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) {
61
+ return null;
62
+ }
63
+ const $mentionPos = state.doc.resolve(pos);
64
+ const parentNode = $mentionPos.node($mentionPos.depth);
65
+ return {
66
+ id: node.attrs.id,
67
+ context: parentNode.textContent.trim() || null,
68
+ nodeSize: node.nodeSize,
69
+ parentNodeType: (_parentNode$type$name = parentNode.type.name) !== null && _parentNode$type$name !== void 0 ? _parentNode$type$name : null,
70
+ pos
71
+ };
72
+ };
73
+
74
+ /**
75
+ * Finds an agent mention that survived a document change when the changed-range
76
+ * scan did not find one. Prefers the previously tracked mention ID when present;
77
+ * otherwise returns a surviving agent mention using the existing traversal order.
78
+ */
79
+ const getSurvivingAgentMentionDetails = (state, preferredId) => {
80
+ const mentionSchema = state.schema.nodes.mention;
81
+ let result = null;
82
+ state.doc.descendants((node, pos) => {
83
+ var _result, _result2;
84
+ if (((_result = result) === null || _result === void 0 ? void 0 : _result.id) === preferredId) {
85
+ return false;
86
+ }
87
+ if (node.type !== mentionSchema || !AGENT_USER_TYPES.has(node.attrs.userType) || !node.attrs.id) {
88
+ return true;
89
+ }
90
+ result = getAgentMentionDetailsAtPos(state, pos, attrs => attrs.id === node.attrs.id);
91
+ return ((_result2 = result) === null || _result2 === void 0 ? void 0 : _result2.id) !== preferredId;
92
+ });
93
+ return result;
94
+ };
95
+
96
+ /**
97
+ * Maps a pending typed agent mention through a document-changing transaction and
98
+ * returns the updated pending state. If the mapped position was deleted or no
99
+ * longer points at the same local mention, the pending mention is cleared.
100
+ */
101
+ const getPendingTypedAgentMentionAfterDocChange = (state, tr, pendingTypedAgentMention, {
102
+ resetTimer
103
+ }) => {
104
+ const mappedPos = tr.mapping.mapResult(pendingTypedAgentMention.pos, 1);
105
+ const resetCount = resetTimer ? pendingTypedAgentMention.resetCount + 1 : pendingTypedAgentMention.resetCount;
106
+ if (mappedPos.deleted) {
107
+ return null;
108
+ }
109
+ const pendingMentionDetails = getAgentMentionDetailsAtPos(state, mappedPos.pos, attrs => attrs.localId === pendingTypedAgentMention.localId);
110
+ return pendingMentionDetails ? {
111
+ id: pendingMentionDetails.id,
112
+ localId: pendingTypedAgentMention.localId,
113
+ nodeSize: pendingMentionDetails.nodeSize,
114
+ pos: pendingMentionDetails.pos,
115
+ resetCount
116
+ } : null;
117
+ };
32
118
  export function createMentionPlugin({
33
119
  pmPluginFactoryParams,
34
120
  fireEvent,
@@ -68,32 +154,71 @@ export function createMentionPlugin({
68
154
  action: null,
69
155
  params: null
70
156
  };
71
- let hasNewPluginState = false;
157
+ let hasPublicPluginStateChanged = false;
72
158
  let newPluginState = pluginState;
159
+ const isAgentMentionsExperimentEnabled = editorExperiment('platform_editor_agent_mentions', true);
73
160
  const hasPositionChanged = oldState.selection.from !== newState.selection.from || oldState.selection.to !== newState.selection.to;
74
161
  if (tr.docChanged || tr.selectionSet && hasPositionChanged) {
75
162
  newPluginState = {
76
163
  ...pluginState,
77
164
  canInsertMention: canMentionBeCreatedInRange(newState.selection.from, newState.selection.to)(newState)
78
165
  };
79
- hasNewPluginState = true;
166
+ hasPublicPluginStateChanged = true;
80
167
  }
81
168
  switch (action) {
169
+ case ACTIONS.COMMIT_PENDING_TYPED_AGENT_MENTION:
170
+ {
171
+ var _newPluginState$lastA;
172
+ const pendingTypedAgentMention = newPluginState.pendingTypedAgentMention;
173
+ // Ignore stale timer callbacks. The localId and resetCount must still match the
174
+ // current pending mention so older timers cannot publish after later user edits.
175
+ 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
+ break;
177
+ }
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;
195
+ break;
196
+ }
82
197
  case ACTIONS.SET_PROVIDER:
83
198
  newPluginState = {
84
199
  ...newPluginState,
85
200
  mentionProvider: params.provider
86
201
  };
87
- hasNewPluginState = true;
202
+ hasPublicPluginStateChanged = true;
88
203
  break;
89
204
  }
90
205
 
91
206
  // When the agent mentions experiment is off, dispatch immediately (original behaviour).
92
207
  // When it's on, defer dispatch to after the agent tracking block below so that
93
208
  // agent-mention state changes are included in the notification.
94
- if (hasNewPluginState && !editorExperiment('platform_editor_agent_mentions', true)) {
209
+ if (hasPublicPluginStateChanged && !isAgentMentionsExperimentEnabled) {
95
210
  pmPluginFactoryParams.dispatch(mentionPluginKey, newPluginState);
96
211
  }
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
+ }
97
222
  if (options !== null && options !== void 0 && options.handleMentionsChanged && tr.docChanged) {
98
223
  var _insm$session, _insm$session2;
99
224
  (_insm$session = insm.session) === null || _insm$session === void 0 ? void 0 : _insm$session.startFeature('mentionDeletionDetection');
@@ -152,7 +277,7 @@ export function createMentionPlugin({
152
277
  (_insm$session2 = insm.session) === null || _insm$session2 === void 0 ? void 0 : _insm$session2.endFeature('mentionDeletionDetection');
153
278
  }
154
279
  const isAIStreaming = Boolean(tr.getMeta(AI_STREAMING_TRANSFORMATION_META_KEY));
155
- if (tr.docChanged && !tr.getMeta('replaceDocument') && !isAIStreaming && editorExperiment('platform_editor_agent_mentions', true)) {
280
+ if (tr.docChanged && !tr.getMeta('replaceDocument') && !isAIStreaming && isAgentMentionsExperimentEnabled) {
156
281
  const mentionSchema = newState.schema.nodes.mention;
157
282
  const newDocRanges = [];
158
283
  const oldDocRanges = [];
@@ -198,28 +323,36 @@ export function createMentionPlugin({
198
323
  oldDocRanges.push(...stepOldRanges);
199
324
  }
200
325
  });
201
- if (stepsTouchMentions || newPluginState.lastInsertedAgentMentionId) {
202
- var _newPluginState$lastA, _newPluginState$lastI, _newPluginState$lastI2, _newPluginState$lastI3;
326
+ const shouldResolveAgentMentionState = stepsTouchMentions || Boolean(newPluginState.lastInsertedAgentMentionId);
327
+ if (shouldResolveAgentMentionState) {
328
+ var _newPluginState$lastA2, _newPluginState$lastI, _newPluginState$lastI2, _newPluginState$lastI3;
203
329
  let agentMentionId = null;
204
330
  let agentMentionContext = null;
205
331
  let agentMentionParentNodeType = null;
206
332
  let newCount = 0;
207
333
  let oldAgentMentionId = null;
208
334
  let oldCount = 0;
335
+ const pendingTypedAgentMentionDetails = {};
209
336
  if (stepsTouchMentions) {
210
337
  for (const [from, to] of newDocRanges) {
211
338
  const clampedTo = Math.min(to, newState.doc.content.size);
212
339
  if (from >= clampedTo) continue;
213
- newState.doc.nodesBetween(from, clampedTo, (node, _pos, parent) => {
340
+ newState.doc.nodesBetween(from, clampedTo, (node, pos) => {
214
341
  if (node.type !== mentionSchema || !AGENT_USER_TYPES.has(node.attrs.userType)) {
215
342
  return true;
216
343
  }
217
344
  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;
348
+ }
218
349
  if (agentMentionId === null && node.attrs.id) {
219
- var _parent$type$name;
220
- agentMentionId = node.attrs.id;
221
- agentMentionParentNodeType = (_parent$type$name = parent === null || parent === void 0 ? void 0 : parent.type.name) !== null && _parent$type$name !== void 0 ? _parent$type$name : null;
222
- agentMentionContext = (parent === null || parent === void 0 ? void 0 : parent.textContent.trim()) || null;
350
+ const agentMentionDetails = getAgentMentionDetailsAtPos(newState, pos, attrs => attrs.id === node.attrs.id);
351
+ if (agentMentionDetails) {
352
+ agentMentionId = agentMentionDetails.id;
353
+ agentMentionContext = agentMentionDetails.context;
354
+ agentMentionParentNodeType = agentMentionDetails.parentNodeType;
355
+ }
223
356
  }
224
357
  return true;
225
358
  });
@@ -243,33 +376,46 @@ export function createMentionPlugin({
243
376
  // When a deletion collapses the new-doc range to a zero-width point, or when
244
377
  // the doc changed but no step covered the tracked mention, the new-doc scan
245
378
  // above finds nothing. Check whether any agent mention survived in the document.
246
- let resolvedFromSurvivor = false;
379
+ let resolvedFromFullDocFallback = false;
247
380
  if (agentMentionId === null && newPluginState.lastInsertedAgentMentionId) {
248
- const prevId = newPluginState.lastInsertedAgentMentionId;
249
- let survivorId = null;
250
- let survivorContext = null;
251
- let survivorParentNodeType = null;
252
- newState.doc.descendants((node, _pos, parent) => {
253
- if (survivorId === prevId) return false;
254
- if (node.type === mentionSchema && AGENT_USER_TYPES.has(node.attrs.userType)) {
255
- var _parent$type$name2;
256
- // Prefer the previously tracked ID; otherwise keep the first found.
257
- survivorId = node.attrs.id;
258
- survivorParentNodeType = (_parent$type$name2 = parent === null || parent === void 0 ? void 0 : parent.type.name) !== null && _parent$type$name2 !== void 0 ? _parent$type$name2 : null;
259
- survivorContext = (parent === null || parent === void 0 ? void 0 : parent.textContent.trim()) || null;
260
- }
261
- return survivorId !== prevId;
262
- });
263
- if (survivorId !== null) {
264
- agentMentionId = survivorId;
265
- agentMentionContext = survivorContext;
266
- agentMentionParentNodeType = survivorParentNodeType;
267
- resolvedFromSurvivor = true;
381
+ const survivorDetails = getSurvivingAgentMentionDetails(newState, newPluginState.lastInsertedAgentMentionId);
382
+ if (survivorDetails) {
383
+ agentMentionId = survivorDetails.id;
384
+ agentMentionContext = survivorDetails.context;
385
+ agentMentionParentNodeType = survivorDetails.parentNodeType;
386
+ resolvedFromFullDocFallback = true;
268
387
  }
269
388
  }
270
- const isNewInsertion = agentMentionId !== null && !resolvedFromSurvivor && (oldAgentMentionId !== agentMentionId || newCount > oldCount);
271
- const newInsertionCount = isNewInsertion ? ((_newPluginState$lastA = newPluginState.lastAgentMentionInsertionCount) !== null && _newPluginState$lastA !== void 0 ? _newPluginState$lastA : 0) + 1 : undefined;
272
- if (agentMentionId !== ((_newPluginState$lastI = newPluginState.lastInsertedAgentMentionId) !== null && _newPluginState$lastI !== void 0 ? _newPluginState$lastI : null) || agentMentionContext !== ((_newPluginState$lastI2 = newPluginState.lastInsertedAgentMentionContext) !== null && _newPluginState$lastI2 !== void 0 ? _newPluginState$lastI2 : null) || agentMentionParentNodeType !== ((_newPluginState$lastI3 = newPluginState.lastInsertedAgentMentionParentNodeType) !== null && _newPluginState$lastI3 !== void 0 ? _newPluginState$lastI3 : null) || newInsertionCount !== undefined) {
389
+ const isNewInsertion = agentMentionId !== null && !resolvedFromFullDocFallback && (oldAgentMentionId !== agentMentionId || newCount > oldCount);
390
+ 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) {
393
+ const pendingTypedAgentMentionLocalId = params === null || params === void 0 ? void 0 : params.localId;
394
+ newPluginState = {
395
+ ...newPluginState,
396
+ pendingTypedAgentMention: {
397
+ id: pendingTypedAgentMentionDetails.current.id,
398
+ localId: pendingTypedAgentMentionLocalId,
399
+ nodeSize: pendingTypedAgentMentionDetails.current.nodeSize,
400
+ pos: pendingTypedAgentMentionDetails.current.pos,
401
+ resetCount: 1
402
+ }
403
+ };
404
+ } else if (isPendingTypedAgentMentionInsertion) {
405
+ // Fallback: if the localId-specific scan missed the typed mention,
406
+ // publish immediately so the insertion is not dropped.
407
+ newPluginState = {
408
+ ...newPluginState,
409
+ pendingTypedAgentMention: null,
410
+ lastInsertedAgentMentionId: agentMentionId,
411
+ lastInsertedAgentMentionContext: agentMentionContext,
412
+ lastInsertedAgentMentionParentNodeType: agentMentionParentNodeType,
413
+ ...(newInsertionCount !== undefined ? {
414
+ lastAgentMentionInsertionCount: newInsertionCount
415
+ } : {})
416
+ };
417
+ hasPublicPluginStateChanged = true;
418
+ } else if (agentMentionId !== ((_newPluginState$lastI = newPluginState.lastInsertedAgentMentionId) !== null && _newPluginState$lastI !== void 0 ? _newPluginState$lastI : null) || agentMentionContext !== ((_newPluginState$lastI2 = newPluginState.lastInsertedAgentMentionContext) !== null && _newPluginState$lastI2 !== void 0 ? _newPluginState$lastI2 : null) || agentMentionParentNodeType !== ((_newPluginState$lastI3 = newPluginState.lastInsertedAgentMentionParentNodeType) !== null && _newPluginState$lastI3 !== void 0 ? _newPluginState$lastI3 : null) || newInsertionCount !== undefined) {
273
419
  newPluginState = {
274
420
  ...newPluginState,
275
421
  lastInsertedAgentMentionId: agentMentionId,
@@ -279,11 +425,19 @@ export function createMentionPlugin({
279
425
  lastAgentMentionInsertionCount: newInsertionCount
280
426
  } : {})
281
427
  };
282
- hasNewPluginState = true;
428
+ hasPublicPluginStateChanged = true;
283
429
  }
284
430
  }
285
431
  }
286
- if (hasNewPluginState && editorExperiment('platform_editor_agent_mentions', true)) {
432
+ if (isAgentMentionsExperimentEnabled && newPluginState.pendingTypedAgentMention && action !== ACTIONS.SET_PENDING_TYPED_AGENT_MENTION && action !== ACTIONS.COMMIT_PENDING_TYPED_AGENT_MENTION && tr.docChanged) {
433
+ newPluginState = {
434
+ ...newPluginState,
435
+ pendingTypedAgentMention: getPendingTypedAgentMentionAfterDocChange(newState, tr, newPluginState.pendingTypedAgentMention, {
436
+ resetTimer: isQualifyingLocalUserDocChange(tr)
437
+ })
438
+ };
439
+ }
440
+ if (hasPublicPluginStateChanged && isAgentMentionsExperimentEnabled) {
287
441
  pmPluginFactoryParams.dispatch(mentionPluginKey, newPluginState);
288
442
  }
289
443
  return newPluginState;
@@ -301,6 +455,47 @@ export function createMentionPlugin({
301
455
  }
302
456
  },
303
457
  view(editorView) {
458
+ const isAgentMentionsEnabled = editorExperiment('platform_editor_agent_mentions', true);
459
+ let pendingTypedAgentMentionTimer;
460
+ let pendingTypedAgentMentionTimerKey = null;
461
+ const clearPendingTypedAgentMentionTimer = () => {
462
+ if (pendingTypedAgentMentionTimer) {
463
+ clearTimeout(pendingTypedAgentMentionTimer);
464
+ pendingTypedAgentMentionTimer = undefined;
465
+ }
466
+ pendingTypedAgentMentionTimerKey = null;
467
+ };
468
+ const schedulePendingTypedAgentMentionTimer = mentionPluginState => {
469
+ if (!isAgentMentionsEnabled) {
470
+ clearPendingTypedAgentMentionTimer();
471
+ return;
472
+ }
473
+ const pendingTypedAgentMention = mentionPluginState === null || mentionPluginState === void 0 ? void 0 : mentionPluginState.pendingTypedAgentMention;
474
+ if (!pendingTypedAgentMention) {
475
+ clearPendingTypedAgentMentionTimer();
476
+ return;
477
+ }
478
+ const timerKey = `${pendingTypedAgentMention.localId}:${pendingTypedAgentMention.resetCount}`;
479
+ if (timerKey === pendingTypedAgentMentionTimerKey) {
480
+ return;
481
+ }
482
+ clearPendingTypedAgentMentionTimer();
483
+ pendingTypedAgentMentionTimerKey = timerKey;
484
+ pendingTypedAgentMentionTimer = setTimeout(() => {
485
+ var _mentionPluginKey$get;
486
+ const latestPendingTypedAgentMention = (_mentionPluginKey$get = mentionPluginKey.getState(editorView.state)) === null || _mentionPluginKey$get === void 0 ? void 0 : _mentionPluginKey$get.pendingTypedAgentMention;
487
+ if (!latestPendingTypedAgentMention || latestPendingTypedAgentMention.localId !== pendingTypedAgentMention.localId || latestPendingTypedAgentMention.resetCount !== pendingTypedAgentMention.resetCount) {
488
+ return;
489
+ }
490
+ editorView.dispatch(editorView.state.tr.setMeta(mentionPluginKey, {
491
+ action: ACTIONS.COMMIT_PENDING_TYPED_AGENT_MENTION,
492
+ params: {
493
+ localId: pendingTypedAgentMention.localId,
494
+ resetCount: pendingTypedAgentMention.resetCount
495
+ }
496
+ }));
497
+ }, AGENT_MENTION_INACTIVITY_MS);
498
+ };
304
499
  const providerHandler = (name, providerPromise) => {
305
500
  switch (name) {
306
501
  case 'mentionProvider':
@@ -346,7 +541,15 @@ export function createMentionPlugin({
346
541
  pmPluginFactoryParams.providerFactory.subscribe('mentionProvider', providerHandler);
347
542
  }
348
543
  return {
544
+ update(view, prevState) {
545
+ const mentionPluginState = mentionPluginKey.getState(view.state);
546
+ if (mentionPluginState === mentionPluginKey.getState(prevState)) {
547
+ return;
548
+ }
549
+ schedulePendingTypedAgentMentionTimer(mentionPluginState);
550
+ },
349
551
  destroy() {
552
+ clearPendingTypedAgentMentionTimer();
350
553
  if (pmPluginFactoryParams.providerFactory) {
351
554
  pmPluginFactoryParams.providerFactory.unsubscribe('mentionProvider', providerHandler);
352
555
  }
@@ -97,7 +97,7 @@ export const buildTypeAheadInviteItemClickedPayload = (duration, upKeyCount, dow
97
97
  }
98
98
  };
99
99
  };
100
- export const buildTypeAheadInsertedPayload = (duration, upKeyCount, downKeyCount, sessionId, insertType, mention, mentionLocalId, mentionList, query, contextIdentifierProvider, taskListId, taskItemId) => {
100
+ export const buildTypeAheadInsertedPayload = (duration, upKeyCount, downKeyCount, sessionId, insertType, mention, mentionLocalId, mentionList, query, contextIdentifierProvider, taskListId, taskItemId, isAgent) => {
101
101
  const {
102
102
  queryLength,
103
103
  spaceInQuery
@@ -133,6 +133,9 @@ export const buildTypeAheadInsertedPayload = (duration, upKeyCount, downKeyCount
133
133
  downKeyCount,
134
134
  memberCount: isTeamType(mention.userType) && mention.context ? mention.context.memberCount : null,
135
135
  includesYou: isTeamType(mention.userType) && mention.context ? mention.context.includesYou : null,
136
+ ...(isAgent ? {
137
+ isAgent
138
+ } : {}),
136
139
  taskListId,
137
140
  taskItemId,
138
141
  localId: mentionLocalId,