react-markdown, lazy-loaded with the first answer (37 kB gzip chunk, main bundle unchanged). No raw HTML, images dropped, links open in a new tab. The system prompt asks for lists, inline code and fenced code blocks.
58 lines
1.8 KiB
TypeScript
58 lines
1.8 KiB
TypeScript
import ReactMarkdown, { type Components } from "react-markdown"
|
|
|
|
/**
|
|
* An assistant's answer as markdown, compact and on the panel's text size.
|
|
*
|
|
* Raw HTML is never rendered, and images are dropped so an answer cannot make
|
|
* the browser fetch a URL on its own. A link opens in a new tab when clicked.
|
|
*/
|
|
const components: Components = {
|
|
a: ({ node: _, ...props }) => (
|
|
<a
|
|
{...props}
|
|
target="_blank"
|
|
rel="noopener noreferrer"
|
|
className="text-primary underline underline-offset-2"
|
|
/>
|
|
),
|
|
ul: ({ node: _, ...props }) => (
|
|
<ul {...props} className="list-disc space-y-1 pl-5" />
|
|
),
|
|
ol: ({ node: _, ...props }) => (
|
|
<ol {...props} className="list-decimal space-y-1 pl-5" />
|
|
),
|
|
h1: ({ node: _, ...props }) => <h3 {...props} className="font-semibold" />,
|
|
h2: ({ node: _, ...props }) => <h3 {...props} className="font-semibold" />,
|
|
h3: ({ node: _, ...props }) => <h3 {...props} className="font-semibold" />,
|
|
h4: ({ node: _, ...props }) => <h4 {...props} className="font-medium" />,
|
|
blockquote: ({ node: _, ...props }) => (
|
|
<blockquote
|
|
{...props}
|
|
className="border-l-2 border-border pl-3 text-muted-foreground"
|
|
/>
|
|
),
|
|
hr: () => <hr className="border-border" />,
|
|
pre: ({ node: _, ...props }) => (
|
|
<pre
|
|
{...props}
|
|
className="overflow-x-auto rounded-md bg-muted p-2 font-mono text-xs [&>code]:bg-transparent [&>code]:p-0"
|
|
/>
|
|
),
|
|
code: ({ node: _, ...props }) => (
|
|
<code
|
|
{...props}
|
|
className="rounded-sm bg-muted px-1 py-0.5 font-mono text-xs"
|
|
/>
|
|
),
|
|
}
|
|
|
|
export default function Markdown({ text }: { text: string }) {
|
|
return (
|
|
<div className="min-w-0 space-y-2 break-words">
|
|
<ReactMarkdown components={components} disallowedElements={["img"]}>
|
|
{text}
|
|
</ReactMarkdown>
|
|
</div>
|
|
)
|
|
}
|