Add 'repo' to MentionType across all editor types, include repos in the @ trigger pool, add repo badge (green chip), Repos section in the mention dropdown, and MentionBadge styles. Wire projectRepos from room context into IMEditor mentionItems.
108 lines
3.0 KiB
TypeScript
108 lines
3.0 KiB
TypeScript
/**
|
||
* Unified mention serialization and parsing utilities.
|
||
* Shared across room chat, issues, and PR comments.
|
||
*/
|
||
|
||
export type MentionType =
|
||
| 'user'
|
||
| 'channel'
|
||
| 'ai'
|
||
| 'repo'
|
||
| 'command'
|
||
| 'special_here'
|
||
| 'special_channel';
|
||
|
||
export const MENTION_TYPES: MentionType[] = [
|
||
'user',
|
||
'channel',
|
||
'ai',
|
||
'repo',
|
||
'command',
|
||
'special_here',
|
||
'special_channel',
|
||
];
|
||
|
||
export interface MentionSpan {
|
||
type: MentionType;
|
||
id: string;
|
||
label: string;
|
||
}
|
||
|
||
export interface ExtractedMentions {
|
||
safeContent: string;
|
||
mentions: MentionSpan[];
|
||
}
|
||
|
||
/**
|
||
* Parse @[type:id:label] tokens from message content.
|
||
* Returns the safe content (with mentions replaced by placeholders) and the parsed mention list.
|
||
*
|
||
* Placeholders use zero-width spaces to prevent markdown from interpreting the text.
|
||
*/
|
||
const PLACEHOLDER_PREFIX = 'MENTION_';
|
||
const PLACEHOLDER_SUFFIX = '';
|
||
|
||
const MENTION_RE = /@\[([a-z_]+):([^:\]]+):([^\]]+)\]/g;
|
||
const PLACEHOLDER_RE = /MENTION_(\d+)/g;
|
||
|
||
export function extractMentions(content: string): ExtractedMentions {
|
||
const mentions: MentionSpan[] = [];
|
||
const safeContent = content.replace(MENTION_RE, (_match, type, id, label) => {
|
||
const idx = mentions.length;
|
||
mentions.push({ type: type as MentionType, id, label });
|
||
return `${PLACEHOLDER_PREFIX}${idx}${PLACEHOLDER_SUFFIX}`;
|
||
});
|
||
return { safeContent, mentions };
|
||
}
|
||
|
||
export function restoreMentions(
|
||
text: string,
|
||
mentions: MentionSpan[],
|
||
): string {
|
||
return text.replace(PLACEHOLDER_RE, (match, idxStr) => {
|
||
const idx = parseInt(idxStr, 10);
|
||
const m = mentions[idx];
|
||
return m ? `@[${m.type}:${m.id}:${m.label}]` : match;
|
||
});
|
||
}
|
||
|
||
/** Build a mention token string for serialization. */
|
||
export function serializeMention(type: MentionType, id: string, label: string): string {
|
||
return `@[${type}:${id}:${label}]`;
|
||
}
|
||
|
||
/** Build a mention token from a user mention. */
|
||
export function serializeUserMention(userId: string, displayName: string): string {
|
||
return serializeMention('user', userId, displayName);
|
||
}
|
||
|
||
/** Build a mention token from a channel mention. */
|
||
export function serializeChannelMention(channelId: string, channelName: string): string {
|
||
return serializeMention('channel', channelId, channelName);
|
||
}
|
||
|
||
/** Build a mention token from an AI mention. */
|
||
export function serializeAiMention(aiId: string, aiName: string): string {
|
||
return serializeMention('ai', aiId, aiName);
|
||
}
|
||
|
||
/** Build a mention token from a repo mention. */
|
||
export function serializeRepoMention(repoName: string, label?: string): string {
|
||
return serializeMention('repo', repoName, label ?? repoName);
|
||
}
|
||
|
||
/** Build a mention token from a command mention. */
|
||
export function serializeCommandMention(commandId: string, commandName: string): string {
|
||
return serializeMention('command', commandId, commandName);
|
||
}
|
||
|
||
/** Build a mention token for @here. */
|
||
export function serializeHereMention(): string {
|
||
return '@[special_here:here:here]';
|
||
}
|
||
|
||
/** Build a mention token for @channel. */
|
||
export function serializeChannelBroadcastMention(): string {
|
||
return '@[special_channel:channel:channel]';
|
||
}
|