spb/note-spboucher-ai
Public
1"use client";2import ReactMarkdown from "react-markdown";3import remarkGfm from "remark-gfm";4import remarkMath from "remark-math";5import rehypeKatex from "rehype-katex";6import { cn } from "@/lib/utils";7import { memo } from "react";8/** Markdown renderer for AI output (GFM + LaTeX). [n] citation markers become clickable chips when onCite is provided. */9export const Markdown = memo(function Markdown({ text, className, onCite }: { text: string; className?: string; onCite?: (n: number) => void }) {10 const processed = onCite ? text.replace(/\[(\d{1,2})\]/g, (_m, n) => `[${n}](#cite-${n})`) : text;11 return (12 <div className={cn("prose-md", className)}>13 <ReactMarkdown remarkPlugins={[remarkGfm, remarkMath]} rehypePlugins={[rehypeKatex]} components={{14 a: ({ href, children, ...p }) => { const m = href?.match(/^#cite-(\d+)$/); if (m && onCite) return <button type="button" className="source-ref" onClick={() => onCite(Number(m[1]))}>{children}</button>; return <a href={href} target={href?.startsWith("/") ? undefined : "_blank"} rel="noopener noreferrer" {...p}>{children}</a>; },15 code: ({ className, children, ...p }) => <code className={className} {...p}>{children}</code>,16 }}>{processed}</ReactMarkdown>17 </div>18 );19});20