@atlaskit/editor-plugin-mentions 14.3.0 → 14.4.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,30 @@
1
1
  # @atlaskit/editor-plugin-mentions
2
2
 
3
+ ## 14.4.1
4
+
5
+ ### Patch Changes
6
+
7
+ - Updated dependencies
8
+
9
+ ## 14.4.0
10
+
11
+ ### Minor Changes
12
+
13
+ - [`dbf22f118dd9e`](https://bitbucket.org/atlassian/atlassian-frontend-monorepo/commits/dbf22f118dd9e) -
14
+ Rewrite Rovo Chat @-mention provider on a single Relay-native RovoChatMentionResource that fires
15
+ people + agents in parallel via Promise. People render as soon as URS resolves and agents pop in
16
+ below when ready. New aliased agentStudio_getAgents query consolidates 3 prefetch round trips into
17
+ 1, drops over-fetched fields, and uses real abort plumbing. Adds opt-in subscribeToItemsUpdates
18
+ multi-emit contract to the editor mention typeahead so progressive emissions are no longer dropped
19
+ after the first frame.
20
+
21
+ ### Patch Changes
22
+
23
+ - [`ddeae9a5f8bdf`](https://bitbucket.org/atlassian/atlassian-frontend-monorepo/commits/ddeae9a5f8bdf) -
24
+ Prevent remote collaborative agent mention insertions from publishing local agent mention
25
+ insertion state.
26
+ - Updated dependencies
27
+
3
28
  ## 14.3.0
4
29
 
5
30
  ### Minor Changes
@@ -39,7 +39,7 @@ var isAgentUserType = function isAgentUserType(userType) {
39
39
  var AI_STREAMING_TRANSFORMATION_META_KEY = 'isAIStreamingTransformation';
40
40
  var AGENT_MENTION_INACTIVITY_MS = 3000;
41
41
  var PACKAGE_NAME = "@atlaskit/editor-plugin-mentions";
42
- var PACKAGE_VERSION = "14.2.3";
42
+ var PACKAGE_VERSION = "14.4.0";
43
43
  var setProvider = function setProvider(provider) {
44
44
  return function (state, dispatch) {
45
45
  if (dispatch) {
@@ -355,8 +355,7 @@ function createMentionPlugin(_ref2) {
355
355
  }
356
356
  (_insm$session2 = _insm.insm.session) === null || _insm$session2 === void 0 || _insm$session2.endFeature('mentionDeletionDetection');
357
357
  }
358
- var isAIStreaming = Boolean(tr.getMeta(AI_STREAMING_TRANSFORMATION_META_KEY));
359
- if (tr.docChanged && !tr.getMeta('replaceDocument') && !isAIStreaming && isAgentMentionsExperimentEnabled) {
358
+ if (isAgentMentionsExperimentEnabled && isQualifyingLocalUserDocChange(tr)) {
360
359
  var _mentionSchema = newState.schema.nodes.mention;
361
360
  var newDocRanges = [];
362
361
  var oldDocRanges = [];
@@ -241,17 +241,77 @@ var buildNodesForTeamMention = function buildNodesForTeamMention(schema, selecte
241
241
  inlineNodes.push(closeBracketText);
242
242
  return _model.Fragment.fromArray(inlineNodes);
243
243
  };
244
- var createTypeAheadConfig = exports.createTypeAheadConfig = function createTypeAheadConfig(_ref6) {
245
- var sanitizePrivateContent = _ref6.sanitizePrivateContent,
246
- mentionInsertDisplayName = _ref6.mentionInsertDisplayName,
247
- fireEvent = _ref6.fireEvent,
248
- HighlightComponent = _ref6.HighlightComponent,
249
- api = _ref6.api,
250
- handleMentionsChanged = _ref6.handleMentionsChanged;
244
+ /**
245
+ * Shared mentions → typeahead-items transformer used by both the
246
+ * single-shot `getItems` Promise path and the multi-emit
247
+ * `subscribeToItemsUpdates` path. Factoring this out avoids any
248
+ * chance the two paths drift in subtle item-shape behaviour (invite
249
+ * item injection, analytics emission, no-results bookkeeping).
250
+ *
251
+ * NOTE: `firstQueryWithoutResults` is captured by closure in
252
+ * `createTypeAheadConfig` and intentionally mutated here as a
253
+ * side-effect — preserves the existing single-shot semantics.
254
+ */
255
+ var makeTransformMentionsToTypeAheadItems = function makeTransformMentionsToTypeAheadItems(_ref6) {
256
+ var fireEvent = _ref6.fireEvent,
257
+ getFirstQueryWithoutResults = _ref6.getFirstQueryWithoutResults,
258
+ setFirstQueryWithoutResults = _ref6.setFirstQueryWithoutResults;
259
+ return function (_ref7) {
260
+ var mentions = _ref7.mentions,
261
+ query = _ref7.query,
262
+ stats = _ref7.stats,
263
+ mentionProvider = _ref7.mentionProvider,
264
+ contextIdentifierProvider = _ref7.contextIdentifierProvider,
265
+ sessionId = _ref7.sessionId;
266
+ var mentionItems = mentions.map(function (mention) {
267
+ return memoizedToItem.call(mention);
268
+ });
269
+ buildAndSendElementsTypeAheadAnalytics(fireEvent)({
270
+ query: query,
271
+ mentions: mentions,
272
+ stats: stats
273
+ });
274
+ if (mentions.length === 0 && getFirstQueryWithoutResults() === null) {
275
+ setFirstQueryWithoutResults(query);
276
+ }
277
+ if (!mentionProvider.shouldEnableInvite || mentionItems.length > 2) {
278
+ return mentionItems;
279
+ }
280
+ var emailDomain = mentionProvider.userEmailDomain;
281
+ return withInviteItem({
282
+ mentionProvider: mentionProvider,
283
+ firstQueryWithoutResults: getFirstQueryWithoutResults() || '',
284
+ currentQuery: query,
285
+ onInviteItemMount: function onInviteItemMount() {
286
+ var _mentionProvider$getS2;
287
+ fireEvent((0, _analytics.buildTypeAheadInviteItemViewedPayload)(sessionId, contextIdentifierProvider, mentionProvider.userRole, (0, _platformFeatureFlags.fg)('jira_invites_auto_tag_new_user_in_mentions_fg') ? {
288
+ isInlineInviteMentionsEnabled: (_mentionProvider$getS2 = mentionProvider.getShouldEnableInlineInvite) === null || _mentionProvider$getS2 === void 0 ? void 0 : _mentionProvider$getS2.call(mentionProvider)
289
+ } : {}));
290
+ },
291
+ emailDomain: emailDomain
292
+ })(mentionItems);
293
+ };
294
+ };
295
+ var createTypeAheadConfig = exports.createTypeAheadConfig = function createTypeAheadConfig(_ref8) {
296
+ var sanitizePrivateContent = _ref8.sanitizePrivateContent,
297
+ mentionInsertDisplayName = _ref8.mentionInsertDisplayName,
298
+ fireEvent = _ref8.fireEvent,
299
+ HighlightComponent = _ref8.HighlightComponent,
300
+ api = _ref8.api,
301
+ handleMentionsChanged = _ref8.handleMentionsChanged;
251
302
  // eslint-disable-next-line @atlaskit/platform/prefer-crypto-random-uuid -- Use crypto.randomUUID instead
252
303
  var sessionId = (0, _uuid.default)();
253
304
  var firstQueryWithoutResults = null;
254
305
  var subscriptionKeys = new Set();
306
+ var transformMentionsToTypeAheadItems = makeTransformMentionsToTypeAheadItems({
307
+ fireEvent: fireEvent,
308
+ getFirstQueryWithoutResults: function getFirstQueryWithoutResults() {
309
+ return firstQueryWithoutResults;
310
+ },
311
+ setFirstQueryWithoutResults: function setFirstQueryWithoutResults(query) {
312
+ firstQueryWithoutResults = query;
313
+ }
314
+ });
255
315
  var typeAhead = {
256
316
  id: _typeAhead.TypeAheadAvailableNodes.MENTION,
257
317
  trigger: '@',
@@ -265,17 +325,17 @@ var createTypeAheadConfig = exports.createTypeAheadConfig = function createTypeA
265
325
  }
266
326
  return null;
267
327
  },
268
- getItems: function getItems(_ref7) {
328
+ getItems: function getItems(_ref9) {
269
329
  var _api$contextIdentifie, _api$contextIdentifie2;
270
- var query = _ref7.query,
271
- editorState = _ref7.editorState;
330
+ var query = _ref9.query,
331
+ editorState = _ref9.editorState;
272
332
  var pluginState = (0, _utils2.getMentionPluginState)(editorState);
273
333
  if (!(pluginState !== null && pluginState !== void 0 && pluginState.mentionProvider)) {
274
334
  return Promise.resolve([]);
275
335
  }
276
336
  var mentionProvider = pluginState.mentionProvider;
277
- var _ref8 = (_api$contextIdentifie = api === null || api === void 0 || (_api$contextIdentifie2 = api.contextIdentifier) === null || _api$contextIdentifie2 === void 0 ? void 0 : _api$contextIdentifie2.sharedState.currentState()) !== null && _api$contextIdentifie !== void 0 ? _api$contextIdentifie : {},
278
- contextIdentifierProvider = _ref8.contextIdentifierProvider;
337
+ var _ref0 = (_api$contextIdentifie = api === null || api === void 0 || (_api$contextIdentifie2 = api.contextIdentifier) === null || _api$contextIdentifie2 === void 0 ? void 0 : _api$contextIdentifie2.sharedState.currentState()) !== null && _api$contextIdentifie !== void 0 ? _api$contextIdentifie : {},
338
+ contextIdentifierProvider = _ref0.contextIdentifierProvider;
279
339
  return new Promise(function (resolve, reject) {
280
340
  // eslint-disable-next-line @atlaskit/platform/prefer-crypto-random-uuid -- Use crypto.randomUUID instead
281
341
  var key = "loadingMentionsForTypeAhead_".concat((0, _uuid.default)());
@@ -287,35 +347,15 @@ var createTypeAheadConfig = exports.createTypeAheadConfig = function createTypeA
287
347
  }
288
348
  mentionProvider.unsubscribe(key);
289
349
  subscriptionKeys.delete(key);
290
- var mentionItems = mentions.map(function (mention) {
291
- return memoizedToItem.call(mention);
292
- });
293
- buildAndSendElementsTypeAheadAnalytics(fireEvent)({
294
- query: query,
350
+ var items = transformMentionsToTypeAheadItems({
295
351
  mentions: mentions,
296
- stats: stats
352
+ query: query,
353
+ stats: stats,
354
+ mentionProvider: mentionProvider,
355
+ contextIdentifierProvider: contextIdentifierProvider,
356
+ sessionId: sessionId
297
357
  });
298
- if (mentions.length === 0 && firstQueryWithoutResults === null) {
299
- firstQueryWithoutResults = query;
300
- }
301
- if (!mentionProvider.shouldEnableInvite || mentionItems.length > 2) {
302
- resolve(mentionItems);
303
- } else {
304
- var emailDomain = mentionProvider.userEmailDomain;
305
- var items = withInviteItem({
306
- mentionProvider: mentionProvider,
307
- firstQueryWithoutResults: firstQueryWithoutResults || '',
308
- currentQuery: query,
309
- onInviteItemMount: function onInviteItemMount() {
310
- var _mentionProvider$getS2;
311
- fireEvent((0, _analytics.buildTypeAheadInviteItemViewedPayload)(sessionId, contextIdentifierProvider, mentionProvider.userRole, (0, _platformFeatureFlags.fg)('jira_invites_auto_tag_new_user_in_mentions_fg') ? {
312
- isInlineInviteMentionsEnabled: (_mentionProvider$getS2 = mentionProvider.getShouldEnableInlineInvite) === null || _mentionProvider$getS2 === void 0 ? void 0 : _mentionProvider$getS2.call(mentionProvider)
313
- } : {}));
314
- },
315
- emailDomain: emailDomain
316
- })(mentionItems);
317
- resolve(items);
318
- }
358
+ resolve(items);
319
359
  };
320
360
  subscriptionKeys.add(key);
321
361
  mentionProvider.subscribe(key, mentionsSubscribeCallback, function () {
@@ -330,8 +370,8 @@ var createTypeAheadConfig = exports.createTypeAheadConfig = function createTypeA
330
370
  }));
331
371
  });
332
372
  },
333
- getSections: function getSections(_ref9) {
334
- var intl = _ref9.intl;
373
+ getSections: function getSections(_ref1) {
374
+ var intl = _ref1.intl;
335
375
  return [_objectSpread({
336
376
  id: 'people',
337
377
  title: intl.formatMessage(_messages.mentionMessages.typeAheadSectionPeople),
@@ -365,12 +405,12 @@ var createTypeAheadConfig = exports.createTypeAheadConfig = function createTypeA
365
405
  onOpen: function onOpen() {
366
406
  firstQueryWithoutResults = null;
367
407
  },
368
- selectItem: function selectItem(state, item, insert, _ref0) {
408
+ selectItem: function selectItem(state, item, insert, _ref10) {
369
409
  var _api$contextIdentifie3, _api$contextIdentifie4;
370
- var mode = _ref0.mode,
371
- stats = _ref0.stats,
372
- query = _ref0.query,
373
- sourceListItem = _ref0.sourceListItem;
410
+ var mode = _ref10.mode,
411
+ stats = _ref10.stats,
412
+ query = _ref10.query,
413
+ sourceListItem = _ref10.sourceListItem;
374
414
  var schema = state.schema;
375
415
  var pluginState = (0, _utils2.getMentionPluginState)(state);
376
416
  var mentionProvider = pluginState.mentionProvider;
@@ -381,8 +421,8 @@ var createTypeAheadConfig = exports.createTypeAheadConfig = function createTypeA
381
421
  accessLevel = _item$mention.accessLevel,
382
422
  userType = _item$mention.userType,
383
423
  isXProductUser = _item$mention.isXProductUser;
384
- 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 : {},
385
- contextIdentifierProvider = _ref1.contextIdentifierProvider;
424
+ var _ref11 = (_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 : {},
425
+ contextIdentifierProvider = _ref11.contextIdentifierProvider;
386
426
  var mentionContext = _objectSpread(_objectSpread({}, contextIdentifierProvider), {}, {
387
427
  sessionId: sessionId
388
428
  });
@@ -495,11 +535,11 @@ var createTypeAheadConfig = exports.createTypeAheadConfig = function createTypeA
495
535
  }
496
536
  return tr;
497
537
  },
498
- dismiss: function dismiss(_ref10) {
499
- var editorState = _ref10.editorState,
500
- query = _ref10.query,
501
- stats = _ref10.stats,
502
- wasItemInserted = _ref10.wasItemInserted;
538
+ dismiss: function dismiss(_ref12) {
539
+ var editorState = _ref12.editorState,
540
+ query = _ref12.query,
541
+ stats = _ref12.stats,
542
+ wasItemInserted = _ref12.wasItemInserted;
503
543
  firstQueryWithoutResults = null;
504
544
  var pickerElapsedTime = stats.startedAt ? performance.now() - stats.startedAt : 0;
505
545
  if (!wasItemInserted) {
@@ -527,5 +567,122 @@ var createTypeAheadConfig = exports.createTypeAheadConfig = function createTypeA
527
567
  sessionId = (0, _uuid.default)();
528
568
  }
529
569
  };
570
+
571
+ /**
572
+ * Opt-in multi-emit path. Subscribes to the provider's stream of
573
+ * results for the lifetime of the type-ahead session — does NOT
574
+ * unsubscribe after the first emission. Lets providers like
575
+ * `RovoChatMentionResource` deliver people-first then merged
576
+ * people + agents to the dropdown without the typeahead dropping
577
+ * the second emission on the floor.
578
+ *
579
+ * Gated behind `rovo_chat_agent_selection` (the same gate that drives
580
+ * agent mentions in the Rovo chat input). The generic type-ahead hook
581
+ * opts a handler into streaming purely by the presence of this method,
582
+ * so we only attach it when the gate is on — otherwise the proven
583
+ * single-shot `getItems` path continues to drive every consumer.
584
+ */
585
+ var subscribeToItemsUpdates = function subscribeToItemsUpdates(_ref13) {
586
+ var _api$contextIdentifie5, _api$contextIdentifie6;
587
+ var query = _ref13.query,
588
+ editorState = _ref13.editorState;
589
+ var pluginState = (0, _utils2.getMentionPluginState)(editorState);
590
+ if (!(pluginState !== null && pluginState !== void 0 && pluginState.mentionProvider)) {
591
+ return {
592
+ initial: Promise.resolve([]),
593
+ subscribe: function subscribe() {
594
+ return function () {};
595
+ }
596
+ };
597
+ }
598
+ var mentionProvider = pluginState.mentionProvider;
599
+ var _ref14 = (_api$contextIdentifie5 = api === null || api === void 0 || (_api$contextIdentifie6 = api.contextIdentifier) === null || _api$contextIdentifie6 === void 0 ? void 0 : _api$contextIdentifie6.sharedState.currentState()) !== null && _api$contextIdentifie5 !== void 0 ? _api$contextIdentifie5 : {},
600
+ contextIdentifierProvider = _ref14.contextIdentifierProvider;
601
+
602
+ // eslint-disable-next-line @atlaskit/platform/prefer-crypto-random-uuid -- Use crypto.randomUUID instead
603
+ var key = "loadingMentionsForTypeAhead_".concat((0, _uuid.default)());
604
+ var initialResolve = null;
605
+ var initialReject = null;
606
+ var initialResolved = false;
607
+ var updateCallback = null;
608
+ var unsubscribed = false;
609
+ var initial = new Promise(function (resolve, reject) {
610
+ initialResolve = resolve;
611
+ initialReject = reject;
612
+ });
613
+ var mentionsSubscribeCallback = function mentionsSubscribeCallback(mentions) {
614
+ var resultQuery = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : '';
615
+ var stats = arguments.length > 2 ? arguments[2] : undefined;
616
+ // Drop emissions tagged with a query that has moved on.
617
+ // Mirrors the same guard in the single-shot `getItems`.
618
+ if (query !== resultQuery) {
619
+ return;
620
+ }
621
+ if (unsubscribed) {
622
+ return;
623
+ }
624
+ var items = transformMentionsToTypeAheadItems({
625
+ mentions: mentions,
626
+ query: query,
627
+ stats: stats,
628
+ mentionProvider: mentionProvider,
629
+ contextIdentifierProvider: contextIdentifierProvider,
630
+ sessionId: sessionId
631
+ });
632
+ if (!initialResolved) {
633
+ var _initialResolve;
634
+ initialResolved = true;
635
+ (_initialResolve = initialResolve) === null || _initialResolve === void 0 || _initialResolve(items);
636
+ } else {
637
+ var _updateCallback;
638
+ (_updateCallback = updateCallback) === null || _updateCallback === void 0 || _updateCallback(items);
639
+ }
640
+ };
641
+ subscriptionKeys.add(key);
642
+ mentionProvider.subscribe(key, mentionsSubscribeCallback, function () {
643
+ if ((0, _experiments.editorExperiment)('platform_editor_offline_editing_web', true)) {
644
+ mentionProvider.unsubscribe(key);
645
+ subscriptionKeys.delete(key);
646
+ if (!initialResolved) {
647
+ var _initialReject;
648
+ initialResolved = true;
649
+ (_initialReject = initialReject) === null || _initialReject === void 0 || _initialReject('FETCH_ERROR');
650
+ }
651
+ }
652
+ });
653
+ mentionProvider.filter(query || '', _objectSpread(_objectSpread({}, contextIdentifierProvider), {}, {
654
+ sessionId: sessionId
655
+ }));
656
+ return {
657
+ initial: initial,
658
+ subscribe: function subscribe(update) {
659
+ if (updateCallback) {
660
+ throw new Error('TypeAhead mention updates support only one subscriber');
661
+ }
662
+ updateCallback = update;
663
+ return function () {
664
+ unsubscribed = true;
665
+ updateCallback = null;
666
+ mentionProvider.unsubscribe(key);
667
+ subscriptionKeys.delete(key);
668
+ // If cleanup runs before the first emission, settle the initial
669
+ // promise so its `.then` chain (and the closures it holds) is
670
+ // released instead of pending forever.
671
+ if (!initialResolved) {
672
+ var _initialResolve2;
673
+ initialResolved = true;
674
+ (_initialResolve2 = initialResolve) === null || _initialResolve2 === void 0 || _initialResolve2([]);
675
+ }
676
+ };
677
+ }
678
+ };
679
+ };
680
+
681
+ // Presence of `subscribeToItemsUpdates` is how the type-ahead hook
682
+ // opts a handler into the multi-emit path, so only expose it when the
683
+ // agent-selection gate is on.
684
+ if ((0, _platformFeatureFlags.fg)('rovo_chat_agent_selection')) {
685
+ typeAhead.subscribeToItemsUpdates = subscribeToItemsUpdates;
686
+ }
530
687
  return typeAhead;
531
688
  };
@@ -24,7 +24,7 @@ const isAgentUserType = userType => {
24
24
  const AI_STREAMING_TRANSFORMATION_META_KEY = 'isAIStreamingTransformation';
25
25
  const AGENT_MENTION_INACTIVITY_MS = 3000;
26
26
  const PACKAGE_NAME = "@atlaskit/editor-plugin-mentions";
27
- const PACKAGE_VERSION = "14.2.3";
27
+ const PACKAGE_VERSION = "14.4.0";
28
28
  const setProvider = provider => (state, dispatch) => {
29
29
  if (dispatch) {
30
30
  dispatch(state.tr.setMeta(mentionPluginKey, {
@@ -333,8 +333,7 @@ export function createMentionPlugin({
333
333
  }
334
334
  (_insm$session2 = insm.session) === null || _insm$session2 === void 0 ? void 0 : _insm$session2.endFeature('mentionDeletionDetection');
335
335
  }
336
- const isAIStreaming = Boolean(tr.getMeta(AI_STREAMING_TRANSFORMATION_META_KEY));
337
- if (tr.docChanged && !tr.getMeta('replaceDocument') && !isAIStreaming && isAgentMentionsExperimentEnabled) {
336
+ if (isAgentMentionsExperimentEnabled && isQualifyingLocalUserDocChange(tr)) {
338
337
  const mentionSchema = newState.schema.nodes.mention;
339
338
  const newDocRanges = [];
340
339
  const oldDocRanges = [];
@@ -214,6 +214,57 @@ const buildNodesForTeamMention = (schema, selectedMention, mentionProvider, sani
214
214
  inlineNodes.push(closeBracketText);
215
215
  return Fragment.fromArray(inlineNodes);
216
216
  };
217
+ /**
218
+ * Shared mentions → typeahead-items transformer used by both the
219
+ * single-shot `getItems` Promise path and the multi-emit
220
+ * `subscribeToItemsUpdates` path. Factoring this out avoids any
221
+ * chance the two paths drift in subtle item-shape behaviour (invite
222
+ * item injection, analytics emission, no-results bookkeeping).
223
+ *
224
+ * NOTE: `firstQueryWithoutResults` is captured by closure in
225
+ * `createTypeAheadConfig` and intentionally mutated here as a
226
+ * side-effect — preserves the existing single-shot semantics.
227
+ */
228
+ const makeTransformMentionsToTypeAheadItems = ({
229
+ fireEvent,
230
+ getFirstQueryWithoutResults,
231
+ setFirstQueryWithoutResults
232
+ }) => {
233
+ return ({
234
+ mentions,
235
+ query,
236
+ stats,
237
+ mentionProvider,
238
+ contextIdentifierProvider,
239
+ sessionId
240
+ }) => {
241
+ const mentionItems = mentions.map(mention => memoizedToItem.call(mention));
242
+ buildAndSendElementsTypeAheadAnalytics(fireEvent)({
243
+ query,
244
+ mentions,
245
+ stats
246
+ });
247
+ if (mentions.length === 0 && getFirstQueryWithoutResults() === null) {
248
+ setFirstQueryWithoutResults(query);
249
+ }
250
+ if (!mentionProvider.shouldEnableInvite || mentionItems.length > 2) {
251
+ return mentionItems;
252
+ }
253
+ const emailDomain = mentionProvider.userEmailDomain;
254
+ return withInviteItem({
255
+ mentionProvider,
256
+ firstQueryWithoutResults: getFirstQueryWithoutResults() || '',
257
+ currentQuery: query,
258
+ onInviteItemMount: () => {
259
+ var _mentionProvider$getS2;
260
+ fireEvent(buildTypeAheadInviteItemViewedPayload(sessionId, contextIdentifierProvider, mentionProvider.userRole, fg('jira_invites_auto_tag_new_user_in_mentions_fg') ? {
261
+ isInlineInviteMentionsEnabled: (_mentionProvider$getS2 = mentionProvider.getShouldEnableInlineInvite) === null || _mentionProvider$getS2 === void 0 ? void 0 : _mentionProvider$getS2.call(mentionProvider)
262
+ } : {}));
263
+ },
264
+ emailDomain
265
+ })(mentionItems);
266
+ };
267
+ };
217
268
  export const createTypeAheadConfig = ({
218
269
  sanitizePrivateContent,
219
270
  mentionInsertDisplayName,
@@ -226,6 +277,13 @@ export const createTypeAheadConfig = ({
226
277
  let sessionId = uuid();
227
278
  let firstQueryWithoutResults = null;
228
279
  const subscriptionKeys = new Set();
280
+ const transformMentionsToTypeAheadItems = makeTransformMentionsToTypeAheadItems({
281
+ fireEvent,
282
+ getFirstQueryWithoutResults: () => firstQueryWithoutResults,
283
+ setFirstQueryWithoutResults: query => {
284
+ firstQueryWithoutResults = query;
285
+ }
286
+ });
229
287
  const typeAhead = {
230
288
  id: TypeAheadAvailableNodes.MENTION,
231
289
  trigger: '@',
@@ -263,33 +321,15 @@ export const createTypeAheadConfig = ({
263
321
  }
264
322
  mentionProvider.unsubscribe(key);
265
323
  subscriptionKeys.delete(key);
266
- const mentionItems = mentions.map(mention => memoizedToItem.call(mention));
267
- buildAndSendElementsTypeAheadAnalytics(fireEvent)({
268
- query,
324
+ const items = transformMentionsToTypeAheadItems({
269
325
  mentions,
270
- stats
326
+ query,
327
+ stats,
328
+ mentionProvider,
329
+ contextIdentifierProvider,
330
+ sessionId
271
331
  });
272
- if (mentions.length === 0 && firstQueryWithoutResults === null) {
273
- firstQueryWithoutResults = query;
274
- }
275
- if (!mentionProvider.shouldEnableInvite || mentionItems.length > 2) {
276
- resolve(mentionItems);
277
- } else {
278
- const emailDomain = mentionProvider.userEmailDomain;
279
- const items = withInviteItem({
280
- mentionProvider,
281
- firstQueryWithoutResults: firstQueryWithoutResults || '',
282
- currentQuery: query,
283
- onInviteItemMount: () => {
284
- var _mentionProvider$getS2;
285
- fireEvent(buildTypeAheadInviteItemViewedPayload(sessionId, contextIdentifierProvider, mentionProvider.userRole, fg('jira_invites_auto_tag_new_user_in_mentions_fg') ? {
286
- isInlineInviteMentionsEnabled: (_mentionProvider$getS2 = mentionProvider.getShouldEnableInlineInvite) === null || _mentionProvider$getS2 === void 0 ? void 0 : _mentionProvider$getS2.call(mentionProvider)
287
- } : {}));
288
- },
289
- emailDomain
290
- })(mentionItems);
291
- resolve(items);
292
- }
332
+ resolve(items);
293
333
  };
294
334
  subscriptionKeys.add(key);
295
335
  mentionProvider.subscribe(key, mentionsSubscribeCallback, () => {
@@ -504,5 +544,123 @@ export const createTypeAheadConfig = ({
504
544
  sessionId = uuid();
505
545
  }
506
546
  };
547
+
548
+ /**
549
+ * Opt-in multi-emit path. Subscribes to the provider's stream of
550
+ * results for the lifetime of the type-ahead session — does NOT
551
+ * unsubscribe after the first emission. Lets providers like
552
+ * `RovoChatMentionResource` deliver people-first then merged
553
+ * people + agents to the dropdown without the typeahead dropping
554
+ * the second emission on the floor.
555
+ *
556
+ * Gated behind `rovo_chat_agent_selection` (the same gate that drives
557
+ * agent mentions in the Rovo chat input). The generic type-ahead hook
558
+ * opts a handler into streaming purely by the presence of this method,
559
+ * so we only attach it when the gate is on — otherwise the proven
560
+ * single-shot `getItems` path continues to drive every consumer.
561
+ */
562
+ const subscribeToItemsUpdates = ({
563
+ query,
564
+ editorState
565
+ }) => {
566
+ var _api$contextIdentifie5, _api$contextIdentifie6;
567
+ const pluginState = getMentionPluginState(editorState);
568
+ if (!(pluginState !== null && pluginState !== void 0 && pluginState.mentionProvider)) {
569
+ return {
570
+ initial: Promise.resolve([]),
571
+ subscribe: () => () => {}
572
+ };
573
+ }
574
+ const {
575
+ mentionProvider
576
+ } = pluginState;
577
+ const {
578
+ contextIdentifierProvider
579
+ } = (_api$contextIdentifie5 = api === null || api === void 0 ? void 0 : (_api$contextIdentifie6 = api.contextIdentifier) === null || _api$contextIdentifie6 === void 0 ? void 0 : _api$contextIdentifie6.sharedState.currentState()) !== null && _api$contextIdentifie5 !== void 0 ? _api$contextIdentifie5 : {};
580
+
581
+ // eslint-disable-next-line @atlaskit/platform/prefer-crypto-random-uuid -- Use crypto.randomUUID instead
582
+ const key = `loadingMentionsForTypeAhead_${uuid()}`;
583
+ let initialResolve = null;
584
+ let initialReject = null;
585
+ let initialResolved = false;
586
+ let updateCallback = null;
587
+ let unsubscribed = false;
588
+ const initial = new Promise((resolve, reject) => {
589
+ initialResolve = resolve;
590
+ initialReject = reject;
591
+ });
592
+ const mentionsSubscribeCallback = (mentions, resultQuery = '', stats) => {
593
+ // Drop emissions tagged with a query that has moved on.
594
+ // Mirrors the same guard in the single-shot `getItems`.
595
+ if (query !== resultQuery) {
596
+ return;
597
+ }
598
+ if (unsubscribed) {
599
+ return;
600
+ }
601
+ const items = transformMentionsToTypeAheadItems({
602
+ mentions,
603
+ query,
604
+ stats,
605
+ mentionProvider,
606
+ contextIdentifierProvider,
607
+ sessionId
608
+ });
609
+ if (!initialResolved) {
610
+ var _initialResolve;
611
+ initialResolved = true;
612
+ (_initialResolve = initialResolve) === null || _initialResolve === void 0 ? void 0 : _initialResolve(items);
613
+ } else {
614
+ var _updateCallback;
615
+ (_updateCallback = updateCallback) === null || _updateCallback === void 0 ? void 0 : _updateCallback(items);
616
+ }
617
+ };
618
+ subscriptionKeys.add(key);
619
+ mentionProvider.subscribe(key, mentionsSubscribeCallback, () => {
620
+ if (editorExperiment('platform_editor_offline_editing_web', true)) {
621
+ mentionProvider.unsubscribe(key);
622
+ subscriptionKeys.delete(key);
623
+ if (!initialResolved) {
624
+ var _initialReject;
625
+ initialResolved = true;
626
+ (_initialReject = initialReject) === null || _initialReject === void 0 ? void 0 : _initialReject('FETCH_ERROR');
627
+ }
628
+ }
629
+ });
630
+ mentionProvider.filter(query || '', {
631
+ ...contextIdentifierProvider,
632
+ sessionId
633
+ });
634
+ return {
635
+ initial,
636
+ subscribe: update => {
637
+ if (updateCallback) {
638
+ throw new Error('TypeAhead mention updates support only one subscriber');
639
+ }
640
+ updateCallback = update;
641
+ return () => {
642
+ unsubscribed = true;
643
+ updateCallback = null;
644
+ mentionProvider.unsubscribe(key);
645
+ subscriptionKeys.delete(key);
646
+ // If cleanup runs before the first emission, settle the initial
647
+ // promise so its `.then` chain (and the closures it holds) is
648
+ // released instead of pending forever.
649
+ if (!initialResolved) {
650
+ var _initialResolve2;
651
+ initialResolved = true;
652
+ (_initialResolve2 = initialResolve) === null || _initialResolve2 === void 0 ? void 0 : _initialResolve2([]);
653
+ }
654
+ };
655
+ }
656
+ };
657
+ };
658
+
659
+ // Presence of `subscribeToItemsUpdates` is how the type-ahead hook
660
+ // opts a handler into the multi-emit path, so only expose it when the
661
+ // agent-selection gate is on.
662
+ if (fg('rovo_chat_agent_selection')) {
663
+ typeAhead.subscribeToItemsUpdates = subscribeToItemsUpdates;
664
+ }
507
665
  return typeAhead;
508
666
  };
@@ -31,7 +31,7 @@ var isAgentUserType = function isAgentUserType(userType) {
31
31
  var AI_STREAMING_TRANSFORMATION_META_KEY = 'isAIStreamingTransformation';
32
32
  var AGENT_MENTION_INACTIVITY_MS = 3000;
33
33
  var PACKAGE_NAME = "@atlaskit/editor-plugin-mentions";
34
- var PACKAGE_VERSION = "14.2.3";
34
+ var PACKAGE_VERSION = "14.4.0";
35
35
  var setProvider = function setProvider(provider) {
36
36
  return function (state, dispatch) {
37
37
  if (dispatch) {
@@ -347,8 +347,7 @@ export function createMentionPlugin(_ref2) {
347
347
  }
348
348
  (_insm$session2 = insm.session) === null || _insm$session2 === void 0 || _insm$session2.endFeature('mentionDeletionDetection');
349
349
  }
350
- var isAIStreaming = Boolean(tr.getMeta(AI_STREAMING_TRANSFORMATION_META_KEY));
351
- if (tr.docChanged && !tr.getMeta('replaceDocument') && !isAIStreaming && isAgentMentionsExperimentEnabled) {
350
+ if (isAgentMentionsExperimentEnabled && isQualifyingLocalUserDocChange(tr)) {
352
351
  var _mentionSchema = newState.schema.nodes.mention;
353
352
  var newDocRanges = [];
354
353
  var oldDocRanges = [];
@@ -232,17 +232,77 @@ var buildNodesForTeamMention = function buildNodesForTeamMention(schema, selecte
232
232
  inlineNodes.push(closeBracketText);
233
233
  return Fragment.fromArray(inlineNodes);
234
234
  };
235
- export var createTypeAheadConfig = function createTypeAheadConfig(_ref6) {
236
- var sanitizePrivateContent = _ref6.sanitizePrivateContent,
237
- mentionInsertDisplayName = _ref6.mentionInsertDisplayName,
238
- fireEvent = _ref6.fireEvent,
239
- HighlightComponent = _ref6.HighlightComponent,
240
- api = _ref6.api,
241
- handleMentionsChanged = _ref6.handleMentionsChanged;
235
+ /**
236
+ * Shared mentions → typeahead-items transformer used by both the
237
+ * single-shot `getItems` Promise path and the multi-emit
238
+ * `subscribeToItemsUpdates` path. Factoring this out avoids any
239
+ * chance the two paths drift in subtle item-shape behaviour (invite
240
+ * item injection, analytics emission, no-results bookkeeping).
241
+ *
242
+ * NOTE: `firstQueryWithoutResults` is captured by closure in
243
+ * `createTypeAheadConfig` and intentionally mutated here as a
244
+ * side-effect — preserves the existing single-shot semantics.
245
+ */
246
+ var makeTransformMentionsToTypeAheadItems = function makeTransformMentionsToTypeAheadItems(_ref6) {
247
+ var fireEvent = _ref6.fireEvent,
248
+ getFirstQueryWithoutResults = _ref6.getFirstQueryWithoutResults,
249
+ setFirstQueryWithoutResults = _ref6.setFirstQueryWithoutResults;
250
+ return function (_ref7) {
251
+ var mentions = _ref7.mentions,
252
+ query = _ref7.query,
253
+ stats = _ref7.stats,
254
+ mentionProvider = _ref7.mentionProvider,
255
+ contextIdentifierProvider = _ref7.contextIdentifierProvider,
256
+ sessionId = _ref7.sessionId;
257
+ var mentionItems = mentions.map(function (mention) {
258
+ return memoizedToItem.call(mention);
259
+ });
260
+ buildAndSendElementsTypeAheadAnalytics(fireEvent)({
261
+ query: query,
262
+ mentions: mentions,
263
+ stats: stats
264
+ });
265
+ if (mentions.length === 0 && getFirstQueryWithoutResults() === null) {
266
+ setFirstQueryWithoutResults(query);
267
+ }
268
+ if (!mentionProvider.shouldEnableInvite || mentionItems.length > 2) {
269
+ return mentionItems;
270
+ }
271
+ var emailDomain = mentionProvider.userEmailDomain;
272
+ return withInviteItem({
273
+ mentionProvider: mentionProvider,
274
+ firstQueryWithoutResults: getFirstQueryWithoutResults() || '',
275
+ currentQuery: query,
276
+ onInviteItemMount: function onInviteItemMount() {
277
+ var _mentionProvider$getS2;
278
+ fireEvent(buildTypeAheadInviteItemViewedPayload(sessionId, contextIdentifierProvider, mentionProvider.userRole, fg('jira_invites_auto_tag_new_user_in_mentions_fg') ? {
279
+ isInlineInviteMentionsEnabled: (_mentionProvider$getS2 = mentionProvider.getShouldEnableInlineInvite) === null || _mentionProvider$getS2 === void 0 ? void 0 : _mentionProvider$getS2.call(mentionProvider)
280
+ } : {}));
281
+ },
282
+ emailDomain: emailDomain
283
+ })(mentionItems);
284
+ };
285
+ };
286
+ export var createTypeAheadConfig = function createTypeAheadConfig(_ref8) {
287
+ var sanitizePrivateContent = _ref8.sanitizePrivateContent,
288
+ mentionInsertDisplayName = _ref8.mentionInsertDisplayName,
289
+ fireEvent = _ref8.fireEvent,
290
+ HighlightComponent = _ref8.HighlightComponent,
291
+ api = _ref8.api,
292
+ handleMentionsChanged = _ref8.handleMentionsChanged;
242
293
  // eslint-disable-next-line @atlaskit/platform/prefer-crypto-random-uuid -- Use crypto.randomUUID instead
243
294
  var sessionId = uuid();
244
295
  var firstQueryWithoutResults = null;
245
296
  var subscriptionKeys = new Set();
297
+ var transformMentionsToTypeAheadItems = makeTransformMentionsToTypeAheadItems({
298
+ fireEvent: fireEvent,
299
+ getFirstQueryWithoutResults: function getFirstQueryWithoutResults() {
300
+ return firstQueryWithoutResults;
301
+ },
302
+ setFirstQueryWithoutResults: function setFirstQueryWithoutResults(query) {
303
+ firstQueryWithoutResults = query;
304
+ }
305
+ });
246
306
  var typeAhead = {
247
307
  id: TypeAheadAvailableNodes.MENTION,
248
308
  trigger: '@',
@@ -256,17 +316,17 @@ export var createTypeAheadConfig = function createTypeAheadConfig(_ref6) {
256
316
  }
257
317
  return null;
258
318
  },
259
- getItems: function getItems(_ref7) {
319
+ getItems: function getItems(_ref9) {
260
320
  var _api$contextIdentifie, _api$contextIdentifie2;
261
- var query = _ref7.query,
262
- editorState = _ref7.editorState;
321
+ var query = _ref9.query,
322
+ editorState = _ref9.editorState;
263
323
  var pluginState = getMentionPluginState(editorState);
264
324
  if (!(pluginState !== null && pluginState !== void 0 && pluginState.mentionProvider)) {
265
325
  return Promise.resolve([]);
266
326
  }
267
327
  var mentionProvider = pluginState.mentionProvider;
268
- var _ref8 = (_api$contextIdentifie = api === null || api === void 0 || (_api$contextIdentifie2 = api.contextIdentifier) === null || _api$contextIdentifie2 === void 0 ? void 0 : _api$contextIdentifie2.sharedState.currentState()) !== null && _api$contextIdentifie !== void 0 ? _api$contextIdentifie : {},
269
- contextIdentifierProvider = _ref8.contextIdentifierProvider;
328
+ var _ref0 = (_api$contextIdentifie = api === null || api === void 0 || (_api$contextIdentifie2 = api.contextIdentifier) === null || _api$contextIdentifie2 === void 0 ? void 0 : _api$contextIdentifie2.sharedState.currentState()) !== null && _api$contextIdentifie !== void 0 ? _api$contextIdentifie : {},
329
+ contextIdentifierProvider = _ref0.contextIdentifierProvider;
270
330
  return new Promise(function (resolve, reject) {
271
331
  // eslint-disable-next-line @atlaskit/platform/prefer-crypto-random-uuid -- Use crypto.randomUUID instead
272
332
  var key = "loadingMentionsForTypeAhead_".concat(uuid());
@@ -278,35 +338,15 @@ export var createTypeAheadConfig = function createTypeAheadConfig(_ref6) {
278
338
  }
279
339
  mentionProvider.unsubscribe(key);
280
340
  subscriptionKeys.delete(key);
281
- var mentionItems = mentions.map(function (mention) {
282
- return memoizedToItem.call(mention);
283
- });
284
- buildAndSendElementsTypeAheadAnalytics(fireEvent)({
285
- query: query,
341
+ var items = transformMentionsToTypeAheadItems({
286
342
  mentions: mentions,
287
- stats: stats
343
+ query: query,
344
+ stats: stats,
345
+ mentionProvider: mentionProvider,
346
+ contextIdentifierProvider: contextIdentifierProvider,
347
+ sessionId: sessionId
288
348
  });
289
- if (mentions.length === 0 && firstQueryWithoutResults === null) {
290
- firstQueryWithoutResults = query;
291
- }
292
- if (!mentionProvider.shouldEnableInvite || mentionItems.length > 2) {
293
- resolve(mentionItems);
294
- } else {
295
- var emailDomain = mentionProvider.userEmailDomain;
296
- var items = withInviteItem({
297
- mentionProvider: mentionProvider,
298
- firstQueryWithoutResults: firstQueryWithoutResults || '',
299
- currentQuery: query,
300
- onInviteItemMount: function onInviteItemMount() {
301
- var _mentionProvider$getS2;
302
- fireEvent(buildTypeAheadInviteItemViewedPayload(sessionId, contextIdentifierProvider, mentionProvider.userRole, fg('jira_invites_auto_tag_new_user_in_mentions_fg') ? {
303
- isInlineInviteMentionsEnabled: (_mentionProvider$getS2 = mentionProvider.getShouldEnableInlineInvite) === null || _mentionProvider$getS2 === void 0 ? void 0 : _mentionProvider$getS2.call(mentionProvider)
304
- } : {}));
305
- },
306
- emailDomain: emailDomain
307
- })(mentionItems);
308
- resolve(items);
309
- }
349
+ resolve(items);
310
350
  };
311
351
  subscriptionKeys.add(key);
312
352
  mentionProvider.subscribe(key, mentionsSubscribeCallback, function () {
@@ -321,8 +361,8 @@ export var createTypeAheadConfig = function createTypeAheadConfig(_ref6) {
321
361
  }));
322
362
  });
323
363
  },
324
- getSections: function getSections(_ref9) {
325
- var intl = _ref9.intl;
364
+ getSections: function getSections(_ref1) {
365
+ var intl = _ref1.intl;
326
366
  return [_objectSpread({
327
367
  id: 'people',
328
368
  title: intl.formatMessage(mentionMessages.typeAheadSectionPeople),
@@ -356,12 +396,12 @@ export var createTypeAheadConfig = function createTypeAheadConfig(_ref6) {
356
396
  onOpen: function onOpen() {
357
397
  firstQueryWithoutResults = null;
358
398
  },
359
- selectItem: function selectItem(state, item, insert, _ref0) {
399
+ selectItem: function selectItem(state, item, insert, _ref10) {
360
400
  var _api$contextIdentifie3, _api$contextIdentifie4;
361
- var mode = _ref0.mode,
362
- stats = _ref0.stats,
363
- query = _ref0.query,
364
- sourceListItem = _ref0.sourceListItem;
401
+ var mode = _ref10.mode,
402
+ stats = _ref10.stats,
403
+ query = _ref10.query,
404
+ sourceListItem = _ref10.sourceListItem;
365
405
  var schema = state.schema;
366
406
  var pluginState = getMentionPluginState(state);
367
407
  var mentionProvider = pluginState.mentionProvider;
@@ -372,8 +412,8 @@ export var createTypeAheadConfig = function createTypeAheadConfig(_ref6) {
372
412
  accessLevel = _item$mention.accessLevel,
373
413
  userType = _item$mention.userType,
374
414
  isXProductUser = _item$mention.isXProductUser;
375
- 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 : {},
376
- contextIdentifierProvider = _ref1.contextIdentifierProvider;
415
+ var _ref11 = (_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 : {},
416
+ contextIdentifierProvider = _ref11.contextIdentifierProvider;
377
417
  var mentionContext = _objectSpread(_objectSpread({}, contextIdentifierProvider), {}, {
378
418
  sessionId: sessionId
379
419
  });
@@ -486,11 +526,11 @@ export var createTypeAheadConfig = function createTypeAheadConfig(_ref6) {
486
526
  }
487
527
  return tr;
488
528
  },
489
- dismiss: function dismiss(_ref10) {
490
- var editorState = _ref10.editorState,
491
- query = _ref10.query,
492
- stats = _ref10.stats,
493
- wasItemInserted = _ref10.wasItemInserted;
529
+ dismiss: function dismiss(_ref12) {
530
+ var editorState = _ref12.editorState,
531
+ query = _ref12.query,
532
+ stats = _ref12.stats,
533
+ wasItemInserted = _ref12.wasItemInserted;
494
534
  firstQueryWithoutResults = null;
495
535
  var pickerElapsedTime = stats.startedAt ? performance.now() - stats.startedAt : 0;
496
536
  if (!wasItemInserted) {
@@ -518,5 +558,122 @@ export var createTypeAheadConfig = function createTypeAheadConfig(_ref6) {
518
558
  sessionId = uuid();
519
559
  }
520
560
  };
561
+
562
+ /**
563
+ * Opt-in multi-emit path. Subscribes to the provider's stream of
564
+ * results for the lifetime of the type-ahead session — does NOT
565
+ * unsubscribe after the first emission. Lets providers like
566
+ * `RovoChatMentionResource` deliver people-first then merged
567
+ * people + agents to the dropdown without the typeahead dropping
568
+ * the second emission on the floor.
569
+ *
570
+ * Gated behind `rovo_chat_agent_selection` (the same gate that drives
571
+ * agent mentions in the Rovo chat input). The generic type-ahead hook
572
+ * opts a handler into streaming purely by the presence of this method,
573
+ * so we only attach it when the gate is on — otherwise the proven
574
+ * single-shot `getItems` path continues to drive every consumer.
575
+ */
576
+ var subscribeToItemsUpdates = function subscribeToItemsUpdates(_ref13) {
577
+ var _api$contextIdentifie5, _api$contextIdentifie6;
578
+ var query = _ref13.query,
579
+ editorState = _ref13.editorState;
580
+ var pluginState = getMentionPluginState(editorState);
581
+ if (!(pluginState !== null && pluginState !== void 0 && pluginState.mentionProvider)) {
582
+ return {
583
+ initial: Promise.resolve([]),
584
+ subscribe: function subscribe() {
585
+ return function () {};
586
+ }
587
+ };
588
+ }
589
+ var mentionProvider = pluginState.mentionProvider;
590
+ var _ref14 = (_api$contextIdentifie5 = api === null || api === void 0 || (_api$contextIdentifie6 = api.contextIdentifier) === null || _api$contextIdentifie6 === void 0 ? void 0 : _api$contextIdentifie6.sharedState.currentState()) !== null && _api$contextIdentifie5 !== void 0 ? _api$contextIdentifie5 : {},
591
+ contextIdentifierProvider = _ref14.contextIdentifierProvider;
592
+
593
+ // eslint-disable-next-line @atlaskit/platform/prefer-crypto-random-uuid -- Use crypto.randomUUID instead
594
+ var key = "loadingMentionsForTypeAhead_".concat(uuid());
595
+ var initialResolve = null;
596
+ var initialReject = null;
597
+ var initialResolved = false;
598
+ var updateCallback = null;
599
+ var unsubscribed = false;
600
+ var initial = new Promise(function (resolve, reject) {
601
+ initialResolve = resolve;
602
+ initialReject = reject;
603
+ });
604
+ var mentionsSubscribeCallback = function mentionsSubscribeCallback(mentions) {
605
+ var resultQuery = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : '';
606
+ var stats = arguments.length > 2 ? arguments[2] : undefined;
607
+ // Drop emissions tagged with a query that has moved on.
608
+ // Mirrors the same guard in the single-shot `getItems`.
609
+ if (query !== resultQuery) {
610
+ return;
611
+ }
612
+ if (unsubscribed) {
613
+ return;
614
+ }
615
+ var items = transformMentionsToTypeAheadItems({
616
+ mentions: mentions,
617
+ query: query,
618
+ stats: stats,
619
+ mentionProvider: mentionProvider,
620
+ contextIdentifierProvider: contextIdentifierProvider,
621
+ sessionId: sessionId
622
+ });
623
+ if (!initialResolved) {
624
+ var _initialResolve;
625
+ initialResolved = true;
626
+ (_initialResolve = initialResolve) === null || _initialResolve === void 0 || _initialResolve(items);
627
+ } else {
628
+ var _updateCallback;
629
+ (_updateCallback = updateCallback) === null || _updateCallback === void 0 || _updateCallback(items);
630
+ }
631
+ };
632
+ subscriptionKeys.add(key);
633
+ mentionProvider.subscribe(key, mentionsSubscribeCallback, function () {
634
+ if (editorExperiment('platform_editor_offline_editing_web', true)) {
635
+ mentionProvider.unsubscribe(key);
636
+ subscriptionKeys.delete(key);
637
+ if (!initialResolved) {
638
+ var _initialReject;
639
+ initialResolved = true;
640
+ (_initialReject = initialReject) === null || _initialReject === void 0 || _initialReject('FETCH_ERROR');
641
+ }
642
+ }
643
+ });
644
+ mentionProvider.filter(query || '', _objectSpread(_objectSpread({}, contextIdentifierProvider), {}, {
645
+ sessionId: sessionId
646
+ }));
647
+ return {
648
+ initial: initial,
649
+ subscribe: function subscribe(update) {
650
+ if (updateCallback) {
651
+ throw new Error('TypeAhead mention updates support only one subscriber');
652
+ }
653
+ updateCallback = update;
654
+ return function () {
655
+ unsubscribed = true;
656
+ updateCallback = null;
657
+ mentionProvider.unsubscribe(key);
658
+ subscriptionKeys.delete(key);
659
+ // If cleanup runs before the first emission, settle the initial
660
+ // promise so its `.then` chain (and the closures it holds) is
661
+ // released instead of pending forever.
662
+ if (!initialResolved) {
663
+ var _initialResolve2;
664
+ initialResolved = true;
665
+ (_initialResolve2 = initialResolve) === null || _initialResolve2 === void 0 || _initialResolve2([]);
666
+ }
667
+ };
668
+ }
669
+ };
670
+ };
671
+
672
+ // Presence of `subscribeToItemsUpdates` is how the type-ahead hook
673
+ // opts a handler into the multi-emit path, so only expose it when the
674
+ // agent-selection gate is on.
675
+ if (fg('rovo_chat_agent_selection')) {
676
+ typeAhead.subscribeToItemsUpdates = subscribeToItemsUpdates;
677
+ }
521
678
  return typeAhead;
522
679
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@atlaskit/editor-plugin-mentions",
3
- "version": "14.3.0",
3
+ "version": "14.4.1",
4
4
  "description": "Mentions plugin for @atlaskit/editor-core",
5
5
  "author": "Atlassian Pty Ltd",
6
6
  "license": "Apache-2.0",
@@ -27,7 +27,7 @@
27
27
  "@atlaskit/editor-plugin-base": "^13.0.0",
28
28
  "@atlaskit/editor-plugin-context-identifier": "^12.0.0",
29
29
  "@atlaskit/editor-plugin-selection": "^12.0.0",
30
- "@atlaskit/editor-plugin-type-ahead": "^12.0.0",
30
+ "@atlaskit/editor-plugin-type-ahead": "^12.1.0",
31
31
  "@atlaskit/editor-prosemirror": "^8.0.0",
32
32
  "@atlaskit/icon": "^36.0.0",
33
33
  "@atlaskit/insm": "^1.0.0",
@@ -41,8 +41,8 @@
41
41
  "@atlaskit/profilecard": "^26.1.0",
42
42
  "@atlaskit/teams-app-config": "^2.0.0",
43
43
  "@atlaskit/theme": "^26.0.0",
44
- "@atlaskit/tmp-editor-statsig": "^109.2.0",
45
- "@atlaskit/tokens": "^14.0.0",
44
+ "@atlaskit/tmp-editor-statsig": "^111.0.0",
45
+ "@atlaskit/tokens": "^15.0.0",
46
46
  "@atlaskit/tooltip": "^23.0.0",
47
47
  "@atlaskit/user-picker": "^13.2.0",
48
48
  "@babel/runtime": "^7.0.0",
@@ -53,7 +53,7 @@
53
53
  "uuid": "^3.1.0"
54
54
  },
55
55
  "peerDependencies": {
56
- "@atlaskit/editor-common": "^116.10.0",
56
+ "@atlaskit/editor-common": "^116.11.0",
57
57
  "react": "^18.2.0",
58
58
  "react-dom": "^18.2.0",
59
59
  "react-intl": "^5.25.1 || ^6.0.0 || ^7.0.0"
@@ -120,6 +120,9 @@
120
120
  },
121
121
  "platform_editor_agent_mentions": {
122
122
  "type": "boolean"
123
+ },
124
+ "rovo_chat_agent_selection": {
125
+ "type": "boolean"
123
126
  }
124
127
  }
125
128
  }