mirror of
https://github.com/CherryHQ/cherry-studio.git
synced 2026-01-07 13:59:28 +08:00
feat: tooluse result display style optimization (#5758)
This commit is contained in:
parent
671bc64529
commit
416a5ebcc6
@ -1,6 +1,7 @@
|
|||||||
import { CheckOutlined, ExpandOutlined, LoadingOutlined, WarningOutlined } from '@ant-design/icons'
|
import { CheckOutlined, ExpandOutlined, LoadingOutlined, WarningOutlined } from '@ant-design/icons'
|
||||||
import { useSettings } from '@renderer/hooks/useSettings'
|
import { useSettings } from '@renderer/hooks/useSettings'
|
||||||
import type { ToolMessageBlock } from '@renderer/types/newMessage'
|
import type { ToolMessageBlock } from '@renderer/types/newMessage'
|
||||||
|
import { useShikiWithMarkdownIt } from '@renderer/utils/shiki'
|
||||||
import { Collapse, message as antdMessage, Modal, Tabs, Tooltip } from 'antd'
|
import { Collapse, message as antdMessage, Modal, Tabs, Tooltip } from 'antd'
|
||||||
import { FC, useMemo, useState } from 'react'
|
import { FC, useMemo, useState } from 'react'
|
||||||
import { useTranslation } from 'react-i18next'
|
import { useTranslation } from 'react-i18next'
|
||||||
@ -25,6 +26,22 @@ const MessageTools: FC<Props> = ({ blocks }) => {
|
|||||||
|
|
||||||
const toolResponse = blocks.metadata?.rawMcpToolResponse
|
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) {
|
if (!toolResponse) {
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
@ -107,7 +124,7 @@ const MessageTools: FC<Props> = ({ blocks }) => {
|
|||||||
),
|
),
|
||||||
children: isDone && result && (
|
children: isDone && result && (
|
||||||
<ToolResponseContainer style={{ fontFamily, fontSize: '12px' }}>
|
<ToolResponseContainer style={{ fontFamily, fontSize: '12px' }}>
|
||||||
<CodeBlock>{JSON.stringify(result, null, 2)}</CodeBlock>
|
<div className="markdown" dangerouslySetInnerHTML={{ __html: styledResult }} />
|
||||||
</ToolResponseContainer>
|
</ToolResponseContainer>
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
@ -183,13 +200,7 @@ const MessageTools: FC<Props> = ({ blocks }) => {
|
|||||||
{
|
{
|
||||||
key: 'raw',
|
key: 'raw',
|
||||||
label: t('message.tools.raw'),
|
label: t('message.tools.raw'),
|
||||||
children: (
|
children: <div className="markdown" dangerouslySetInnerHTML={{ __html: styledResult }} />
|
||||||
<CodeBlock>
|
|
||||||
{typeof expandedResponse.content === 'string'
|
|
||||||
? expandedResponse.content
|
|
||||||
: JSON.stringify(expandedResponse.content, null, 2)}
|
|
||||||
</CodeBlock>
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
]}
|
]}
|
||||||
/>
|
/>
|
||||||
@ -300,9 +311,7 @@ const CollapsibleIcon = styled.i`
|
|||||||
`
|
`
|
||||||
|
|
||||||
const ToolResponseContainer = styled.div`
|
const ToolResponseContainer = styled.div`
|
||||||
background: var(--color-bg-1);
|
|
||||||
border-radius: 0 0 4px 4px;
|
border-radius: 0 0 4px 4px;
|
||||||
padding: 12px 16px;
|
|
||||||
overflow: auto;
|
overflow: auto;
|
||||||
max-height: 300px;
|
max-height: 300px;
|
||||||
border-top: none;
|
border-top: none;
|
||||||
@ -317,14 +326,6 @@ const PreviewBlock = styled.div`
|
|||||||
user-select: text;
|
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`
|
const ExpandedResponseContainer = styled.div`
|
||||||
background: var(--color-bg-1);
|
background: var(--color-bg-1);
|
||||||
border-radius: 8px;
|
border-radius: 8px;
|
||||||
|
|||||||
@ -1,6 +1,8 @@
|
|||||||
|
import { useTheme } from '@renderer/context/ThemeProvider'
|
||||||
import { ThemeMode } from '@renderer/types'
|
import { ThemeMode } from '@renderer/types'
|
||||||
import { MarkdownItShikiOptions, setupMarkdownIt } from '@shikijs/markdown-it'
|
import { MarkdownItShikiOptions, setupMarkdownIt } from '@shikijs/markdown-it'
|
||||||
import MarkdownIt from 'markdown-it'
|
import MarkdownIt from 'markdown-it'
|
||||||
|
import { useEffect, useRef, useState } from 'react'
|
||||||
import { BuiltinLanguage, BuiltinTheme, bundledLanguages, createHighlighter } from 'shiki'
|
import { BuiltinLanguage, BuiltinTheme, bundledLanguages, createHighlighter } from 'shiki'
|
||||||
|
|
||||||
const defaultOptions = {
|
const defaultOptions = {
|
||||||
@ -33,3 +35,22 @@ export function getShikiInstance(theme: ThemeMode) {
|
|||||||
setupMarkdownIt(markdownit, highlighter, options)
|
setupMarkdownIt(markdownit, highlighter, options)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function useShikiWithMarkdownIt(content: string) {
|
||||||
|
const [renderedMarkdown, setRenderedMarkdown] = useState('')
|
||||||
|
const md = useRef<MarkdownIt>(
|
||||||
|
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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user