profileShare

rasmusjy / roundtable

Read-only snapshot

No repository description.

main default branch 181 files Expires Sep 13, 2026, 9:06 AM
rich-text.tsx 1,198 bytes
1 import { Fragment } from 'react';
2 import { cn } from '@/lib/utils';
3
4 /**
5 * Minimal, XSS-safe renderer for model output.
6 *
7 * Model text is untrusted, so we never set innerHTML - we build React nodes.
8 * We support just enough Markdown to read well: paragraphs, `**bold**`, and
9 * `inline code`.
10 */
11 export function RichText({ text, className }: { text: string; className?: string }) {
12 const paragraphs = text.split(/\n{2,}/);
13 return (
14 <div className={cn('prose-debate', className)}>
15 {paragraphs.map((para, i) => (
16 <p key={i}>{renderInline(para)}</p>
17 ))}
18 </div>
19 );
20 }
21
22 function renderInline(text: string): React.ReactNode {
23 // Split on **bold** and `code`, keeping delimiters.
24 const parts = text.split(/(\*\*[^*]+\*\*|`[^`]+`)/g);
25 return parts.map((part, i) => {
26 if (part.startsWith('**') && part.endsWith('**')) {
27 return <strong key={i}>{part.slice(2, -2)}</strong>;
28 }
29 if (part.startsWith('`') && part.endsWith('`')) {
30 return (
31 <code key={i} className="rounded bg-muted px-1 py-0.5 font-mono text-[0.85em]">
32 {part.slice(1, -1)}
33 </code>
34 );
35 }
36 return <Fragment key={i}>{part}</Fragment>;
37 });
38 }
39