Public Access
feat: add LaTeX rendering utilities and Tex component
This commit is contained in:
1 parent
611277ee8a
commit
95de63bd98
2 files changed
+56
No files matched your search
@@ -0,0 +1,14 @@
|
||||
import { renderLatex } from '../utils/latex'
|
||||
|
||||
interface TexProps {
|
||||
children?: string | null
|
||||
tag?: keyof JSX.IntrinsicElements
|
||||
className?: string
|
||||
style?: React.CSSProperties
|
||||
}
|
||||
|
||||
// Safe: renderLatex only produces KaTeX-controlled HTML + HTML-escaped plain text.
|
||||
export default function Tex({ children, tag: Tag = 'span', className, style }: TexProps) {
|
||||
const html = String(children ?? '').split('\n').map(renderLatex).join('<br/>')
|
||||
return <Tag className={className} style={style} dangerouslySetInnerHTML={{ __html: html }} />
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
import katex from 'katex'
|
||||
|
||||
export const escapeHtml = (s: string): string =>
|
||||
String(s)
|
||||
.replace(/&/g, '&')
|
||||
.replace(/</g, '<')
|
||||
.replace(/>/g, '>')
|
||||
.replace(/"/g, '"')
|
||||
.replace(/'/g, ''')
|
||||
|
||||
export const renderLatex = (text: string): string => {
|
||||
if (!text) return ''
|
||||
let rest = String(text)
|
||||
// display math: $$...$$
|
||||
rest = rest.replace(/\$\$([\s\S]+?)\$\$/g, (_, tex: string) => {
|
||||
try {
|
||||
return `<span class="katex-display-wrap">${katex.renderToString(tex, { displayMode: true, throwOnError: false })}</span>`
|
||||
} catch {
|
||||
return `<code>${escapeHtml(tex)}</code>`
|
||||
}
|
||||
})
|
||||
// inline math: $...$
|
||||
rest = rest.replace(/\$([^$\n]+?)\$/g, (_, tex: string) => {
|
||||
try {
|
||||
return katex.renderToString(tex, { displayMode: false, throwOnError: false })
|
||||
} catch {
|
||||
return `<code>${escapeHtml(tex)}</code>`
|
||||
}
|
||||
})
|
||||
return rest
|
||||
}
|
||||
|
||||
export const highlightCode = (code: string, _lang: string): string => {
|
||||
// escapeHtml runs first — no raw user content reaches the span injection
|
||||
const escaped = escapeHtml(code)
|
||||
return escaped
|
||||
.replace(/(\/\/[^\n]*|#[^\n]*)/g, '<span class="cm-comment">$1</span>')
|
||||
.replace(/("[^&]*?"|'[^&]*?'|`[^`]*?`)/g, '<span class="cm-string">$1</span>')
|
||||
.replace(/\b(function|return|if|else|const|let|var|for|while|import|export|from|class|new|this|def|print|in|not|and|or|True|False|None|null|undefined|true|false)\b/g, '<span class="cm-keyword">$1</span>')
|
||||
.replace(/\b(\d+(?:\.\d+)?)\b/g, '<span class="cm-number">$1</span>')
|
||||
.replace(/\b([a-zA-Z_][a-zA-Z0-9_]*)\s*(?=\()/g, '<span class="cm-fn">$1</span>')
|
||||
}
|
||||
Reference in new issue
Block a user