refactor(markdown): Optimize rendering with memoized components and dynamic imports #2889

- Memoize math engine selection
- Create dynamic components callback for markdown rendering
- Optimize import statements for KaTeX extensions
This commit is contained in:
kangfenmao 2025-03-05 21:14:29 +08:00
parent 70eb0a9187
commit 1738a74e8c

View File

@ -1,12 +1,13 @@
import 'katex/dist/katex.min.css' import 'katex/dist/katex.min.css'
import 'katex/dist/contrib/copy-tex' import 'katex/dist/contrib/copy-tex'
import 'katex/dist/contrib/mhchem'
import MarkdownShadowDOMRenderer from '@renderer/components/MarkdownShadowDOMRenderer' import MarkdownShadowDOMRenderer from '@renderer/components/MarkdownShadowDOMRenderer'
import { useSettings } from '@renderer/hooks/useSettings' import { useSettings } from '@renderer/hooks/useSettings'
import type { Message } from '@renderer/types' import type { Message } from '@renderer/types'
import { escapeBrackets, removeSvgEmptyLines, withGeminiGrounding } from '@renderer/utils/formats' import { escapeBrackets, removeSvgEmptyLines, withGeminiGrounding } from '@renderer/utils/formats'
import { isEmpty } from 'lodash' import { isEmpty } from 'lodash'
import { type FC, useMemo } from 'react' import { type FC, useCallback, useMemo } from 'react'
import { useTranslation } from 'react-i18next' import { useTranslation } from 'react-i18next'
import ReactMarkdown, { type Components } from 'react-markdown' import ReactMarkdown, { type Components } from 'react-markdown'
import rehypeKatex from 'rehype-katex' import rehypeKatex from 'rehype-katex'
@ -16,8 +17,6 @@ import rehypeRaw from 'rehype-raw'
import remarkGfm from 'remark-gfm' import remarkGfm from 'remark-gfm'
import remarkMath from 'remark-math' import remarkMath from 'remark-math'
import 'katex/dist/contrib/mhchem'
import CodeBlock from './CodeBlock' import CodeBlock from './CodeBlock'
import ImagePreview from './ImagePreview' import ImagePreview from './ImagePreview'
import Link from './Link' import Link from './Link'
@ -33,7 +32,7 @@ const Markdown: FC<Props> = ({ message }) => {
const { t } = useTranslation() const { t } = useTranslation()
const { renderInputMessageAsMarkdown, mathEngine } = useSettings() const { renderInputMessageAsMarkdown, mathEngine } = useSettings()
const rehypeMath = mathEngine === 'KaTeX' ? rehypeKatex : rehypeMathjax const rehypeMath = useMemo(() => (mathEngine === 'KaTeX' ? rehypeKatex : rehypeMathjax), [mathEngine])
const messageContent = useMemo(() => { const messageContent = useMemo(() => {
const empty = isEmpty(message.content) const empty = isEmpty(message.content)
@ -47,6 +46,20 @@ const Markdown: FC<Props> = ({ message }) => {
return hasElements ? [rehypeRaw, rehypeMath] : [rehypeMath] return hasElements ? [rehypeRaw, rehypeMath] : [rehypeMath]
}, [messageContent, rehypeMath]) }, [messageContent, rehypeMath])
const components = useCallback(() => {
const baseComponents = {
a: Link,
code: CodeBlock,
img: ImagePreview
} as Partial<Components>
if (messageContent.includes('<style>')) {
baseComponents.style = MarkdownShadowDOMRenderer as any
}
return baseComponents
}, [messageContent])
if (message.role === 'user' && !renderInputMessageAsMarkdown) { if (message.role === 'user' && !renderInputMessageAsMarkdown) {
return <p style={{ marginBottom: 5, whiteSpace: 'pre-wrap' }}>{messageContent}</p> return <p style={{ marginBottom: 5, whiteSpace: 'pre-wrap' }}>{messageContent}</p>
} }
@ -56,14 +69,7 @@ const Markdown: FC<Props> = ({ message }) => {
rehypePlugins={rehypePlugins} rehypePlugins={rehypePlugins}
remarkPlugins={[remarkMath, remarkGfm]} remarkPlugins={[remarkMath, remarkGfm]}
className="markdown" className="markdown"
components={ components={components()}
{
style: MarkdownShadowDOMRenderer,
a: Link,
code: CodeBlock,
img: ImagePreview
} as Partial<Components>
}
remarkRehypeOptions={{ remarkRehypeOptions={{
footnoteLabel: t('common.footnotes'), footnoteLabel: t('common.footnotes'),
footnoteLabelTagName: 'h4', footnoteLabelTagName: 'h4',