/** * Shared mention trigger detection. * * Detects when the user has typed "@" followed by an optional query * at the cursor position, and returns the match info for opening * a mention panel. */ /** Characters that BLOCK @ trigger (only @ itself, to prevent @@). */ const BLOCKED_BEFORE_AT = /[@]/; export interface MentionTriggerMatch { /** Index of the @ character in the full text. */ atIndex: number; /** The query text after @ (may be empty). */ query: string; } /** * Check whether the text before the cursor contains an active @ mention trigger. * * Rules: * - @ must be at position 0, or preceded by a boundary character * (whitespace, Chinese/English punctuation, brackets). * - The query (text after @ up to the cursor) must not contain * any boundary character or space. * - Returns null if no trigger is found. */ export function detectMentionTrigger(textBeforeCursor: string): MentionTriggerMatch | null { const atIdx = textBeforeCursor.lastIndexOf("@"); if (atIdx < 0) return null; // @ must be at start or not preceded by another @ if (atIdx > 0 && BLOCKED_BEFORE_AT.test(textBeforeCursor[atIdx - 1])) { return null; } const query = textBeforeCursor.slice(atIdx + 1); // Query must not contain spaces or punctuation that would break a mention token if (/[\s,。、;:!??!.,;:(){}[\]<>""''《》【】@]/.test(query)) { return null; } return { atIndex: atIdx, query }; } /** Token pattern for @图片1, @视频1, @文本1, @音频1, @附件1, @素材1, etc. */ export const MENTION_TOKEN_RE = /@(?:图片|视频|文本|音频|附件|素材)\d+/g;