@atlaskit/editor-plugin-mentions 18.2.7 → 18.2.9
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 +14 -0
- package/dist/cjs/mentionsPlugin.js +2 -2
- package/dist/cjs/pm-plugins/main.js +10 -808
- package/dist/es2019/mentionsPlugin.js +2 -2
- package/dist/es2019/pm-plugins/main.js +3 -772
- package/dist/esm/mentionsPlugin.js +2 -2
- package/dist/esm/pm-plugins/main.js +11 -808
- package/dist/types/pm-plugins/main.d.ts +0 -15
- package/package.json +6 -6
|
@@ -1,16 +1,11 @@
|
|
|
1
1
|
import { ACTION, ACTION_SUBJECT, ACTION_SUBJECT_ID, EVENT_TYPE } from '@atlaskit/editor-common/analytics';
|
|
2
2
|
import { SafePlugin } from '@atlaskit/editor-common/safe-plugin';
|
|
3
|
-
import { DecorationSet } from '@atlaskit/editor-prosemirror/view';
|
|
4
3
|
import { insm } from '@atlaskit/insm';
|
|
5
|
-
import {
|
|
4
|
+
import { SLI_EVENT_TYPE, SMART_EVENT_TYPE } from '@atlaskit/mention/resource';
|
|
6
5
|
import { ComponentNames } from '@atlaskit/mention/types';
|
|
7
|
-
import { isExperimentEnabled } from '@atlaskit/platform-feature-experiments/is-experiment-enabled';
|
|
8
6
|
import { fg } from '@atlaskit/platform-feature-flags/fg';
|
|
9
|
-
import { editorExperiment } from '@atlaskit/tmp-editor-statsig/editor-experiment';
|
|
10
7
|
import { MentionNodeView } from '../nodeviews/mentionNodeView';
|
|
11
8
|
import { MENTION_PROVIDER_REJECTED, MENTION_PROVIDER_UNDEFINED } from '../types';
|
|
12
|
-
import { getAgentMentionParentContext } from './agent-mention-context';
|
|
13
|
-
import { nextRunStateDecorations } from './agent-run-state';
|
|
14
9
|
import { mentionPluginKey } from './key';
|
|
15
10
|
import { canMentionBeCreatedInRange } from './utils';
|
|
16
11
|
export const ACTIONS = {
|
|
@@ -36,29 +31,8 @@ export const ACTIONS = {
|
|
|
36
31
|
*/
|
|
37
32
|
CLEAR_PENDING_PASTED_AGENT_MENTION: 'CLEAR_PENDING_PASTED_AGENT_MENTION'
|
|
38
33
|
};
|
|
39
|
-
|
|
40
|
-
// 'AGENT' is not in the ADF schema UserType enum but is used at runtime.
|
|
41
|
-
|
|
42
|
-
const AGENT_USER_TYPES = new Set(['APP', 'AGENT']);
|
|
43
|
-
const isAgentUserType = userType => {
|
|
44
|
-
return typeof userType === 'string' && AGENT_USER_TYPES.has(userType);
|
|
45
|
-
};
|
|
46
|
-
const getAgentMentionName = (text, fallbackName) => {
|
|
47
|
-
const trimmedFallbackName = typeof fallbackName === 'string' ? fallbackName.trim() : '';
|
|
48
|
-
const normalizedFallbackName = (trimmedFallbackName.startsWith('@') ? trimmedFallbackName.slice(1).trim() : trimmedFallbackName) || null;
|
|
49
|
-
if (typeof text !== 'string') {
|
|
50
|
-
return normalizedFallbackName;
|
|
51
|
-
}
|
|
52
|
-
const trimmedText = text.trim();
|
|
53
|
-
const displayName = trimmedText.startsWith('@') ? trimmedText.slice(1).trim() : trimmedText;
|
|
54
|
-
const normalizedName = displayName || normalizedFallbackName;
|
|
55
|
-
return normalizedName;
|
|
56
|
-
};
|
|
57
|
-
const AI_STREAMING_TRANSFORMATION_META_KEY = 'isAIStreamingTransformation';
|
|
58
|
-
const AGENT_MENTION_INACTIVITY_MS = 3000;
|
|
59
|
-
const MAX_PENDING_TYPED_AGENT_MENTION_FOCUS_DEFERS = 20;
|
|
60
34
|
const PACKAGE_NAME = "@atlaskit/editor-plugin-mentions";
|
|
61
|
-
const PACKAGE_VERSION = "18.2.
|
|
35
|
+
const PACKAGE_VERSION = "18.2.8";
|
|
62
36
|
const setProvider = provider => (state, dispatch) => {
|
|
63
37
|
if (dispatch) {
|
|
64
38
|
dispatch(state.tr.setMeta(mentionPluginKey, {
|
|
@@ -70,266 +44,6 @@ const setProvider = provider => (state, dispatch) => {
|
|
|
70
44
|
}
|
|
71
45
|
return true;
|
|
72
46
|
};
|
|
73
|
-
|
|
74
|
-
/**
|
|
75
|
-
* Returns true when a transaction represents a local user document edit that
|
|
76
|
-
* should restart pending agent-mention inactivity tracking.
|
|
77
|
-
*
|
|
78
|
-
* Remote/collab updates, replace-document transactions, AI streaming transforms,
|
|
79
|
-
* selection-only movements, and metadata-only transactions are intentionally ignored.
|
|
80
|
-
*/
|
|
81
|
-
const isQualifyingLocalUserDocChange = tr => {
|
|
82
|
-
const isAIStreaming = Boolean(tr.getMeta(AI_STREAMING_TRANSFORMATION_META_KEY));
|
|
83
|
-
return tr.docChanged && !tr.getMeta('isRemote') && !tr.getMeta('replaceDocument') && !isAIStreaming;
|
|
84
|
-
};
|
|
85
|
-
const isLocalSelectionChange = (tr, hasPositionChanged) => {
|
|
86
|
-
const isAIStreaming = Boolean(tr.getMeta(AI_STREAMING_TRANSFORMATION_META_KEY));
|
|
87
|
-
|
|
88
|
-
// Pressing Enter can move selection through a doc split without setting tr.selectionSet
|
|
89
|
-
// or changing from/to numerically, so local doc changes are checked against the
|
|
90
|
-
// pending mention's current parent before publishing.
|
|
91
|
-
return (hasPositionChanged || tr.docChanged) && !tr.getMeta('isRemote') && !tr.getMeta('replaceDocument') && !isAIStreaming;
|
|
92
|
-
};
|
|
93
|
-
|
|
94
|
-
/**
|
|
95
|
-
* Reads agent-mention details from a known document position without traversing
|
|
96
|
-
* the document. Callers pass a matcher so mapped positions are only accepted
|
|
97
|
-
* when they still point at the same pending/tracked mention.
|
|
98
|
-
*/
|
|
99
|
-
const getAgentMentionDetailsAtPos = (state, pos, matchesMention, fallbackName) => {
|
|
100
|
-
var _parentNode$type$name;
|
|
101
|
-
if (pos < 0 || pos > state.doc.content.size) {
|
|
102
|
-
return null;
|
|
103
|
-
}
|
|
104
|
-
const node = state.doc.nodeAt(pos);
|
|
105
|
-
const mentionSchema = state.schema.nodes.mention;
|
|
106
|
-
if ((node === null || node === void 0 ? void 0 : node.type) !== mentionSchema || !isAgentUserType(node.attrs.userType) || !matchesMention(node.attrs) || !node.attrs.id || !node.attrs.localId) {
|
|
107
|
-
return null;
|
|
108
|
-
}
|
|
109
|
-
const $mentionPos = state.doc.resolve(Math.min(pos + node.nodeSize, state.doc.content.size));
|
|
110
|
-
const parentNode = $mentionPos.node($mentionPos.depth);
|
|
111
|
-
const id = node.attrs.id;
|
|
112
|
-
const name = getAgentMentionName(node.attrs.text, fallbackName);
|
|
113
|
-
return {
|
|
114
|
-
id,
|
|
115
|
-
localId: node.attrs.localId,
|
|
116
|
-
context: getAgentMentionParentContext(parentNode, node.attrs.localId),
|
|
117
|
-
name,
|
|
118
|
-
prompt: parentNode.textContent.trim() || null,
|
|
119
|
-
nodeSize: node.nodeSize,
|
|
120
|
-
parentEnd: $mentionPos.end($mentionPos.depth),
|
|
121
|
-
parentNodeType: (_parentNode$type$name = parentNode.type.name) !== null && _parentNode$type$name !== void 0 ? _parentNode$type$name : null,
|
|
122
|
-
parentStart: $mentionPos.start($mentionPos.depth),
|
|
123
|
-
pos
|
|
124
|
-
};
|
|
125
|
-
};
|
|
126
|
-
|
|
127
|
-
/**
|
|
128
|
-
* Finds an agent mention that survived a document change when the changed-range
|
|
129
|
-
* scan did not find one. Uses the tracked localId as the mention instance identity
|
|
130
|
-
* so same-agent mentions elsewhere in the document cannot be selected as fallback.
|
|
131
|
-
*/
|
|
132
|
-
const getSurvivingAgentMentionDetails = (state, preferredLocalId, preferredName) => {
|
|
133
|
-
const mentionSchema = state.schema.nodes.mention;
|
|
134
|
-
let result = null;
|
|
135
|
-
state.doc.descendants((node, pos) => {
|
|
136
|
-
if (result) {
|
|
137
|
-
return false;
|
|
138
|
-
}
|
|
139
|
-
if (node.type !== mentionSchema || !isAgentUserType(node.attrs.userType) || node.attrs.localId !== preferredLocalId) {
|
|
140
|
-
return true;
|
|
141
|
-
}
|
|
142
|
-
result = getAgentMentionDetailsAtPos(state, pos, attrs => attrs.localId === preferredLocalId, preferredName);
|
|
143
|
-
return !result;
|
|
144
|
-
});
|
|
145
|
-
return result;
|
|
146
|
-
};
|
|
147
|
-
|
|
148
|
-
/**
|
|
149
|
-
* Maps a pending typed agent mention through a document-changing transaction and
|
|
150
|
-
* returns the updated pending state. If the mapped position was deleted or no
|
|
151
|
-
* longer points at the same local mention, the pending mention is cleared.
|
|
152
|
-
*/
|
|
153
|
-
const getPendingTypedAgentMentionAfterDocChange = (state, tr, pendingTypedAgentMention, {
|
|
154
|
-
resetTimer
|
|
155
|
-
}) => {
|
|
156
|
-
const mappedPos = tr.mapping.mapResult(pendingTypedAgentMention.pos, 1);
|
|
157
|
-
const resetCount = resetTimer ? pendingTypedAgentMention.resetCount + 1 : pendingTypedAgentMention.resetCount;
|
|
158
|
-
if (mappedPos.deleted) {
|
|
159
|
-
return null;
|
|
160
|
-
}
|
|
161
|
-
const pendingMentionDetails = getAgentMentionDetailsAtPos(state, mappedPos.pos, attrs => attrs.localId === pendingTypedAgentMention.localId, pendingTypedAgentMention.name);
|
|
162
|
-
return pendingMentionDetails ? {
|
|
163
|
-
id: pendingMentionDetails.id,
|
|
164
|
-
localId: pendingTypedAgentMention.localId,
|
|
165
|
-
name: pendingMentionDetails.name,
|
|
166
|
-
nodeSize: pendingMentionDetails.nodeSize,
|
|
167
|
-
parentNodeType: pendingMentionDetails.parentNodeType,
|
|
168
|
-
pos: pendingMentionDetails.pos,
|
|
169
|
-
resetCount
|
|
170
|
-
} : null;
|
|
171
|
-
};
|
|
172
|
-
const hasPendingMentionMovedToNewParent = (oldState, tr, previousPendingTypedAgentMention, pendingMentionDetails) => {
|
|
173
|
-
if (!previousPendingTypedAgentMention) {
|
|
174
|
-
return false;
|
|
175
|
-
}
|
|
176
|
-
const previousMentionDetails = getAgentMentionDetailsAtPos(oldState, previousPendingTypedAgentMention.pos, attrs => attrs.localId === previousPendingTypedAgentMention.localId);
|
|
177
|
-
|
|
178
|
-
// Keep the previous parent boundary associated with the left side of an
|
|
179
|
-
// insertion at that boundary, so typing at the start of the parent does not
|
|
180
|
-
// look like the pending mention moved into a new parent.
|
|
181
|
-
const mappedPreviousParentStart = previousMentionDetails && tr.mapping.map(previousMentionDetails.parentStart, -1);
|
|
182
|
-
return Boolean(previousMentionDetails && mappedPreviousParentStart !== pendingMentionDetails.parentStart);
|
|
183
|
-
};
|
|
184
|
-
const isSelectionOutsideDirectParent = (state, pendingMentionDetails) => {
|
|
185
|
-
return state.selection.from < pendingMentionDetails.parentStart || state.selection.to > pendingMentionDetails.parentEnd;
|
|
186
|
-
};
|
|
187
|
-
|
|
188
|
-
/**
|
|
189
|
-
* Finalises a pending typed agent mention by copying its details into the
|
|
190
|
-
* public lastInserted* plugin state after the caller has already resolved the
|
|
191
|
-
* pending mention from the current document.
|
|
192
|
-
*/
|
|
193
|
-
const commitResolvedPendingTypedAgentMention = (pluginState, pendingMentionDetails) => {
|
|
194
|
-
var _pluginState$lastAgen;
|
|
195
|
-
return {
|
|
196
|
-
hasPublicPluginStateChanged: true,
|
|
197
|
-
pluginState: {
|
|
198
|
-
...pluginState,
|
|
199
|
-
pendingTypedAgentMention: null,
|
|
200
|
-
lastInsertedAgentMentionId: pendingMentionDetails.id,
|
|
201
|
-
lastInsertedAgentMentionLocalId: pendingMentionDetails.localId,
|
|
202
|
-
lastInsertedAgentMentionContext: pendingMentionDetails.context,
|
|
203
|
-
lastInsertedAgentMentionName: pendingMentionDetails.name,
|
|
204
|
-
lastInsertedAgentMentionPrompt: pendingMentionDetails.prompt,
|
|
205
|
-
lastInsertedAgentMentionParentNodeType: pendingMentionDetails.parentNodeType,
|
|
206
|
-
lastAgentMentionInsertionCount: ((_pluginState$lastAgen = pluginState.lastAgentMentionInsertionCount) !== null && _pluginState$lastAgen !== void 0 ? _pluginState$lastAgen : 0) + 1
|
|
207
|
-
}
|
|
208
|
-
};
|
|
209
|
-
};
|
|
210
|
-
|
|
211
|
-
/**
|
|
212
|
-
* Resolves and finalises a pending typed agent mention. If the tracked mention
|
|
213
|
-
* no longer resolves, the stale pending state is cleared without dispatching a
|
|
214
|
-
* public update.
|
|
215
|
-
*/
|
|
216
|
-
const commitPendingTypedAgentMention = (state, pluginState, pendingTypedAgentMention) => {
|
|
217
|
-
const pendingMentionDetails = getAgentMentionDetailsAtPos(state, pendingTypedAgentMention.pos, attrs => attrs.localId === pendingTypedAgentMention.localId, pendingTypedAgentMention.name);
|
|
218
|
-
if (!pendingMentionDetails) {
|
|
219
|
-
return {
|
|
220
|
-
hasPublicPluginStateChanged: false,
|
|
221
|
-
pluginState: {
|
|
222
|
-
...pluginState,
|
|
223
|
-
pendingTypedAgentMention: null
|
|
224
|
-
}
|
|
225
|
-
};
|
|
226
|
-
}
|
|
227
|
-
return commitResolvedPendingTypedAgentMention(pluginState, pendingMentionDetails);
|
|
228
|
-
};
|
|
229
|
-
const hasTrackedAgentMentionState = pluginState => Boolean(pluginState.pendingTypedAgentMention) || Boolean(pluginState.pendingPastedAgentMention) && fg('platform_editor_agent_mentions_drop_one_fixes') || pluginState.lastInsertedAgentMentionId != null || pluginState.lastInsertedAgentMentionLocalId != null || pluginState.lastInsertedAgentMentionContext != null || pluginState.lastInsertedAgentMentionName != null || pluginState.lastInsertedAgentMentionPrompt != null || pluginState.lastInsertedAgentMentionParentNodeType != null;
|
|
230
|
-
|
|
231
|
-
/**
|
|
232
|
-
* Clears agent mention state that points at a specific document snapshot.
|
|
233
|
-
* replaceDocument swaps content wholesale, so pending typed mentions and
|
|
234
|
-
* lastInserted* details from the previous document must be cleared together.
|
|
235
|
-
*/
|
|
236
|
-
const clearTrackedAgentMentionState = pluginState => {
|
|
237
|
-
return {
|
|
238
|
-
...pluginState,
|
|
239
|
-
pendingTypedAgentMention: null,
|
|
240
|
-
...(fg('platform_editor_agent_mentions_drop_one_fixes') ? {
|
|
241
|
-
pendingPastedAgentMention: null
|
|
242
|
-
} : {}),
|
|
243
|
-
lastInsertedAgentMentionId: null,
|
|
244
|
-
lastInsertedAgentMentionLocalId: null,
|
|
245
|
-
lastInsertedAgentMentionContext: null,
|
|
246
|
-
lastInsertedAgentMentionName: null,
|
|
247
|
-
lastInsertedAgentMentionPrompt: null,
|
|
248
|
-
lastInsertedAgentMentionParentNodeType: null
|
|
249
|
-
};
|
|
250
|
-
};
|
|
251
|
-
|
|
252
|
-
/**
|
|
253
|
-
* Attempts to synchronously resolve an agent mention name from the mention
|
|
254
|
-
* provider's cache. Falls back to undefined if the provider doesn't support
|
|
255
|
-
* name resolution, the result is a Promise (async/cache miss), or the status
|
|
256
|
-
* is not OK.
|
|
257
|
-
*/
|
|
258
|
-
const resolveCachedAgentMentionName = (mentionProvider, params, id) => {
|
|
259
|
-
if (params !== null && params !== void 0 && params.name || !isResolvingMentionProvider(mentionProvider)) {
|
|
260
|
-
var _params$name;
|
|
261
|
-
return (_params$name = params === null || params === void 0 ? void 0 : params.name) !== null && _params$name !== void 0 ? _params$name : undefined;
|
|
262
|
-
}
|
|
263
|
-
const result = mentionProvider.resolveMentionName(id);
|
|
264
|
-
if (!(result instanceof Promise) && result.status === MentionNameStatus.OK) {
|
|
265
|
-
return result.name || undefined;
|
|
266
|
-
}
|
|
267
|
-
return undefined;
|
|
268
|
-
};
|
|
269
|
-
|
|
270
|
-
/**
|
|
271
|
-
* Resolves the name of a pasted agent mention via the mention provider and
|
|
272
|
-
* dispatches the appropriate action to update plugin state.
|
|
273
|
-
*
|
|
274
|
-
* Handles both async (Promise) and synchronous cache-hit results:
|
|
275
|
-
* - On OK resolution → dispatches RESOLVE_PASTED_AGENT_MENTION_NAME so the nudge
|
|
276
|
-
* shows the correct agent name.
|
|
277
|
-
* - On non-OK result → dispatches CLEAR_PENDING_PASTED_AGENT_MENTION to prevent
|
|
278
|
-
* an infinite retry loop on subsequent update() calls.
|
|
279
|
-
*
|
|
280
|
-
* Guards against duplicate in-flight requests via pendingResolveMentionIds.
|
|
281
|
-
*/
|
|
282
|
-
export const resolveAndDispatchPastedAgentMentionName = (agentId, mentionProvider, pendingResolveMentionIds, view) => {
|
|
283
|
-
if (pendingResolveMentionIds.has(agentId)) {
|
|
284
|
-
return;
|
|
285
|
-
}
|
|
286
|
-
pendingResolveMentionIds.add(agentId);
|
|
287
|
-
const result = mentionProvider.resolveMentionName(agentId);
|
|
288
|
-
if (result instanceof Promise) {
|
|
289
|
-
result.then(nameDetails => {
|
|
290
|
-
pendingResolveMentionIds.delete(agentId);
|
|
291
|
-
if (nameDetails.status === MentionNameStatus.OK && nameDetails.name) {
|
|
292
|
-
view.dispatch(view.state.tr.setMeta(mentionPluginKey, {
|
|
293
|
-
action: ACTIONS.RESOLVE_PASTED_AGENT_MENTION_NAME,
|
|
294
|
-
params: {
|
|
295
|
-
id: agentId,
|
|
296
|
-
name: nameDetails.name
|
|
297
|
-
}
|
|
298
|
-
}));
|
|
299
|
-
} else {
|
|
300
|
-
// Non-OK status — clear pendingPastedAgentMention to prevent
|
|
301
|
-
// an infinite retry loop on subsequent update() calls.
|
|
302
|
-
view.dispatch(view.state.tr.setMeta(mentionPluginKey, {
|
|
303
|
-
action: ACTIONS.CLEAR_PENDING_PASTED_AGENT_MENTION,
|
|
304
|
-
params: {
|
|
305
|
-
id: agentId
|
|
306
|
-
}
|
|
307
|
-
}));
|
|
308
|
-
}
|
|
309
|
-
});
|
|
310
|
-
} else if (result.status === MentionNameStatus.OK && result.name) {
|
|
311
|
-
pendingResolveMentionIds.delete(agentId);
|
|
312
|
-
// Synchronous hit on a second update cycle (e.g. provider warmed up
|
|
313
|
-
// between the paste transaction and this view update).
|
|
314
|
-
view.dispatch(view.state.tr.setMeta(mentionPluginKey, {
|
|
315
|
-
action: ACTIONS.RESOLVE_PASTED_AGENT_MENTION_NAME,
|
|
316
|
-
params: {
|
|
317
|
-
id: agentId,
|
|
318
|
-
name: result.name
|
|
319
|
-
}
|
|
320
|
-
}));
|
|
321
|
-
} else {
|
|
322
|
-
pendingResolveMentionIds.delete(agentId);
|
|
323
|
-
// Non-OK synchronous status — clear pendingPastedAgentMention to prevent
|
|
324
|
-
// an infinite retry loop on subsequent update() calls.
|
|
325
|
-
view.dispatch(view.state.tr.setMeta(mentionPluginKey, {
|
|
326
|
-
action: ACTIONS.CLEAR_PENDING_PASTED_AGENT_MENTION,
|
|
327
|
-
params: {
|
|
328
|
-
id: agentId
|
|
329
|
-
}
|
|
330
|
-
}));
|
|
331
|
-
}
|
|
332
|
-
};
|
|
333
47
|
export function createMentionPlugin({
|
|
334
48
|
pmPluginFactoryParams,
|
|
335
49
|
fireEvent,
|
|
@@ -362,7 +76,6 @@ export function createMentionPlugin({
|
|
|
362
76
|
};
|
|
363
77
|
},
|
|
364
78
|
apply(tr, pluginState, oldState, newState) {
|
|
365
|
-
var _params$runStateByLoc;
|
|
366
79
|
const {
|
|
367
80
|
action,
|
|
368
81
|
params
|
|
@@ -389,82 +102,7 @@ export function createMentionPlugin({
|
|
|
389
102
|
hasPublicPluginStateChanged = true;
|
|
390
103
|
break;
|
|
391
104
|
}
|
|
392
|
-
|
|
393
|
-
if (nextDecorations !== newPluginState.runStateDecorations) {
|
|
394
|
-
newPluginState = {
|
|
395
|
-
...newPluginState,
|
|
396
|
-
runStateDecorations: nextDecorations
|
|
397
|
-
};
|
|
398
|
-
}
|
|
399
|
-
if (!isExperimentEnabled('platform_editor_agent_mentions_separate_pm_plugin') && editorExperiment('platform_editor_agent_mentions', true)) {
|
|
400
|
-
switch (action) {
|
|
401
|
-
case ACTIONS.COMMIT_PENDING_TYPED_AGENT_MENTION:
|
|
402
|
-
{
|
|
403
|
-
const pendingTypedAgentMention = newPluginState.pendingTypedAgentMention;
|
|
404
|
-
// Ignore stale timer callbacks. The localId and resetCount must still match the
|
|
405
|
-
// current pending mention so older timers cannot publish after later user edits.
|
|
406
|
-
if (!pendingTypedAgentMention || pendingTypedAgentMention.localId !== (params === null || params === void 0 ? void 0 : params.localId) || pendingTypedAgentMention.resetCount !== (params === null || params === void 0 ? void 0 : params.resetCount)) {
|
|
407
|
-
break;
|
|
408
|
-
}
|
|
409
|
-
const commitResult = commitPendingTypedAgentMention(newState, newPluginState, pendingTypedAgentMention);
|
|
410
|
-
newPluginState = commitResult.pluginState;
|
|
411
|
-
hasPublicPluginStateChanged = hasPublicPluginStateChanged || commitResult.hasPublicPluginStateChanged;
|
|
412
|
-
break;
|
|
413
|
-
}
|
|
414
|
-
case ACTIONS.DISCARD_PENDING_TYPED_AGENT_MENTION:
|
|
415
|
-
{
|
|
416
|
-
// Silently discard the pending typed agent mention without committing it.
|
|
417
|
-
const pendingTypedAgentMentionToDiscard = newPluginState.pendingTypedAgentMention;
|
|
418
|
-
if (!pendingTypedAgentMentionToDiscard || pendingTypedAgentMentionToDiscard.localId !== (params === null || params === void 0 ? void 0 : params.localId) || pendingTypedAgentMentionToDiscard.resetCount !== (params === null || params === void 0 ? void 0 : params.resetCount)) {
|
|
419
|
-
break;
|
|
420
|
-
}
|
|
421
|
-
newPluginState = {
|
|
422
|
-
...newPluginState,
|
|
423
|
-
pendingTypedAgentMention: null
|
|
424
|
-
};
|
|
425
|
-
hasPublicPluginStateChanged = true;
|
|
426
|
-
break;
|
|
427
|
-
}
|
|
428
|
-
case ACTIONS.RESOLVE_PASTED_AGENT_MENTION_NAME:
|
|
429
|
-
{
|
|
430
|
-
var _newPluginState$pendi, _newPluginState$pendi2, _newPluginState$pendi3, _newPluginState$pendi4, _newPluginState$pendi5, _newPluginState$pendi6;
|
|
431
|
-
// Guard: only apply if there is a matching pending pasted mention (staleness check).
|
|
432
|
-
if (!(params !== null && params !== void 0 && params.id) || !(params !== null && params !== void 0 && params.name) || ((_newPluginState$pendi = newPluginState.pendingPastedAgentMention) === null || _newPluginState$pendi === void 0 ? void 0 : _newPluginState$pendi.id) !== params.id || !fg('platform_editor_agent_mentions_drop_one_fixes')) {
|
|
433
|
-
break;
|
|
434
|
-
}
|
|
435
|
-
newPluginState = {
|
|
436
|
-
...newPluginState,
|
|
437
|
-
pendingPastedAgentMention: null,
|
|
438
|
-
lastInsertedAgentMentionId: (_newPluginState$pendi2 = newPluginState.pendingPastedAgentMention) === null || _newPluginState$pendi2 === void 0 ? void 0 : _newPluginState$pendi2.id,
|
|
439
|
-
lastInsertedAgentMentionLocalId: (_newPluginState$pendi3 = newPluginState.pendingPastedAgentMention) === null || _newPluginState$pendi3 === void 0 ? void 0 : _newPluginState$pendi3.localId,
|
|
440
|
-
lastInsertedAgentMentionContext: (_newPluginState$pendi4 = newPluginState.pendingPastedAgentMention) === null || _newPluginState$pendi4 === void 0 ? void 0 : _newPluginState$pendi4.context,
|
|
441
|
-
lastInsertedAgentMentionName: params.name,
|
|
442
|
-
lastInsertedAgentMentionPrompt: (_newPluginState$pendi5 = newPluginState.pendingPastedAgentMention) === null || _newPluginState$pendi5 === void 0 ? void 0 : _newPluginState$pendi5.prompt,
|
|
443
|
-
lastInsertedAgentMentionParentNodeType: (_newPluginState$pendi6 = newPluginState.pendingPastedAgentMention) === null || _newPluginState$pendi6 === void 0 ? void 0 : _newPluginState$pendi6.parentNodeType
|
|
444
|
-
};
|
|
445
|
-
hasPublicPluginStateChanged = true;
|
|
446
|
-
break;
|
|
447
|
-
}
|
|
448
|
-
case ACTIONS.CLEAR_PENDING_PASTED_AGENT_MENTION:
|
|
449
|
-
{
|
|
450
|
-
var _newPluginState$pendi7;
|
|
451
|
-
if (!(params !== null && params !== void 0 && params.id) || ((_newPluginState$pendi7 = newPluginState.pendingPastedAgentMention) === null || _newPluginState$pendi7 === void 0 ? void 0 : _newPluginState$pendi7.id) !== params.id || !fg('platform_editor_agent_mentions_drop_one_fixes')) {
|
|
452
|
-
break;
|
|
453
|
-
}
|
|
454
|
-
// resolveMentionName() returned a non-OK status — clear the pending state.
|
|
455
|
-
newPluginState = {
|
|
456
|
-
...newPluginState,
|
|
457
|
-
pendingPastedAgentMention: null
|
|
458
|
-
};
|
|
459
|
-
break;
|
|
460
|
-
}
|
|
461
|
-
}
|
|
462
|
-
}
|
|
463
|
-
|
|
464
|
-
// When the agent mentions experiment is off, dispatch immediately (original behaviour).
|
|
465
|
-
// When it's on, defer dispatch to after the agent tracking block below so that
|
|
466
|
-
// agent-mention state changes are included in the notification.
|
|
467
|
-
if (hasPublicPluginStateChanged && (isExperimentEnabled('platform_editor_agent_mentions_separate_pm_plugin') || !editorExperiment('platform_editor_agent_mentions', true))) {
|
|
105
|
+
if (hasPublicPluginStateChanged) {
|
|
468
106
|
pmPluginFactoryParams.dispatch(mentionPluginKey, newPluginState);
|
|
469
107
|
}
|
|
470
108
|
if (options !== null && options !== void 0 && options.handleMentionsChanged && tr.docChanged) {
|
|
@@ -524,279 +162,6 @@ export function createMentionPlugin({
|
|
|
524
162
|
}
|
|
525
163
|
(_insm$session2 = insm.session) === null || _insm$session2 === void 0 ? void 0 : _insm$session2.endFeature('mentionDeletionDetection');
|
|
526
164
|
}
|
|
527
|
-
if (!isExperimentEnabled('platform_editor_agent_mentions_separate_pm_plugin') && editorExperiment('platform_editor_agent_mentions', true) && isQualifyingLocalUserDocChange(tr)) {
|
|
528
|
-
const mentionSchema = newState.schema.nodes.mention;
|
|
529
|
-
const newDocRanges = [];
|
|
530
|
-
const oldDocRanges = [];
|
|
531
|
-
let stepsTouchMentions = false;
|
|
532
|
-
tr.steps.forEach(step => {
|
|
533
|
-
let found = false;
|
|
534
|
-
// Only merge a step's ranges if it actually touched an agent mention,
|
|
535
|
-
// so unrelated steps (e.g. mark-only changes) don't inflate the scan area.
|
|
536
|
-
const stepNewRanges = [];
|
|
537
|
-
const stepOldRanges = [];
|
|
538
|
-
step.getMap().forEach((oldFrom, oldTo, newFrom, newTo) => {
|
|
539
|
-
stepOldRanges.push([oldFrom, oldTo]);
|
|
540
|
-
stepNewRanges.push([newFrom, newTo]);
|
|
541
|
-
if (!found) {
|
|
542
|
-
// Clamp positions: delete-only steps can produce newTo > doc.content.size.
|
|
543
|
-
const clampedNewFrom = Math.min(newFrom, newState.doc.content.size);
|
|
544
|
-
const clampedNewTo = Math.min(newTo, newState.doc.content.size);
|
|
545
|
-
if (clampedNewFrom < clampedNewTo) {
|
|
546
|
-
newState.doc.nodesBetween(clampedNewFrom, clampedNewTo, node => {
|
|
547
|
-
if (node.type === mentionSchema && isAgentUserType(node.attrs.userType)) {
|
|
548
|
-
found = true;
|
|
549
|
-
}
|
|
550
|
-
return !found;
|
|
551
|
-
});
|
|
552
|
-
}
|
|
553
|
-
if (!found) {
|
|
554
|
-
const clampedOldFrom = Math.min(oldFrom, oldState.doc.content.size);
|
|
555
|
-
const clampedOldTo = Math.min(oldTo, oldState.doc.content.size);
|
|
556
|
-
if (clampedOldFrom < clampedOldTo) {
|
|
557
|
-
oldState.doc.nodesBetween(clampedOldFrom, clampedOldTo, node => {
|
|
558
|
-
if (node.type === mentionSchema && AGENT_USER_TYPES.has(node.attrs.userType)) {
|
|
559
|
-
found = true;
|
|
560
|
-
}
|
|
561
|
-
return !found;
|
|
562
|
-
});
|
|
563
|
-
}
|
|
564
|
-
}
|
|
565
|
-
}
|
|
566
|
-
});
|
|
567
|
-
if (found) {
|
|
568
|
-
stepsTouchMentions = true;
|
|
569
|
-
newDocRanges.push(...stepNewRanges);
|
|
570
|
-
oldDocRanges.push(...stepOldRanges);
|
|
571
|
-
}
|
|
572
|
-
});
|
|
573
|
-
const shouldResolveAgentMentionState = stepsTouchMentions || Boolean(newPluginState.lastInsertedAgentMentionLocalId);
|
|
574
|
-
if (shouldResolveAgentMentionState) {
|
|
575
|
-
var _newPluginState$lastI, _newPluginState$lastA, _newPluginState$lastI2, _newPluginState$lastI3, _newPluginState$lastI4, _newPluginState$lastI5, _newPluginState$lastI6;
|
|
576
|
-
let agentMentionId = null;
|
|
577
|
-
let agentMentionLocalId = null;
|
|
578
|
-
let agentMentionContext = null;
|
|
579
|
-
let agentMentionName = null;
|
|
580
|
-
let agentMentionPrompt = null;
|
|
581
|
-
let agentMentionParentNodeType = null;
|
|
582
|
-
const existingAgentMentionLocalIdsInChangedRanges = new Set();
|
|
583
|
-
let pendingTypedAgentMentionDetails = null;
|
|
584
|
-
if (stepsTouchMentions) {
|
|
585
|
-
for (const [from, to] of newDocRanges) {
|
|
586
|
-
const clampedTo = Math.min(to, newState.doc.content.size);
|
|
587
|
-
if (from >= clampedTo) {
|
|
588
|
-
continue;
|
|
589
|
-
}
|
|
590
|
-
newState.doc.nodesBetween(from, clampedTo, (node, pos) => {
|
|
591
|
-
if (node.type !== mentionSchema || !isAgentUserType(node.attrs.userType)) {
|
|
592
|
-
return true;
|
|
593
|
-
}
|
|
594
|
-
if (pendingTypedAgentMentionDetails === null && action === ACTIONS.SET_PENDING_TYPED_AGENT_MENTION && node.attrs.localId === (params === null || params === void 0 ? void 0 : params.localId)) {
|
|
595
|
-
pendingTypedAgentMentionDetails = getAgentMentionDetailsAtPos(newState, pos, attrs => attrs.localId === params.localId, params.name);
|
|
596
|
-
}
|
|
597
|
-
if (agentMentionLocalId === null && node.attrs.localId) {
|
|
598
|
-
// Try to recover the name synchronously from the mention provider's
|
|
599
|
-
// name cache (populated by cacheMentionName on typed insert).
|
|
600
|
-
const cachedName = fg('platform_editor_agent_mentions_drop_one_fixes') ? resolveCachedAgentMentionName(mentionProvider, params, node.attrs.id) : params === null || params === void 0 ? void 0 : params.name;
|
|
601
|
-
const agentMentionDetails = getAgentMentionDetailsAtPos(newState, pos, attrs => attrs.localId === node.attrs.localId, cachedName);
|
|
602
|
-
if (agentMentionDetails) {
|
|
603
|
-
agentMentionId = agentMentionDetails.id;
|
|
604
|
-
agentMentionLocalId = agentMentionDetails.localId;
|
|
605
|
-
agentMentionContext = agentMentionDetails.context;
|
|
606
|
-
agentMentionName = agentMentionDetails.name;
|
|
607
|
-
agentMentionPrompt = agentMentionDetails.prompt;
|
|
608
|
-
agentMentionParentNodeType = agentMentionDetails.parentNodeType;
|
|
609
|
-
}
|
|
610
|
-
}
|
|
611
|
-
return true;
|
|
612
|
-
});
|
|
613
|
-
}
|
|
614
|
-
for (const [from, to] of oldDocRanges) {
|
|
615
|
-
const clampedOldTo = Math.min(to, oldState.doc.content.size);
|
|
616
|
-
if (from >= clampedOldTo) {
|
|
617
|
-
continue;
|
|
618
|
-
}
|
|
619
|
-
oldState.doc.nodesBetween(from, clampedOldTo, node => {
|
|
620
|
-
if (node.type !== mentionSchema || !isAgentUserType(node.attrs.userType)) {
|
|
621
|
-
return true;
|
|
622
|
-
}
|
|
623
|
-
if (node.attrs.localId) {
|
|
624
|
-
existingAgentMentionLocalIdsInChangedRanges.add(node.attrs.localId);
|
|
625
|
-
}
|
|
626
|
-
return true;
|
|
627
|
-
});
|
|
628
|
-
}
|
|
629
|
-
}
|
|
630
|
-
|
|
631
|
-
// When a deletion collapses the new-doc range to a zero-width point, or when
|
|
632
|
-
// the doc changed but no step covered the tracked mention, the new-doc scan
|
|
633
|
-
// above finds nothing. Check whether any agent mention survived in the document.
|
|
634
|
-
let resolvedFromFullDocFallback = false;
|
|
635
|
-
if (agentMentionId === null && newPluginState.lastInsertedAgentMentionLocalId) {
|
|
636
|
-
const survivorDetails = getSurvivingAgentMentionDetails(newState, newPluginState.lastInsertedAgentMentionLocalId, newPluginState.lastInsertedAgentMentionName);
|
|
637
|
-
if (survivorDetails) {
|
|
638
|
-
agentMentionId = survivorDetails.id;
|
|
639
|
-
agentMentionLocalId = survivorDetails.localId;
|
|
640
|
-
agentMentionContext = survivorDetails.context;
|
|
641
|
-
agentMentionName = survivorDetails.name;
|
|
642
|
-
agentMentionPrompt = survivorDetails.prompt;
|
|
643
|
-
agentMentionParentNodeType = survivorDetails.parentNodeType;
|
|
644
|
-
resolvedFromFullDocFallback = true;
|
|
645
|
-
}
|
|
646
|
-
}
|
|
647
|
-
const trackedAgentMentionLocalId = (_newPluginState$lastI = newPluginState.lastInsertedAgentMentionLocalId) !== null && _newPluginState$lastI !== void 0 ? _newPluginState$lastI : null;
|
|
648
|
-
const changedRangeMentionIsNew = agentMentionLocalId !== null && !existingAgentMentionLocalIdsInChangedRanges.has(agentMentionLocalId);
|
|
649
|
-
if (agentMentionLocalId !== null && !changedRangeMentionIsNew && agentMentionLocalId !== trackedAgentMentionLocalId) {
|
|
650
|
-
const survivorDetails = trackedAgentMentionLocalId ? getSurvivingAgentMentionDetails(newState, trackedAgentMentionLocalId, newPluginState.lastInsertedAgentMentionName) : null;
|
|
651
|
-
if (survivorDetails) {
|
|
652
|
-
agentMentionId = survivorDetails.id;
|
|
653
|
-
agentMentionLocalId = survivorDetails.localId;
|
|
654
|
-
agentMentionContext = survivorDetails.context;
|
|
655
|
-
agentMentionName = survivorDetails.name;
|
|
656
|
-
agentMentionParentNodeType = survivorDetails.parentNodeType;
|
|
657
|
-
resolvedFromFullDocFallback = true;
|
|
658
|
-
} else {
|
|
659
|
-
agentMentionId = null;
|
|
660
|
-
agentMentionLocalId = null;
|
|
661
|
-
agentMentionContext = null;
|
|
662
|
-
agentMentionName = null;
|
|
663
|
-
agentMentionParentNodeType = null;
|
|
664
|
-
}
|
|
665
|
-
}
|
|
666
|
-
const isNewInsertion = agentMentionId !== null && !resolvedFromFullDocFallback && changedRangeMentionIsNew;
|
|
667
|
-
const isPendingTypedAgentMentionInsertion = isNewInsertion && action === ACTIONS.SET_PENDING_TYPED_AGENT_MENTION && typeof (params === null || params === void 0 ? void 0 : params.localId) === 'string';
|
|
668
|
-
const newInsertionCount = isNewInsertion ? ((_newPluginState$lastA = newPluginState.lastAgentMentionInsertionCount) !== null && _newPluginState$lastA !== void 0 ? _newPluginState$lastA : 0) + 1 : undefined;
|
|
669
|
-
const pendingTypedAgentMentionDetailsForState = pendingTypedAgentMentionDetails;
|
|
670
|
-
if (isPendingTypedAgentMentionInsertion && pendingTypedAgentMentionDetailsForState) {
|
|
671
|
-
const pendingTypedAgentMentionLocalId = params === null || params === void 0 ? void 0 : params.localId;
|
|
672
|
-
newPluginState = {
|
|
673
|
-
...newPluginState,
|
|
674
|
-
pendingTypedAgentMention: {
|
|
675
|
-
id: pendingTypedAgentMentionDetailsForState.id,
|
|
676
|
-
localId: pendingTypedAgentMentionLocalId,
|
|
677
|
-
name: pendingTypedAgentMentionDetailsForState.name,
|
|
678
|
-
nodeSize: pendingTypedAgentMentionDetailsForState.nodeSize,
|
|
679
|
-
parentNodeType: pendingTypedAgentMentionDetailsForState.parentNodeType,
|
|
680
|
-
pos: pendingTypedAgentMentionDetailsForState.pos,
|
|
681
|
-
resetCount: 1
|
|
682
|
-
}
|
|
683
|
-
};
|
|
684
|
-
} else if (isPendingTypedAgentMentionInsertion) {
|
|
685
|
-
// Fallback: if the localId-specific scan missed the typed mention,
|
|
686
|
-
// publish immediately so the insertion is not dropped.
|
|
687
|
-
newPluginState = {
|
|
688
|
-
...newPluginState,
|
|
689
|
-
pendingTypedAgentMention: null,
|
|
690
|
-
lastInsertedAgentMentionId: agentMentionId,
|
|
691
|
-
lastInsertedAgentMentionLocalId: agentMentionLocalId,
|
|
692
|
-
lastInsertedAgentMentionContext: agentMentionContext,
|
|
693
|
-
lastInsertedAgentMentionName: agentMentionName,
|
|
694
|
-
lastInsertedAgentMentionPrompt: agentMentionPrompt,
|
|
695
|
-
lastInsertedAgentMentionParentNodeType: agentMentionParentNodeType,
|
|
696
|
-
...(newInsertionCount !== undefined ? {
|
|
697
|
-
lastAgentMentionInsertionCount: newInsertionCount
|
|
698
|
-
} : {})
|
|
699
|
-
};
|
|
700
|
-
hasPublicPluginStateChanged = true;
|
|
701
|
-
} else if (agentMentionId !== ((_newPluginState$lastI2 = newPluginState.lastInsertedAgentMentionId) !== null && _newPluginState$lastI2 !== void 0 ? _newPluginState$lastI2 : null) || agentMentionLocalId !== ((_newPluginState$lastI3 = newPluginState.lastInsertedAgentMentionLocalId) !== null && _newPluginState$lastI3 !== void 0 ? _newPluginState$lastI3 : null) || agentMentionName !== ((_newPluginState$lastI4 = newPluginState.lastInsertedAgentMentionName) !== null && _newPluginState$lastI4 !== void 0 ? _newPluginState$lastI4 : null) || agentMentionPrompt !== ((_newPluginState$lastI5 = newPluginState.lastInsertedAgentMentionPrompt) !== null && _newPluginState$lastI5 !== void 0 ? _newPluginState$lastI5 : null) || agentMentionParentNodeType !== ((_newPluginState$lastI6 = newPluginState.lastInsertedAgentMentionParentNodeType) !== null && _newPluginState$lastI6 !== void 0 ? _newPluginState$lastI6 : null) || newInsertionCount !== undefined) {
|
|
702
|
-
var _options$getIsRovoPan;
|
|
703
|
-
// Suppress the nudge for pasted/non-typed agent mentions when Rovo is already
|
|
704
|
-
// open.
|
|
705
|
-
// Task items are exempt only while
|
|
706
|
-
// platform_editor_agent_mentions_no_task_autofire is off (they auto-fire
|
|
707
|
-
// instead). With it on they are suppressed like every other block.
|
|
708
|
-
const isTaskItemMentionForPaste = agentMentionParentNodeType === 'taskItem' && !fg('platform_editor_agent_mentions_no_task_autofire');
|
|
709
|
-
if (!isTaskItemMentionForPaste && isNewInsertion && options !== null && options !== void 0 && (_options$getIsRovoPan = options.getIsRovoPanelOpen) !== null && _options$getIsRovoPan !== void 0 && _options$getIsRovoPan.call(options)) {
|
|
710
|
-
// Rovo is already open — skip setting lastInsertedAgentMention* so the
|
|
711
|
-
// downstream nudge listener never fires.
|
|
712
|
-
} else if (agentMentionName === null && agentMentionId !== null && isResolvingMentionProvider(newPluginState.mentionProvider) && fg('platform_editor_agent_mentions_drop_one_fixes')) {
|
|
713
|
-
// Store details in pendingPastedAgentMention (private, not broadcast), preventing a double-nudge.
|
|
714
|
-
newPluginState = {
|
|
715
|
-
...newPluginState,
|
|
716
|
-
pendingPastedAgentMention: {
|
|
717
|
-
id: agentMentionId,
|
|
718
|
-
localId: agentMentionLocalId,
|
|
719
|
-
context: agentMentionContext,
|
|
720
|
-
prompt: agentMentionPrompt,
|
|
721
|
-
parentNodeType: agentMentionParentNodeType
|
|
722
|
-
},
|
|
723
|
-
...(newInsertionCount !== undefined ? {
|
|
724
|
-
lastAgentMentionInsertionCount: newInsertionCount
|
|
725
|
-
} : {})
|
|
726
|
-
};
|
|
727
|
-
// the nudge does not fire until the name is resolved.
|
|
728
|
-
} else {
|
|
729
|
-
// Not suppressed — update state so the nudge fires normally.
|
|
730
|
-
newPluginState = {
|
|
731
|
-
...newPluginState,
|
|
732
|
-
lastInsertedAgentMentionId: agentMentionId,
|
|
733
|
-
lastInsertedAgentMentionLocalId: agentMentionLocalId,
|
|
734
|
-
lastInsertedAgentMentionContext: agentMentionContext,
|
|
735
|
-
lastInsertedAgentMentionName: agentMentionName,
|
|
736
|
-
lastInsertedAgentMentionPrompt: agentMentionPrompt,
|
|
737
|
-
lastInsertedAgentMentionParentNodeType: agentMentionParentNodeType,
|
|
738
|
-
...(newInsertionCount !== undefined ? {
|
|
739
|
-
lastAgentMentionInsertionCount: newInsertionCount
|
|
740
|
-
} : {})
|
|
741
|
-
};
|
|
742
|
-
hasPublicPluginStateChanged = true;
|
|
743
|
-
}
|
|
744
|
-
}
|
|
745
|
-
}
|
|
746
|
-
}
|
|
747
|
-
if (!isExperimentEnabled('platform_editor_agent_mentions_separate_pm_plugin') && editorExperiment('platform_editor_agent_mentions', true) && tr.docChanged && tr.getMeta('replaceDocument') && hasTrackedAgentMentionState(newPluginState)) {
|
|
748
|
-
newPluginState = clearTrackedAgentMentionState(newPluginState);
|
|
749
|
-
hasPublicPluginStateChanged = true;
|
|
750
|
-
}
|
|
751
|
-
if (!isExperimentEnabled('platform_editor_agent_mentions_separate_pm_plugin') && editorExperiment('platform_editor_agent_mentions', true) && newPluginState.pendingTypedAgentMention && action !== ACTIONS.SET_PENDING_TYPED_AGENT_MENTION && action !== ACTIONS.COMMIT_PENDING_TYPED_AGENT_MENTION && tr.docChanged) {
|
|
752
|
-
newPluginState = {
|
|
753
|
-
...newPluginState,
|
|
754
|
-
pendingTypedAgentMention: getPendingTypedAgentMentionAfterDocChange(newState, tr, newPluginState.pendingTypedAgentMention, {
|
|
755
|
-
resetTimer: isQualifyingLocalUserDocChange(tr)
|
|
756
|
-
})
|
|
757
|
-
};
|
|
758
|
-
}
|
|
759
|
-
if (!isExperimentEnabled('platform_editor_agent_mentions_separate_pm_plugin') && editorExperiment('platform_editor_agent_mentions', true)) {
|
|
760
|
-
// Typed agent mentions stay pending while the user is still editing around them,
|
|
761
|
-
// but leaving the mention's direct parent means they have moved on from that
|
|
762
|
-
// paragraph/block. Publish immediately in that case instead of waiting for the
|
|
763
|
-
// inactivity timer.
|
|
764
|
-
const shouldCheckPendingTypedAgentMentionParent = isLocalSelectionChange(tr, hasPositionChanged);
|
|
765
|
-
if (newPluginState.pendingTypedAgentMention && action !== ACTIONS.SET_PENDING_TYPED_AGENT_MENTION && action !== ACTIONS.COMMIT_PENDING_TYPED_AGENT_MENTION && shouldCheckPendingTypedAgentMentionParent) {
|
|
766
|
-
const pendingTypedAgentMention = newPluginState.pendingTypedAgentMention;
|
|
767
|
-
const pendingMentionDetails = getAgentMentionDetailsAtPos(newState, pendingTypedAgentMention.pos, attrs => attrs.localId === pendingTypedAgentMention.localId, pendingTypedAgentMention.name);
|
|
768
|
-
if (!pendingMentionDetails) {
|
|
769
|
-
newPluginState = {
|
|
770
|
-
...newPluginState,
|
|
771
|
-
pendingTypedAgentMention: null
|
|
772
|
-
};
|
|
773
|
-
} else if (hasPendingMentionMovedToNewParent(oldState, tr, pluginState.pendingTypedAgentMention, pendingMentionDetails) || isSelectionOutsideDirectParent(newState, pendingMentionDetails)) {
|
|
774
|
-
var _options$getIsRovoPan2;
|
|
775
|
-
// Suppress the nudge when Rovo chat is already open, mirroring the same
|
|
776
|
-
// guard used in the inactivity-timer path. Task-item mentions are
|
|
777
|
-
// exempt only while platform_editor_agent_mentions_no_task_autofire is off
|
|
778
|
-
// (they auto-fire the mini modal and never show a nudge). With it on they
|
|
779
|
-
// are suppressed like any block.
|
|
780
|
-
const isTaskItemMention = pendingMentionDetails.parentNodeType === 'taskItem' && !fg('platform_editor_agent_mentions_no_task_autofire');
|
|
781
|
-
if (!isTaskItemMention && options !== null && options !== void 0 && (_options$getIsRovoPan2 = options.getIsRovoPanelOpen) !== null && _options$getIsRovoPan2 !== void 0 && _options$getIsRovoPan2.call(options)) {
|
|
782
|
-
// Discard without committing so lastInsertedAgentMention* fields are
|
|
783
|
-
// never set and the downstream nudge listener never fires.
|
|
784
|
-
newPluginState = {
|
|
785
|
-
...newPluginState,
|
|
786
|
-
pendingTypedAgentMention: null
|
|
787
|
-
};
|
|
788
|
-
hasPublicPluginStateChanged = true;
|
|
789
|
-
} else {
|
|
790
|
-
const commitResult = commitResolvedPendingTypedAgentMention(newPluginState, pendingMentionDetails);
|
|
791
|
-
newPluginState = commitResult.pluginState;
|
|
792
|
-
hasPublicPluginStateChanged = hasPublicPluginStateChanged || commitResult.hasPublicPluginStateChanged;
|
|
793
|
-
}
|
|
794
|
-
}
|
|
795
|
-
}
|
|
796
|
-
}
|
|
797
|
-
if (hasPublicPluginStateChanged && !isExperimentEnabled('platform_editor_agent_mentions_separate_pm_plugin') && editorExperiment('platform_editor_agent_mentions', true)) {
|
|
798
|
-
pmPluginFactoryParams.dispatch(mentionPluginKey, newPluginState);
|
|
799
|
-
}
|
|
800
165
|
return newPluginState;
|
|
801
166
|
}
|
|
802
167
|
},
|
|
@@ -810,125 +175,9 @@ export function createMentionPlugin({
|
|
|
810
175
|
portalProviderAPI: pmPluginFactoryParams.portalProviderAPI
|
|
811
176
|
});
|
|
812
177
|
}
|
|
813
|
-
},
|
|
814
|
-
// Duplicated in platform/packages/editor/editor-plugin-mentions/src/pm-plugins/agent.ts
|
|
815
|
-
// Can remove once platform_editor_agent_mentions_separate_pm_plugin has been cleaned up
|
|
816
|
-
decorations(state) {
|
|
817
|
-
var _mentionPluginKey$get;
|
|
818
|
-
const runStateDecorations = (_mentionPluginKey$get = mentionPluginKey.getState(state)) === null || _mentionPluginKey$get === void 0 ? void 0 : _mentionPluginKey$get.runStateDecorations;
|
|
819
|
-
if (!runStateDecorations || runStateDecorations === DecorationSet.empty) {
|
|
820
|
-
return undefined;
|
|
821
|
-
}
|
|
822
|
-
if (isExperimentEnabled('platform_editor_agent_mentions_separate_pm_plugin') || !editorExperiment('platform_editor_agent_mentions', true) || !isExperimentEnabled('platform_editor_agent_mention_state_anim')) {
|
|
823
|
-
return undefined;
|
|
824
|
-
}
|
|
825
|
-
return runStateDecorations;
|
|
826
178
|
}
|
|
827
179
|
},
|
|
828
180
|
view(editorView) {
|
|
829
|
-
let pendingTypedAgentMentionTimer;
|
|
830
|
-
let pendingTypedAgentMentionTimerKey = null;
|
|
831
|
-
let pendingTypedAgentMentionFocusDeferCount = 0;
|
|
832
|
-
// Tracks agent IDs for which resolveMentionName() is already in-flight,
|
|
833
|
-
// preventing duplicate concurrent Promises for the same ID across update() cycles.
|
|
834
|
-
const pendingResolveMentionIds = new Set();
|
|
835
|
-
|
|
836
|
-
/**
|
|
837
|
-
* Clears the currently scheduled pending typed-agent-mention timer.
|
|
838
|
-
*
|
|
839
|
-
* By default this also resets the focus defer count because a new pending mention,
|
|
840
|
-
* local edit reset, or cleanup should start a fresh escape-hatch window. Focus-based
|
|
841
|
-
* retries pass `preserveFocusDeferCount` so repeated defers for the same pending
|
|
842
|
-
* mention are counted toward the bounded retry cap.
|
|
843
|
-
*/
|
|
844
|
-
const clearPendingTypedAgentMentionTimer = ({
|
|
845
|
-
preserveFocusDeferCount = false
|
|
846
|
-
} = {}) => {
|
|
847
|
-
if (pendingTypedAgentMentionTimer) {
|
|
848
|
-
clearTimeout(pendingTypedAgentMentionTimer);
|
|
849
|
-
pendingTypedAgentMentionTimer = undefined;
|
|
850
|
-
}
|
|
851
|
-
pendingTypedAgentMentionTimerKey = null;
|
|
852
|
-
if (!preserveFocusDeferCount) {
|
|
853
|
-
pendingTypedAgentMentionFocusDeferCount = 0;
|
|
854
|
-
}
|
|
855
|
-
};
|
|
856
|
-
|
|
857
|
-
/**
|
|
858
|
-
* Typed agent mentions intentionally wait before invoking the agent so content the
|
|
859
|
-
* user adds next can become context. That context can be authored through
|
|
860
|
-
* editor-adjacent UI, such as mention typeahead, date picker, or status picker,
|
|
861
|
-
* where focus leaves the ProseMirror editor but remains in the active document.
|
|
862
|
-
* Treat that as continued authoring activity by restarting the inactivity window.
|
|
863
|
-
* This only defers the inactivity timer path: existing selection-change handling can
|
|
864
|
-
* still publish or clear the pending mention sooner. After 20 focus defers (~1 minute),
|
|
865
|
-
* publish anyway so pathological focus states cannot keep the mention pending indefinitely.
|
|
866
|
-
*/
|
|
867
|
-
const shouldDeferPendingTypedAgentMention = () => typeof document !== 'undefined' && document.hasFocus() && !editorView.hasFocus();
|
|
868
|
-
const schedulePendingTypedAgentMentionTimer = (mentionPluginState, {
|
|
869
|
-
preserveFocusDeferCount = false
|
|
870
|
-
} = {}) => {
|
|
871
|
-
if (isExperimentEnabled('platform_editor_agent_mentions_separate_pm_plugin') || !editorExperiment('platform_editor_agent_mentions', true)) {
|
|
872
|
-
clearPendingTypedAgentMentionTimer();
|
|
873
|
-
return;
|
|
874
|
-
}
|
|
875
|
-
const pendingTypedAgentMention = mentionPluginState === null || mentionPluginState === void 0 ? void 0 : mentionPluginState.pendingTypedAgentMention;
|
|
876
|
-
if (!pendingTypedAgentMention) {
|
|
877
|
-
clearPendingTypedAgentMentionTimer();
|
|
878
|
-
return;
|
|
879
|
-
}
|
|
880
|
-
const timerKey = `${pendingTypedAgentMention.localId}:${pendingTypedAgentMention.resetCount}`;
|
|
881
|
-
if (timerKey === pendingTypedAgentMentionTimerKey) {
|
|
882
|
-
return;
|
|
883
|
-
}
|
|
884
|
-
clearPendingTypedAgentMentionTimer({
|
|
885
|
-
preserveFocusDeferCount
|
|
886
|
-
});
|
|
887
|
-
pendingTypedAgentMentionTimerKey = timerKey;
|
|
888
|
-
pendingTypedAgentMentionTimer = setTimeout(() => {
|
|
889
|
-
var _mentionPluginKey$get2;
|
|
890
|
-
{
|
|
891
|
-
var _options$getIsRovoPan3;
|
|
892
|
-
// Suppress the agent-mention nudge when the Rovo panel is already open. Task
|
|
893
|
-
// items are exempt only while platform_editor_agent_mentions_no_task_autofire
|
|
894
|
-
// is off (they auto-fire instead). With it on they are suppressed like every
|
|
895
|
-
// other block.
|
|
896
|
-
const isTaskItemMention = pendingTypedAgentMention.parentNodeType === 'taskItem' && !fg('platform_editor_agent_mentions_no_task_autofire');
|
|
897
|
-
if (!isTaskItemMention && options !== null && options !== void 0 && (_options$getIsRovoPan3 = options.getIsRovoPanelOpen) !== null && _options$getIsRovoPan3 !== void 0 && _options$getIsRovoPan3.call(options)) {
|
|
898
|
-
// Dispatch a discard so pendingTypedAgentMention is nulled out of plugin
|
|
899
|
-
// state.
|
|
900
|
-
editorView.dispatch(editorView.state.tr.setMeta(mentionPluginKey, {
|
|
901
|
-
action: ACTIONS.DISCARD_PENDING_TYPED_AGENT_MENTION,
|
|
902
|
-
params: {
|
|
903
|
-
localId: pendingTypedAgentMention.localId,
|
|
904
|
-
resetCount: pendingTypedAgentMention.resetCount
|
|
905
|
-
}
|
|
906
|
-
}));
|
|
907
|
-
return;
|
|
908
|
-
}
|
|
909
|
-
}
|
|
910
|
-
const latestPendingTypedAgentMention = (_mentionPluginKey$get2 = mentionPluginKey.getState(editorView.state)) === null || _mentionPluginKey$get2 === void 0 ? void 0 : _mentionPluginKey$get2.pendingTypedAgentMention;
|
|
911
|
-
if (!latestPendingTypedAgentMention || latestPendingTypedAgentMention.localId !== pendingTypedAgentMention.localId || latestPendingTypedAgentMention.resetCount !== pendingTypedAgentMention.resetCount) {
|
|
912
|
-
return;
|
|
913
|
-
}
|
|
914
|
-
if (shouldDeferPendingTypedAgentMention() && pendingTypedAgentMentionFocusDeferCount < MAX_PENDING_TYPED_AGENT_MENTION_FOCUS_DEFERS) {
|
|
915
|
-
pendingTypedAgentMentionFocusDeferCount++;
|
|
916
|
-
pendingTypedAgentMentionTimerKey = null;
|
|
917
|
-
schedulePendingTypedAgentMentionTimer(mentionPluginKey.getState(editorView.state), {
|
|
918
|
-
preserveFocusDeferCount: true
|
|
919
|
-
});
|
|
920
|
-
return;
|
|
921
|
-
}
|
|
922
|
-
pendingTypedAgentMentionFocusDeferCount = 0;
|
|
923
|
-
editorView.dispatch(editorView.state.tr.setMeta(mentionPluginKey, {
|
|
924
|
-
action: ACTIONS.COMMIT_PENDING_TYPED_AGENT_MENTION,
|
|
925
|
-
params: {
|
|
926
|
-
localId: pendingTypedAgentMention.localId,
|
|
927
|
-
resetCount: pendingTypedAgentMention.resetCount
|
|
928
|
-
}
|
|
929
|
-
}));
|
|
930
|
-
}, AGENT_MENTION_INACTIVITY_MS);
|
|
931
|
-
};
|
|
932
181
|
const providerHandler = (name, providerPromise) => {
|
|
933
182
|
switch (name) {
|
|
934
183
|
case 'mentionProvider':
|
|
@@ -974,25 +223,7 @@ export function createMentionPlugin({
|
|
|
974
223
|
pmPluginFactoryParams.providerFactory.subscribe('mentionProvider', providerHandler);
|
|
975
224
|
}
|
|
976
225
|
return {
|
|
977
|
-
update(view, prevState) {
|
|
978
|
-
const mentionPluginState = mentionPluginKey.getState(view.state);
|
|
979
|
-
if (mentionPluginState === mentionPluginKey.getState(prevState)) {
|
|
980
|
-
return;
|
|
981
|
-
}
|
|
982
|
-
if (!isExperimentEnabled('platform_editor_agent_mentions_separate_pm_plugin')) {
|
|
983
|
-
var _mentionPluginState$p;
|
|
984
|
-
schedulePendingTypedAgentMentionTimer(mentionPluginState);
|
|
985
|
-
|
|
986
|
-
// If a pasted agent mention has no name (cache miss), resolve it async
|
|
987
|
-
// via the mention provider and dispatch RESOLVE_PASTED_AGENT_MENTION_NAME
|
|
988
|
-
// so the nudge displays the correct name.
|
|
989
|
-
if (editorExperiment('platform_editor_agent_mentions', true) && (mentionPluginState === null || mentionPluginState === void 0 ? void 0 : (_mentionPluginState$p = mentionPluginState.pendingPastedAgentMention) === null || _mentionPluginState$p === void 0 ? void 0 : _mentionPluginState$p.id) != null && mentionPluginState !== null && mentionPluginState !== void 0 && mentionPluginState.mentionProvider && isResolvingMentionProvider(mentionPluginState.mentionProvider) && fg('platform_editor_agent_mentions_drop_one_fixes')) {
|
|
990
|
-
resolveAndDispatchPastedAgentMentionName(mentionPluginState.pendingPastedAgentMention.id, mentionPluginState.mentionProvider, pendingResolveMentionIds, view);
|
|
991
|
-
}
|
|
992
|
-
}
|
|
993
|
-
},
|
|
994
226
|
destroy() {
|
|
995
|
-
clearPendingTypedAgentMentionTimer();
|
|
996
227
|
if (pmPluginFactoryParams.providerFactory) {
|
|
997
228
|
pmPluginFactoryParams.providerFactory.unsubscribe('mentionProvider', providerHandler);
|
|
998
229
|
}
|