From 00f9923ed752048f54a4e3a5cec6cbd8d95f2ec6 Mon Sep 17 00:00:00 2001 From: zaidmukaddam Date: Wed, 18 Sep 2024 20:50:51 +0530 Subject: [PATCH] Update Katex styles and rendering in markdown content --- app/globals.css | 27 ++++++++++++ app/layout.tsx | 1 + app/search/page.tsx | 101 +++++++++++++++++++++++++------------------- package.json | 1 + pnpm-lock.yaml | 27 +++++++++--- tailwind.config.ts | 5 +++ 6 files changed, 112 insertions(+), 50 deletions(-) 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