"use client"; /** * MentionText — renders user-authored plain text with `@handle` tokens turned * into profile links. * * The single place that decides what a mention looks like. Shared by comment * bodies and profile bios so the two can't drift apart, and so the * "@handle" grammar lives next to the regex the composer * (`MentionTextarea`) uses to detect it. * * Input is always PLAIN TEXT, never HTML — we split on the token and emit * React nodes, so nothing user-supplied is ever interpreted as markup. * `whitespace-pre-line` on the wrapper preserves the author's line breaks * (bios are multi-line) without needing
injection. */ import Link from "next/link"; import { cn } from "@/lib/utils"; /** Capturing group keeps the delimiters, so split() yields text and tokens * alternately. Handle charset matches MentionTextarea's MENTION_RE. */ const MENTION_TOKEN = /(@[A-Za-z0-9_]+)/g; /** Split text into plain runs + clickable @handle links. Exported for callers * that need the nodes without the wrapper element. */ export function renderMentions(text: string) { return text.split(MENTION_TOKEN).map((part, i) => { if (part.length > 1 && part[0] === "@") { return ( {part} ); } return part; }); } export function MentionText({ text, className, }: { text: string; className?: string; }) { return ( {renderMentions(text)} ); }