mirror of
https://github.com/CherryHQ/cherry-studio.git
synced 2026-01-09 14:59:27 +08:00
Fix/7973 (#8059)
* fix: 7973 查看原始数据的按钮没有了 * refactor(MessageTools): replace PreviewBlock with CollapsedContent for improved preview rendering --------- Co-authored-by: suyao <sy20010504@gmail.com>
This commit is contained in:
parent
094eb5c17e
commit
e0eac6ab7e
@ -764,7 +764,8 @@
|
|||||||
"invoking": "Invoking",
|
"invoking": "Invoking",
|
||||||
"pending": "Pending",
|
"pending": "Pending",
|
||||||
"preview": "Preview",
|
"preview": "Preview",
|
||||||
"autoApproveEnabled": "Auto-approve enabled for this tool"
|
"autoApproveEnabled": "Auto-approve enabled for this tool",
|
||||||
|
"raw": "Raw"
|
||||||
},
|
},
|
||||||
"topic.added": "New topic added",
|
"topic.added": "New topic added",
|
||||||
"upgrade.success.button": "Restart",
|
"upgrade.success.button": "Restart",
|
||||||
|
|||||||
@ -764,7 +764,8 @@
|
|||||||
"invoking": "呼び出し中",
|
"invoking": "呼び出し中",
|
||||||
"pending": "保留中",
|
"pending": "保留中",
|
||||||
"preview": "プレビュー",
|
"preview": "プレビュー",
|
||||||
"autoApproveEnabled": "このツールは自動承認が有効になっています"
|
"autoApproveEnabled": "このツールは自動承認が有効になっています",
|
||||||
|
"raw": "生データ"
|
||||||
},
|
},
|
||||||
"topic.added": "新しいトピックが追加されました",
|
"topic.added": "新しいトピックが追加されました",
|
||||||
"upgrade.success.button": "再起動",
|
"upgrade.success.button": "再起動",
|
||||||
|
|||||||
@ -764,7 +764,8 @@
|
|||||||
"invoking": "Вызов",
|
"invoking": "Вызов",
|
||||||
"pending": "Ожидание",
|
"pending": "Ожидание",
|
||||||
"preview": "Предпросмотр",
|
"preview": "Предпросмотр",
|
||||||
"autoApproveEnabled": "Для этого инструмента включен автоматический одобрен"
|
"autoApproveEnabled": "Для этого инструмента включен автоматический одобрен",
|
||||||
|
"raw": "Исходный"
|
||||||
},
|
},
|
||||||
"topic.added": "Новый топик добавлен",
|
"topic.added": "Новый топик добавлен",
|
||||||
"upgrade.success.button": "Перезапустить",
|
"upgrade.success.button": "Перезапустить",
|
||||||
|
|||||||
@ -764,7 +764,8 @@
|
|||||||
"invoking": "调用中",
|
"invoking": "调用中",
|
||||||
"pending": "等待中",
|
"pending": "等待中",
|
||||||
"preview": "预览",
|
"preview": "预览",
|
||||||
"autoApproveEnabled": "此工具已启用自动批准"
|
"autoApproveEnabled": "此工具已启用自动批准",
|
||||||
|
"raw": "原始"
|
||||||
},
|
},
|
||||||
"topic.added": "话题添加成功",
|
"topic.added": "话题添加成功",
|
||||||
"upgrade.success.button": "重启",
|
"upgrade.success.button": "重启",
|
||||||
|
|||||||
@ -764,7 +764,8 @@
|
|||||||
"invoking": "調用中",
|
"invoking": "調用中",
|
||||||
"pending": "等待中",
|
"pending": "等待中",
|
||||||
"preview": "預覽",
|
"preview": "預覽",
|
||||||
"autoApproveEnabled": "此工具已啟用自動批准"
|
"autoApproveEnabled": "此工具已啟用自動批准",
|
||||||
|
"raw": "原始碼"
|
||||||
},
|
},
|
||||||
"topic.added": "新話題已新增",
|
"topic.added": "新話題已新增",
|
||||||
"upgrade.success.button": "重新啟動",
|
"upgrade.success.button": "重新啟動",
|
||||||
|
|||||||
@ -1,11 +1,11 @@
|
|||||||
import { CheckOutlined, CloseOutlined, LoadingOutlined, WarningOutlined } from '@ant-design/icons'
|
import { CheckOutlined, CloseOutlined, ExpandOutlined, LoadingOutlined, WarningOutlined } from '@ant-design/icons'
|
||||||
import { useCodeStyle } from '@renderer/context/CodeStyleProvider'
|
import { useCodeStyle } from '@renderer/context/CodeStyleProvider'
|
||||||
import { useMCPServers } from '@renderer/hooks/useMCPServers'
|
import { useMCPServers } from '@renderer/hooks/useMCPServers'
|
||||||
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 { isToolAutoApproved } from '@renderer/utils/mcp-tools'
|
import { isToolAutoApproved } from '@renderer/utils/mcp-tools'
|
||||||
import { cancelToolAction, confirmToolAction } from '@renderer/utils/userConfirmation'
|
import { cancelToolAction, confirmToolAction } from '@renderer/utils/userConfirmation'
|
||||||
import { Button, Collapse, ConfigProvider, Dropdown, Flex, message as antdMessage, Tooltip } from 'antd'
|
import { Button, Collapse, ConfigProvider, Dropdown, Flex, message as antdMessage, Modal, Tabs, Tooltip } from 'antd'
|
||||||
import { message } from 'antd'
|
import { message } from 'antd'
|
||||||
import Logger from 'electron-log/renderer'
|
import Logger from 'electron-log/renderer'
|
||||||
import { ChevronDown, ChevronRight, CirclePlay, CircleX, PauseCircle, ShieldCheck } from 'lucide-react'
|
import { ChevronDown, ChevronRight, CirclePlay, CircleX, PauseCircle, ShieldCheck } from 'lucide-react'
|
||||||
@ -26,6 +26,7 @@ const MessageTools: FC<Props> = ({ block }) => {
|
|||||||
const { t } = useTranslation()
|
const { t } = useTranslation()
|
||||||
const { messageFont, fontSize } = useSettings()
|
const { messageFont, fontSize } = useSettings()
|
||||||
const { mcpServers, updateMCPServer } = useMCPServers()
|
const { mcpServers, updateMCPServer } = useMCPServers()
|
||||||
|
const [expandedResponse, setExpandedResponse] = useState<{ content: string; title: string } | null>(null)
|
||||||
|
|
||||||
const toolResponse = block.metadata?.rawMcpToolResponse
|
const toolResponse = block.metadata?.rawMcpToolResponse
|
||||||
|
|
||||||
@ -221,6 +222,20 @@ const MessageTools: FC<Props> = ({ block }) => {
|
|||||||
<StatusIndicator status={status} hasError={hasError}>
|
<StatusIndicator status={status} hasError={hasError}>
|
||||||
{renderStatusIndicator(status, hasError)}
|
{renderStatusIndicator(status, hasError)}
|
||||||
</StatusIndicator>
|
</StatusIndicator>
|
||||||
|
<Tooltip title={t('common.expand')} mouseEnterDelay={0.5}>
|
||||||
|
<ActionButton
|
||||||
|
className="message-action-button"
|
||||||
|
onClick={(e) => {
|
||||||
|
e.stopPropagation()
|
||||||
|
setExpandedResponse({
|
||||||
|
content: JSON.stringify(response, null, 2),
|
||||||
|
title: tool.name
|
||||||
|
})
|
||||||
|
}}
|
||||||
|
aria-label={t('common.expand')}>
|
||||||
|
<ExpandOutlined />
|
||||||
|
</ActionButton>
|
||||||
|
</Tooltip>
|
||||||
{!isPending && !isInvoking && (
|
{!isPending && !isInvoking && (
|
||||||
<Tooltip title={t('common.copy')} mouseEnterDelay={0.5}>
|
<Tooltip title={t('common.copy')} mouseEnterDelay={0.5}>
|
||||||
<ActionButton
|
<ActionButton
|
||||||
@ -259,92 +274,173 @@ const MessageTools: FC<Props> = ({ block }) => {
|
|||||||
return items
|
return items
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
const renderPreview = (content: string) => {
|
||||||
<ConfigProvider
|
if (!content) return null
|
||||||
theme={{
|
|
||||||
components: {
|
|
||||||
Button: {
|
|
||||||
borderRadiusSM: 6
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}}>
|
|
||||||
<ToolContainer>
|
|
||||||
<ToolContentWrapper className={status}>
|
|
||||||
<CollapseContainer
|
|
||||||
ghost
|
|
||||||
activeKey={activeKeys}
|
|
||||||
size="small"
|
|
||||||
onChange={handleCollapseChange}
|
|
||||||
className="message-tools-container"
|
|
||||||
items={getCollapseItems()}
|
|
||||||
expandIconPosition="end"
|
|
||||||
expandIcon={({ isActive }) => (
|
|
||||||
<ExpandIcon $isActive={isActive} size={18} color="var(--color-text-3)" strokeWidth={1.5} />
|
|
||||||
)}
|
|
||||||
/>
|
|
||||||
{(isPending || isInvoking) && (
|
|
||||||
<ActionsBar>
|
|
||||||
<ActionLabel>
|
|
||||||
{isPending ? t('settings.mcp.tools.autoApprove.tooltip.confirm') : t('message.tools.invoking')}
|
|
||||||
</ActionLabel>
|
|
||||||
|
|
||||||
<ActionButtonsGroup>
|
try {
|
||||||
{isPending && (
|
const parsedResult = JSON.parse(content)
|
||||||
<Button
|
switch (parsedResult.content[0]?.type) {
|
||||||
color="danger"
|
case 'text':
|
||||||
variant="filled"
|
return (
|
||||||
size="small"
|
<CollapsedContent
|
||||||
onClick={() => {
|
isExpanded={true}
|
||||||
handleCancelTool()
|
resultString={JSON.stringify(JSON.parse(parsedResult.content[0].text), null, 2)}
|
||||||
}}>
|
/>
|
||||||
<CircleX size={15} className="lucide-custom" />
|
)
|
||||||
{t('common.cancel')}
|
|
||||||
</Button>
|
default:
|
||||||
)}
|
return <CollapsedContent isExpanded={true} resultString={JSON.stringify(parsedResult, null, 2)} />
|
||||||
{isInvoking && toolResponse?.id ? (
|
}
|
||||||
<Button
|
} catch (e) {
|
||||||
size="small"
|
console.error('failed to render the preview of mcp results:', e)
|
||||||
color="danger"
|
return <CollapsedContent isExpanded={true} resultString={JSON.stringify(e, null, 2)} />
|
||||||
variant="solid"
|
}
|
||||||
className="abort-button"
|
}
|
||||||
onClick={(e) => {
|
|
||||||
e.stopPropagation()
|
return (
|
||||||
handleAbortTool()
|
<>
|
||||||
}}>
|
<ConfigProvider
|
||||||
<PauseCircle className="lucide-custom" size={14} />
|
theme={{
|
||||||
{t('chat.input.pause')}
|
components: {
|
||||||
</Button>
|
Button: {
|
||||||
) : (
|
borderRadiusSM: 6
|
||||||
<StyledDropdownButton
|
}
|
||||||
size="small"
|
}
|
||||||
type="primary"
|
}}>
|
||||||
icon={<ChevronDown size={14} />}
|
<ToolContainer>
|
||||||
onClick={() => {
|
<ToolContentWrapper className={status}>
|
||||||
handleConfirmTool()
|
<CollapseContainer
|
||||||
}}
|
ghost
|
||||||
menu={{
|
activeKey={activeKeys}
|
||||||
items: [
|
size="small"
|
||||||
{
|
onChange={handleCollapseChange}
|
||||||
key: 'autoApprove',
|
className="message-tools-container"
|
||||||
label: t('settings.mcp.tools.autoApprove'),
|
items={getCollapseItems()}
|
||||||
onClick: () => {
|
expandIconPosition="end"
|
||||||
handleAutoApprove()
|
expandIcon={({ isActive }) => (
|
||||||
|
<ExpandIcon $isActive={isActive} size={18} color="var(--color-text-3)" strokeWidth={1.5} />
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
{(isPending || isInvoking) && (
|
||||||
|
<ActionsBar>
|
||||||
|
<ActionLabel>
|
||||||
|
{isPending ? t('settings.mcp.tools.autoApprove.tooltip.confirm') : t('message.tools.invoking')}
|
||||||
|
</ActionLabel>
|
||||||
|
|
||||||
|
<ActionButtonsGroup>
|
||||||
|
{isPending && (
|
||||||
|
<Button
|
||||||
|
color="danger"
|
||||||
|
variant="filled"
|
||||||
|
size="small"
|
||||||
|
onClick={() => {
|
||||||
|
handleCancelTool()
|
||||||
|
}}>
|
||||||
|
<CircleX size={15} className="lucide-custom" />
|
||||||
|
{t('common.cancel')}
|
||||||
|
</Button>
|
||||||
|
)}
|
||||||
|
{isInvoking && toolResponse?.id ? (
|
||||||
|
<Button
|
||||||
|
size="small"
|
||||||
|
color="danger"
|
||||||
|
variant="solid"
|
||||||
|
className="abort-button"
|
||||||
|
onClick={(e) => {
|
||||||
|
e.stopPropagation()
|
||||||
|
handleAbortTool()
|
||||||
|
}}>
|
||||||
|
<PauseCircle className="lucide-custom" size={14} />
|
||||||
|
{t('chat.input.pause')}
|
||||||
|
</Button>
|
||||||
|
) : (
|
||||||
|
<StyledDropdownButton
|
||||||
|
size="small"
|
||||||
|
type="primary"
|
||||||
|
icon={<ChevronDown size={14} />}
|
||||||
|
onClick={() => {
|
||||||
|
handleConfirmTool()
|
||||||
|
}}
|
||||||
|
menu={{
|
||||||
|
items: [
|
||||||
|
{
|
||||||
|
key: 'autoApprove',
|
||||||
|
label: t('settings.mcp.tools.autoApprove'),
|
||||||
|
onClick: () => {
|
||||||
|
handleAutoApprove()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
]
|
||||||
]
|
}}>
|
||||||
}}>
|
<CirclePlay size={15} className="lucide-custom" />
|
||||||
<CirclePlay size={15} className="lucide-custom" />
|
<CountdownText>
|
||||||
<CountdownText>
|
{t('settings.mcp.tools.run', 'Run')} ({countdown}s)
|
||||||
{t('settings.mcp.tools.run', 'Run')} ({countdown}s)
|
</CountdownText>
|
||||||
</CountdownText>
|
</StyledDropdownButton>
|
||||||
</StyledDropdownButton>
|
)}
|
||||||
)}
|
</ActionButtonsGroup>
|
||||||
</ActionButtonsGroup>
|
</ActionsBar>
|
||||||
</ActionsBar>
|
)}
|
||||||
)}
|
</ToolContentWrapper>
|
||||||
</ToolContentWrapper>
|
</ToolContainer>
|
||||||
</ToolContainer>
|
</ConfigProvider>
|
||||||
</ConfigProvider>
|
<Modal
|
||||||
|
title={expandedResponse?.title}
|
||||||
|
open={!!expandedResponse}
|
||||||
|
onCancel={() => setExpandedResponse(null)}
|
||||||
|
footer={null}
|
||||||
|
width="80%"
|
||||||
|
centered
|
||||||
|
transitionName="animation-move-down"
|
||||||
|
styles={{ body: { maxHeight: '80vh', overflow: 'auto' } }}>
|
||||||
|
{expandedResponse && (
|
||||||
|
<ExpandedResponseContainer
|
||||||
|
style={{
|
||||||
|
fontFamily: messageFont === 'serif' ? 'var(--font-family-serif)' : 'var(--font-family)',
|
||||||
|
fontSize
|
||||||
|
}}>
|
||||||
|
<Tabs
|
||||||
|
tabBarExtraContent={
|
||||||
|
<ActionButton
|
||||||
|
className="copy-expanded-button"
|
||||||
|
onClick={() => {
|
||||||
|
navigator.clipboard.writeText(
|
||||||
|
typeof expandedResponse.content === 'string'
|
||||||
|
? expandedResponse.content
|
||||||
|
: JSON.stringify(expandedResponse.content, null, 2)
|
||||||
|
)
|
||||||
|
antdMessage.success({ content: t('message.copied'), key: 'copy-expanded' })
|
||||||
|
}}
|
||||||
|
aria-label={t('common.copy')}>
|
||||||
|
<i className="iconfont icon-copy"></i>
|
||||||
|
</ActionButton>
|
||||||
|
}
|
||||||
|
items={[
|
||||||
|
{
|
||||||
|
key: 'preview',
|
||||||
|
label: t('message.tools.preview'),
|
||||||
|
children: renderPreview(expandedResponse.content)
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'raw',
|
||||||
|
label: t('message.tools.raw'),
|
||||||
|
children: (
|
||||||
|
<CollapsedContent
|
||||||
|
isExpanded={true}
|
||||||
|
resultString={
|
||||||
|
typeof expandedResponse.content === 'string'
|
||||||
|
? expandedResponse.content
|
||||||
|
: JSON.stringify(expandedResponse.content, null, 2)
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
]}
|
||||||
|
/>
|
||||||
|
</ExpandedResponseContainer>
|
||||||
|
)}
|
||||||
|
</Modal>
|
||||||
|
</>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -563,4 +659,27 @@ const ToolResponseContainer = styled.div`
|
|||||||
position: relative;
|
position: relative;
|
||||||
`
|
`
|
||||||
|
|
||||||
|
const ExpandedResponseContainer = styled.div`
|
||||||
|
background: var(--color-bg-1);
|
||||||
|
border-radius: 8px;
|
||||||
|
padding: 16px;
|
||||||
|
position: relative;
|
||||||
|
|
||||||
|
.copy-expanded-button {
|
||||||
|
position: absolute;
|
||||||
|
top: 10px;
|
||||||
|
right: 10px;
|
||||||
|
background-color: var(--color-bg-2);
|
||||||
|
border-radius: 4px;
|
||||||
|
z-index: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
pre {
|
||||||
|
margin: 0;
|
||||||
|
white-space: pre-wrap;
|
||||||
|
word-break: break-word;
|
||||||
|
color: var(--color-text);
|
||||||
|
}
|
||||||
|
`
|
||||||
|
|
||||||
export default memo(MessageTools)
|
export default memo(MessageTools)
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user