diff --git a/src/renderer/src/pages/home/Messages/MessageTools.tsx b/src/renderer/src/pages/home/Messages/MessageTools.tsx index 7a19462923..495c56cf10 100644 --- a/src/renderer/src/pages/home/Messages/MessageTools.tsx +++ b/src/renderer/src/pages/home/Messages/MessageTools.tsx @@ -1,6 +1,7 @@ import { CheckOutlined, ExpandOutlined, LoadingOutlined, WarningOutlined } from '@ant-design/icons' import { useSettings } from '@renderer/hooks/useSettings' import type { ToolMessageBlock } from '@renderer/types/newMessage' +import { useShikiWithMarkdownIt } from '@renderer/utils/shiki' import { Collapse, message as antdMessage, Modal, Tabs, Tooltip } from 'antd' import { FC, useMemo, useState } from 'react' import { useTranslation } from 'react-i18next' @@ -25,6 +26,22 @@ const MessageTools: FC = ({ blocks }) => { const toolResponse = blocks.metadata?.rawMcpToolResponse + const resultString = useMemo(() => { + try { + return JSON.stringify( + { + params: toolResponse?.tool?.inputSchema, + response: toolResponse?.response + }, + null, + 2 + ) + } catch (e) { + return 'Invalid Result' + } + }, [toolResponse]) + const { renderedMarkdown: styledResult } = useShikiWithMarkdownIt(`\`\`\`json\n${resultString}\n\`\`\``) + if (!toolResponse) { return null } @@ -107,7 +124,7 @@ const MessageTools: FC = ({ blocks }) => { ), children: isDone && result && ( - {JSON.stringify(result, null, 2)} +
) }) @@ -183,13 +200,7 @@ const MessageTools: FC = ({ blocks }) => { { key: 'raw', label: t('message.tools.raw'), - children: ( - - {typeof expandedResponse.content === 'string' - ? expandedResponse.content - : JSON.stringify(expandedResponse.content, null, 2)} - - ) + children:
} ]} /> @@ -300,9 +311,7 @@ const CollapsibleIcon = styled.i` ` const ToolResponseContainer = styled.div` - background: var(--color-bg-1); border-radius: 0 0 4px 4px; - padding: 12px 16px; overflow: auto; max-height: 300px; border-top: none; @@ -317,14 +326,6 @@ const PreviewBlock = styled.div` user-select: text; ` -const CodeBlock = styled.pre` - margin: 0; - white-space: pre-wrap; - word-break: break-word; - color: var(--color-text); - font-family: ubuntu; -` - const ExpandedResponseContainer = styled.div` background: var(--color-bg-1); border-radius: 8px; diff --git a/src/renderer/src/utils/shiki.ts b/src/renderer/src/utils/shiki.ts index ec2b662ae7..b0b9470c63 100644 --- a/src/renderer/src/utils/shiki.ts +++ b/src/renderer/src/utils/shiki.ts @@ -1,6 +1,8 @@ +import { useTheme } from '@renderer/context/ThemeProvider' import { ThemeMode } from '@renderer/types' import { MarkdownItShikiOptions, setupMarkdownIt } from '@shikijs/markdown-it' import MarkdownIt from 'markdown-it' +import { useEffect, useRef, useState } from 'react' import { BuiltinLanguage, BuiltinTheme, bundledLanguages, createHighlighter } from 'shiki' const defaultOptions = { @@ -33,3 +35,22 @@ export function getShikiInstance(theme: ThemeMode) { setupMarkdownIt(markdownit, highlighter, options) } } + +export function useShikiWithMarkdownIt(content: string) { + const [renderedMarkdown, setRenderedMarkdown] = useState('') + const md = useRef( + new MarkdownIt({ + linkify: true, // 自动转换 URL 为链接 + typographer: true // 启用印刷格式优化 + }) + ) + const { theme } = useTheme() + useEffect(() => { + const sk = getShikiInstance(theme) + md.current.use(sk) + setRenderedMarkdown(md.current.render(content)) + }, [content, theme]) + return { + renderedMarkdown + } +}