fix(desktop): highlight full multi-word display names in composer ove… (#163)

This commit is contained in:
Wes
2026-03-23 09:25:23 -07:00
committed by GitHub
parent 4603d37ae7
commit 035ff1d650
5 changed files with 121 additions and 53 deletions

View File

@@ -0,0 +1,23 @@
/**
* Escape special regex characters in a string.
*/
export function escapeRegExp(str: string): string {
return str.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
}
/**
* Build a regex that matches @mentions, trying known multi-word names first
* (longest-first to avoid partial matches), then falling back to @\S+.
*/
export function buildMentionPattern(mentionNames: string[]): RegExp {
const sorted = [...new Set(mentionNames)]
.filter((name) => name.trim().length > 0)
.sort((a, b) => b.length - a.length);
if (sorted.length === 0) {
return /@\S+/g;
}
const nameAlternatives = sorted.map((name) => escapeRegExp(name)).join("|");
return new RegExp(`@(?:${nameAlternatives}|\\S+)`, "g");
}

View File

@@ -7,6 +7,8 @@
* falls back to the generic `@\S+` pattern for unknown mentions.
*/
import { buildMentionPattern } from "./mentionPattern";
type RemarkMentionsOptions = {
mentionNames?: string[];
};
@@ -42,25 +44,6 @@ function walkChildren(node: any, mentionPattern: RegExp) {
}
}
function escapeRegExp(str: string): string {
return str.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
}
function buildMentionPattern(mentionNames: string[]): RegExp {
// Deduplicate and sort longest-first so "John Doe" is matched before "John"
const sorted = [...new Set(mentionNames)]
.filter((name) => name.trim().length > 0)
.sort((a, b) => b.length - a.length);
if (sorted.length === 0) {
return /@\S+/g;
}
// Build alternation: try known names first, then fall back to @\S+
const nameAlternatives = sorted.map((name) => escapeRegExp(name)).join("|");
return new RegExp(`@(?:${nameAlternatives}|\\S+)`, "g");
}
function splitMentions(text: string, mentionPattern: RegExp) {
// Reset lastIndex — the pattern is reused across text nodes with the `g` flag
mentionPattern.lastIndex = 0;