diff --git a/src/components/CodeEditor.jsx b/src/components/CodeEditor.jsx index f14de7a8..99262590 100644 --- a/src/components/CodeEditor.jsx +++ b/src/components/CodeEditor.jsx @@ -17,6 +17,7 @@ import remarkGfm from 'remark-gfm'; import remarkMath from 'remark-math'; import rehypeKatex from 'rehype-katex'; import rehypeRaw from 'rehype-raw'; +import { normalizeLatexDelimiters } from '../utils/latexNormalizer'; import { Prism as SyntaxHighlighter } from 'react-syntax-highlighter'; import { oneDark as prismOneDark } from 'react-syntax-highlighter/dist/esm/styles/prism'; import { api } from '../utils/api'; @@ -138,7 +139,7 @@ function MarkdownPreview({ content }) { rehypePlugins={rehypePlugins} components={markdownPreviewComponents} > - {content} + {normalizeLatexDelimiters(content ?? '')} ); } diff --git a/src/components/ResearchLab.jsx b/src/components/ResearchLab.jsx index 80073f3c..316937d5 100644 --- a/src/components/ResearchLab.jsx +++ b/src/components/ResearchLab.jsx @@ -4,6 +4,7 @@ import ReactMarkdown from 'react-markdown'; import remarkGfm from 'remark-gfm'; import remarkMath from 'remark-math'; import rehypeKatex from 'rehype-katex'; +import { normalizeLatexDelimiters } from '../utils/latexNormalizer'; import { FlaskConical, RefreshCw, FileText, BookOpen, Settings2, Lightbulb, GitBranch, FolderOpen, ChevronDown, ChevronRight, ExternalLink, @@ -755,7 +756,7 @@ function OverviewCard({ instance, config, researchBrief, compact = false }) { rehypePlugins={[rehypeKatex]} components={OVERVIEW_MARKDOWN_COMPONENTS} > - {section.content} + {normalizeLatexDelimiters(section.content)} @@ -1915,7 +1916,7 @@ function IdeaCard({ projectName, config, projectFileSet, compact = false }) { rehypePlugins={rehypePlugins} components={ideaMarkdownComponents} > - {ideaText} + {normalizeLatexDelimiters(ideaText)} )} @@ -2244,7 +2245,7 @@ function FileViewer({ projectName, file, onClose }) { rehypePlugins={[rehypeKatex]} components={markdownComponents} > - {content} + {normalizeLatexDelimiters(content ?? '')} diff --git a/src/components/chat/utils/chatFormatting.ts b/src/components/chat/utils/chatFormatting.ts index c838d509..7b12da54 100644 --- a/src/components/chat/utils/chatFormatting.ts +++ b/src/components/chat/utils/chatFormatting.ts @@ -1,3 +1,5 @@ +import { normalizeLatexDelimiters } from '../../../utils/latexNormalizer'; + export function decodeHtmlEntities(text: string) { if (!text) return text; return text @@ -20,11 +22,16 @@ export function normalizeInlineCodeFences(text: string) { export function unescapeWithMathProtection(text: string) { if (!text || typeof text !== 'string') return text; + // Rewrite \[..\] / \(..\) into $-delimited form before masking — the mask + // below only covers $-delimiters, so without this the \\t → \t replacement + // would corrupt \theta / \tau / \n-commands inside bracket-delimited math. + let processedText = normalizeLatexDelimiters(text); + const mathBlocks: string[] = []; const placeholderPrefix = '__MATH_BLOCK_'; const placeholderSuffix = '__'; - let processedText = text.replace(/\$\$([\s\S]*?)\$\$|\$([^\$\n]+?)\$/g, (match) => { + processedText = processedText.replace(/\$\$([\s\S]*?)\$\$|\$([^\$\n]+?)\$/g, (match) => { const index = mathBlocks.length; mathBlocks.push(match); return `${placeholderPrefix}${index}${placeholderSuffix}`; diff --git a/src/components/chat/view/subcomponents/ChatContextFilePreview.tsx b/src/components/chat/view/subcomponents/ChatContextFilePreview.tsx index 539d2b4e..f5c43db0 100644 --- a/src/components/chat/view/subcomponents/ChatContextFilePreview.tsx +++ b/src/components/chat/view/subcomponents/ChatContextFilePreview.tsx @@ -1,6 +1,7 @@ import { useEffect, useMemo, useState } from 'react'; import { useTranslation } from 'react-i18next'; import ReactMarkdown from 'react-markdown'; +import { normalizeLatexDelimiters } from '../../../../utils/latexNormalizer'; import remarkGfm from 'remark-gfm'; import remarkMath from 'remark-math'; import rehypeKatex from 'rehype-katex'; @@ -307,7 +308,7 @@ export default function ChatContextFilePreview({