From 124cb7dab7abf9f87860a43b10c9324aec6a3877 Mon Sep 17 00:00:00 2001
From: CanisMinor <i@canisminor.cc>
Date: Mon, 24 Jun 2024 14:30:27 +0800
Subject: [PATCH] =?UTF-8?q?=F0=9F=90=9B=20fix:=20Fix=20LaTeX=20Render=20(#?=
 =?UTF-8?q?168)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

- fix https://github.com/lobehub/lobe-chat/issues/2911
- fix https://github.com/lobehub/lobe-chat/issues/2651
- fix https://github.com/lobehub/lobe-chat/issues/2070
- fix https://github.com/lobehub/lobe-chat/issues/2494
- fix https://github.com/lobehub/lobe-chat/issues/1620
- fix https://github.com/lobehub/lobe-chat/issues/1787
- fix https://github.com/lobehub/lobe-chat/issues/2651
- fix https://github.com/lobehub/lobe-chat/issues/2494
- fix https://github.com/lobehub/lobe-chat/issues/1620
- fix https://github.com/lobehub/lobe-chat/issues/1471
- fix https://github.com/lobehub/lobe-chat/issues/1110
- fix https://github.com/lobehub/lobe-chat/issues/676
- fix https://github.com/lobehub/lobe-chat/issues/485
- fix https://github.com/lobehub/lobe-chat/issues/144
---
 package.json           |  2 +-
 src/Markdown/index.tsx | 15 ++++++++++++---
 src/Markdown/utils.ts  | 34 ++++++++++++++++++++++++++++++++++
 3 files changed, 47 insertions(+), 4 deletions(-)
 create mode 100644 src/Markdown/utils.ts

diff --git a/package.json b/package.json
index 8a74d209..5d24effc 100644
--- a/package.json
+++ b/package.json
@@ -139,7 +139,7 @@
     "@types/react": "18.2.40",
     "@types/react-dom": "^18.3.0",
     "@types/uuid": "^9.0.8",
-    "@vitest/coverage-v8": "^1.6.0",
+    "@vitest/coverage-v8": "~1.2.2",
     "antd-style": "^3.6.2",
     "babel-plugin-antd-style": "^1.0.4",
     "commitlint": "^19.3.0",
diff --git a/src/Markdown/index.tsx b/src/Markdown/index.tsx
index 9cbd6e73..1edf5261 100644
--- a/src/Markdown/index.tsx
+++ b/src/Markdown/index.tsx
@@ -21,6 +21,7 @@ import { CodeFullFeatured, CodeLite } from './CodeBlock';
 import type { TypographyProps } from './Typography';
 import { useStyles as useMarkdownStyles } from './markdown.style';
 import { useStyles } from './style';
+import { escapeBrackets, escapeDollarNumber, escapeMhchem } from './utils';
 
 export interface MarkdownProps extends TypographyProps {
   allowHtml?: boolean;
@@ -33,6 +34,7 @@ export interface MarkdownProps extends TypographyProps {
     video?: Partial<VideoProps>;
   };
   enableImageGallery?: boolean;
+  enableLatex?: boolean;
   fullFeaturedCodeBlock?: boolean;
   onDoubleClick?: () => void;
   style?: CSSProperties;
@@ -46,6 +48,7 @@ const Markdown = memo<MarkdownProps>(
     style,
     fullFeaturedCodeBlock,
     onDoubleClick,
+    enableLatex = true,
     enableImageGallery = true,
     componentProps,
     allowHtml,
@@ -60,6 +63,11 @@ const Markdown = memo<MarkdownProps>(
     const { styles: mdStyles } = useMarkdownStyles({ fontSize, headerMultiple, marginMultiple });
     const isChatMode = variant === 'chat';
 
+    const escapedContent = useMemo(() => {
+      if (!enableLatex) return children;
+      return escapeMhchem(escapeBrackets(escapeDollarNumber(children)));
+    }, [children, enableLatex]);
+
     const components: Components = useMemo(
       () => ({
         a: (props: any) => <Link {...props} {...componentProps?.a} />,
@@ -88,11 +96,12 @@ const Markdown = memo<MarkdownProps>(
     );
 
     const rehypePlugins = useMemo(
-      () => [allowHtml && rehypeRaw, rehypeKatex].filter(Boolean) as any,
+      () => [allowHtml && rehypeRaw, enableLatex && rehypeKatex].filter(Boolean) as any,
       [allowHtml],
     );
     const remarkPlugins = useMemo(
-      () => [remarkGfm, remarkMath, isChatMode && remarkBreaks].filter(Boolean) as any,
+      () =>
+        [remarkGfm, enableLatex && remarkMath, isChatMode && remarkBreaks].filter(Boolean) as any,
       [isChatMode],
     );
 
@@ -128,7 +137,7 @@ const Markdown = memo<MarkdownProps>(
             remarkPlugins={remarkPlugins}
             {...rest}
           >
-            {children}
+            {escapedContent}
           </ReactMarkdown>
         </ImageGallery>
       </article>
diff --git a/src/Markdown/utils.ts b/src/Markdown/utils.ts
new file mode 100644
index 00000000..e40ba463
--- /dev/null
+++ b/src/Markdown/utils.ts
@@ -0,0 +1,34 @@
+export function escapeDollarNumber(text: string) {
+  let escapedText = '';
+
+  for (let i = 0; i < text.length; i += 1) {
+    let char = text[i];
+    const nextChar = text[i + 1] || ' ';
+
+    if (char === '$' && nextChar >= '0' && nextChar <= '9') {
+      char = '\\$';
+    }
+
+    escapedText += char;
+  }
+
+  return escapedText;
+}
+
+export function escapeBrackets(text: string) {
+  const pattern = /(```[\S\s]*?```|`.*?`)|\\\[([\S\s]*?[^\\])\\]|\\\((.*?)\\\)/g;
+  return text.replaceAll(pattern, (match, codeBlock, squareBracket, roundBracket) => {
+    if (codeBlock) {
+      return codeBlock;
+    } else if (squareBracket) {
+      return `$$${squareBracket}$$`;
+    } else if (roundBracket) {
+      return `$${roundBracket}$`;
+    }
+    return match;
+  });
+}
+
+export function escapeMhchem(text: string) {
+  return text.replaceAll('$\\ce{', '$\\\\ce{').replaceAll('$\\pu{', '$\\\\pu{');
+}