Update Katex styles and rendering in markdown content
This commit is contained in:
parent
6ec4100e50
commit
00f9923ed7
@ -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 */
|
||||
|
||||
@ -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';
|
||||
|
||||
@ -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
|
||||
</p>
|
||||
)}
|
||||
</div>
|
||||
<div className="flex items-center justify-between px-3 py-2 bg-gray-50 text-xs text-gray-500">
|
||||
<span className="truncate max-w-[80%]">{href}</span>
|
||||
<ExternalLink size={14} className="flex-shrink-0" />
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
@ -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<ReactRenderer> = {
|
||||
paragraph(children) {
|
||||
if (typeof children === 'string') {
|
||||
const parts = children.split(/(\[.*?\])/g);
|
||||
return (
|
||||
<p>
|
||||
{parts.map((part, index) => {
|
||||
if (part.startsWith('[') && part.endsWith(']')) {
|
||||
const equation = part.slice(1, -1);
|
||||
<p className="my-4">
|
||||
{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 (
|
||||
<span key={index} dangerouslySetInnerHTML={{
|
||||
__html: renderEquation(equation)
|
||||
}} />
|
||||
<Latex key={index} macros={latexMacros}>
|
||||
{part}
|
||||
</Latex>
|
||||
);
|
||||
} else if (part.startsWith('$') && part.endsWith('$')) {
|
||||
// Inline equation
|
||||
return (
|
||||
<Latex key={index} macros={latexMacros}>
|
||||
{part}
|
||||
</Latex>
|
||||
);
|
||||
} // add $$ for display mode equations
|
||||
else if (part.startsWith('$$') && part.endsWith('$$')) {
|
||||
// Display mode equation
|
||||
return (
|
||||
<Latex key={index} macros={latexMacros}>
|
||||
{part}
|
||||
</Latex>
|
||||
);
|
||||
}
|
||||
return <span key={index}>{part}</span>;
|
||||
// Regular text
|
||||
return part;
|
||||
});
|
||||
}
|
||||
return child;
|
||||
})}
|
||||
</p>
|
||||
);
|
||||
}
|
||||
return <p>{children}</p>;
|
||||
},
|
||||
code(children, language) {
|
||||
return <CodeBlock language={language}>{String(children)}</CodeBlock>;
|
||||
@ -1787,7 +1798,7 @@ The o1-mini is a new OpenAI model that is optimized for reasoning tasks. Current
|
||||
</sup>
|
||||
);
|
||||
}
|
||||
return isValidUrl(href) ? renderHoverCard(href, text) : <a href={href}>{text}</a>;
|
||||
return isValidUrl(href) ? renderHoverCard(href, text) : <a href={href} className="text-blue-600 hover:underline">{text}</a>;
|
||||
},
|
||||
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 <ListTag className="list-inside list-disc my-2">{children}</ListTag>;
|
||||
return <ListTag className="list-inside list-disc my-4 pl-4">{children}</ListTag>;
|
||||
},
|
||||
listItem(children) {
|
||||
return <li className="my-1">{children}</li>;
|
||||
return <li className="my-2">{children}</li>;
|
||||
},
|
||||
blockquote(children) {
|
||||
return <blockquote className="border-l-4 border-gray-300 pl-4 italic my-4">{children}</blockquote>;
|
||||
},
|
||||
};
|
||||
|
||||
const preprocessedContent = useMemo(() => preprocessContent(content), [content]);
|
||||
|
||||
return (
|
||||
<div className="prose prose-sm sm:prose-base max-w-none">
|
||||
<Marked renderer={renderer}>{content}</Marked>
|
||||
<div className="markdown-body">
|
||||
<Marked renderer={renderer}>{preprocessedContent}</Marked>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
@ -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",
|
||||
|
||||
@ -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:
|
||||
|
||||
@ -84,6 +84,11 @@ const config = {
|
||||
},
|
||||
},
|
||||
plugins: [require("tailwindcss-animate"), require("@tailwindcss/typography")],
|
||||
safelist: [
|
||||
{
|
||||
pattern: /katex-.*/,
|
||||
},
|
||||
],
|
||||
} satisfies Config
|
||||
|
||||
export default config
|
||||
Loading…
Reference in New Issue
Block a user