refactor(chat): enhance UserMessage component with mention parsing and segment rendering for improved message display

This commit is contained in:
Anish Sarkar 2026-05-01 04:02:24 +05:30
parent 511f4fde64
commit 8b4f136668
3 changed files with 97 additions and 80 deletions

View file

@ -0,0 +1,54 @@
import type { MentionedDocumentInfo } from "@/atoms/chat/mentioned-documents.atom";
export type MentionSegment =
| { type: "text"; value: string; start: number }
| { type: "mention"; doc: MentionedDocumentInfo; start: number };
/**
* Tokenizes a user message into text and `@mention` segments.
*
* Pure: no React, no DOM, no side effects. Safe to unit-test and reuse.
*
* Mentions are matched greedily by longest title first so that a longer title
* (e.g. `@Project Roadmap`) is never shadowed by a shorter prefix
* (e.g. `@Project`).
*/
export function parseMentionSegments(
text: string,
docs: ReadonlyArray<MentionedDocumentInfo>
): MentionSegment[] {
if (text.length === 0) return [];
if (docs.length === 0) return [{ type: "text", value: text, start: 0 }];
const tokens = docs
.map((doc) => ({ doc, token: `@${doc.title}` }))
.sort((a, b) => b.token.length - a.token.length);
const segments: MentionSegment[] = [];
let i = 0;
let buffer = "";
let bufferStart = 0;
while (i < text.length) {
const tokenMatch = tokens.find(({ token }) => text.startsWith(token, i));
if (tokenMatch) {
if (buffer) {
segments.push({ type: "text", value: buffer, start: bufferStart });
buffer = "";
}
segments.push({ type: "mention", doc: tokenMatch.doc, start: i });
i += tokenMatch.token.length;
bufferStart = i;
continue;
}
if (!buffer) bufferStart = i;
buffer += text[i];
i += 1;
}
if (buffer) {
segments.push({ type: "text", value: buffer, start: bufferStart });
}
return segments;
}