sempero / src /components /markdown.tsx
armand0e's picture
feat(markdown): add Prism-based syntax highlighting (rehype-prism-plus) and global theme; finalize LaTeX pipeline
ecbf6f1
"use client";
import ReactMarkdown from "react-markdown";
import remarkGfm from "remark-gfm";
import remarkMath from "remark-math";
import rehypeKatex from "rehype-katex";
import rehypePrism from "rehype-prism-plus";
import type { Components } from "react-markdown";
import { cn } from "@/lib/utils";
interface MarkdownProps {
children: string;
className?: string;
}
export function Markdown({ children, className }: MarkdownProps) {
const normalizeMathDelimiters = (src: string) => {
const segments = src.split(/(```[\s\S]*?```)/g);
return segments
.map((seg) => (seg.startsWith("```") ? seg : seg.replace(/\$\$\$/g, "$$")))
.join("");
};
const content = normalizeMathDelimiters(children);
const components: Components = {
pre: ({ children, ...props }) => (
<pre {...props} className={cn("code-block", (props as any).className)}>
{children}
</pre>
),
code: ({ children, className: codeClassName, ...props }) => {
const isInline = !codeClassName;
if (isInline) {
return (
<code {...props} className={cn("inline-code", (props as any).className)}>
{children}
</code>
);
}
return (
<code {...props} className={codeClassName}>
{children}
</code>
);
},
p: ({ children }) => <p>{children}</p>,
ul: ({ children }) => <ul>{children}</ul>,
ol: ({ children }) => <ol>{children}</ol>,
li: ({ children }) => <li>{children}</li>,
h1: ({ children }) => <h1>{children}</h1>,
h2: ({ children }) => <h2>{children}</h2>,
h3: ({ children }) => <h3>{children}</h3>,
h4: ({ children }) => <h4>{children}</h4>,
blockquote: ({ children }) => <blockquote>{children}</blockquote>,
a: ({ children, href }) => (
<a href={href} target="_blank" rel="noopener noreferrer">
{children}
</a>
),
strong: ({ children }) => <strong>{children}</strong>,
em: ({ children }) => <em>{children}</em>,
hr: () => <hr />,
table: ({ children }) => (
<div className="table-wrapper">
<table>{children}</table>
</div>
),
thead: ({ children }) => <thead>{children}</thead>,
tbody: ({ children }) => <tbody>{children}</tbody>,
tr: ({ children }) => <tr>{children}</tr>,
th: ({ children }) => <th>{children}</th>,
td: ({ children }) => <td>{children}</td>,
};
return (
<ReactMarkdown
className={cn("markdown-content", className)}
remarkPlugins={[remarkGfm, remarkMath]}
rehypePlugins={[rehypeKatex, rehypePrism]}
components={components}
>
{content}
</ReactMarkdown>
);
}