SPB Git
2.5 KB · 50 lines tsx
1"use client";2import { memo, useMemo, type ReactNode } from "react";3import ReactMarkdown from "react-markdown";4import remarkGfm from "remark-gfm";5import remarkMath from "remark-math";6import rehypeKatex from "rehype-katex";7import rehypeHighlight from "rehype-highlight";8import { Eye } from "lucide-react";9import { CopyButton } from "../ui";1011const PREVIEWABLE = new Set(["html", "svg", "mermaid", "jsx", "tsx", "react", "markdown", "md", "python", "py", "typescript", "ts"]);1213export interface MarkdownProps { text: string; onPreview?: (code: string, lang: string) => void; className?: string }1415function Code({ className, children, onPreview, ...rest }: { className?: string; children?: ReactNode; onPreview?: MarkdownProps["onPreview"]; node?: unknown }) {16  const lang = /language-([\w+-]+)/.exec(className ?? "")?.[1];17  const raw = String(children ?? "").replace(/\n$/, "");18  if (!lang && !raw.includes("\n")) return <code className={className} {...rest}>{children}</code>;19  return (20    <>21      <div className="code-head not-prose">22        <span>{lang ?? "texte"}</span>23        <span className="flex items-center gap-1">24          {lang && PREVIEWABLE.has(lang) && onPreview && <button type="button" onClick={() => onPreview(raw, lang)} className="inline-flex items-center gap-1 rounded-md px-1.5 py-1 text-xs text-fg-2 hover:bg-surface-2 hover:text-fg"><Eye className="h-3.5 w-3.5" /> Aperçu</button>}25          <CopyButton text={raw} />26        </span>27      </div>28      <pre><code className={className} {...rest}>{children}</code></pre>29    </>30  );31}3233export const Markdown = memo(function Markdown({ text, onPreview, className }: MarkdownProps) {34  // Normalize \[ \] and \( \) LaTeX delimiters that some models emit35  const src = useMemo(() => text.replace(/\\\[((?:.|\n)*?)\\\]/g, (_, m) => `$$${m}$$`).replace(/\\\((.+?)\\\)/g, (_, m) => `$${m}$`), [text]);36  return (37    <div className={`prose-ai ${className ?? ""}`}>38      <ReactMarkdown remarkPlugins={[remarkGfm, remarkMath]} rehypePlugins={[rehypeKatex, [rehypeHighlight, { ignoreMissing: true, detect: false }]]}39        components={{40          code: (p) => <Code {...p} onPreview={onPreview} />,41          pre: ({ children }) => <>{children}</>,42          a: ({ href, children }) => <a href={href} target="_blank" rel="noopener noreferrer">{children}</a>,43          table: ({ children }) => <div className="overflow-x-auto"><table>{children}</table></div>,44        }}>45        {src}46      </ReactMarkdown>47    </div>48  );49});50
Files · main
Menu

Theme