diff --git a/app/globals.css b/app/globals.css
index 3761638..df2d29e 100644
--- a/app/globals.css
+++ b/app/globals.css
@@ -19,6 +19,33 @@ body {
inset 0 1px 0 0 #ffffff52;
}
+.markdown-body .katex {
+ font-size: 1.1em;
+}
+
+.markdown-body .katex-display {
+ overflow-x: auto;
+ overflow-y: hidden;
+ padding-top: 0.5em;
+ padding-bottom: 0.5em;
+ margin-top: 1em;
+ margin-bottom: 1em;
+}
+
+.markdown-body .katex-display > .katex {
+ font-size: 1.21em;
+}
+
+.markdown-body .katex-display > .katex > .katex-html {
+ display: block;
+ position: relative;
+}
+
+.markdown-body .katex-display > .katex > .katex-html > .tag {
+ position: absolute;
+ right: 0;
+}
+
@layer utilities {
/* Hide scrollbar for Chrome, Safari and Opera */
diff --git a/app/layout.tsx b/app/layout.tsx
index 89406e9..8069a26 100644
--- a/app/layout.tsx
+++ b/app/layout.tsx
@@ -1,4 +1,5 @@
import "./globals.css";
+import 'katex/dist/katex.min.css';
import { Metadata, Viewport } from "next";
import { Toaster } from "sonner";
import { Inter, Instrument_Serif, IBM_Plex_Mono } from 'next/font/google';
diff --git a/app/search/page.tsx b/app/search/page.tsx
index 70e10c4..81c1932 100644
--- a/app/search/page.tsx
+++ b/app/search/page.tsx
@@ -1,5 +1,6 @@
/* eslint-disable @next/next/no-img-element */
"use client";
+import 'katex/dist/katex.min.css';
import
React,
@@ -15,9 +16,9 @@ React,
import ReactMarkdown, { Components } from 'react-markdown';
import Marked, { ReactRenderer } from 'marked-react';
import katex from 'katex';
+import Latex from 'react-latex-next';
import { track } from '@vercel/analytics';
import { useSearchParams } from 'next/navigation';
-import 'katex/dist/katex.min.css';
import { useChat } from 'ai/react';
import { ToolInvocation } from 'ai';
import { toast } from 'sonner';
@@ -1624,21 +1625,12 @@ The o1-mini is a new OpenAI model that is optimized for reasoning tasks. Current
return metadata;
}, [metadataCache]);
- const renderEquation = (equation: string): string => {
- try {
- return katex.renderToString(equation, {
- throwOnError: false,
- displayMode: true,
- strict: false,
- trust: true,
- macros: {
- "\\,": "\\:"
- }
- });
- } catch (error) {
- console.error("KaTeX rendering error:", error);
- return equation;
- }
+ const preprocessContent = (text: string) => {
+ // Replace block-level LaTeX
+ text = text.replace(/\$\$(.*?)\$\$/g, '\\[$1\\]');
+ // Replace bracket-enclosed LaTeX
+ text = text.replace(/\[(.*?)\]/g, '\\[$1\\]');
+ return text;
};
const CodeBlock = ({ language, children }: { language: string | undefined; children: string }) => {
@@ -1721,10 +1713,6 @@ The o1-mini is a new OpenAI model that is optimized for reasoning tasks. Current
)}
-
- {href}
-
-
);
};
@@ -1753,27 +1741,50 @@ The o1-mini is a new OpenAI model that is optimized for reasoning tasks. Current
);
};
+ const latexMacros = {
+ "\\display": "\\displaystyle",
+ };
+
const renderer: Partial = {
paragraph(children) {
- if (typeof children === 'string') {
- const parts = children.split(/(\[.*?\])/g);
- return (
-
- {parts.map((part, index) => {
- if (part.startsWith('[') && part.endsWith(']')) {
- const equation = part.slice(1, -1);
- return (
-
- );
- }
- return {part};
- })}
-
- );
- }
- return {children}
;
+ return (
+
+ {React.Children.map(children, (child) => {
+ if (typeof child === 'string') {
+ // Split the string to handle inline and display equations separately
+ const parts = child.split(/(\\\[.*?\\\]|\$.*?\$)/gs);
+ return parts.map((part, index) => {
+ if (part.startsWith('\\[') && part.endsWith('\\]')) {
+ // Display mode equation
+ return (
+
+ {part}
+
+ );
+ } else if (part.startsWith('$') && part.endsWith('$')) {
+ // Inline equation
+ return (
+
+ {part}
+
+ );
+ } // add $$ for display mode equations
+ else if (part.startsWith('$$') && part.endsWith('$$')) {
+ // Display mode equation
+ return (
+
+ {part}
+
+ );
+ }
+ // Regular text
+ return part;
+ });
+ }
+ return child;
+ })}
+
+ );
},
code(children, language) {
return {String(children)};
@@ -1787,7 +1798,7 @@ The o1-mini is a new OpenAI model that is optimized for reasoning tasks. Current
);
}
- return isValidUrl(href) ? renderHoverCard(href, text) : {text};
+ return isValidUrl(href) ? renderHoverCard(href, text) : {text};
},
heading(children, level) {
const HeadingTag = `h${level}` as keyof JSX.IntrinsicElements;
@@ -1796,19 +1807,21 @@ The o1-mini is a new OpenAI model that is optimized for reasoning tasks. Current
},
list(children, ordered) {
const ListTag = ordered ? 'ol' : 'ul';
- return {children};
+ return {children};
},
listItem(children) {
- return {children};
+ return {children};
},
blockquote(children) {
return {children}
;
},
};
+ const preprocessedContent = useMemo(() => preprocessContent(content), [content]);
+
return (
-
-
{content}
+
+ {preprocessedContent}
);
};
diff --git a/package.json b/package.json
index ca46e31..8a13168 100644
--- a/package.json
+++ b/package.json
@@ -53,6 +53,7 @@
"openai": "^4.56.0",
"react": "^18",
"react-dom": "^18",
+ "react-latex-next": "^3.0.0",
"react-markdown": "^9.0.1",
"react-syntax-highlighter": "^15.5.0",
"react-tweet": "^3.2.1",
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index cf4c381..41f5425 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -85,7 +85,7 @@ dependencies:
version: 1.4.0
ai:
specifier: latest
- version: 3.3.40(openai@4.56.0)(react@18.3.1)(svelte@4.2.18)(vue@3.4.35)(zod@3.23.8)
+ version: 3.3.41(openai@4.56.0)(react@18.3.1)(svelte@4.2.18)(vue@3.4.35)(zod@3.23.8)
anthropic-vertex-ai:
specifier: ^1.0.0
version: 1.0.0(zod@3.23.8)
@@ -137,6 +137,9 @@ dependencies:
react-dom:
specifier: ^18
version: 18.3.1(react@18.3.1)
+ react-latex-next:
+ specifier: ^3.0.0
+ version: 3.0.0(react-dom@18.3.1)(react@18.3.1)
react-markdown:
specifier: ^9.0.1
version: 9.0.1(@types/react@18.3.3)(react@18.3.1)
@@ -424,8 +427,8 @@ packages:
zod-to-json-schema: 3.23.2(zod@3.23.8)
dev: false
- /@ai-sdk/vue@0.0.49(vue@3.4.35)(zod@3.23.8):
- resolution: {integrity: sha512-GLjk5uhn0dA8iXpqdF91NyOw+VCgDIo22zrdkRtDg+nLaqkFSjgdDLAp7CL+ihW4F0/IkpZym3j0lFi9LiCjZA==}
+ /@ai-sdk/vue@0.0.50(vue@3.4.35)(zod@3.23.8):
+ resolution: {integrity: sha512-eIWfxqpKwRdL3rxJMg1HDJcjfugFJGg4P934Tl69S7UCot2/U4BPZoESVJQFroS1elbKHaMRgv0ZJt1ddWQPjQ==}
engines: {node: '>=18'}
peerDependencies:
vue: ^3.3.4
@@ -1921,8 +1924,8 @@ packages:
humanize-ms: 1.2.1
dev: false
- /ai@3.3.40(openai@4.56.0)(react@18.3.1)(svelte@4.2.18)(vue@3.4.35)(zod@3.23.8):
- resolution: {integrity: sha512-EqCrKHKO0TIsZANJEO3QY9uNXiZC9owhpE/jn9Yt83ET3qPrdIi3zbx/MXX8F6ivmKt2vzoYzR4a5Z7l9kJLJQ==}
+ /ai@3.3.41(openai@4.56.0)(react@18.3.1)(svelte@4.2.18)(vue@3.4.35)(zod@3.23.8):
+ resolution: {integrity: sha512-unWUqw0hnZo0irhdedTv8ef7IEiySBCO3zjPxx1/k0kI1G0whKYq8l83k/LzqShLekc2Qg3gyyhdEO+39ptegw==}
engines: {node: '>=18'}
peerDependencies:
openai: ^4.42.0
@@ -1948,7 +1951,7 @@ packages:
'@ai-sdk/solid': 0.0.47(zod@3.23.8)
'@ai-sdk/svelte': 0.0.49(svelte@4.2.18)(zod@3.23.8)
'@ai-sdk/ui-utils': 0.0.44(zod@3.23.8)
- '@ai-sdk/vue': 0.0.49(vue@3.4.35)(zod@3.23.8)
+ '@ai-sdk/vue': 0.0.50(vue@3.4.35)(zod@3.23.8)
'@opentelemetry/api': 1.9.0
eventsource-parser: 1.1.2
json-schema: 0.4.0
@@ -5347,6 +5350,18 @@ packages:
/react-is@16.13.1:
resolution: {integrity: sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==}
+ /react-latex-next@3.0.0(react-dom@18.3.1)(react@18.3.1):
+ resolution: {integrity: sha512-x70f1b1G7TronVigsRgKHKYYVUNfZk/3bciFyYX1lYLQH2y3/TXku3+5Vap8MDbJhtopePSYBsYWS6jhzIdz+g==}
+ engines: {node: '>=12', npm: '>=5'}
+ peerDependencies:
+ react: ^15.0.0 || ^16.0.0 || ^17.0.0 || ^18.0.0
+ react-dom: ^15.0.0 || ^16.0.0 || ^17.0.0 || ^18.0.0
+ dependencies:
+ katex: 0.16.11
+ react: 18.3.1
+ react-dom: 18.3.1(react@18.3.1)
+ dev: false
+
/react-markdown@9.0.1(@types/react@18.3.3)(react@18.3.1):
resolution: {integrity: sha512-186Gw/vF1uRkydbsOIkcGXw7aHq0sZOCRFFjGrr7b9+nVZg4UfA4enXCaxm4fUzecU38sWfrNDitGhshuU7rdg==}
peerDependencies:
diff --git a/tailwind.config.ts b/tailwind.config.ts
index d2bfc56..1a6aecf 100644
--- a/tailwind.config.ts
+++ b/tailwind.config.ts
@@ -84,6 +84,11 @@ const config = {
},
},
plugins: [require("tailwindcss-animate"), require("@tailwindcss/typography")],
+ safelist: [
+ {
+ pattern: /katex-.*/,
+ },
+ ],
} satisfies Config
export default config
\ No newline at end of file