feat: add svg preview
This commit is contained in:
parent
96737ed695
commit
0074d5c8b4
@ -94,15 +94,9 @@
|
||||
background-color: var(--color-border);
|
||||
}
|
||||
|
||||
.katex-display {
|
||||
span {
|
||||
white-space: pre;
|
||||
}
|
||||
}
|
||||
|
||||
span {
|
||||
white-space: pre-wrap;
|
||||
}
|
||||
|
||||
p code,
|
||||
li code {
|
||||
|
||||
@ -8,6 +8,7 @@ import styled from 'styled-components'
|
||||
|
||||
import Artifacts from './Artifacts'
|
||||
import Mermaid from './Mermaid'
|
||||
import SvgPreview from './SvgPreview'
|
||||
|
||||
interface CodeBlockProps {
|
||||
children: string
|
||||
@ -79,8 +80,20 @@ const CodeBlock: React.FC<CodeBlockProps> = ({ children, className }) => {
|
||||
return <Mermaid chart={children} />
|
||||
}
|
||||
|
||||
if (language === 'svg') {
|
||||
return (
|
||||
<CodeBlockWrapper className="code-block">
|
||||
<CodeHeader>
|
||||
<CodeLanguage>{'<SVG>'}</CodeLanguage>
|
||||
<CopyButton text={children} />
|
||||
</CodeHeader>
|
||||
<SvgPreview>{children}</SvgPreview>
|
||||
</CodeBlockWrapper>
|
||||
)
|
||||
}
|
||||
|
||||
return match ? (
|
||||
<div className="code-block">
|
||||
<CodeBlockWrapper className="code-block">
|
||||
<CodeHeader>
|
||||
<div style={{ display: 'flex', alignItems: 'center', gap: '8px' }}>
|
||||
{codeCollapsible && shouldShowExpandButton && (
|
||||
@ -118,7 +131,7 @@ const CodeBlock: React.FC<CodeBlockProps> = ({ children, className }) => {
|
||||
</CodeFooter>
|
||||
)}
|
||||
{language === 'html' && children?.includes('</html>') && <Artifacts html={children} />}
|
||||
</div>
|
||||
</CodeBlockWrapper>
|
||||
) : (
|
||||
<code className={className}>{children}</code>
|
||||
)
|
||||
@ -142,6 +155,8 @@ const CopyButton: React.FC<{ text: string; style?: React.CSSProperties }> = ({ t
|
||||
)
|
||||
}
|
||||
|
||||
const CodeBlockWrapper = styled.div``
|
||||
|
||||
const CodeContent = styled.div<{ isShowLineNumbers: boolean }>`
|
||||
.shiki {
|
||||
padding: 1em;
|
||||
|
||||
@ -2,7 +2,7 @@ import 'katex/dist/katex.min.css'
|
||||
|
||||
import { useSettings } from '@renderer/hooks/useSettings'
|
||||
import { Message } from '@renderer/types'
|
||||
import { escapeBrackets } from '@renderer/utils/formula'
|
||||
import { escapeBrackets, removeSvgEmptyLines } from '@renderer/utils/formula'
|
||||
import { isEmpty } from 'lodash'
|
||||
import { FC, useMemo } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
@ -19,7 +19,7 @@ import ImagePreview from './ImagePreview'
|
||||
import Link from './Link'
|
||||
|
||||
const ALLOWED_ELEMENTS =
|
||||
/<(style|p|div|span|b|i|strong|em|ul|ol|li|table|tr|td|th|thead|tbody|h[1-6]|blockquote|pre|code|br|hr)/i
|
||||
/<(style|p|div|span|b|i|strong|em|ul|ol|li|table|tr|td|th|thead|tbody|h[1-6]|blockquote|pre|code|br|hr|svg|path|circle|rect|line|polyline|polygon|text|g|defs|title|desc|tspan)/i
|
||||
|
||||
interface Props {
|
||||
message: Message
|
||||
@ -35,7 +35,7 @@ const Markdown: FC<Props> = ({ message }) => {
|
||||
const empty = isEmpty(message.content)
|
||||
const paused = message.status === 'paused'
|
||||
const content = empty && paused ? t('message.chat.completion.paused') : message.content
|
||||
return escapeBrackets(content)
|
||||
return removeSvgEmptyLines(escapeBrackets(content))
|
||||
}, [message.content, message.status, t])
|
||||
|
||||
const rehypePlugins = useMemo(() => {
|
||||
@ -52,7 +52,6 @@ const Markdown: FC<Props> = ({ message }) => {
|
||||
className="markdown"
|
||||
rehypePlugins={rehypePlugins}
|
||||
remarkPlugins={[remarkMath, remarkGfm]}
|
||||
disallowedElements={mathEngine === 'KaTeX' ? ['style'] : []}
|
||||
components={
|
||||
{
|
||||
a: Link,
|
||||
|
||||
16
src/renderer/src/pages/home/Markdown/SvgPreview.tsx
Normal file
16
src/renderer/src/pages/home/Markdown/SvgPreview.tsx
Normal file
@ -0,0 +1,16 @@
|
||||
const SvgPreview = ({ children }: { children: string }) => {
|
||||
return (
|
||||
<div
|
||||
dangerouslySetInnerHTML={{ __html: children }}
|
||||
style={{
|
||||
padding: '1em',
|
||||
backgroundColor: 'white',
|
||||
border: '0.5px solid var(--color-code-background)',
|
||||
borderTopLeftRadius: 0,
|
||||
borderTopRightRadius: 0
|
||||
}}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
export default SvgPreview
|
||||
@ -43,3 +43,16 @@ export function extractTitle(html: string): string | null {
|
||||
|
||||
return null
|
||||
}
|
||||
|
||||
export function removeSvgEmptyLines(text: string): string {
|
||||
// 用正则表达式匹配 <svg> 标签内的内容
|
||||
const svgPattern = /(<svg[\s\S]*?<\/svg>)/g
|
||||
|
||||
return text.replace(svgPattern, (svgMatch) => {
|
||||
// 将 SVG 内容按行分割,过滤掉空行,然后重新组合
|
||||
return svgMatch
|
||||
.split('\n')
|
||||
.filter((line) => line.trim() !== '')
|
||||
.join('\n')
|
||||
})
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user