Files
app/frontend/src/components/Chat/Markdown.tsx
T
stroblme 8f8330c9e0 Assistant answers render as markdown
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.
2026-10-15 17:18:12 +02:00

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>
)
}