mirror of
https://github.com/CherryHQ/cherry-studio.git
synced 2025-12-29 05:51:26 +08:00
feat(TranslatePage): replace ReactMarkdown with MarkdownIt. (#7545)
* feat(TranslatePage): replace ReactMarkdown with MarkdownIt. * fix: line wrapping in plain text and shiki code block --------- Co-authored-by: one <wangan.cs@gmail.com>
This commit is contained in:
parent
ad0b10c517
commit
acbe8c7605
@ -4,6 +4,7 @@ import CopyIcon from '@renderer/components/Icons/CopyIcon'
|
||||
import { HStack } from '@renderer/components/Layout'
|
||||
import { isEmbeddingModel } from '@renderer/config/models'
|
||||
import { translateLanguageOptions } from '@renderer/config/translate'
|
||||
import { useCodeStyle } from '@renderer/context/CodeStyleProvider'
|
||||
import db from '@renderer/databases'
|
||||
import { useDefaultModel } from '@renderer/hooks/useAssistant'
|
||||
import { useProviders } from '@renderer/hooks/useProvider'
|
||||
@ -26,7 +27,6 @@ import { find, isEmpty, sortBy } from 'lodash'
|
||||
import { HelpCircle, Settings2, TriangleAlert } from 'lucide-react'
|
||||
import { FC, useEffect, useMemo, useRef, useState } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import ReactMarkdown from 'react-markdown'
|
||||
import styled from 'styled-components'
|
||||
|
||||
let _text = ''
|
||||
@ -216,9 +216,11 @@ const TranslateSettings: FC<{
|
||||
|
||||
const TranslatePage: FC = () => {
|
||||
const { t } = useTranslation()
|
||||
const { shikiMarkdownIt } = useCodeStyle()
|
||||
const [targetLanguage, setTargetLanguage] = useState(_targetLanguage)
|
||||
const [text, setText] = useState(_text)
|
||||
const [result, setResult] = useState(_result)
|
||||
const [renderedMarkdown, setRenderedMarkdown] = useState<string>('')
|
||||
const { translateModel, setTranslateModel } = useDefaultModel()
|
||||
const [loading, setLoading] = useState(false)
|
||||
const [copied, setCopied] = useState(false)
|
||||
@ -379,6 +381,24 @@ const TranslatePage: FC = () => {
|
||||
isEmpty(text) && setResult('')
|
||||
}, [text])
|
||||
|
||||
// Render markdown content when result or enableMarkdown changes
|
||||
useEffect(() => {
|
||||
if (enableMarkdown && result) {
|
||||
let isMounted = true
|
||||
shikiMarkdownIt(result).then((rendered) => {
|
||||
if (isMounted) {
|
||||
setRenderedMarkdown(rendered)
|
||||
}
|
||||
})
|
||||
return () => {
|
||||
isMounted = false
|
||||
}
|
||||
} else {
|
||||
setRenderedMarkdown('')
|
||||
return undefined
|
||||
}
|
||||
}, [result, enableMarkdown, shikiMarkdownIt])
|
||||
|
||||
useEffect(() => {
|
||||
runAsyncFunction(async () => {
|
||||
const targetLang = await db.settings.get({ id: 'translate:target:language' })
|
||||
@ -607,13 +627,13 @@ const TranslatePage: FC = () => {
|
||||
/>
|
||||
</OperationBar>
|
||||
|
||||
<OutputText ref={outputTextRef} onScroll={handleOutputScroll} className="selectable">
|
||||
<OutputText ref={outputTextRef} onScroll={handleOutputScroll} className={'selectable'}>
|
||||
{!result ? (
|
||||
t('translate.output.placeholder')
|
||||
) : enableMarkdown ? (
|
||||
<ReactMarkdown>{result}</ReactMarkdown>
|
||||
<div className="markdown" dangerouslySetInnerHTML={{ __html: renderedMarkdown }} />
|
||||
) : (
|
||||
result
|
||||
<div className="plain">{result}</div>
|
||||
)}
|
||||
</OutputText>
|
||||
</OutputContainer>
|
||||
@ -703,7 +723,19 @@ const OutputText = styled.div`
|
||||
flex: 1;
|
||||
padding: 5px 16px;
|
||||
overflow-y: auto;
|
||||
white-space: pre-wrap;
|
||||
|
||||
.plain {
|
||||
white-space: pre-wrap;
|
||||
overflow-wrap: break-word;
|
||||
}
|
||||
|
||||
.markdown {
|
||||
/* for shiki code block overflow */
|
||||
.line * {
|
||||
white-space: pre-wrap;
|
||||
overflow-wrap: break-word;
|
||||
}
|
||||
}
|
||||
`
|
||||
|
||||
const TranslateButton = styled(Button)``
|
||||
|
||||
@ -149,13 +149,27 @@ export async function getMarkdownIt(theme: string, markdown: string) {
|
||||
const md = await mdInitializer.get()
|
||||
const { fromHighlighter } = await import('@shikijs/markdown-it/core')
|
||||
|
||||
let actualTheme = theme
|
||||
try {
|
||||
actualTheme = await loadThemeIfNeeded(highlighter, theme)
|
||||
} catch (error) {
|
||||
console.debug(`Failed to load theme '${theme}', using 'one-light' as fallback:`, error)
|
||||
actualTheme = 'one-light'
|
||||
}
|
||||
|
||||
const themes: Record<string, string> = {
|
||||
'one-light': 'one-light',
|
||||
'material-theme-darker': 'material-theme-darker'
|
||||
}
|
||||
|
||||
if (actualTheme !== 'one-light' && actualTheme !== 'material-theme-darker') {
|
||||
themes[actualTheme] = actualTheme
|
||||
}
|
||||
|
||||
md.use(
|
||||
fromHighlighter(highlighter, {
|
||||
themes: {
|
||||
'one-light': 'one-light',
|
||||
'material-theme-darker': 'material-theme-darker'
|
||||
},
|
||||
defaultColor: theme,
|
||||
themes,
|
||||
defaultColor: actualTheme,
|
||||
defaultLanguage: 'json',
|
||||
fallbackLanguage: 'json'
|
||||
})
|
||||
|
||||
Loading…
Reference in New Issue
Block a user