Add markdown and LaTeX math rendering for chat messages#6
Add markdown and LaTeX math rendering for chat messages#6tpaulshippy wants to merge 4 commits intomainfrom
Conversation
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (2)
🚧 Files skipped from review as they are similar to previous changes (2)
📝 WalkthroughWalkthroughAssistant messages now render Markdown and LaTeX via a new Changes
Sequence DiagramsequenceDiagram
participant User
participant ChatMessage
participant MarkdownRenderer
participant marked
participant katex
participant WebView
User->>ChatMessage: deliver message object
alt message.role == "user"
ChatMessage->>ChatMessage: render `ThemedText` (plain)
else
ChatMessage->>MarkdownRenderer: pass `message.text`
MarkdownRenderer->>marked: parse markdown -> HTML
marked-->>MarkdownRenderer: parsed HTML
MarkdownRenderer->>katex: render LaTeX segments
katex-->>MarkdownRenderer: rendered HTML snippets
MarkdownRenderer->>MarkdownRenderer: assemble theme-aware HTML/CSS
MarkdownRenderer->>WebView: load generated HTML
WebView-->>User: display formatted content
end
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~20 minutes Poem
🚥 Pre-merge checks | ✅ 2 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 7
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@front/components/ChatMessage.tsx`:
- Line 9: The import for MarkdownRenderer in ChatMessage.tsx uses a default
import but the component is exported as a named export; change the import
statement to a named import (import { MarkdownRenderer } from
"@/components/MarkdownRenderer") so it matches the component's named export and
coding guidelines, updating any references in this file that assume the default
import to use the named symbol MarkdownRenderer.
In `@front/components/MarkdownRenderer.tsx`:
- Line 136: The file currently uses a default export for the React component;
change it to a named export by exporting the MarkdownRenderer symbol as a named
export (e.g., export const MarkdownRenderer = ...) or by replacing the final
default export with a named export (export { MarkdownRenderer }); then update
all import sites to use the named import (import { MarkdownRenderer } from
'...') and ensure the component file stays a .tsx TypeScript component.
- Line 39: Update the KaTeX CDN stylesheet URL in MarkdownRenderer.tsx so its
version matches the installed runtime (package.json's ^0.16.45); locate the
<link rel="stylesheet"
href="https://cdn.jsdelivr.net/npm/katex@0.16.9/dist/katex.min.css"> in the
MarkdownRenderer component and change the version segment to 0.16.45 (or the
exact runtime version) so the CSS and KaTeX runtime are identical.
- Around line 33-34: The parsed HTML from marked.parse (htmlContent) is injected
into the WebView (component using originWhitelist) without sanitization creating
XSS risk; fix by sanitizing the parsed HTML output with a library like
sanitize-html or isomorphic-dompurify before assigning to htmlContent and use
that sanitized string for the WebView source, and harden the WebView props by
tightening originWhitelist (avoid ['*']), disabling/excluding JavaScript
execution (e.g., javaScriptEnabled=false), disabling DOM storage, and removing
permissive settings so only trusted origins are allowed; update references to
marked.parse, htmlContent, and the WebView component props accordingly.
- Around line 125-132: Extract the inline style object from the WebView into a
StyleSheet by creating e.g. const styles = StyleSheet.create({ webview: {
backgroundColor: 'transparent', minHeight: 50 } }) and apply it to the WebView
via style={styles.webview}; additionally add startInLoadingState={true} and a
renderError prop that returns a simple fallback component (e.g. a View/Text) to
the WebView element so the MarkdownRenderer component handles loading and error
states properly.
- Around line 18-24: Update the inline LaTeX regex in MarkdownRenderer.tsx that
currently uses the capturing group ([^)]+) which fails on expressions with
nested parentheses; change that group to a non-greedy [\s\S]+? (matching any
char including newlines) so the .replace call that renders inline math via
katex.renderToString(latex, { throwOnError: false, displayMode: false }) will
capture full expressions like \(2(3*6)+(82-(a+5))=a+4\); keep the existing
try/catch and do not alter the separate display-mode pattern.
In `@front/package.json`:
- Around line 49-50: Jest will fail to parse the new ESM-only dependency
"marked" (and potentially the ESM build of "katex"), so update your Jest config
(jest.config.js or the "jest" field) to ensure node_modules/marked and
node_modules/katex are transformed (i.e., modify transformIgnorePatterns to
exclude these packages from being ignored, for example by using a negative
lookahead like /(?!marked|katex)/ in the pattern); target the
transformIgnorePatterns setting and add "marked" and "katex" so Babel/jest can
transpile them during tests.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: d5f5ce6b-eba6-4f28-aa8f-a1a841653c63
⛔ Files ignored due to path filters (1)
front/package-lock.jsonis excluded by!**/package-lock.json
📒 Files selected for processing (3)
front/components/ChatMessage.tsxfront/components/MarkdownRenderer.tsxfront/package.json
| import { ActivityIndicator } from "react-native"; | ||
| import { useThemeColor } from "@/hooks/useThemeColor"; | ||
| import { IconSymbol } from "@/components/ui/IconSymbol"; | ||
| import MarkdownRenderer from "@/components/MarkdownRenderer"; |
There was a problem hiding this comment.
🛠️ Refactor suggestion | 🟠 Major
Use named import for MarkdownRenderer.
Line 9 should switch to a named import to match the component export convention.
Suggested change
-import MarkdownRenderer from "@/components/MarkdownRenderer";
+import { MarkdownRenderer } from "@/components/MarkdownRenderer";As per coding guidelines, "Place React Native components in the components directory, use TypeScript for all component files, and export components as named exports".
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| import MarkdownRenderer from "@/components/MarkdownRenderer"; | |
| import { MarkdownRenderer } from "@/components/MarkdownRenderer"; |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@front/components/ChatMessage.tsx` at line 9, The import for MarkdownRenderer
in ChatMessage.tsx uses a default import but the component is exported as a
named export; change the import statement to a named import (import {
MarkdownRenderer } from "@/components/MarkdownRenderer") so it matches the
component's named export and coding guidelines, updating any references in this
file that assume the default import to use the named symbol MarkdownRenderer.
| const htmlContent = marked.parse(processedContent, { breaks: true, gfm: true }); | ||
|
|
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
find . -name "MarkdownRenderer.tsx" -type fRepository: tpaulshippy/bots
Length of output: 99
🏁 Script executed:
cat -n front/components/MarkdownRenderer.tsxRepository: tpaulshippy/bots
Length of output: 4363
🏁 Script executed:
head -150 front/components/MarkdownRenderer.tsx | tail -120Repository: tpaulshippy/bots
Length of output: 2888
Unsanitized HTML from marked output injected into WebView (XSS risk).
The markdown content is parsed by marked.parse() (line 33) which can render arbitrary HTML, then injected directly into the WebView (lines 125-127) with permissive originWhitelist={['*']}. An attacker can inject JavaScript through markdown syntax.
Fix by sanitizing the parsed HTML output (not the input) using a library like sanitize-html or isomorphic-dompurify, and harden the WebView:
Suggested hardening
import { marked } from 'marked';
+ import sanitizeHtml from 'sanitize-html';
- const htmlContent = marked.parse(processedContent, { breaks: true, gfm: true });
+ const rawHtml = marked.parse(processedContent, { breaks: true, gfm: true });
+ const htmlContent = sanitizeHtml(rawHtml, {
+ allowedTags: ['b', 'i', 'em', 'strong', 'a', 'p', 'br', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'ul', 'ol', 'li', 'code', 'pre', 'blockquote', 'img', 'table', 'thead', 'tbody', 'tr', 'td', 'th', 'hr', 'span'],
+ allowedAttributes: { 'a': ['href'], 'img': ['src', 'alt'] }
+ });
return (
<WebView
- originWhitelist={['*']}
+ originWhitelist={['about:blank']}
source={{ html }}
+ javaScriptEnabled={false}
+ onShouldStartLoadWithRequest={(req) => req.url === 'about:blank'}Also applies to: 125-127
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@front/components/MarkdownRenderer.tsx` around lines 33 - 34, The parsed HTML
from marked.parse (htmlContent) is injected into the WebView (component using
originWhitelist) without sanitization creating XSS risk; fix by sanitizing the
parsed HTML output with a library like sanitize-html or isomorphic-dompurify
before assigning to htmlContent and use that sanitized string for the WebView
source, and harden the WebView props by tightening originWhitelist (avoid
['*']), disabling/excluding JavaScript execution (e.g.,
javaScriptEnabled=false), disabling DOM storage, and removing permissive
settings so only trusted origins are allowed; update references to marked.parse,
htmlContent, and the WebView component props accordingly.
| <html> | ||
| <head> | ||
| <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0"> | ||
| <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/katex@0.16.9/dist/katex.min.css"> |
There was a problem hiding this comment.
🧩 Analysis chain
🌐 Web query:
Does KaTeX guarantee CSS compatibility across all 0.16.x patch versions, or should renderToString output and CSS always use the exact same version?
💡 Result:
KaTeX does not guarantee CSS compatibility across all 0.16.x patch versions. The renderToString output and CSS must use the exact same version to ensure correct rendering.
Citations:
- 1: Is Katex HTML not backwards compatible? KaTeX/KaTeX#3577
- 2: https://katex.org/docs/issues
- 3: SSR markup renders incorrectly. In-Browser renders correctly. KaTeX/KaTeX#2693
- 4: https://katex.org/docs/issues.html
- 5: https://github.com/Khan/KaTeX/blob/master/README.md
🏁 Script executed:
cat front/package.json | grep -A 2 -B 2 '"katex"'Repository: tpaulshippy/bots
Length of output: 201
🏁 Script executed:
cat -n front/components/MarkdownRenderer.tsx | sed -n '35,45p'Repository: tpaulshippy/bots
Length of output: 543
Update CDN KaTeX CSS to match installed runtime version.
Line 39 pins CDN CSS to 0.16.9, but package.json specifies ^0.16.45. KaTeX does not guarantee CSS compatibility across patch versions—the CSS and runtime output must use the exact same version to avoid rendering and style mismatches. Update the CDN link to 0.16.45 or higher to align with the installed runtime.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@front/components/MarkdownRenderer.tsx` at line 39, Update the KaTeX CDN
stylesheet URL in MarkdownRenderer.tsx so its version matches the installed
runtime (package.json's ^0.16.45); locate the <link rel="stylesheet"
href="https://cdn.jsdelivr.net/npm/katex@0.16.9/dist/katex.min.css"> in the
MarkdownRenderer component and change the version segment to 0.16.45 (or the
exact runtime version) so the CSS and KaTeX runtime are identical.
| <WebView | ||
| originWhitelist={['*']} | ||
| source={{ html }} | ||
| style={{ backgroundColor: 'transparent', minHeight: 50 }} | ||
| scrollEnabled={false} | ||
| showsVerticalScrollIndicator={false} | ||
| messagingEnabled={false} | ||
| /> |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
cat -n front/components/MarkdownRenderer.tsx | head -150Repository: tpaulshippy/bots
Length of output: 4363
Move inline style to StyleSheet.create and add WebView loading/error handling.
Line 128 uses an inline style object that should be extracted to StyleSheet.create. Additionally, add startInLoadingState and renderError props to provide proper loading and error states per the coding guidelines for this component directory.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@front/components/MarkdownRenderer.tsx` around lines 125 - 132, Extract the
inline style object from the WebView into a StyleSheet by creating e.g. const
styles = StyleSheet.create({ webview: { backgroundColor: 'transparent',
minHeight: 50 } }) and apply it to the WebView via style={styles.webview};
additionally add startInLoadingState={true} and a renderError prop that returns
a simple fallback component (e.g. a View/Text) to the WebView element so the
MarkdownRenderer component handles loading and error states properly.
| ); | ||
| }; | ||
|
|
||
| export default MarkdownRenderer; No newline at end of file |
There was a problem hiding this comment.
🛠️ Refactor suggestion | 🟠 Major
Export this component as a named export.
Switch from default export to named export, then update import sites accordingly.
Suggested change
-const MarkdownRenderer = ({ content }: MarkdownRendererProps) => {
+export const MarkdownRenderer = ({ content }: MarkdownRendererProps) => {
// ...
};
-
-export default MarkdownRenderer;As per coding guidelines, "Place React Native components in the components directory, use TypeScript for all component files, and export components as named exports".
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@front/components/MarkdownRenderer.tsx` at line 136, The file currently uses a
default export for the React component; change it to a named export by exporting
the MarkdownRenderer symbol as a named export (e.g., export const
MarkdownRenderer = ...) or by replacing the final default export with a named
export (export { MarkdownRenderer }); then update all import sites to use the
named import (import { MarkdownRenderer } from '...') and ensure the component
file stays a .tsx TypeScript component.
Summary
\(a=54.5\)) using KaTeXMarkdownRenderercomponent that renders styled HTML in a WebViewChatMessage.tsxto use MarkdownRenderer for assistant messages while keeping plain text for user messagesTesting
Test with messages containing:
code, lists, links, blockquotes\(2(3*6)+(82-(a+5))=a+4\)renders as\(a=54.5\)Summary by CodeRabbit