diff --git a/src/renderer/src/i18n/locales/en-us.json b/src/renderer/src/i18n/locales/en-us.json index b243bf7548..5b2169ebdd 100644 --- a/src/renderer/src/i18n/locales/en-us.json +++ b/src/renderer/src/i18n/locales/en-us.json @@ -2715,6 +2715,17 @@ "title": "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": { "actions": "Actions", "active": "Active", @@ -2945,10 +2956,6 @@ "title": "Input Settings" }, "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", "model": { "title": "Model Settings" diff --git a/src/renderer/src/i18n/locales/ja-jp.json b/src/renderer/src/i18n/locales/ja-jp.json index 55229c8cdf..c9dfd6418b 100644 --- a/src/renderer/src/i18n/locales/ja-jp.json +++ b/src/renderer/src/i18n/locales/ja-jp.json @@ -2715,6 +2715,17 @@ "title": "起動", "totray": "起動時にトレイに最小化" }, + "math": { + "engine": { + "label": "数式エンジン", + "none": "なし" + }, + "single_dollar": { + "label": "$...$ を有効にする", + "tip": "単一のドル記号 $...$ で囲まれた数式をレンダリングします。デフォルトで有効です。" + }, + "title": "数式設定" + }, "mcp": { "actions": "操作", "active": "有効", @@ -2945,10 +2956,6 @@ "title": "入力設定" }, "markdown_rendering_input_message": "Markdownで入力メッセージをレンダリング", - "math_engine": { - "label": "数式エンジン", - "none": "なし" - }, "metrics": "最初のトークンまでの時間 {{time_first_token_millsec}}ms | トークン速度 {{token_speed}} tok/sec", "model": { "title": "モデル設定" diff --git a/src/renderer/src/i18n/locales/ru-ru.json b/src/renderer/src/i18n/locales/ru-ru.json index 9cff784fd9..e7b000c671 100644 --- a/src/renderer/src/i18n/locales/ru-ru.json +++ b/src/renderer/src/i18n/locales/ru-ru.json @@ -2715,6 +2715,17 @@ "title": "Запуск", "totray": "Свернуть в трей при запуске" }, + "math": { + "engine": { + "label": "Математический движок", + "none": "Нет" + }, + "single_dollar": { + "label": "Включить $...$", + "tip": "Отображать математические формулы, заключенные в одиночные символы доллара $...$. По умолчанию включено." + }, + "title": "Настройки математических формул" + }, "mcp": { "actions": "Действия", "active": "Активен", @@ -2945,10 +2956,6 @@ "title": "Настройки ввода" }, "markdown_rendering_input_message": "Отображение ввода в формате Markdown", - "math_engine": { - "label": "Математический движок", - "none": "Нет" - }, "metrics": "{{time_first_token_millsec}}ms до первого токена | {{token_speed}} tok/sec", "model": { "title": "Настройки модели" diff --git a/src/renderer/src/i18n/locales/zh-cn.json b/src/renderer/src/i18n/locales/zh-cn.json index 81a7094f83..8aedda3c48 100644 --- a/src/renderer/src/i18n/locales/zh-cn.json +++ b/src/renderer/src/i18n/locales/zh-cn.json @@ -2715,6 +2715,17 @@ "title": "启动", "totray": "启动时最小化到托盘" }, + "math": { + "engine": { + "label": "数学公式引擎", + "none": "无" + }, + "single_dollar": { + "label": "启用 $...$", + "tip": "渲染单个美元符号 $...$ 包裹的数学公式,默认启用。" + }, + "title": "数学公式设置" + }, "mcp": { "actions": "操作", "active": "启用", @@ -2945,10 +2956,6 @@ "title": "输入设置" }, "markdown_rendering_input_message": "Markdown 渲染输入消息", - "math_engine": { - "label": "数学公式引擎", - "none": "无" - }, "metrics": "首字时延 {{time_first_token_millsec}} ms | 每秒 {{token_speed}} tokens", "model": { "title": "模型设置" diff --git a/src/renderer/src/i18n/locales/zh-tw.json b/src/renderer/src/i18n/locales/zh-tw.json index 8af0f5477b..188a7782c1 100644 --- a/src/renderer/src/i18n/locales/zh-tw.json +++ b/src/renderer/src/i18n/locales/zh-tw.json @@ -2715,6 +2715,17 @@ "title": "啟動", "totray": "啟動時最小化到系统匣" }, + "math": { + "engine": { + "label": "數學公式引擎", + "none": "無" + }, + "single_dollar": { + "label": "啟用 $...$", + "tip": "渲染單個美元符號 $...$ 包裹的數學公式,默認啟用。" + }, + "title": "數學公式設定" + }, "mcp": { "actions": "操作", "active": "啟用", @@ -2945,10 +2956,6 @@ "title": "輸入設定" }, "markdown_rendering_input_message": "Markdown 渲染輸入訊息", - "math_engine": { - "label": "數學公式引擎", - "none": "無" - }, "metrics": "首字延遲 {{time_first_token_millsec}} ms | 每秒 {{token_speed}} tokens", "model": { "title": "模型設定" diff --git a/src/renderer/src/pages/home/Markdown/Markdown.tsx b/src/renderer/src/pages/home/Markdown/Markdown.tsx index d0590b1496..98a24b8735 100644 --- a/src/renderer/src/pages/home/Markdown/Markdown.tsx +++ b/src/renderer/src/pages/home/Markdown/Markdown.tsx @@ -46,7 +46,7 @@ interface Props { const Markdown: FC = ({ block, postProcess }) => { const { t } = useTranslation() - const { mathEngine } = useSettings() + const { mathEngine, mathEnableSingleDollar } = useSettings() const isTrulyDone = 'status' in block && block.status === 'success' const [displayedContent, setDisplayedContent] = useState(postProcess ? postProcess(block.content) : block.content) @@ -98,10 +98,10 @@ const Markdown: FC = ({ block, postProcess }) => { remarkDisableConstructs(['codeIndented']) ] if (mathEngine !== 'none') { - plugins.push(remarkMath) + plugins.push([remarkMath, { singleDollarTextMath: mathEnableSingleDollar }]) } return plugins - }, [mathEngine]) + }, [mathEngine, mathEnableSingleDollar]) const messageContent = useMemo(() => { if ('status' in block && block.status === 'paused' && isEmpty(block.content)) { diff --git a/src/renderer/src/pages/home/Markdown/__tests__/Markdown.test.tsx b/src/renderer/src/pages/home/Markdown/__tests__/Markdown.test.tsx index 6adb5f5736..b7e1ee8b52 100644 --- a/src/renderer/src/pages/home/Markdown/__tests__/Markdown.test.tsx +++ b/src/renderer/src/pages/home/Markdown/__tests__/Markdown.test.tsx @@ -144,7 +144,7 @@ describe('Markdown', () => { vi.clearAllMocks() // Default settings - mockUseSettings.mockReturnValue({ mathEngine: 'KaTeX' }) + mockUseSettings.mockReturnValue({ mathEngine: 'KaTeX', mathEnableSingleDollar: true }) mockUseTranslation.mockReturnValue({ t: (key: string) => (key === 'message.chat.completion.paused' ? 'Paused' : key) }) @@ -270,7 +270,7 @@ describe('Markdown', () => { describe('math engine configuration', () => { it('should configure KaTeX when mathEngine is KaTeX', () => { - mockUseSettings.mockReturnValue({ mathEngine: 'KaTeX' }) + mockUseSettings.mockReturnValue({ mathEngine: 'KaTeX', mathEnableSingleDollar: true }) render() @@ -279,7 +279,7 @@ describe('Markdown', () => { }) it('should configure MathJax when mathEngine is MathJax', () => { - mockUseSettings.mockReturnValue({ mathEngine: 'MathJax' }) + mockUseSettings.mockReturnValue({ mathEngine: 'MathJax', mathEnableSingleDollar: true }) render() @@ -288,7 +288,7 @@ describe('Markdown', () => { }) it('should not load math plugins when mathEngine is none', () => { - mockUseSettings.mockReturnValue({ mathEngine: 'none' }) + mockUseSettings.mockReturnValue({ mathEngine: 'none', mathEnableSingleDollar: true }) render() @@ -384,12 +384,12 @@ describe('Markdown', () => { }) it('should re-render when math engine changes', () => { - mockUseSettings.mockReturnValue({ mathEngine: 'KaTeX' }) + mockUseSettings.mockReturnValue({ mathEngine: 'KaTeX', mathEnableSingleDollar: true }) const { rerender } = render() expect(screen.getByTestId('markdown-content')).toBeInTheDocument() - mockUseSettings.mockReturnValue({ mathEngine: 'MathJax' }) + mockUseSettings.mockReturnValue({ mathEngine: 'MathJax', mathEnableSingleDollar: true }) rerender() // Should still render correctly with new math engine diff --git a/src/renderer/src/pages/home/Markdown/plugins/rehypeHeadingIds.ts b/src/renderer/src/pages/home/Markdown/plugins/rehypeHeadingIds.ts index e3e7e6db75..bb5862336f 100644 --- a/src/renderer/src/pages/home/Markdown/plugins/rehypeHeadingIds.ts +++ b/src/renderer/src/pages/home/Markdown/plugins/rehypeHeadingIds.ts @@ -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' /** diff --git a/src/renderer/src/pages/home/Tabs/SettingsTab.tsx b/src/renderer/src/pages/home/Tabs/SettingsTab.tsx index b290c34754..8c8309d04e 100644 --- a/src/renderer/src/pages/home/Tabs/SettingsTab.tsx +++ b/src/renderer/src/pages/home/Tabs/SettingsTab.tsx @@ -29,6 +29,7 @@ import { setEnableBackspaceDeleteModel, setEnableQuickPanelTriggers, setFontSize, + setMathEnableSingleDollar, setMathEngine, setMessageFont, setMessageNavigation, @@ -97,6 +98,7 @@ const SettingsTab: FC = (props) => { codeImageTools, codeExecution, mathEngine, + mathEnableSingleDollar, autoTranslateWithSpace, pasteLongTextThreshold, multiModelMessageStyle, @@ -382,19 +384,6 @@ const SettingsTab: FC = (props) => { /> - - {t('settings.messages.math_engine.label')} - dispatch(setMathEngine(value as MathEngine))} - options={[ - { value: 'KaTeX', label: 'KaTeX' }, - { value: 'MathJax', label: 'MathJax' }, - { value: 'none', label: t('settings.messages.math_engine.none') } - ]} - /> - - {t('settings.font_size.title')} @@ -418,6 +407,37 @@ const SettingsTab: FC = (props) => { + + + + {t('settings.math.engine.label')} + dispatch(setMathEngine(value as MathEngine))} + options={[ + { value: 'KaTeX', label: 'KaTeX' }, + { value: 'MathJax', label: 'MathJax' }, + { value: 'none', label: t('settings.math.engine.none') } + ]} + /> + + + + + {t('settings.math.single_dollar.label')}{' '} + + + + + dispatch(setMathEnableSingleDollar(checked))} + /> + + + + diff --git a/src/renderer/src/store/index.ts b/src/renderer/src/store/index.ts index adcb9c1b9c..7cf350dafa 100644 --- a/src/renderer/src/store/index.ts +++ b/src/renderer/src/store/index.ts @@ -62,7 +62,7 @@ const persistedReducer = persistReducer( { key: 'cherry-studio', storage, - version: 130, + version: 131, blacklist: ['runtime', 'messages', 'messageBlocks', 'tabs'], migrate }, diff --git a/src/renderer/src/store/migrate.ts b/src/renderer/src/store/migrate.ts index c1a50a46d9..9c9133168a 100644 --- a/src/renderer/src/store/migrate.ts +++ b/src/renderer/src/store/migrate.ts @@ -2094,6 +2094,15 @@ const migrateConfig = { logger.error('migrate 130 error', error as Error) return state } + }, + '131': (state: RootState) => { + try { + state.settings.mathEnableSingleDollar = true + return state + } catch (error) { + logger.error('migrate 131 error', error as Error) + return state + } } } diff --git a/src/renderer/src/store/settings.ts b/src/renderer/src/store/settings.ts index f5861e8494..68b9b8d02c 100644 --- a/src/renderer/src/store/settings.ts +++ b/src/renderer/src/store/settings.ts @@ -105,6 +105,7 @@ export interface SettingsState { codeWrappable: boolean codeImageTools: boolean mathEngine: MathEngine + mathEnableSingleDollar: boolean messageStyle: 'plain' | 'bubble' foldDisplayMode: 'expanded' | 'compact' gridColumns: number @@ -287,6 +288,7 @@ export const initialState: SettingsState = { codeWrappable: false, codeImageTools: false, mathEngine: 'KaTeX', + mathEnableSingleDollar: true, messageStyle: 'plain', foldDisplayMode: 'expanded', gridColumns: 2, @@ -616,6 +618,9 @@ const settingsSlice = createSlice({ setMathEngine: (state, action: PayloadAction) => { state.mathEngine = action.payload }, + setMathEnableSingleDollar: (state, action: PayloadAction) => { + state.mathEnableSingleDollar = action.payload + }, setFoldDisplayMode: (state, action: PayloadAction<'expanded' | 'compact'>) => { state.foldDisplayMode = action.payload }, @@ -898,6 +903,7 @@ export const { setCodeWrappable, setCodeImageTools, setMathEngine, + setMathEnableSingleDollar, setFoldDisplayMode, setGridColumns, setGridPopoverTrigger,