fix(chat): render LaTeX \\[..\\] / \\(..\\) delimiters (Issue #50)#191
Open
MarkSiqiZhang wants to merge 4 commits intomainfrom
Open
fix(chat): render LaTeX \\[..\\] / \\(..\\) delimiters (Issue #50)#191MarkSiqiZhang wants to merge 4 commits intomainfrom
MarkSiqiZhang wants to merge 4 commits intomainfrom
Conversation
Wire normalizeLatexDelimiters into unescapeWithMathProtection so bracket-delimited math is rewritten to $-delimiters before the mask/unescape step. Without this, \[..\] and \(..\) rendered as literal text, and the \\t → \t replacement inside unescapeWithMathProtection corrupted \theta / \tau / \n-commands that sat inside bracket-delimited blocks (issue #50).
…dering across components - Added normalizeLatexDelimiters to CodeEditor, ResearchLab, ChatContextFilePreview, and SurveyPage components to ensure LaTeX content is properly formatted before rendering. - This change enhances the handling of LaTeX delimiters, improving the overall user experience when displaying mathematical content.
Co-authored-by: Copilot Autofix powered by AI <223894421+github-code-quality[bot]@users.noreply.github.com>
MarkSiqiZhang
added a commit
that referenced
this pull request
Apr 17, 2026
Add a MermaidBlock component that renders ```mermaid fences inline as SVG via a lazy-loaded mermaid@11 import. The chat Markdown CodeBlock short-circuits to MermaidBlock when language === 'mermaid', bypassing syntax highlighting. Streaming partial sources are debounced 150ms and fall back to a <pre> of the raw source on parse errors, so users never see a flashing error during a live stream. Theme is driven by useTheme() so diagrams repaint live on dark-mode toggle without remount. Also tighten formatFileTreeInContent so it no longer mis-wraps non-tree content in ```text fences — the likely cause of the "fence marker / text label leak" symptom in issue #50. Two guardrails: 1. Skip detection inside fenced code blocks (insideFence toggle). 2. Only commit the wrap when the collected block contains at least one strong signal (├── or └──); lone-│ clusters (ASCII pipelines, Mermaid-ish art) pass through unwrapped. i18n: add codeBlock.rendering / codeBlock.renderError in en/zh-CN/ko; reuse existing codeBlock.copy / codeBlock.copied for the diagram copy button. Scope: chat surface only, mirroring PR #191. ChatContextFilePreview, ResearchLab, and CodeEditor are deferred to a follow-up. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
6 tasks
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
\[..\](display) /\(..\)(inline) now render via KaTeX instead of showing as raw source.\theta → <TAB>hetacorruption that hit bracket-delimited blocks because the existing math mask only covered$..$/$$..$$.ResearchLab.jsx,CodeEditor.jsx,ChatContextFilePreview.tsx,SurveyPage.tsxdon't route throughunescapeWithMathProtectionand are left for a follow-up.Root cause
Two bugs stacked:
remark-math@6recognizes only$delimiters, so\[..\]/\(..\)never became math nodes. Worse, CommonMark treats\[/\]/\(/\)as valid backslash-escapes of ASCII punctuation, so remark silently stripped the backslashes, leaving[ r_t(...) ]rendered as prose.unescapeWithMathProtectionprotects math blocks from its own\\t → \treplacement via a mask/unmask trick — but the mask regex only matched$..$/$$..$$. Any\theta/\tau/\n*-command inside bracket-delimited math lost the backslash when its\twas turned into a literal TAB character.Changes
src/utils/latexNormalizer.ts(new)\[..\]→$$..$$and\(..\)→$..$outside fenced code blocks and inline code. Single-pass alternation regex, fast-path short-circuit when no brackets present, returns same string reference on the no-op path (preservesuseMemoidentity).src/components/chat/utils/chatFormatting.tsunescapeWithMathProtection, so bracket math is rewritten to$-form before the existing mask/replace/unmask runs — bringing it under the same protection umbrella.Why hook into
unescapeWithMathProtectioninstead of the 5 ReactMarkdown call sitesAll 10+ chat render paths already funnel through
unescapeWithMathProtection(viamessageTransforms.tsanduseChatRealtimeHandlers.ts). A 2-line patch there covers the whole chat surface without touching any React component. The non-chat renderers don't call this function and are deferred to a follow-up PR.Test plan
\[ r_t(\theta) = \frac{\pi_\theta(a|s)}{\pi_{\theta_{old}}(a|s)} \]— verify KaTeX renders the fraction (DOM check:<span class="katex">present, with bothkatex-mathmlandkatex-htmlchildren).\( y = mx + b \)— verify KaTeX inline rendering.$..$/$$..$$messages still render (no regression).arr\[0\](e.g. a Rust sample) — verify the code is NOT mangled intoarr$$0$$.\theta/\tau/\pi_{new}inside bracket-delimited math are intact (no<TAB>hetacorruption).npm run typecheck— passesnpm run build— passes