diff --git a/src/renderer/src/components/CodeBlockView/view.tsx b/src/renderer/src/components/CodeBlockView/view.tsx index 46f6cbce86..5addfa077c 100644 --- a/src/renderer/src/components/CodeBlockView/view.tsx +++ b/src/renderer/src/components/CodeBlockView/view.tsx @@ -19,7 +19,7 @@ import { MAX_COLLAPSED_CODE_HEIGHT } from '@renderer/config/constant' import { useSettings } from '@renderer/hooks/useSettings' import { pyodideService } from '@renderer/services/PyodideService' import { getExtensionByLanguage } from '@renderer/utils/code-language' -import { extractHtmlTitle } from '@renderer/utils/formats' +import { extractHtmlTitle, getFileNameFromHtmlTitle } from '@renderer/utils/formats' import dayjs from 'dayjs' import React, { memo, startTransition, useCallback, useEffect, useMemo, useRef, useState } from 'react' import { useTranslation } from 'react-i18next' @@ -135,8 +135,8 @@ export const CodeBlockView: React.FC = memo(({ children, language, onSave let fileName = '' // 尝试提取 HTML 标题 - if (language === 'html' && children.includes('')) { - fileName = extractHtmlTitle(children) || '' + if (language === 'html') { + fileName = getFileNameFromHtmlTitle(extractHtmlTitle(children)) || '' } // 默认使用日期格式命名 diff --git a/src/renderer/src/i18n/locales/en-us.json b/src/renderer/src/i18n/locales/en-us.json index 8b328b1ee9..646a5c48cc 100644 --- a/src/renderer/src/i18n/locales/en-us.json +++ b/src/renderer/src/i18n/locales/en-us.json @@ -538,6 +538,10 @@ "tip": "The run button will be displayed in the toolbar of executable code blocks, please do not execute dangerous code!", "title": "Code Execution" }, + "code_fancy_block": { + "label": "Fancy code block", + "tip": "Enable fancy style for code block, e.g., html card" + }, "code_image_tools": { "label": "Enable preview tools", "tip": "Enable preview tools for images rendered from code blocks such as mermaid" diff --git a/src/renderer/src/i18n/locales/zh-cn.json b/src/renderer/src/i18n/locales/zh-cn.json index 3ea4d77577..5122edc291 100644 --- a/src/renderer/src/i18n/locales/zh-cn.json +++ b/src/renderer/src/i18n/locales/zh-cn.json @@ -538,6 +538,10 @@ "tip": "可执行的代码块工具栏中会显示运行按钮,注意不要执行危险代码!", "title": "代码执行" }, + "code_fancy_block": { + "label": "花式代码块", + "tip": "使用更美观的代码块样式,例如 HTML 卡片" + }, "code_image_tools": { "label": "启用预览工具", "tip": "为 mermaid 等代码块渲染后的图像启用预览工具" diff --git a/src/renderer/src/i18n/locales/zh-tw.json b/src/renderer/src/i18n/locales/zh-tw.json index c970cff3e6..364a5b7e3e 100644 --- a/src/renderer/src/i18n/locales/zh-tw.json +++ b/src/renderer/src/i18n/locales/zh-tw.json @@ -538,6 +538,10 @@ "tip": "可執行的程式碼塊工具欄中會顯示運行按鈕,注意不要執行危險程式碼!", "title": "程式碼執行" }, + "code_fancy_block": { + "label": "花式程式碼區塊", + "tip": "使用更美觀的程式碼區塊樣式,例如 HTML 卡片" + }, "code_image_tools": { "label": "啟用預覽工具", "tip": "為 mermaid 等程式碼區塊渲染後的圖像啟用預覽工具" diff --git a/src/renderer/src/pages/home/Markdown/CodeBlock.tsx b/src/renderer/src/pages/home/Markdown/CodeBlock.tsx index c1527979d6..8aa71940cc 100644 --- a/src/renderer/src/pages/home/Markdown/CodeBlock.tsx +++ b/src/renderer/src/pages/home/Markdown/CodeBlock.tsx @@ -1,4 +1,5 @@ import { CodeBlockView, HtmlArtifactsCard } from '@renderer/components/CodeBlockView' +import { useSettings } from '@renderer/hooks/useSettings' import { EVENT_NAMES, EventEmitter } from '@renderer/services/EventService' import store from '@renderer/store' import { messageBlocksSelectors } from '@renderer/store/messageBlock' @@ -19,6 +20,7 @@ const CodeBlock: React.FC = ({ children, className, node, blockId }) => { const languageMatch = /language-([\w-+]+)/.exec(className || '') const isMultiline = children?.includes('\n') const language = languageMatch?.[1] ?? (isMultiline ? 'text' : null) + const { codeFancyBlock } = useSettings() // 代码块 id const id = useMemo(() => getCodeBlockId(node?.position?.start), [node?.position?.start]) @@ -41,10 +43,12 @@ const CodeBlock: React.FC = ({ children, className, node, blockId }) => { ) if (language !== null) { - // HTML 代码块特殊处理 - if (language === 'html') { - const isOpenFence = isOpenFenceBlock(children?.length, languageMatch?.[1]?.length, node?.position) - return + // Fancy code block + if (codeFancyBlock) { + if (language === 'html') { + const isOpenFence = isOpenFenceBlock(children?.length, languageMatch?.[1]?.length, node?.position) + return + } } return ( diff --git a/src/renderer/src/pages/home/Markdown/__tests__/CodeBlock.test.tsx b/src/renderer/src/pages/home/Markdown/__tests__/CodeBlock.test.tsx index c84cd14b4e..9ada24b829 100644 --- a/src/renderer/src/pages/home/Markdown/__tests__/CodeBlock.test.tsx +++ b/src/renderer/src/pages/home/Markdown/__tests__/CodeBlock.test.tsx @@ -12,6 +12,7 @@ const mocks = vi.hoisted(() => ({ getCodeBlockId: vi.fn(), isOpenFenceBlock: vi.fn(), selectById: vi.fn(), + useSettings: vi.fn().mockReturnValue({ codeFancyBlock: true }), CodeBlockView: vi.fn(({ onSave, children }) => (
{children} @@ -53,6 +54,10 @@ vi.mock('@renderer/store/messageBlock', () => ({ } })) +vi.mock('@renderer/hooks/useSettings', () => ({ + useSettings: () => mocks.useSettings() +})) + vi.mock('@renderer/components/CodeBlockView', () => ({ CodeBlockView: mocks.CodeBlockView, HtmlArtifactsCard: mocks.HtmlArtifactsCard diff --git a/src/renderer/src/pages/home/Tabs/SettingsTab.tsx b/src/renderer/src/pages/home/Tabs/SettingsTab.tsx index c04aaf6138..d6450803a0 100644 --- a/src/renderer/src/pages/home/Tabs/SettingsTab.tsx +++ b/src/renderer/src/pages/home/Tabs/SettingsTab.tsx @@ -23,6 +23,7 @@ import { setCodeCollapsible, setCodeEditor, setCodeExecution, + setCodeFancyBlock, setCodeImageTools, setCodeShowLineNumbers, setCodeViewer, @@ -99,6 +100,7 @@ const SettingsTab: FC = (props) => { codeViewer, codeImageTools, codeExecution, + codeFancyBlock, mathEngine, mathEnableSingleDollar, autoTranslateWithSpace, @@ -451,6 +453,18 @@ const SettingsTab: FC = (props) => { /> + + + {t('chat.settings.code_fancy_block.label')} + + + dispatch(setCodeFancyBlock(checked))} + /> + + {t('chat.settings.code_execution.title')} diff --git a/src/renderer/src/store/migrate.ts b/src/renderer/src/store/migrate.ts index 7111243cce..6ff56ec83e 100644 --- a/src/renderer/src/store/migrate.ts +++ b/src/renderer/src/store/migrate.ts @@ -2417,6 +2417,17 @@ const migrateConfig = { logger.error('migrate 150 error', error as Error) return state } + }, + '151': (state: RootState) => { + try { + if (state.settings) { + state.settings.codeFancyBlock = true + } + return state + } catch (error) { + logger.error('migrate 151 error', error as Error) + return state + } } } diff --git a/src/renderer/src/store/settings.ts b/src/renderer/src/store/settings.ts index a82a7a00d0..79f2797abd 100644 --- a/src/renderer/src/store/settings.ts +++ b/src/renderer/src/store/settings.ts @@ -97,6 +97,7 @@ export interface SettingsState { codeCollapsible: boolean codeWrappable: boolean codeImageTools: boolean + codeFancyBlock: boolean mathEngine: MathEngine mathEnableSingleDollar: boolean messageStyle: 'plain' | 'bubble' @@ -282,6 +283,7 @@ export const initialState: SettingsState = { codeCollapsible: false, codeWrappable: false, codeImageTools: false, + codeFancyBlock: true, mathEngine: 'KaTeX', mathEnableSingleDollar: true, messageStyle: 'plain', @@ -611,6 +613,9 @@ const settingsSlice = createSlice({ setCodeImageTools: (state, action: PayloadAction) => { state.codeImageTools = action.payload }, + setCodeFancyBlock: (state, action: PayloadAction) => { + state.codeFancyBlock = action.payload + }, setMathEngine: (state, action: PayloadAction) => { state.mathEngine = action.payload }, @@ -900,6 +905,7 @@ export const { setCodeCollapsible, setCodeWrappable, setCodeImageTools, + setCodeFancyBlock, setMathEngine, setMathEnableSingleDollar, setFoldDisplayMode,