fix: shadow markdown (#1871)
问题:[Bug]: 当模型回复的html代码没有正确在代码框中时,html代码内容会影响到UI界面 #1767 原因:解析html之后css会污染应用样式 解决:将markdown完全放入shadow dom中,shadow dom天然隔绝样式,即可解决 * feat: Conditionally hide thinking loader for paused messages * feat: Implement Shadow DOM for Markdown rendering * feat: Add StyleProvider to Shadow DOM Markdown rendering * fix: Refactor Markdown rendering with inline ShadowDOM component Modify ReactMarkdown component to use style component for ShadowDOM rendering instead of wrapping component, simplifying the rendering approach --------- Co-authored-by: lizhixuan <zhixuan.li@banosuperapp.com>
This commit is contained in:
parent
a12d10f4f7
commit
13b465fe73
63
src/renderer/src/components/MarkdownShadowDOMRenderer.tsx
Normal file
63
src/renderer/src/components/MarkdownShadowDOMRenderer.tsx
Normal file
@ -0,0 +1,63 @@
|
|||||||
|
import { StyleProvider } from '@ant-design/cssinjs'
|
||||||
|
import React, { useEffect, useRef } from 'react'
|
||||||
|
import { createPortal } from 'react-dom'
|
||||||
|
import { StyleSheetManager } from 'styled-components'
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
children: React.ReactNode
|
||||||
|
}
|
||||||
|
|
||||||
|
const ShadowDOMRenderer: React.FC<Props> = ({ children }) => {
|
||||||
|
const hostRef = useRef<HTMLDivElement>(null)
|
||||||
|
const [shadowRoot, setShadowRoot] = React.useState<ShadowRoot | null>(null)
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const host = hostRef.current
|
||||||
|
if (!host) return
|
||||||
|
|
||||||
|
// 创建 shadow root
|
||||||
|
const shadow = host.shadowRoot || host.attachShadow({ mode: 'open' })
|
||||||
|
|
||||||
|
// 获取原始样式表
|
||||||
|
const markdownStyleSheet = Array.from(document.styleSheets).find((sheet) => {
|
||||||
|
try {
|
||||||
|
return Array.from(sheet.cssRules).some((rule: CSSRule) => {
|
||||||
|
return rule.cssText?.includes('.markdown')
|
||||||
|
})
|
||||||
|
} catch {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
if (markdownStyleSheet) {
|
||||||
|
const style = document.createElement('style')
|
||||||
|
const cssRules = Array.from(markdownStyleSheet.cssRules)
|
||||||
|
.map((rule) => rule.cssText)
|
||||||
|
.join('\n')
|
||||||
|
|
||||||
|
style.textContent = cssRules
|
||||||
|
shadow.appendChild(style)
|
||||||
|
}
|
||||||
|
|
||||||
|
setShadowRoot(shadow)
|
||||||
|
}, [])
|
||||||
|
|
||||||
|
if (!shadowRoot) {
|
||||||
|
return <div ref={hostRef} />
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div ref={hostRef}>
|
||||||
|
{createPortal(
|
||||||
|
<StyleSheetManager target={shadowRoot}>
|
||||||
|
<StyleProvider container={shadowRoot} layer>
|
||||||
|
{children}
|
||||||
|
</StyleProvider>
|
||||||
|
</StyleSheetManager>,
|
||||||
|
shadowRoot
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default ShadowDOMRenderer
|
||||||
@ -1,13 +1,14 @@
|
|||||||
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 MarkdownShadowDOMRenderer from '@renderer/components/MarkdownShadowDOMRenderer'
|
||||||
import { useSettings } from '@renderer/hooks/useSettings'
|
import { useSettings } from '@renderer/hooks/useSettings'
|
||||||
import { 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 { FC, useMemo } from 'react'
|
import { type FC, useMemo } from 'react'
|
||||||
import { useTranslation } from 'react-i18next'
|
import { useTranslation } from 'react-i18next'
|
||||||
import ReactMarkdown, { Components } from 'react-markdown'
|
import ReactMarkdown, { type Components } from 'react-markdown'
|
||||||
import rehypeKatex from 'rehype-katex'
|
import rehypeKatex from 'rehype-katex'
|
||||||
// @ts-ignore next-line
|
// @ts-ignore next-line
|
||||||
import rehypeMathjax from 'rehype-mathjax'
|
import rehypeMathjax from 'rehype-mathjax'
|
||||||
@ -52,11 +53,12 @@ const Markdown: FC<Props> = ({ message }) => {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<ReactMarkdown
|
<ReactMarkdown
|
||||||
className="markdown"
|
|
||||||
rehypePlugins={rehypePlugins}
|
rehypePlugins={rehypePlugins}
|
||||||
remarkPlugins={[remarkMath, remarkGfm]}
|
remarkPlugins={[remarkMath, remarkGfm]}
|
||||||
|
className="markdown"
|
||||||
components={
|
components={
|
||||||
{
|
{
|
||||||
|
style: MarkdownShadowDOMRenderer,
|
||||||
a: Link,
|
a: Link,
|
||||||
code: CodeBlock,
|
code: CodeBlock,
|
||||||
img: ImagePreview
|
img: ImagePreview
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user