mirror of
https://github.com/CherryHQ/cherry-studio.git
synced 2026-01-01 17:59:09 +08:00
feat(Markdown): support disabling single dollar math (#9131)
* feat(Markdown): support disabling single dollar math * fix: lint error
This commit is contained in:
parent
ceef19e55b
commit
4cda5f1787
@ -2715,6 +2715,17 @@
|
|||||||
"title": "Launch",
|
"title": "Launch",
|
||||||
"totray": "Minimize to Tray on Launch"
|
"totray": "Minimize to Tray on Launch"
|
||||||
},
|
},
|
||||||
|
"math": {
|
||||||
|
"engine": {
|
||||||
|
"label": "Math engine",
|
||||||
|
"none": "None"
|
||||||
|
},
|
||||||
|
"single_dollar": {
|
||||||
|
"label": "Enable $...$",
|
||||||
|
"tip": "Render math equations quoted by single dollar signs $...$. Default is enabled."
|
||||||
|
},
|
||||||
|
"title": "Math Settings"
|
||||||
|
},
|
||||||
"mcp": {
|
"mcp": {
|
||||||
"actions": "Actions",
|
"actions": "Actions",
|
||||||
"active": "Active",
|
"active": "Active",
|
||||||
@ -2945,10 +2956,6 @@
|
|||||||
"title": "Input Settings"
|
"title": "Input Settings"
|
||||||
},
|
},
|
||||||
"markdown_rendering_input_message": "Markdown render input message",
|
"markdown_rendering_input_message": "Markdown render input message",
|
||||||
"math_engine": {
|
|
||||||
"label": "Math engine",
|
|
||||||
"none": "None"
|
|
||||||
},
|
|
||||||
"metrics": "{{time_first_token_millsec}}ms to first token | {{token_speed}} tok/sec",
|
"metrics": "{{time_first_token_millsec}}ms to first token | {{token_speed}} tok/sec",
|
||||||
"model": {
|
"model": {
|
||||||
"title": "Model Settings"
|
"title": "Model Settings"
|
||||||
|
|||||||
@ -2715,6 +2715,17 @@
|
|||||||
"title": "起動",
|
"title": "起動",
|
||||||
"totray": "起動時にトレイに最小化"
|
"totray": "起動時にトレイに最小化"
|
||||||
},
|
},
|
||||||
|
"math": {
|
||||||
|
"engine": {
|
||||||
|
"label": "数式エンジン",
|
||||||
|
"none": "なし"
|
||||||
|
},
|
||||||
|
"single_dollar": {
|
||||||
|
"label": "$...$ を有効にする",
|
||||||
|
"tip": "単一のドル記号 $...$ で囲まれた数式をレンダリングします。デフォルトで有効です。"
|
||||||
|
},
|
||||||
|
"title": "数式設定"
|
||||||
|
},
|
||||||
"mcp": {
|
"mcp": {
|
||||||
"actions": "操作",
|
"actions": "操作",
|
||||||
"active": "有効",
|
"active": "有効",
|
||||||
@ -2945,10 +2956,6 @@
|
|||||||
"title": "入力設定"
|
"title": "入力設定"
|
||||||
},
|
},
|
||||||
"markdown_rendering_input_message": "Markdownで入力メッセージをレンダリング",
|
"markdown_rendering_input_message": "Markdownで入力メッセージをレンダリング",
|
||||||
"math_engine": {
|
|
||||||
"label": "数式エンジン",
|
|
||||||
"none": "なし"
|
|
||||||
},
|
|
||||||
"metrics": "最初のトークンまでの時間 {{time_first_token_millsec}}ms | トークン速度 {{token_speed}} tok/sec",
|
"metrics": "最初のトークンまでの時間 {{time_first_token_millsec}}ms | トークン速度 {{token_speed}} tok/sec",
|
||||||
"model": {
|
"model": {
|
||||||
"title": "モデル設定"
|
"title": "モデル設定"
|
||||||
|
|||||||
@ -2715,6 +2715,17 @@
|
|||||||
"title": "Запуск",
|
"title": "Запуск",
|
||||||
"totray": "Свернуть в трей при запуске"
|
"totray": "Свернуть в трей при запуске"
|
||||||
},
|
},
|
||||||
|
"math": {
|
||||||
|
"engine": {
|
||||||
|
"label": "Математический движок",
|
||||||
|
"none": "Нет"
|
||||||
|
},
|
||||||
|
"single_dollar": {
|
||||||
|
"label": "Включить $...$",
|
||||||
|
"tip": "Отображать математические формулы, заключенные в одиночные символы доллара $...$. По умолчанию включено."
|
||||||
|
},
|
||||||
|
"title": "Настройки математических формул"
|
||||||
|
},
|
||||||
"mcp": {
|
"mcp": {
|
||||||
"actions": "Действия",
|
"actions": "Действия",
|
||||||
"active": "Активен",
|
"active": "Активен",
|
||||||
@ -2945,10 +2956,6 @@
|
|||||||
"title": "Настройки ввода"
|
"title": "Настройки ввода"
|
||||||
},
|
},
|
||||||
"markdown_rendering_input_message": "Отображение ввода в формате Markdown",
|
"markdown_rendering_input_message": "Отображение ввода в формате Markdown",
|
||||||
"math_engine": {
|
|
||||||
"label": "Математический движок",
|
|
||||||
"none": "Нет"
|
|
||||||
},
|
|
||||||
"metrics": "{{time_first_token_millsec}}ms до первого токена | {{token_speed}} tok/sec",
|
"metrics": "{{time_first_token_millsec}}ms до первого токена | {{token_speed}} tok/sec",
|
||||||
"model": {
|
"model": {
|
||||||
"title": "Настройки модели"
|
"title": "Настройки модели"
|
||||||
|
|||||||
@ -2715,6 +2715,17 @@
|
|||||||
"title": "启动",
|
"title": "启动",
|
||||||
"totray": "启动时最小化到托盘"
|
"totray": "启动时最小化到托盘"
|
||||||
},
|
},
|
||||||
|
"math": {
|
||||||
|
"engine": {
|
||||||
|
"label": "数学公式引擎",
|
||||||
|
"none": "无"
|
||||||
|
},
|
||||||
|
"single_dollar": {
|
||||||
|
"label": "启用 $...$",
|
||||||
|
"tip": "渲染单个美元符号 $...$ 包裹的数学公式,默认启用。"
|
||||||
|
},
|
||||||
|
"title": "数学公式设置"
|
||||||
|
},
|
||||||
"mcp": {
|
"mcp": {
|
||||||
"actions": "操作",
|
"actions": "操作",
|
||||||
"active": "启用",
|
"active": "启用",
|
||||||
@ -2945,10 +2956,6 @@
|
|||||||
"title": "输入设置"
|
"title": "输入设置"
|
||||||
},
|
},
|
||||||
"markdown_rendering_input_message": "Markdown 渲染输入消息",
|
"markdown_rendering_input_message": "Markdown 渲染输入消息",
|
||||||
"math_engine": {
|
|
||||||
"label": "数学公式引擎",
|
|
||||||
"none": "无"
|
|
||||||
},
|
|
||||||
"metrics": "首字时延 {{time_first_token_millsec}} ms | 每秒 {{token_speed}} tokens",
|
"metrics": "首字时延 {{time_first_token_millsec}} ms | 每秒 {{token_speed}} tokens",
|
||||||
"model": {
|
"model": {
|
||||||
"title": "模型设置"
|
"title": "模型设置"
|
||||||
|
|||||||
@ -2715,6 +2715,17 @@
|
|||||||
"title": "啟動",
|
"title": "啟動",
|
||||||
"totray": "啟動時最小化到系统匣"
|
"totray": "啟動時最小化到系统匣"
|
||||||
},
|
},
|
||||||
|
"math": {
|
||||||
|
"engine": {
|
||||||
|
"label": "數學公式引擎",
|
||||||
|
"none": "無"
|
||||||
|
},
|
||||||
|
"single_dollar": {
|
||||||
|
"label": "啟用 $...$",
|
||||||
|
"tip": "渲染單個美元符號 $...$ 包裹的數學公式,默認啟用。"
|
||||||
|
},
|
||||||
|
"title": "數學公式設定"
|
||||||
|
},
|
||||||
"mcp": {
|
"mcp": {
|
||||||
"actions": "操作",
|
"actions": "操作",
|
||||||
"active": "啟用",
|
"active": "啟用",
|
||||||
@ -2945,10 +2956,6 @@
|
|||||||
"title": "輸入設定"
|
"title": "輸入設定"
|
||||||
},
|
},
|
||||||
"markdown_rendering_input_message": "Markdown 渲染輸入訊息",
|
"markdown_rendering_input_message": "Markdown 渲染輸入訊息",
|
||||||
"math_engine": {
|
|
||||||
"label": "數學公式引擎",
|
|
||||||
"none": "無"
|
|
||||||
},
|
|
||||||
"metrics": "首字延遲 {{time_first_token_millsec}} ms | 每秒 {{token_speed}} tokens",
|
"metrics": "首字延遲 {{time_first_token_millsec}} ms | 每秒 {{token_speed}} tokens",
|
||||||
"model": {
|
"model": {
|
||||||
"title": "模型設定"
|
"title": "模型設定"
|
||||||
|
|||||||
@ -46,7 +46,7 @@ interface Props {
|
|||||||
|
|
||||||
const Markdown: FC<Props> = ({ block, postProcess }) => {
|
const Markdown: FC<Props> = ({ block, postProcess }) => {
|
||||||
const { t } = useTranslation()
|
const { t } = useTranslation()
|
||||||
const { mathEngine } = useSettings()
|
const { mathEngine, mathEnableSingleDollar } = useSettings()
|
||||||
|
|
||||||
const isTrulyDone = 'status' in block && block.status === 'success'
|
const isTrulyDone = 'status' in block && block.status === 'success'
|
||||||
const [displayedContent, setDisplayedContent] = useState(postProcess ? postProcess(block.content) : block.content)
|
const [displayedContent, setDisplayedContent] = useState(postProcess ? postProcess(block.content) : block.content)
|
||||||
@ -98,10 +98,10 @@ const Markdown: FC<Props> = ({ block, postProcess }) => {
|
|||||||
remarkDisableConstructs(['codeIndented'])
|
remarkDisableConstructs(['codeIndented'])
|
||||||
]
|
]
|
||||||
if (mathEngine !== 'none') {
|
if (mathEngine !== 'none') {
|
||||||
plugins.push(remarkMath)
|
plugins.push([remarkMath, { singleDollarTextMath: mathEnableSingleDollar }])
|
||||||
}
|
}
|
||||||
return plugins
|
return plugins
|
||||||
}, [mathEngine])
|
}, [mathEngine, mathEnableSingleDollar])
|
||||||
|
|
||||||
const messageContent = useMemo(() => {
|
const messageContent = useMemo(() => {
|
||||||
if ('status' in block && block.status === 'paused' && isEmpty(block.content)) {
|
if ('status' in block && block.status === 'paused' && isEmpty(block.content)) {
|
||||||
|
|||||||
@ -144,7 +144,7 @@ describe('Markdown', () => {
|
|||||||
vi.clearAllMocks()
|
vi.clearAllMocks()
|
||||||
|
|
||||||
// Default settings
|
// Default settings
|
||||||
mockUseSettings.mockReturnValue({ mathEngine: 'KaTeX' })
|
mockUseSettings.mockReturnValue({ mathEngine: 'KaTeX', mathEnableSingleDollar: true })
|
||||||
mockUseTranslation.mockReturnValue({
|
mockUseTranslation.mockReturnValue({
|
||||||
t: (key: string) => (key === 'message.chat.completion.paused' ? 'Paused' : key)
|
t: (key: string) => (key === 'message.chat.completion.paused' ? 'Paused' : key)
|
||||||
})
|
})
|
||||||
@ -270,7 +270,7 @@ describe('Markdown', () => {
|
|||||||
|
|
||||||
describe('math engine configuration', () => {
|
describe('math engine configuration', () => {
|
||||||
it('should configure KaTeX when mathEngine is KaTeX', () => {
|
it('should configure KaTeX when mathEngine is KaTeX', () => {
|
||||||
mockUseSettings.mockReturnValue({ mathEngine: 'KaTeX' })
|
mockUseSettings.mockReturnValue({ mathEngine: 'KaTeX', mathEnableSingleDollar: true })
|
||||||
|
|
||||||
render(<Markdown block={createMainTextBlock()} />)
|
render(<Markdown block={createMainTextBlock()} />)
|
||||||
|
|
||||||
@ -279,7 +279,7 @@ describe('Markdown', () => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
it('should configure MathJax when mathEngine is MathJax', () => {
|
it('should configure MathJax when mathEngine is MathJax', () => {
|
||||||
mockUseSettings.mockReturnValue({ mathEngine: 'MathJax' })
|
mockUseSettings.mockReturnValue({ mathEngine: 'MathJax', mathEnableSingleDollar: true })
|
||||||
|
|
||||||
render(<Markdown block={createMainTextBlock()} />)
|
render(<Markdown block={createMainTextBlock()} />)
|
||||||
|
|
||||||
@ -288,7 +288,7 @@ describe('Markdown', () => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
it('should not load math plugins when mathEngine is none', () => {
|
it('should not load math plugins when mathEngine is none', () => {
|
||||||
mockUseSettings.mockReturnValue({ mathEngine: 'none' })
|
mockUseSettings.mockReturnValue({ mathEngine: 'none', mathEnableSingleDollar: true })
|
||||||
|
|
||||||
render(<Markdown block={createMainTextBlock()} />)
|
render(<Markdown block={createMainTextBlock()} />)
|
||||||
|
|
||||||
@ -384,12 +384,12 @@ describe('Markdown', () => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
it('should re-render when math engine changes', () => {
|
it('should re-render when math engine changes', () => {
|
||||||
mockUseSettings.mockReturnValue({ mathEngine: 'KaTeX' })
|
mockUseSettings.mockReturnValue({ mathEngine: 'KaTeX', mathEnableSingleDollar: true })
|
||||||
const { rerender } = render(<Markdown block={createMainTextBlock()} />)
|
const { rerender } = render(<Markdown block={createMainTextBlock()} />)
|
||||||
|
|
||||||
expect(screen.getByTestId('markdown-content')).toBeInTheDocument()
|
expect(screen.getByTestId('markdown-content')).toBeInTheDocument()
|
||||||
|
|
||||||
mockUseSettings.mockReturnValue({ mathEngine: 'MathJax' })
|
mockUseSettings.mockReturnValue({ mathEngine: 'MathJax', mathEnableSingleDollar: true })
|
||||||
rerender(<Markdown block={createMainTextBlock()} />)
|
rerender(<Markdown block={createMainTextBlock()} />)
|
||||||
|
|
||||||
// Should still render correctly with new math engine
|
// Should still render correctly with new math engine
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
import type { Root, Node, Element, Text } from 'hast'
|
import type { Element, Node, Root, Text } from 'hast'
|
||||||
import { visit } from 'unist-util-visit'
|
import { visit } from 'unist-util-visit'
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@ -29,6 +29,7 @@ import {
|
|||||||
setEnableBackspaceDeleteModel,
|
setEnableBackspaceDeleteModel,
|
||||||
setEnableQuickPanelTriggers,
|
setEnableQuickPanelTriggers,
|
||||||
setFontSize,
|
setFontSize,
|
||||||
|
setMathEnableSingleDollar,
|
||||||
setMathEngine,
|
setMathEngine,
|
||||||
setMessageFont,
|
setMessageFont,
|
||||||
setMessageNavigation,
|
setMessageNavigation,
|
||||||
@ -97,6 +98,7 @@ const SettingsTab: FC<Props> = (props) => {
|
|||||||
codeImageTools,
|
codeImageTools,
|
||||||
codeExecution,
|
codeExecution,
|
||||||
mathEngine,
|
mathEngine,
|
||||||
|
mathEnableSingleDollar,
|
||||||
autoTranslateWithSpace,
|
autoTranslateWithSpace,
|
||||||
pasteLongTextThreshold,
|
pasteLongTextThreshold,
|
||||||
multiModelMessageStyle,
|
multiModelMessageStyle,
|
||||||
@ -382,19 +384,6 @@ const SettingsTab: FC<Props> = (props) => {
|
|||||||
/>
|
/>
|
||||||
</SettingRow>
|
</SettingRow>
|
||||||
<SettingDivider />
|
<SettingDivider />
|
||||||
<SettingRow>
|
|
||||||
<SettingRowTitleSmall>{t('settings.messages.math_engine.label')}</SettingRowTitleSmall>
|
|
||||||
<Selector
|
|
||||||
value={mathEngine}
|
|
||||||
onChange={(value) => dispatch(setMathEngine(value as MathEngine))}
|
|
||||||
options={[
|
|
||||||
{ value: 'KaTeX', label: 'KaTeX' },
|
|
||||||
{ value: 'MathJax', label: 'MathJax' },
|
|
||||||
{ value: 'none', label: t('settings.messages.math_engine.none') }
|
|
||||||
]}
|
|
||||||
/>
|
|
||||||
</SettingRow>
|
|
||||||
<SettingDivider />
|
|
||||||
<SettingRow>
|
<SettingRow>
|
||||||
<SettingRowTitleSmall>{t('settings.font_size.title')}</SettingRowTitleSmall>
|
<SettingRowTitleSmall>{t('settings.font_size.title')}</SettingRowTitleSmall>
|
||||||
</SettingRow>
|
</SettingRow>
|
||||||
@ -418,6 +407,37 @@ const SettingsTab: FC<Props> = (props) => {
|
|||||||
<SettingDivider />
|
<SettingDivider />
|
||||||
</SettingGroup>
|
</SettingGroup>
|
||||||
</CollapsibleSettingGroup>
|
</CollapsibleSettingGroup>
|
||||||
|
<CollapsibleSettingGroup title={t('settings.math.title')} defaultExpanded={true}>
|
||||||
|
<SettingGroup>
|
||||||
|
<SettingRow>
|
||||||
|
<SettingRowTitleSmall>{t('settings.math.engine.label')}</SettingRowTitleSmall>
|
||||||
|
<Selector
|
||||||
|
value={mathEngine}
|
||||||
|
onChange={(value) => dispatch(setMathEngine(value as MathEngine))}
|
||||||
|
options={[
|
||||||
|
{ value: 'KaTeX', label: 'KaTeX' },
|
||||||
|
{ value: 'MathJax', label: 'MathJax' },
|
||||||
|
{ value: 'none', label: t('settings.math.engine.none') }
|
||||||
|
]}
|
||||||
|
/>
|
||||||
|
</SettingRow>
|
||||||
|
<SettingDivider />
|
||||||
|
<SettingRow>
|
||||||
|
<SettingRowTitleSmall>
|
||||||
|
{t('settings.math.single_dollar.label')}{' '}
|
||||||
|
<Tooltip title={t('settings.math.single_dollar.tip')}>
|
||||||
|
<CircleHelp size={14} style={{ marginLeft: 4 }} color="var(--color-text-2)" />
|
||||||
|
</Tooltip>
|
||||||
|
</SettingRowTitleSmall>
|
||||||
|
<Switch
|
||||||
|
size="small"
|
||||||
|
checked={mathEnableSingleDollar}
|
||||||
|
onChange={(checked) => dispatch(setMathEnableSingleDollar(checked))}
|
||||||
|
/>
|
||||||
|
</SettingRow>
|
||||||
|
<SettingDivider />
|
||||||
|
</SettingGroup>
|
||||||
|
</CollapsibleSettingGroup>
|
||||||
<CollapsibleSettingGroup title={t('chat.settings.code.title')} defaultExpanded={true}>
|
<CollapsibleSettingGroup title={t('chat.settings.code.title')} defaultExpanded={true}>
|
||||||
<SettingGroup>
|
<SettingGroup>
|
||||||
<SettingRow>
|
<SettingRow>
|
||||||
|
|||||||
@ -62,7 +62,7 @@ const persistedReducer = persistReducer(
|
|||||||
{
|
{
|
||||||
key: 'cherry-studio',
|
key: 'cherry-studio',
|
||||||
storage,
|
storage,
|
||||||
version: 130,
|
version: 131,
|
||||||
blacklist: ['runtime', 'messages', 'messageBlocks', 'tabs'],
|
blacklist: ['runtime', 'messages', 'messageBlocks', 'tabs'],
|
||||||
migrate
|
migrate
|
||||||
},
|
},
|
||||||
|
|||||||
@ -2094,6 +2094,15 @@ const migrateConfig = {
|
|||||||
logger.error('migrate 130 error', error as Error)
|
logger.error('migrate 130 error', error as Error)
|
||||||
return state
|
return state
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
'131': (state: RootState) => {
|
||||||
|
try {
|
||||||
|
state.settings.mathEnableSingleDollar = true
|
||||||
|
return state
|
||||||
|
} catch (error) {
|
||||||
|
logger.error('migrate 131 error', error as Error)
|
||||||
|
return state
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -105,6 +105,7 @@ export interface SettingsState {
|
|||||||
codeWrappable: boolean
|
codeWrappable: boolean
|
||||||
codeImageTools: boolean
|
codeImageTools: boolean
|
||||||
mathEngine: MathEngine
|
mathEngine: MathEngine
|
||||||
|
mathEnableSingleDollar: boolean
|
||||||
messageStyle: 'plain' | 'bubble'
|
messageStyle: 'plain' | 'bubble'
|
||||||
foldDisplayMode: 'expanded' | 'compact'
|
foldDisplayMode: 'expanded' | 'compact'
|
||||||
gridColumns: number
|
gridColumns: number
|
||||||
@ -287,6 +288,7 @@ export const initialState: SettingsState = {
|
|||||||
codeWrappable: false,
|
codeWrappable: false,
|
||||||
codeImageTools: false,
|
codeImageTools: false,
|
||||||
mathEngine: 'KaTeX',
|
mathEngine: 'KaTeX',
|
||||||
|
mathEnableSingleDollar: true,
|
||||||
messageStyle: 'plain',
|
messageStyle: 'plain',
|
||||||
foldDisplayMode: 'expanded',
|
foldDisplayMode: 'expanded',
|
||||||
gridColumns: 2,
|
gridColumns: 2,
|
||||||
@ -616,6 +618,9 @@ const settingsSlice = createSlice({
|
|||||||
setMathEngine: (state, action: PayloadAction<MathEngine>) => {
|
setMathEngine: (state, action: PayloadAction<MathEngine>) => {
|
||||||
state.mathEngine = action.payload
|
state.mathEngine = action.payload
|
||||||
},
|
},
|
||||||
|
setMathEnableSingleDollar: (state, action: PayloadAction<boolean>) => {
|
||||||
|
state.mathEnableSingleDollar = action.payload
|
||||||
|
},
|
||||||
setFoldDisplayMode: (state, action: PayloadAction<'expanded' | 'compact'>) => {
|
setFoldDisplayMode: (state, action: PayloadAction<'expanded' | 'compact'>) => {
|
||||||
state.foldDisplayMode = action.payload
|
state.foldDisplayMode = action.payload
|
||||||
},
|
},
|
||||||
@ -898,6 +903,7 @@ export const {
|
|||||||
setCodeWrappable,
|
setCodeWrappable,
|
||||||
setCodeImageTools,
|
setCodeImageTools,
|
||||||
setMathEngine,
|
setMathEngine,
|
||||||
|
setMathEnableSingleDollar,
|
||||||
setFoldDisplayMode,
|
setFoldDisplayMode,
|
||||||
setGridColumns,
|
setGridColumns,
|
||||||
setGridPopoverTrigger,
|
setGridPopoverTrigger,
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user