Render math in chat messages with KaTeX
Add a marked extension that tokenizes math delimiters and renders them with KaTeX: `$$ ... $$` / `\[ ... \]` as block, `$ ... $` / `\( ... \)` and mid-paragraph `$$ ... $$` as inline. Inline `$ ... $` requires a non-space after the opening `$`, no newline inside, and a non-digit after the closing `$`, so prose like "$100 から $200" is left alone; `\$` still escapes a literal dollar sign. Parse errors fall back to KaTeX's red inline message instead of throwing, and math whose closing delimiter has not streamed in yet stays plain text. DOMPurify strips `semantics` and `annotation` by default, which would leak the raw TeX out of KaTeX's MathML output, so both are allowed explicitly. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -2,6 +2,8 @@
|
||||
import { computed } from 'vue'
|
||||
import { marked } from 'marked'
|
||||
import DOMPurify from 'dompurify'
|
||||
import 'katex/dist/katex.min.css'
|
||||
import { mathExtension, mathSanitizeConfig } from '../utils/math'
|
||||
|
||||
const props = defineProps<{
|
||||
content: string
|
||||
@@ -9,6 +11,7 @@ const props = defineProps<{
|
||||
}>()
|
||||
|
||||
marked.setOptions({ breaks: true, gfm: true })
|
||||
marked.use(mathExtension)
|
||||
|
||||
function closeOpenFences(src: string): string {
|
||||
const fenceCount = (src.match(/^```/gm) ?? []).length
|
||||
@@ -18,7 +21,7 @@ function closeOpenFences(src: string): string {
|
||||
|
||||
const html = computed(() => {
|
||||
const raw = props.pending ? closeOpenFences(props.content) : props.content
|
||||
return DOMPurify.sanitize(marked.parse(raw) as string)
|
||||
return DOMPurify.sanitize(marked.parse(raw) as string, mathSanitizeConfig)
|
||||
})
|
||||
</script>
|
||||
|
||||
@@ -104,6 +107,15 @@ const html = computed(() => {
|
||||
/* 水平線 */
|
||||
.md :deep(hr) { border-color: #333; margin: 10px 0; }
|
||||
|
||||
/* 数式 (KaTeX) */
|
||||
.md :deep(.katex) { font-size: 1.05em; }
|
||||
.md :deep(.katex-display) {
|
||||
margin: 10px 0;
|
||||
padding: 2px 0;
|
||||
overflow-x: auto;
|
||||
overflow-y: hidden;
|
||||
}
|
||||
|
||||
/* リンク */
|
||||
.md :deep(a) { color: #60a5fa; text-decoration: none; }
|
||||
.md :deep(a:hover) { text-decoration: underline; }
|
||||
|
||||
Reference in New Issue
Block a user