89 lines
2.6 KiB
TypeScript
89 lines
2.6 KiB
TypeScript
import "katex/dist/katex.min.css";
|
|
import { FC, memo } from "react";
|
|
import ReactMarkdown, { Options } from "react-markdown";
|
|
import rehypeKatex from "rehype-katex";
|
|
import remarkGfm from "remark-gfm";
|
|
import remarkMath from "remark-math";
|
|
|
|
import { CodeBlock } from "./codeblock";
|
|
|
|
const MemoizedReactMarkdown: FC<Options> = memo(
|
|
ReactMarkdown,
|
|
(prevProps, nextProps) =>
|
|
prevProps.children === nextProps.children &&
|
|
prevProps.className === nextProps.className,
|
|
);
|
|
|
|
const preprocessLaTeX = (content: string) => {
|
|
// Replace block-level LaTeX delimiters \[ \] with $$ $$
|
|
const blockProcessedContent = content.replace(
|
|
/\\\[([\s\S]*?)\\\]/g,
|
|
(_, equation) => `$$${equation}$$`,
|
|
);
|
|
// Replace inline LaTeX delimiters \( \) with $ $
|
|
const inlineProcessedContent = blockProcessedContent.replace(
|
|
/\\\[([\s\S]*?)\\\]/g,
|
|
(_, equation) => `$${equation}$`,
|
|
);
|
|
return inlineProcessedContent;
|
|
};
|
|
|
|
const preprocessMedia = (content: string) => {
|
|
// Remove `sandbox:` from the beginning of the URL
|
|
// to fix OpenAI's models issue appending `sandbox:` to the relative URL
|
|
return content.replace(/(sandbox|attachment|snt):/g, "");
|
|
};
|
|
|
|
const preprocessContent = (content: string) => {
|
|
return preprocessMedia(preprocessLaTeX(content));
|
|
};
|
|
|
|
export default function Markdown({ content }: { content: string }) {
|
|
const processedContent = preprocessContent(content);
|
|
|
|
return (
|
|
<MemoizedReactMarkdown
|
|
className="prose dark:prose-invert prose-p:leading-relaxed prose-pre:p-0 break-words custom-markdown"
|
|
remarkPlugins={[remarkGfm, remarkMath]}
|
|
rehypePlugins={[rehypeKatex as any]}
|
|
components={{
|
|
p({ children }) {
|
|
return <p className="mb-2 last:mb-0">{children}</p>;
|
|
},
|
|
code({ node, inline, className, children, ...props }) {
|
|
if (children.length) {
|
|
if (children[0] == "▍") {
|
|
return (
|
|
<span className="mt-1 animate-pulse cursor-default">▍</span>
|
|
);
|
|
}
|
|
|
|
children[0] = (children[0] as string).replace("`▍`", "▍");
|
|
}
|
|
|
|
const match = /language-(\w+)/.exec(className || "");
|
|
|
|
if (inline) {
|
|
return (
|
|
<code className={className} {...props}>
|
|
{children}
|
|
</code>
|
|
);
|
|
}
|
|
|
|
return (
|
|
<CodeBlock
|
|
key={Math.random()}
|
|
language={(match && match[1]) || ""}
|
|
value={String(children).replace(/\n$/, "")}
|
|
{...props}
|
|
/>
|
|
);
|
|
},
|
|
}}
|
|
>
|
|
{processedContent}
|
|
</MemoizedReactMarkdown>
|
|
);
|
|
}
|