@atlaskit/editor-plugin-mentions
Version:
Mentions plugin for @atlaskit/editor-core
757 lines (745 loc) • 42.1 kB
JavaScript
import _slicedToArray from "@babel/runtime/helpers/slicedToArray";
import _defineProperty from "@babel/runtime/helpers/defineProperty";
function _createForOfIteratorHelper(r, e) { var t = "undefined" != typeof Symbol && r[Symbol.iterator] || r["@@iterator"]; if (!t) { if (Array.isArray(r) || (t = _unsupportedIterableToArray(r)) || e && r && "number" == typeof r.length) { t && (r = t); var _n = 0, F = function F() {}; return { s: F, n: function n() { return _n >= r.length ? { done: !0 } : { done: !1, value: r[_n++] }; }, e: function e(r) { throw r; }, f: F }; } throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); } var o, a = !0, u = !1; return { s: function s() { t = t.call(r); }, n: function n() { var r = t.next(); return a = r.done, r; }, e: function e(r) { u = !0, o = r; }, f: function f() { try { a || null == t.return || t.return(); } finally { if (u) throw o; } } }; }
function _unsupportedIterableToArray(r, a) { if (r) { if ("string" == typeof r) return _arrayLikeToArray(r, a); var t = {}.toString.call(r).slice(8, -1); return "Object" === t && r.constructor && (t = r.constructor.name), "Map" === t || "Set" === t ? Array.from(r) : "Arguments" === t || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(t) ? _arrayLikeToArray(r, a) : void 0; } }
function _arrayLikeToArray(r, a) { (null == a || a > r.length) && (a = r.length); for (var e = 0, n = Array(a); e < a; e++) n[e] = r[e]; return n; }
function ownKeys(e, r) { var t = Object.keys(e); if (Object.getOwnPropertySymbols) { var o = Object.getOwnPropertySymbols(e); r && (o = o.filter(function (r) { return Object.getOwnPropertyDescriptor(e, r).enumerable; })), t.push.apply(t, o); } return t; }
function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? ownKeys(Object(t), !0).forEach(function (r) { _defineProperty(e, r, t[r]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function (r) { Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r)); }); } return e; }
import { SafePlugin } from '@atlaskit/editor-common/safe-plugin';
import { PluginKey } from '@atlaskit/editor-prosemirror/state';
import { isResolvingMentionProvider, MentionNameStatus } from '@atlaskit/mention/resource';
import { fg } from '@atlaskit/platform-feature-flags/fg';
import { editorExperiment } from '@atlaskit/tmp-editor-statsig/experiments';
import { getAgentMentionParentContext } from './agent-mention-context';
import { mentionPluginKey } from './key';
import { ACTIONS } from './main';
// 'AGENT' is not in the ADF schema UserType enum but is used at runtime.
var AGENT_USER_TYPES = new Set(['APP', 'AGENT']);
var isAgentUserType = function isAgentUserType(userType) {
return typeof userType === 'string' && AGENT_USER_TYPES.has(userType);
};
var getAgentMentionName = function getAgentMentionName(text, fallbackName) {
var trimmedFallbackName = typeof fallbackName === 'string' ? fallbackName.trim() : '';
var normalizedFallbackName = (trimmedFallbackName.startsWith('@') ? trimmedFallbackName.slice(1).trim() : trimmedFallbackName) || null;
if (typeof text !== 'string') {
return normalizedFallbackName;
}
var trimmedText = text.trim();
var displayName = trimmedText.startsWith('@') ? trimmedText.slice(1).trim() : trimmedText;
var normalizedName = displayName || normalizedFallbackName;
return normalizedName;
};
var AI_STREAMING_TRANSFORMATION_META_KEY = 'isAIStreamingTransformation';
var AGENT_MENTION_INACTIVITY_MS = 3000;
var MAX_PENDING_TYPED_AGENT_MENTION_FOCUS_DEFERS = 20;
/**
* Returns true when a transaction represents a local user document edit that
* should restart pending agent-mention inactivity tracking.
*
* Remote/collab updates, replace-document transactions, AI streaming transforms,
* selection-only movements, and metadata-only transactions are intentionally ignored.
*/
var isQualifyingLocalUserDocChange = function isQualifyingLocalUserDocChange(tr) {
var isAIStreaming = Boolean(tr.getMeta(AI_STREAMING_TRANSFORMATION_META_KEY));
return tr.docChanged && !tr.getMeta('isRemote') && !tr.getMeta('replaceDocument') && !isAIStreaming;
};
var isLocalSelectionChange = function isLocalSelectionChange(tr, hasPositionChanged) {
var isAIStreaming = Boolean(tr.getMeta(AI_STREAMING_TRANSFORMATION_META_KEY));
// Pressing Enter can move selection through a doc split without setting tr.selectionSet
// or changing from/to numerically, so local doc changes are checked against the
// pending mention's current parent before publishing.
return (hasPositionChanged || tr.docChanged) && !tr.getMeta('isRemote') && !tr.getMeta('replaceDocument') && !isAIStreaming;
};
/**
* Reads agent-mention details from a known document position without traversing
* the document. Callers pass a matcher so mapped positions are only accepted
* when they still point at the same pending/tracked mention.
*/
var getAgentMentionDetailsAtPos = function getAgentMentionDetailsAtPos(state, pos, matchesMention, fallbackName) {
var _parentNode$type$name;
if (pos < 0 || pos > state.doc.content.size) {
return null;
}
var node = state.doc.nodeAt(pos);
var mentionSchema = state.schema.nodes.mention;
if ((node === null || node === void 0 ? void 0 : node.type) !== mentionSchema || !isAgentUserType(node.attrs.userType) || !matchesMention(node.attrs) || !node.attrs.id || !node.attrs.localId) {
return null;
}
var $mentionPos = state.doc.resolve(Math.min(pos + node.nodeSize, state.doc.content.size));
var parentNode = $mentionPos.node($mentionPos.depth);
var id = node.attrs.id;
var name = getAgentMentionName(node.attrs.text, fallbackName);
return {
id: id,
localId: node.attrs.localId,
context: getAgentMentionParentContext(parentNode, node.attrs.localId),
name: name,
prompt: parentNode.textContent.trim() || null,
nodeSize: node.nodeSize,
parentEnd: $mentionPos.end($mentionPos.depth),
parentNodeType: (_parentNode$type$name = parentNode.type.name) !== null && _parentNode$type$name !== void 0 ? _parentNode$type$name : null,
parentStart: $mentionPos.start($mentionPos.depth),
pos: pos
};
};
/**
* Finds an agent mention that survived a document change when the changed-range
* scan did not find one. Uses the tracked localId as the mention instance identity
* so same-agent mentions elsewhere in the document cannot be selected as fallback.
*/
var getSurvivingAgentMentionDetails = function getSurvivingAgentMentionDetails(state, preferredLocalId, preferredName) {
var mentionSchema = state.schema.nodes.mention;
var result = null;
state.doc.descendants(function (node, pos) {
if (result) {
return false;
}
if (node.type !== mentionSchema || !isAgentUserType(node.attrs.userType) || node.attrs.localId !== preferredLocalId) {
return true;
}
result = getAgentMentionDetailsAtPos(state, pos, function (attrs) {
return attrs.localId === preferredLocalId;
}, preferredName);
return !result;
});
return result;
};
/**
* Maps a pending typed agent mention through a document-changing transaction and
* returns the updated pending state. If the mapped position was deleted or no
* longer points at the same local mention, the pending mention is cleared.
*/
var getPendingTypedAgentMentionAfterDocChange = function getPendingTypedAgentMentionAfterDocChange(state, tr, pendingTypedAgentMention, _ref) {
var resetTimer = _ref.resetTimer;
var mappedPos = tr.mapping.mapResult(pendingTypedAgentMention.pos, 1);
var resetCount = resetTimer ? pendingTypedAgentMention.resetCount + 1 : pendingTypedAgentMention.resetCount;
if (mappedPos.deleted) {
return null;
}
var pendingMentionDetails = getAgentMentionDetailsAtPos(state, mappedPos.pos, function (attrs) {
return attrs.localId === pendingTypedAgentMention.localId;
}, pendingTypedAgentMention.name);
return pendingMentionDetails ? {
id: pendingMentionDetails.id,
localId: pendingTypedAgentMention.localId,
name: pendingMentionDetails.name,
nodeSize: pendingMentionDetails.nodeSize,
parentNodeType: pendingMentionDetails.parentNodeType,
pos: pendingMentionDetails.pos,
resetCount: resetCount
} : null;
};
var hasPendingMentionMovedToNewParent = function hasPendingMentionMovedToNewParent(oldState, tr, previousPendingTypedAgentMention, pendingMentionDetails) {
if (!previousPendingTypedAgentMention) {
return false;
}
var previousMentionDetails = getAgentMentionDetailsAtPos(oldState, previousPendingTypedAgentMention.pos, function (attrs) {
return attrs.localId === previousPendingTypedAgentMention.localId;
});
// Keep the previous parent boundary associated with the left side of an
// insertion at that boundary, so typing at the start of the parent does not
// look like the pending mention moved into a new parent.
var mappedPreviousParentStart = previousMentionDetails && tr.mapping.map(previousMentionDetails.parentStart, -1);
return Boolean(previousMentionDetails && mappedPreviousParentStart !== pendingMentionDetails.parentStart);
};
var isSelectionOutsideDirectParent = function isSelectionOutsideDirectParent(state, pendingMentionDetails) {
return state.selection.from < pendingMentionDetails.parentStart || state.selection.to > pendingMentionDetails.parentEnd;
};
/**
* Finalises a pending typed agent mention by copying its details into the
* public lastInserted* plugin state after the caller has already resolved the
* pending mention from the current document.
*/
var commitResolvedPendingTypedAgentMention = function commitResolvedPendingTypedAgentMention(pluginState, pendingMentionDetails) {
var _pluginState$lastAgen;
return {
hasPublicPluginStateChanged: true,
pluginState: _objectSpread(_objectSpread({}, pluginState), {}, {
pendingTypedAgentMention: null,
lastInsertedAgentMentionId: pendingMentionDetails.id,
lastInsertedAgentMentionLocalId: pendingMentionDetails.localId,
lastInsertedAgentMentionContext: pendingMentionDetails.context,
lastInsertedAgentMentionName: pendingMentionDetails.name,
lastInsertedAgentMentionPrompt: pendingMentionDetails.prompt,
lastInsertedAgentMentionParentNodeType: pendingMentionDetails.parentNodeType,
lastAgentMentionInsertionCount: ((_pluginState$lastAgen = pluginState.lastAgentMentionInsertionCount) !== null && _pluginState$lastAgen !== void 0 ? _pluginState$lastAgen : 0) + 1
})
};
};
/**
* Resolves and finalises a pending typed agent mention. If the tracked mention
* no longer resolves, the stale pending state is cleared without dispatching a
* public update.
*/
var commitPendingTypedAgentMention = function commitPendingTypedAgentMention(state, pluginState, pendingTypedAgentMention) {
var pendingMentionDetails = getAgentMentionDetailsAtPos(state, pendingTypedAgentMention.pos, function (attrs) {
return attrs.localId === pendingTypedAgentMention.localId;
}, pendingTypedAgentMention.name);
if (!pendingMentionDetails) {
return {
hasPublicPluginStateChanged: false,
pluginState: _objectSpread(_objectSpread({}, pluginState), {}, {
pendingTypedAgentMention: null
})
};
}
return commitResolvedPendingTypedAgentMention(pluginState, pendingMentionDetails);
};
var hasTrackedAgentMentionState = function hasTrackedAgentMentionState(pluginState) {
return 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;
};
/**
* Clears agent mention state that points at a specific document snapshot.
* replaceDocument swaps content wholesale, so pending typed mentions and
* lastInserted* details from the previous document must be cleared together.
*/
var clearTrackedAgentMentionState = function clearTrackedAgentMentionState(pluginState) {
return _objectSpread(_objectSpread(_objectSpread({}, pluginState), {}, {
pendingTypedAgentMention: null
}, fg('platform_editor_agent_mentions_drop_one_fixes') ? {
pendingPastedAgentMention: null
} : {}), {}, {
lastInsertedAgentMentionId: null,
lastInsertedAgentMentionLocalId: null,
lastInsertedAgentMentionContext: null,
lastInsertedAgentMentionName: null,
lastInsertedAgentMentionPrompt: null,
lastInsertedAgentMentionParentNodeType: null
});
};
/**
* Attempts to synchronously resolve an agent mention name from the mention
* provider's cache. Falls back to undefined if the provider doesn't support
* name resolution, the result is a Promise (async/cache miss), or the status
* is not OK.
*/
var resolveCachedAgentMentionName = function resolveCachedAgentMentionName(mentionProvider, params, id) {
if (params !== null && params !== void 0 && params.name || !isResolvingMentionProvider(mentionProvider)) {
var _params$name;
return (_params$name = params === null || params === void 0 ? void 0 : params.name) !== null && _params$name !== void 0 ? _params$name : undefined;
}
var result = mentionProvider.resolveMentionName(id);
if (!(result instanceof Promise) && result.status === MentionNameStatus.OK) {
return result.name || undefined;
}
return undefined;
};
/**
* Resolves the name of a pasted agent mention via the mention provider and
* dispatches the appropriate action to update plugin state.
*/
export var resolveAndDispatchPastedAgentMentionName = function resolveAndDispatchPastedAgentMentionName(agentId, mentionProvider, pendingResolveMentionIds, view) {
if (pendingResolveMentionIds.has(agentId)) {
return;
}
pendingResolveMentionIds.add(agentId);
var result = mentionProvider.resolveMentionName(agentId);
if (result instanceof Promise) {
result.then(function (nameDetails) {
pendingResolveMentionIds.delete(agentId);
if (nameDetails.status === MentionNameStatus.OK && nameDetails.name) {
view.dispatch(view.state.tr.setMeta(mentionPluginKey, {
action: ACTIONS.RESOLVE_PASTED_AGENT_MENTION_NAME,
params: {
id: agentId,
name: nameDetails.name
}
}));
} else {
view.dispatch(view.state.tr.setMeta(mentionPluginKey, {
action: ACTIONS.CLEAR_PENDING_PASTED_AGENT_MENTION,
params: {
id: agentId
}
}));
}
}).catch(function () {
pendingResolveMentionIds.delete(agentId);
});
} else if (result.status === MentionNameStatus.OK && result.name) {
pendingResolveMentionIds.delete(agentId);
view.dispatch(view.state.tr.setMeta(mentionPluginKey, {
action: ACTIONS.RESOLVE_PASTED_AGENT_MENTION_NAME,
params: {
id: agentId,
name: result.name
}
}));
} else {
pendingResolveMentionIds.delete(agentId);
view.dispatch(view.state.tr.setMeta(mentionPluginKey, {
action: ACTIONS.CLEAR_PENDING_PASTED_AGENT_MENTION,
params: {
id: agentId
}
}));
}
};
export var applyAgentMentionState = function applyAgentMentionState(_ref2) {
var tr = _ref2.tr,
pluginState = _ref2.pluginState,
oldState = _ref2.oldState,
newState = _ref2.newState,
getIsRovoPanelOpen = _ref2.getIsRovoPanelOpen;
// mentionPluginKey intentionally remains the shared transaction action channel while
// agentMentionPluginKey owns the extracted agent state.
var _ref3 = tr.getMeta(mentionPluginKey) || {
action: null,
params: null
},
action = _ref3.action,
params = _ref3.params;
var hasPublicPluginStateChanged = false;
var newPluginState = pluginState;
var isAgentMentionsExperimentEnabled = editorExperiment('platform_editor_agent_mentions', true);
var hasPositionChanged = oldState.selection.from !== newState.selection.from || oldState.selection.to !== newState.selection.to;
switch (action) {
case ACTIONS.COMMIT_PENDING_TYPED_AGENT_MENTION:
{
var pendingTypedAgentMention = newPluginState.pendingTypedAgentMention;
if (!isAgentMentionsExperimentEnabled || !pendingTypedAgentMention || pendingTypedAgentMention.localId !== (params === null || params === void 0 ? void 0 : params.localId) || pendingTypedAgentMention.resetCount !== (params === null || params === void 0 ? void 0 : params.resetCount)) {
break;
}
var commitResult = commitPendingTypedAgentMention(newState, newPluginState, pendingTypedAgentMention);
newPluginState = commitResult.pluginState;
hasPublicPluginStateChanged = hasPublicPluginStateChanged || commitResult.hasPublicPluginStateChanged;
break;
}
case ACTIONS.DISCARD_PENDING_TYPED_AGENT_MENTION:
{
var pendingTypedAgentMentionToDiscard = newPluginState.pendingTypedAgentMention;
if (!isAgentMentionsExperimentEnabled || !pendingTypedAgentMentionToDiscard || pendingTypedAgentMentionToDiscard.localId !== (params === null || params === void 0 ? void 0 : params.localId) || pendingTypedAgentMentionToDiscard.resetCount !== (params === null || params === void 0 ? void 0 : params.resetCount)) {
break;
}
newPluginState = _objectSpread(_objectSpread({}, newPluginState), {}, {
pendingTypedAgentMention: null
});
hasPublicPluginStateChanged = true;
break;
}
case ACTIONS.SET_PROVIDER:
newPluginState = _objectSpread(_objectSpread({}, newPluginState), {}, {
mentionProvider: params.provider
});
hasPublicPluginStateChanged = true;
break;
case ACTIONS.RESOLVE_PASTED_AGENT_MENTION_NAME:
{
var _newPluginState$pendi, _newPluginState$pendi2, _newPluginState$pendi3, _newPluginState$pendi4, _newPluginState$pendi5, _newPluginState$pendi6;
if (!isAgentMentionsExperimentEnabled || !(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')) {
break;
}
newPluginState = _objectSpread(_objectSpread({}, newPluginState), {}, {
pendingPastedAgentMention: null,
lastInsertedAgentMentionId: (_newPluginState$pendi2 = newPluginState.pendingPastedAgentMention) === null || _newPluginState$pendi2 === void 0 ? void 0 : _newPluginState$pendi2.id,
lastInsertedAgentMentionLocalId: (_newPluginState$pendi3 = newPluginState.pendingPastedAgentMention) === null || _newPluginState$pendi3 === void 0 ? void 0 : _newPluginState$pendi3.localId,
lastInsertedAgentMentionContext: (_newPluginState$pendi4 = newPluginState.pendingPastedAgentMention) === null || _newPluginState$pendi4 === void 0 ? void 0 : _newPluginState$pendi4.context,
lastInsertedAgentMentionName: params.name,
lastInsertedAgentMentionPrompt: (_newPluginState$pendi5 = newPluginState.pendingPastedAgentMention) === null || _newPluginState$pendi5 === void 0 ? void 0 : _newPluginState$pendi5.prompt,
lastInsertedAgentMentionParentNodeType: (_newPluginState$pendi6 = newPluginState.pendingPastedAgentMention) === null || _newPluginState$pendi6 === void 0 ? void 0 : _newPluginState$pendi6.parentNodeType
});
hasPublicPluginStateChanged = true;
break;
}
case ACTIONS.CLEAR_PENDING_PASTED_AGENT_MENTION:
{
var _newPluginState$pendi7;
if (!isAgentMentionsExperimentEnabled || !(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')) {
break;
}
newPluginState = _objectSpread(_objectSpread({}, newPluginState), {}, {
pendingPastedAgentMention: null
});
break;
}
}
if (isAgentMentionsExperimentEnabled && isQualifyingLocalUserDocChange(tr)) {
var mentionSchema = newState.schema.nodes.mention;
var newDocRanges = [];
var oldDocRanges = [];
var stepsTouchMentions = false;
tr.steps.forEach(function (step) {
var found = false;
var stepNewRanges = [];
var stepOldRanges = [];
step.getMap().forEach(function (oldFrom, oldTo, newFrom, newTo) {
stepOldRanges.push([oldFrom, oldTo]);
stepNewRanges.push([newFrom, newTo]);
if (!found) {
var clampedNewFrom = Math.min(newFrom, newState.doc.content.size);
var clampedNewTo = Math.min(newTo, newState.doc.content.size);
if (clampedNewFrom < clampedNewTo) {
newState.doc.nodesBetween(clampedNewFrom, clampedNewTo, function (node) {
if (node.type === mentionSchema && isAgentUserType(node.attrs.userType)) {
found = true;
}
return !found;
});
}
if (!found) {
var clampedOldFrom = Math.min(oldFrom, oldState.doc.content.size);
var clampedOldTo = Math.min(oldTo, oldState.doc.content.size);
if (clampedOldFrom < clampedOldTo) {
oldState.doc.nodesBetween(clampedOldFrom, clampedOldTo, function (node) {
if (node.type === mentionSchema && AGENT_USER_TYPES.has(node.attrs.userType)) {
found = true;
}
return !found;
});
}
}
}
});
if (found) {
stepsTouchMentions = true;
newDocRanges.push.apply(newDocRanges, stepNewRanges);
oldDocRanges.push.apply(oldDocRanges, stepOldRanges);
}
});
var shouldResolveAgentMentionState = stepsTouchMentions || Boolean(newPluginState.lastInsertedAgentMentionLocalId);
if (shouldResolveAgentMentionState) {
var _newPluginState$lastI, _newPluginState$lastA, _newPluginState$lastI2, _newPluginState$lastI3, _newPluginState$lastI4, _newPluginState$lastI5, _newPluginState$lastI6;
var agentMentionId = null;
var agentMentionLocalId = null;
var agentMentionContext = null;
var agentMentionName = null;
var agentMentionPrompt = null;
var agentMentionParentNodeType = null;
var existingAgentMentionLocalIdsInChangedRanges = new Set();
var pendingTypedAgentMentionDetails = null;
if (stepsTouchMentions) {
var _iterator = _createForOfIteratorHelper(newDocRanges),
_step;
try {
for (_iterator.s(); !(_step = _iterator.n()).done;) {
var _step$value = _slicedToArray(_step.value, 2),
from = _step$value[0],
to = _step$value[1];
var clampedTo = Math.min(to, newState.doc.content.size);
if (from >= clampedTo) {
continue;
}
newState.doc.nodesBetween(from, clampedTo, function (node, pos) {
if (node.type !== mentionSchema || !isAgentUserType(node.attrs.userType)) {
return true;
}
if (pendingTypedAgentMentionDetails === null && action === ACTIONS.SET_PENDING_TYPED_AGENT_MENTION && node.attrs.localId === (params === null || params === void 0 ? void 0 : params.localId)) {
pendingTypedAgentMentionDetails = getAgentMentionDetailsAtPos(newState, pos, function (attrs) {
return attrs.localId === params.localId;
}, params.name);
}
if (agentMentionLocalId === null && node.attrs.localId) {
var cachedName = fg('platform_editor_agent_mentions_drop_one_fixes') ? resolveCachedAgentMentionName(newPluginState.mentionProvider, params, node.attrs.id) : params === null || params === void 0 ? void 0 : params.name;
var agentMentionDetails = getAgentMentionDetailsAtPos(newState, pos, function (attrs) {
return attrs.localId === node.attrs.localId;
}, cachedName);
if (agentMentionDetails) {
agentMentionId = agentMentionDetails.id;
agentMentionLocalId = agentMentionDetails.localId;
agentMentionContext = agentMentionDetails.context;
agentMentionName = agentMentionDetails.name;
agentMentionPrompt = agentMentionDetails.prompt;
agentMentionParentNodeType = agentMentionDetails.parentNodeType;
}
}
return true;
});
}
} catch (err) {
_iterator.e(err);
} finally {
_iterator.f();
}
var _iterator2 = _createForOfIteratorHelper(oldDocRanges),
_step2;
try {
for (_iterator2.s(); !(_step2 = _iterator2.n()).done;) {
var _step2$value = _slicedToArray(_step2.value, 2),
_from = _step2$value[0],
_to = _step2$value[1];
var clampedOldTo = Math.min(_to, oldState.doc.content.size);
if (_from >= clampedOldTo) {
continue;
}
oldState.doc.nodesBetween(_from, clampedOldTo, function (node) {
if (node.type !== mentionSchema || !isAgentUserType(node.attrs.userType)) {
return true;
}
if (node.attrs.localId) {
existingAgentMentionLocalIdsInChangedRanges.add(node.attrs.localId);
}
return true;
});
}
} catch (err) {
_iterator2.e(err);
} finally {
_iterator2.f();
}
}
var resolvedFromFullDocFallback = false;
if (agentMentionId === null && newPluginState.lastInsertedAgentMentionLocalId) {
var survivorDetails = getSurvivingAgentMentionDetails(newState, newPluginState.lastInsertedAgentMentionLocalId, newPluginState.lastInsertedAgentMentionName);
if (survivorDetails) {
agentMentionId = survivorDetails.id;
agentMentionLocalId = survivorDetails.localId;
agentMentionContext = survivorDetails.context;
agentMentionName = survivorDetails.name;
agentMentionPrompt = survivorDetails.prompt;
agentMentionParentNodeType = survivorDetails.parentNodeType;
resolvedFromFullDocFallback = true;
}
}
var trackedAgentMentionLocalId = (_newPluginState$lastI = newPluginState.lastInsertedAgentMentionLocalId) !== null && _newPluginState$lastI !== void 0 ? _newPluginState$lastI : null;
var changedRangeMentionIsNew = agentMentionLocalId !== null && !existingAgentMentionLocalIdsInChangedRanges.has(agentMentionLocalId);
if (agentMentionLocalId !== null && !changedRangeMentionIsNew && agentMentionLocalId !== trackedAgentMentionLocalId) {
var _survivorDetails = trackedAgentMentionLocalId ? getSurvivingAgentMentionDetails(newState, trackedAgentMentionLocalId, newPluginState.lastInsertedAgentMentionName) : null;
if (_survivorDetails) {
agentMentionId = _survivorDetails.id;
agentMentionLocalId = _survivorDetails.localId;
agentMentionContext = _survivorDetails.context;
agentMentionName = _survivorDetails.name;
agentMentionParentNodeType = _survivorDetails.parentNodeType;
resolvedFromFullDocFallback = true;
} else {
agentMentionId = null;
agentMentionLocalId = null;
agentMentionContext = null;
agentMentionName = null;
agentMentionParentNodeType = null;
}
}
var isNewInsertion = agentMentionId !== null && !resolvedFromFullDocFallback && changedRangeMentionIsNew;
var isPendingTypedAgentMentionInsertion = isNewInsertion && action === ACTIONS.SET_PENDING_TYPED_AGENT_MENTION && typeof (params === null || params === void 0 ? void 0 : params.localId) === 'string';
var newInsertionCount = isNewInsertion ? ((_newPluginState$lastA = newPluginState.lastAgentMentionInsertionCount) !== null && _newPluginState$lastA !== void 0 ? _newPluginState$lastA : 0) + 1 : undefined;
var pendingTypedAgentMentionDetailsForState = pendingTypedAgentMentionDetails;
if (isPendingTypedAgentMentionInsertion && pendingTypedAgentMentionDetailsForState) {
var pendingTypedAgentMentionLocalId = params === null || params === void 0 ? void 0 : params.localId;
newPluginState = _objectSpread(_objectSpread({}, newPluginState), {}, {
pendingTypedAgentMention: {
id: pendingTypedAgentMentionDetailsForState.id,
localId: pendingTypedAgentMentionLocalId,
name: pendingTypedAgentMentionDetailsForState.name,
nodeSize: pendingTypedAgentMentionDetailsForState.nodeSize,
parentNodeType: pendingTypedAgentMentionDetailsForState.parentNodeType,
pos: pendingTypedAgentMentionDetailsForState.pos,
resetCount: 1
}
});
} else if (isPendingTypedAgentMentionInsertion) {
newPluginState = _objectSpread(_objectSpread({}, newPluginState), {}, {
pendingTypedAgentMention: null,
lastInsertedAgentMentionId: agentMentionId,
lastInsertedAgentMentionLocalId: agentMentionLocalId,
lastInsertedAgentMentionContext: agentMentionContext,
lastInsertedAgentMentionName: agentMentionName,
lastInsertedAgentMentionPrompt: agentMentionPrompt,
lastInsertedAgentMentionParentNodeType: agentMentionParentNodeType
}, newInsertionCount !== undefined ? {
lastAgentMentionInsertionCount: newInsertionCount
} : {});
hasPublicPluginStateChanged = true;
} 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) {
var isTaskItemMentionForPaste = agentMentionParentNodeType === 'taskItem';
if (!isTaskItemMentionForPaste && isNewInsertion && getIsRovoPanelOpen !== null && getIsRovoPanelOpen !== void 0 && getIsRovoPanelOpen()) {
// Rovo is already open, so do not publish a nudge-triggering state change.
} else if (agentMentionName === null && agentMentionId !== null && isResolvingMentionProvider(newPluginState.mentionProvider) && fg('platform_editor_agent_mentions_drop_one_fixes')) {
newPluginState = _objectSpread(_objectSpread({}, newPluginState), {}, {
pendingPastedAgentMention: {
id: agentMentionId,
localId: agentMentionLocalId,
context: agentMentionContext,
prompt: agentMentionPrompt,
parentNodeType: agentMentionParentNodeType
}
}, newInsertionCount !== undefined ? {
lastAgentMentionInsertionCount: newInsertionCount
} : {});
} else {
newPluginState = _objectSpread(_objectSpread({}, newPluginState), {}, {
lastInsertedAgentMentionId: agentMentionId,
lastInsertedAgentMentionLocalId: agentMentionLocalId,
lastInsertedAgentMentionContext: agentMentionContext,
lastInsertedAgentMentionName: agentMentionName,
lastInsertedAgentMentionPrompt: agentMentionPrompt,
lastInsertedAgentMentionParentNodeType: agentMentionParentNodeType
}, newInsertionCount !== undefined ? {
lastAgentMentionInsertionCount: newInsertionCount
} : {});
hasPublicPluginStateChanged = true;
}
}
}
}
if (isAgentMentionsExperimentEnabled && tr.docChanged && tr.getMeta('replaceDocument') && hasTrackedAgentMentionState(newPluginState)) {
newPluginState = clearTrackedAgentMentionState(newPluginState);
hasPublicPluginStateChanged = true;
}
if (isAgentMentionsExperimentEnabled && newPluginState.pendingTypedAgentMention && action !== ACTIONS.SET_PENDING_TYPED_AGENT_MENTION && action !== ACTIONS.COMMIT_PENDING_TYPED_AGENT_MENTION && tr.docChanged) {
newPluginState = _objectSpread(_objectSpread({}, newPluginState), {}, {
pendingTypedAgentMention: getPendingTypedAgentMentionAfterDocChange(newState, tr, newPluginState.pendingTypedAgentMention, {
resetTimer: isQualifyingLocalUserDocChange(tr)
})
});
}
var shouldCheckPendingTypedAgentMentionParent = isLocalSelectionChange(tr, hasPositionChanged);
if (isAgentMentionsExperimentEnabled && newPluginState.pendingTypedAgentMention && action !== ACTIONS.SET_PENDING_TYPED_AGENT_MENTION && action !== ACTIONS.COMMIT_PENDING_TYPED_AGENT_MENTION && shouldCheckPendingTypedAgentMentionParent) {
var _pendingTypedAgentMention = newPluginState.pendingTypedAgentMention;
var pendingMentionDetails = getAgentMentionDetailsAtPos(newState, _pendingTypedAgentMention.pos, function (attrs) {
return attrs.localId === _pendingTypedAgentMention.localId;
}, _pendingTypedAgentMention.name);
if (!pendingMentionDetails) {
newPluginState = _objectSpread(_objectSpread({}, newPluginState), {}, {
pendingTypedAgentMention: null
});
} else if (hasPendingMentionMovedToNewParent(oldState, tr, pluginState.pendingTypedAgentMention, pendingMentionDetails) || isSelectionOutsideDirectParent(newState, pendingMentionDetails)) {
if (pendingMentionDetails.parentNodeType !== 'taskItem' && getIsRovoPanelOpen !== null && getIsRovoPanelOpen !== void 0 && getIsRovoPanelOpen()) {
newPluginState = _objectSpread(_objectSpread({}, newPluginState), {}, {
pendingTypedAgentMention: null
});
hasPublicPluginStateChanged = true;
} else {
var _commitResult = commitResolvedPendingTypedAgentMention(newPluginState, pendingMentionDetails);
newPluginState = _commitResult.pluginState;
hasPublicPluginStateChanged = hasPublicPluginStateChanged || _commitResult.hasPublicPluginStateChanged;
}
}
}
return {
hasPublicPluginStateChanged: hasPublicPluginStateChanged,
pluginState: newPluginState
};
};
export var createAgentMentionPluginView = function createAgentMentionPluginView(_ref4) {
var editorView = _ref4.editorView,
getIsRovoPanelOpen = _ref4.getIsRovoPanelOpen,
stateKey = _ref4.stateKey;
var isAgentMentionsEnabled = editorExperiment('platform_editor_agent_mentions', true);
var pendingTypedAgentMentionTimer;
var pendingTypedAgentMentionTimerKey = null;
var pendingTypedAgentMentionFocusDeferCount = 0;
var pendingResolveMentionIds = new Set();
var clearPendingTypedAgentMentionTimer = function clearPendingTypedAgentMentionTimer() {
var _ref5 = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {},
_ref5$preserveFocusDe = _ref5.preserveFocusDeferCount,
preserveFocusDeferCount = _ref5$preserveFocusDe === void 0 ? false : _ref5$preserveFocusDe;
if (pendingTypedAgentMentionTimer) {
clearTimeout(pendingTypedAgentMentionTimer);
pendingTypedAgentMentionTimer = undefined;
}
pendingTypedAgentMentionTimerKey = null;
if (!preserveFocusDeferCount) {
pendingTypedAgentMentionFocusDeferCount = 0;
}
};
var shouldDeferPendingTypedAgentMention = function shouldDeferPendingTypedAgentMention() {
return typeof document !== 'undefined' && document.hasFocus() && !editorView.hasFocus();
};
var _schedulePendingTypedAgentMentionTimer = function schedulePendingTypedAgentMentionTimer(mentionPluginState) {
var _ref6 = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {},
_ref6$preserveFocusDe = _ref6.preserveFocusDeferCount,
preserveFocusDeferCount = _ref6$preserveFocusDe === void 0 ? false : _ref6$preserveFocusDe;
if (!isAgentMentionsEnabled) {
clearPendingTypedAgentMentionTimer();
return;
}
var pendingTypedAgentMention = mentionPluginState === null || mentionPluginState === void 0 ? void 0 : mentionPluginState.pendingTypedAgentMention;
if (!pendingTypedAgentMention) {
clearPendingTypedAgentMentionTimer();
return;
}
var timerKey = "".concat(pendingTypedAgentMention.localId, ":").concat(pendingTypedAgentMention.resetCount);
if (timerKey === pendingTypedAgentMentionTimerKey) {
return;
}
clearPendingTypedAgentMentionTimer({
preserveFocusDeferCount: preserveFocusDeferCount
});
pendingTypedAgentMentionTimerKey = timerKey;
pendingTypedAgentMentionTimer = setTimeout(function () {
var _stateKey$getState;
var isTaskItemMention = pendingTypedAgentMention.parentNodeType === 'taskItem';
if (!isTaskItemMention && getIsRovoPanelOpen !== null && getIsRovoPanelOpen !== void 0 && getIsRovoPanelOpen()) {
// Agent actions continue to use mentionPluginKey as the shared transaction meta channel.
editorView.dispatch(editorView.state.tr.setMeta(mentionPluginKey, {
action: ACTIONS.DISCARD_PENDING_TYPED_AGENT_MENTION,
params: {
localId: pendingTypedAgentMention.localId,
resetCount: pendingTypedAgentMention.resetCount
}
}));
return;
}
var latestPendingTypedAgentMention = (_stateKey$getState = stateKey.getState(editorView.state)) === null || _stateKey$getState === void 0 ? void 0 : _stateKey$getState.pendingTypedAgentMention;
if (!latestPendingTypedAgentMention || latestPendingTypedAgentMention.localId !== pendingTypedAgentMention.localId || latestPendingTypedAgentMention.resetCount !== pendingTypedAgentMention.resetCount) {
return;
}
if (shouldDeferPendingTypedAgentMention() && pendingTypedAgentMentionFocusDeferCount < MAX_PENDING_TYPED_AGENT_MENTION_FOCUS_DEFERS) {
pendingTypedAgentMentionFocusDeferCount++;
pendingTypedAgentMentionTimerKey = null;
_schedulePendingTypedAgentMentionTimer(stateKey.getState(editorView.state), {
preserveFocusDeferCount: true
});
return;
}
pendingTypedAgentMentionFocusDeferCount = 0;
editorView.dispatch(editorView.state.tr.setMeta(mentionPluginKey, {
action: ACTIONS.COMMIT_PENDING_TYPED_AGENT_MENTION,
params: {
localId: pendingTypedAgentMention.localId,
resetCount: pendingTypedAgentMention.resetCount
}
}));
}, AGENT_MENTION_INACTIVITY_MS);
};
return {
update: function update(view, prevState) {
var _mentionPluginState$p;
var mentionPluginState = stateKey.getState(view.state);
if (mentionPluginState === stateKey.getState(prevState)) {
return;
}
_schedulePendingTypedAgentMentionTimer(mentionPluginState);
if (editorExperiment('platform_editor_agent_mentions', true) && (mentionPluginState === null || mentionPluginState === 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')) {
resolveAndDispatchPastedAgentMentionName(mentionPluginState.pendingPastedAgentMention.id, mentionPluginState.mentionProvider, pendingResolveMentionIds, view);
}
},
destroy: function destroy() {
clearPendingTypedAgentMentionTimer();
}
};
};
export var agentMentionPluginKey = new PluginKey('agentMention');
export var createAgentMentionPlugin = function createAgentMentionPlugin(_ref7) {
var options = _ref7.options,
pmPluginFactoryParams = _ref7.pmPluginFactoryParams;
return new SafePlugin({
key: agentMentionPluginKey,
state: {
init: function init() {
return {};
},
apply: function apply(tr, pluginState, oldState, newState) {
var result = applyAgentMentionState({
tr: tr,
pluginState: pluginState,
oldState: oldState,
newState: newState,
getIsRovoPanelOpen: options === null || options === void 0 ? void 0 : options.getIsRovoPanelOpen
});
var mentionPluginState = mentionPluginKey.getState(newState);
if (result.hasPublicPluginStateChanged) {
pmPluginFactoryParams.dispatch(mentionPluginKey, _objectSpread(_objectSpread({}, mentionPluginState), result.pluginState));
}
return result.pluginState;
}
},
view: function view(editorView) {
return createAgentMentionPluginView({
editorView: editorView,
getIsRovoPanelOpen: options === null || options === void 0 ? void 0 : options.getIsRovoPanelOpen,
stateKey: agentMentionPluginKey
});
}
});
};