export interface MentionUser { id: number name: string color: string | null } export function activeMention(text: string, caret: number) { const before = text.slice(0, caret) const start = before.lastIndexOf('@') if (start < 0 || (start > 0 && !/[\s([{]/.test(before[start - 1]!))) return null const query = before.slice(start + 1) if (query.length > 50 || /[\r\n@]/.test(query)) return null return { start, end: caret, query } } export function mentionParts(text: string, users: MentionUser[]) { const names = [...users].sort((a, b) => b.name.length - a.name.length) const parts: { text: string, user?: MentionUser }[] = [] let last = 0 for (let i = 0; i < text.length; i++) { if (text[i] !== '@' || (i > 0 && !/[\s([{]/.test(text[i - 1]!))) continue const user = names.find(u => text.slice(i + 1, i + 1 + u.name.length).toLowerCase() === u.name.toLowerCase() && !/[\p{L}\p{N}_-]/u.test(text[i + 1 + u.name.length] ?? '')) if (!user) continue if (i > last) parts.push({ text: text.slice(last, i) }) last = i + 1 + user.name.length parts.push({ text: text.slice(i, last), user }) i = last - 1 } if (last < text.length) parts.push({ text: text.slice(last) }) return parts } export function mentionedUserIds(content: string, users: MentionUser[]) { return new Set(content.split(/https?:\/\/[^\s<>"']+/g) .flatMap(text => mentionParts(text, users).flatMap(part => part.user ? [part.user.id] : []))) }