From a5525c065cd90a7869cc810abd8307e7d21c86af Mon Sep 17 00:00:00 2001 From: jialin Date: Thu, 13 Mar 2025 18:45:34 +0800 Subject: [PATCH] style: chat message render markdown by default --- src/components/icon-font/index.tsx | 2 +- src/components/markdown-viewer/index.less | 7 + src/locales/en-US/playground.ts | 1 + src/locales/ru-RU/playground.ts | 1 + src/locales/zh-CN/playground.ts | 1 + .../playground/components/ground-left.tsx | 24 +- .../playground/components/message-input.tsx | 4 +- .../components/multiple-chat/content-item.tsx | 220 +++++++++- .../components/multiple-chat/index.tsx | 24 +- .../multiple-chat/message-actions.tsx | 75 ++-- .../components/multiple-chat/message-body.tsx | 408 ++++++++++-------- .../multiple-chat/message-content.tsx | 2 + .../multiple-chat/think-content.tsx | 35 +- .../components/multiple-chat/think-parser.ts | 6 +- src/pages/playground/config/types.ts | 7 +- src/pages/playground/style/content-item.less | 26 +- src/pages/playground/style/think-content.less | 4 +- 17 files changed, 528 insertions(+), 319 deletions(-) diff --git a/src/components/icon-font/index.tsx b/src/components/icon-font/index.tsx index 3c9e572a..617c1c00 100644 --- a/src/components/icon-font/index.tsx +++ b/src/components/icon-font/index.tsx @@ -2,7 +2,7 @@ import { createFromIconfontCN } from '@ant-design/icons'; // import './iconfont/iconfont.js'; const IconFont = createFromIconfontCN({ - scriptUrl: '//at.alicdn.com/t/c/font_4613488_yikpvmcl8ag.js' + scriptUrl: '//at.alicdn.com/t/c/font_4613488_s7szfcrvqw.js' }); export default IconFont; diff --git a/src/components/markdown-viewer/index.less b/src/components/markdown-viewer/index.less index 02f9059f..d8b9a339 100644 --- a/src/components/markdown-viewer/index.less +++ b/src/components/markdown-viewer/index.less @@ -9,6 +9,13 @@ border-color: var(--ant-color-split); } + hr { + border: none; + height: 1px; + background-color: var(--ant-color-split); + border-color: var(--ant-color-split); + } + p { margin-bottom: 0; } diff --git a/src/locales/en-US/playground.ts b/src/locales/en-US/playground.ts index 0a0e6647..97c260e1 100644 --- a/src/locales/en-US/playground.ts +++ b/src/locales/en-US/playground.ts @@ -140,6 +140,7 @@ export default { 'playground.image.edit': 'Edit', 'playground.image.fitview': 'Fit View', 'playground.chat.aithought': 'CoT', + 'playground.chat.thinking': 'Thinking...', 'playground.image.mask.uploaded': 'Mask Uploaded', 'playground.image.mask.upload': 'Upload Mask: No additional drawing allowed after upload.', diff --git a/src/locales/ru-RU/playground.ts b/src/locales/ru-RU/playground.ts index 889fd625..851e1ff6 100644 --- a/src/locales/ru-RU/playground.ts +++ b/src/locales/ru-RU/playground.ts @@ -137,6 +137,7 @@ export default { 'playground.image.edit': 'Редактировать', 'playground.image.fitview': 'Подогнать размер', 'playground.chat.aithought': 'Рассуждение (CoT)', + 'playground.chat.thinking': 'TODO: Translate key "playground.chat.thinking"', 'playground.image.mask.uploaded': 'Маска загружена', 'playground.image.mask.upload': 'TODO: Translate key "playground.image.mask.upload"', diff --git a/src/locales/zh-CN/playground.ts b/src/locales/zh-CN/playground.ts index 4d9fd0ea..09dd6364 100644 --- a/src/locales/zh-CN/playground.ts +++ b/src/locales/zh-CN/playground.ts @@ -135,6 +135,7 @@ export default { 'playground.image.edit': '编辑图片', 'playground.image.fitview': '适应视图', 'playground.chat.aithought': '思考过程', + 'playground.chat.thinking': '思考中...', 'playground.image.mask.uploaded': '遮罩已上传', 'playground.image.mask.upload': '上传遮罩:上传后将不可再绘制', 'playground.params.frequency_penalty.tips': `数值介于 -2.0 和 2.0 之间。正值会根据新词在文本中已出现的频率对其进行惩罚,从而降低模型逐字重复相同内容的可能性。`, diff --git a/src/pages/playground/components/ground-left.tsx b/src/pages/playground/components/ground-left.tsx index ea1005a3..fef291b1 100644 --- a/src/pages/playground/components/ground-left.tsx +++ b/src/pages/playground/components/ground-left.tsx @@ -40,7 +40,8 @@ const GroundLeft: React.FC = forwardRef((props, ref) => { const [actions, setActions] = useState([ 'upload', 'delete', - 'copy' + 'copy', + 'edit' ]); const { @@ -107,15 +108,6 @@ const GroundLeft: React.FC = forwardRef((props, ref) => { setShow(false); }; - const handleOnCheck = (e: any) => { - const checked = e.target.checked; - if (checked) { - setActions(['upload', 'delete', 'copy', 'markdown']); - } else { - setActions(['upload', 'delete', 'copy']); - } - }; - return (
@@ -163,18 +155,8 @@ const GroundLeft: React.FC = forwardRef((props, ref) => { minRows: 5, maxRows: 5 }} - actions={[ - 'clear', - 'layout', - 'role', - 'upload', - 'add', - 'paste', - 'check' - ]} + actions={['clear', 'layout', 'role', 'upload', 'add', 'paste']} defaultChecked={false} - checkLabel="Markdown" - onCheck={handleOnCheck} loading={loading} disabled={!parameters.model} isEmpty={!messageList.length} diff --git a/src/pages/playground/components/message-input.tsx b/src/pages/playground/components/message-input.tsx index fb53831d..4b29efd1 100644 --- a/src/pages/playground/components/message-input.tsx +++ b/src/pages/playground/components/message-input.tsx @@ -285,7 +285,9 @@ const MessageInput: React.FC = forwardRef( return; } e.preventDefault(); - handleSendMessage(); + if (!loading) { + handleSendMessage(); + } } }; diff --git a/src/pages/playground/components/multiple-chat/content-item.tsx b/src/pages/playground/components/multiple-chat/content-item.tsx index 1542f938..9316c9a3 100644 --- a/src/pages/playground/components/multiple-chat/content-item.tsx +++ b/src/pages/playground/components/multiple-chat/content-item.tsx @@ -1,9 +1,14 @@ +import IconFont from '@/components/icon-font'; +import { StopOutlined } from '@ant-design/icons'; import { useIntl } from '@umijs/max'; -import React from 'react'; +import { Button } from 'antd'; +import React, { useMemo } from 'react'; +import { Roles } from '../../config'; import { MessageItem, MessageItemAction } from '../../config/types'; import '../../style/content-item.less'; import MessageActions from './message-actions'; import MessageBody from './message-body'; +import ThinkParser from './think-parser'; interface MessageItemProps { data: MessageItem; @@ -15,10 +20,81 @@ interface MessageItemProps { onDelete?: () => void; } -const contentRenderOptions = [ - { label: 'Markdown', value: 'markdown' }, - { label: 'Plain Text', value: 'plain' } -]; +interface SaveActionsProps { + handleSave: () => void; + handleCancel: () => void; +} + +interface ThoughtStatusProps { + collapsed: boolean; + setCollapsed: (collapsed: boolean) => void; + isThinking: boolean; +} + +const SaveActions: React.FC = ({ + handleSave, + handleCancel +}) => { + const intl = useIntl(); + return ( +
+
+ + +
+
+ ); +}; + +const ThoughtStatus: React.FC = ({ + collapsed, + setCollapsed, + isThinking +}) => { + const intl = useIntl(); + return ( +
+ +
+ ); +}; const ContentItem: React.FC = ({ updateMessage, @@ -27,35 +103,141 @@ const ContentItem: React.FC = ({ data, editable, showTitle = true, - actions = ['upload', 'delete', 'copy'] + actions = ['upload', 'delete', 'copy', 'edit'] }) => { const intl = useIntl(); + const [showMarkdown, setShowMarkdown] = React.useState(true); + const messageRef = React.useRef(null); + const thinkerRef = React.useRef(null); + const [collapsed, setCollapsed] = React.useState(false); + + const handleOnEdit = () => { + setShowMarkdown(false); + messageRef.current?.setEditContent(data.content); + }; + + const handleSave = () => { + messageRef.current?.handleSaveEdit(); + setShowMarkdown(true); + }; + + const handleCancel = () => { + messageRef.current?.handleCancelEdit(); + setShowMarkdown(true); + }; + + const handleUpdateMessage = (message: MessageItem) => { + updateMessage?.(message); + if (data.role === Roles.Assistant) { + thinkerRef.current?.reset(); + } + }; + + const reasoningContent = useMemo(() => { + if (data.role === Roles.User) { + return { + thought: '', + isThinking: false, + result: '' + }; + } + if (!thinkerRef.current && showMarkdown) { + thinkerRef.current = new ThinkParser(); + } + if (showMarkdown) { + const res = thinkerRef.current.parse(data.content); + return res; + } + return { + thought: '', + isThinking: false, + result: data.content + }; + }, [data.content, showMarkdown, data.role]); + + const renderTitle = useMemo(() => { + if (!showTitle) { + return null; + } + let roleTitle: React.ReactNode = intl.formatMessage({ + id: `playground.${data.role}` + }); + let avatar = ( + + ); + + if (data.role === Roles.Assistant) { + avatar = ( + + ); + } + + if (data.title) { + roleTitle = data.title; + } + return ( +
+ {avatar} + {roleTitle} + {data.role === Roles.Assistant && reasoningContent.thought && ( + + )} +
+ ); + }, [ + data.role, + data.title, + intl, + showTitle, + reasoningContent.thought, + reasoningContent.isThinking, + collapsed + ]); return (
+
{showTitle && renderTitle}
- {showTitle && ( -
- {data.title ?? - intl.formatMessage({ id: `playground.${data.role}` })} -
+ {showMarkdown ? ( + + ) : ( + )} -
); diff --git a/src/pages/playground/components/multiple-chat/index.tsx b/src/pages/playground/components/multiple-chat/index.tsx index 966af52f..b01ea3ba 100644 --- a/src/pages/playground/components/multiple-chat/index.tsx +++ b/src/pages/playground/components/multiple-chat/index.tsx @@ -43,7 +43,8 @@ const MultiCompare: React.FC = ({ modelList, loaded }) => { const [actions, setActions] = useState([ 'upload', 'delete', - 'copy' + 'copy', + 'edit' ]); const isLoading = useMemo(() => { @@ -222,15 +223,6 @@ const MultiCompare: React.FC = ({ modelList, loaded }) => { handleUpdateModelList(value); }; - const handleOnCheck = (e: any) => { - const checked = e.target.checked; - if (checked) { - setActions(['upload', 'delete', 'copy', 'markdown']); - } else { - setActions(['upload', 'delete', 'copy']); - } - }; - useEffect(() => { modelRefs.current = {}; let list = _.take(modelList, spans.count); @@ -305,18 +297,8 @@ const MultiCompare: React.FC = ({ modelList, loaded }) => { clearAll={handleClearAll} updateLayout={updateLayout} setModelSelections={handleUpdateModelSelections} - actions={[ - 'clear', - 'layout', - 'role', - 'upload', - 'add', - 'paste', - 'check' - ]} + actions={['clear', 'layout', 'role', 'upload', 'add', 'paste']} defaultChecked={false} - checkLabel="Markdown" - onCheck={handleOnCheck} defaultSize={{ minRows: 5, maxRows: 5 diff --git a/src/pages/playground/components/multiple-chat/message-actions.tsx b/src/pages/playground/components/multiple-chat/message-actions.tsx index e4a2c296..9f10a928 100644 --- a/src/pages/playground/components/multiple-chat/message-actions.tsx +++ b/src/pages/playground/components/multiple-chat/message-actions.tsx @@ -1,9 +1,5 @@ import CopyButton from '@/components/copy-button'; -import { - FileMarkdownOutlined, - FileTextOutlined, - MinusCircleOutlined -} from '@ant-design/icons'; +import { EditOutlined, MinusCircleOutlined } from '@ant-design/icons'; import { useIntl } from '@umijs/max'; import { Button, Tooltip } from 'antd'; import React, { useCallback } from 'react'; @@ -14,24 +10,19 @@ import UploadImg from '../upload-img'; interface MessageActionsProps { data: MessageItem; actions: MessageItemAction[]; - renderMode?: string; - renderModeChange?: (value: string) => void; + loading?: boolean; updateMessage?: (message: MessageItem) => void; onDelete?: () => void; + onEdit?: () => void; } -const renderOptions = [ - { label: 'Markdown', value: 'markdown', icon: }, - { label: 'Plain', value: 'plain', icon: } -]; - const MessageActions: React.FC = ({ actions, data, - renderMode, - renderModeChange, + loading, updateMessage, - onDelete + onDelete, + onEdit }) => { const intl = useIntl(); @@ -49,30 +40,44 @@ const MessageActions: React.FC = ({ return ( <> - {actions.length > 1 ? ( + {actions.length > 1 && !loading ? (
- {actions.includes('upload') && data.role === Roles.User && ( - - )} - {data.content && actions.includes('copy') && ( - - )} - {actions.includes('delete') && ( - -
) : null} diff --git a/src/pages/playground/components/multiple-chat/message-body.tsx b/src/pages/playground/components/multiple-chat/message-body.tsx index 857caf13..cf7e514c 100644 --- a/src/pages/playground/components/multiple-chat/message-body.tsx +++ b/src/pages/playground/components/multiple-chat/message-body.tsx @@ -2,217 +2,214 @@ import FullMarkdown from '@/components/markdown-viewer/full-markdown'; import { Input } from 'antd'; import classNames from 'classnames'; import _ from 'lodash'; -import React, { useCallback, useMemo, useRef } from 'react'; +import React, { + forwardRef, + useCallback, + useImperativeHandle, + useRef +} from 'react'; import { Roles } from '../../config'; import { MessageItem, MessageItemAction } from '../../config/types'; import ThumbImg from '../thumb-img'; import ThinkContent from './think-content'; -import ThinkParser from './think-parser'; interface MessageBodyProps { + ref?: any; data: MessageItem; editable?: boolean; loading?: boolean; showTitle?: boolean; actions?: MessageItemAction[]; + showMarkdown?: boolean; + reasoningContent: { + thought: string; + isThinking: boolean; + result: string; + }; + collapsed?: boolean; updateMessage?: (message: MessageItem) => void; onDelete?: () => void; } -const MessageBody: React.FC = ({ - editable, - data, - loading, - actions, - updateMessage -}) => { - const inputRef = useRef(null); - const imgCountRef = useRef(0); - const thinkerRef = useRef(null); - - const content = useMemo(() => { - if (!thinkerRef.current && actions?.includes('markdown')) { - thinkerRef.current = new ThinkParser(); - } - if (actions?.includes('markdown')) { - const res = thinkerRef.current.parse(data.content); - console.log('markdown parse:', res); - return res; - } - return { - thought: '', - result: data.content - }; - }, [data.content, actions]); - - const getPasteContent = useCallback( - async (event: any) => { - // @ts-ignore - const clipboardData = event.clipboardData || window.clipboardData; - const items = clipboardData.items; - const imgPromises: Promise[] = []; - - for (let i = 0; i < items.length; i++) { - let item = items[i]; - - if (item.kind === 'file' && item.type.indexOf('image') !== -1) { - const file = item.getAsFile(); - const imgPromise = new Promise((resolve, reject) => { - const reader = new FileReader(); - reader.onload = function (event) { - const base64String = event.target?.result as string; - if (base64String) { - resolve(base64String); - } else { - reject('Failed to convert image to base64'); - } - }; - reader.readAsDataURL(file); - }); - imgPromises.push(imgPromise); - } else if (item.kind === 'string') { - // string +const MessageBody: React.FC = forwardRef( + ( + { + editable, + data, + loading, + reasoningContent, + showMarkdown, + collapsed, + updateMessage + }, + ref + ) => { + const inputRef = useRef(null); + const imgCountRef = useRef(0); + const [editContent, setEditContent] = React.useState(data.content); + + const getPasteContent = useCallback( + async (event: any) => { + // @ts-ignore + const clipboardData = event.clipboardData || window.clipboardData; + const items = clipboardData.items; + const imgPromises: Promise[] = []; + + for (let i = 0; i < items.length; i++) { + let item = items[i]; + + if (item.kind === 'file' && item.type.indexOf('image') !== -1) { + const file = item.getAsFile(); + const imgPromise = new Promise((resolve, reject) => { + const reader = new FileReader(); + reader.onload = function (event) { + const base64String = event.target?.result as string; + if (base64String) { + resolve(base64String); + } else { + reject('Failed to convert image to base64'); + } + }; + reader.readAsDataURL(file); + }); + imgPromises.push(imgPromise); + } else if (item.kind === 'string') { + // string + } } - } - try { - const imgs = await Promise.all(imgPromises); - if (imgs.length) { - const list = _.map(imgs, (img: string) => { - imgCountRef.current += 1; - return { - uid: imgCountRef.current, - dataUrl: img - }; - }); - - updateMessage?.({ - role: data.role, - content: data.content, - uid: data.uid, - imgs: [...(data.imgs || []), ...list] - }); + try { + const imgs = await Promise.all(imgPromises); + if (imgs.length) { + const list = _.map(imgs, (img: string) => { + imgCountRef.current += 1; + return { + uid: imgCountRef.current, + dataUrl: img + }; + }); + + updateMessage?.({ + role: data.role, + content: data.content, + uid: data.uid, + imgs: [...(data.imgs || []), ...list] + }); + } + } catch (error) { + console.error('Error processing images:', error); } - } catch (error) { - console.error('Error processing images:', error); + }, + [data, updateMessage] + ); + + const handleOnPaste = (e: any) => { + const text = e.clipboardData.getData('text'); + if (!text) { + e.preventDefault(); + getPasteContent(e); } - }, - [data, updateMessage] - ); + }; - const handleOnPaste = (e: any) => { - const text = e.clipboardData.getData('text'); - if (!text) { - e.preventDefault(); - getPasteContent(e); - } - }; + const handleDeleteImg = (uid: number | string) => { + const list = _.filter(data.imgs, (item: MessageItem) => item.uid !== uid); + updateMessage?.({ + role: data.role, + content: data.content, + uid: data.uid, + imgs: list + }); + }; - const handleDeleteImg = (uid: number | string) => { - const list = _.filter(data.imgs, (item: MessageItem) => item.uid !== uid); - updateMessage?.({ - role: data.role, - content: data.content, - uid: data.uid, - imgs: list - }); - }; + const handleMessageChange = (e: any) => { + updateMessage?.({ + imgs: data.imgs || [], + role: data.role, + content: e.target.value, + uid: data.uid + }); + }; - const handleMessageChange = (e: any) => { - updateMessage?.({ - imgs: data.imgs || [], - role: data.role, - content: e.target.value, - uid: data.uid - }); - if (data.role === Roles.Assistant) { - thinkerRef.current?.reset(); - } - }; + const handleEdit = (e: any) => { + setEditContent(e.target.value); + }; - const handleDeleteLastImage = useCallback(() => { - if (data.imgs && data.imgs?.length > 0) { - const newImgList = [...(data.imgs || [])]; - const lastImage = newImgList.pop(); - if (lastImage) { - handleDeleteImg(lastImage.uid); - } - } - }, [data.imgs, handleDeleteImg]); - - const handleKeyDown = useCallback( - (event: any) => { - if ( - event.key === 'Backspace' && - data.content === '' && - data.imgs && - data.imgs?.length > 0 - ) { - // inputref blur - event.preventDefault(); - handleDeleteLastImage(); + const handleCancelEdit = () => { + setEditContent(data.content); + }; + + const handleSaveEdit = () => { + updateMessage?.({ + role: data.role, + content: editContent, + uid: data.uid, + imgs: data.imgs + }); + }; + + const handleDeleteLastImage = useCallback(() => { + if (data.imgs && data.imgs?.length > 0) { + const newImgList = [...(data.imgs || [])]; + const lastImage = newImgList.pop(); + if (lastImage) { + handleDeleteImg(lastImage.uid); + } } - }, - [data, handleDeleteLastImage] - ); - const handleClickWrapper = (e: any) => { - e.stopPropagation(); - e.preventDefault(); - inputRef.current?.focus(); - }; + }, [data.imgs, handleDeleteImg]); - if (!editable) { - return ( -
- - {data.content &&
{data.content}
} -
+ const handleKeyDown = useCallback( + (event: any) => { + if ( + event.key === 'Backspace' && + data.content === '' && + data.imgs && + data.imgs?.length > 0 + ) { + // inputref blur + event.preventDefault(); + handleDeleteLastImage(); + } + }, + [data, handleDeleteLastImage] ); - } + const handleClickWrapper = (e: any) => { + e.stopPropagation(); + e.preventDefault(); + inputRef.current?.focus(); + }; + + const renderReadOnlyMode = () => { + return ( +
+ + {data.content &&
{data.content}
} +
+ ); + }; - return ( -
- - <> - {data.role === Roles.User ? ( - { + return ( +
+ - ) : ( <> - {actions?.includes('markdown') ? ( - <> - - - - ) : ( + {data.role === Roles.User ? ( = ({ onChange={handleMessageChange} onPaste={handleOnPaste} /> + ) : ( + <> + {showMarkdown ? ( + <> + +
+ +
+ + ) : ( + + )} + )} - )} - -
- ); -}; +
+ ); + }; + + useImperativeHandle(ref, () => ({ + handleSaveEdit, + handleCancelEdit, + setEditContent + })); + + return <>{editable ? renderEditMode() : renderReadOnlyMode()}; + } +); export default MessageBody; diff --git a/src/pages/playground/components/multiple-chat/message-content.tsx b/src/pages/playground/components/multiple-chat/message-content.tsx index bedc7e76..188b5ca5 100644 --- a/src/pages/playground/components/multiple-chat/message-content.tsx +++ b/src/pages/playground/components/multiple-chat/message-content.tsx @@ -17,6 +17,7 @@ const MessageContent: React.FC = ({ messageList, editable, showTitle = true, + loading, actions = ['upload', 'delete', 'copy'] }) => { const updateMessage = (index: number, message: MessageItem) => { @@ -37,6 +38,7 @@ const MessageContent: React.FC = ({
{messageList.map((item, index) => ( = ({ content }) => { - const intl = useIntl(); - const [collapsed, setCollapsed] = useState(false); +const ThinkContent: React.FC = ({ content, collapsed }) => { return ( <> {content ? (
-
- -
- {!collapsed &&
{content}
} + {!collapsed && ( +
+ +
+ )}
) : null} diff --git a/src/pages/playground/components/multiple-chat/think-parser.ts b/src/pages/playground/components/multiple-chat/think-parser.ts index c7c87846..00fa84fb 100644 --- a/src/pages/playground/components/multiple-chat/think-parser.ts +++ b/src/pages/playground/components/multiple-chat/think-parser.ts @@ -49,7 +49,11 @@ class ThinkParser { } } - return { thought: this.thought.trim(), result: this.result }; + return { + thought: this.thought.trim(), + result: this.result, + isThinking: this.collecting + }; } reset() { diff --git a/src/pages/playground/config/types.ts b/src/pages/playground/config/types.ts index 148ecc88..f15dc8c6 100644 --- a/src/pages/playground/config/types.ts +++ b/src/pages/playground/config/types.ts @@ -6,7 +6,12 @@ export interface ModelSelectionItem extends Global.BaseOption { type?: string; } -export type MessageItemAction = 'upload' | 'delete' | 'copy' | 'markdown'; +export type MessageItemAction = + | 'upload' + | 'delete' + | 'copy' + | 'markdown' + | 'edit'; export interface MessageItem { content: string; diff --git a/src/pages/playground/style/content-item.less b/src/pages/playground/style/content-item.less index 05abf71c..c2215aad 100644 --- a/src/pages/playground/style/content-item.less +++ b/src/pages/playground/style/content-item.less @@ -1,5 +1,6 @@ .content-item { - margin-bottom: 12px; + margin-bottom: 24px; + position: relative; .markdown-viewer { h4 { @@ -12,10 +13,19 @@ &-role { display: flex; align-items: center; - justify-content: space-between; + justify-content: flex-end; font-weight: var(--font-weight-bold); margin-bottom: 8px; height: 24px; + position: sticky; + top: 0; + z-index: 100; + } + + .content-item-title { + position: absolute; + top: 0; + left: 0; } .role { @@ -26,6 +36,15 @@ .actions { display: none; + border: 1px solid var(--ant-color-border); + border-radius: var(--border-radius-base); + background-color: var(--color-white-1); + } + + .actions-wrap { + display: flex; + align-items: center; + justify-content: flex-start; } .actions .mrkd-switch { @@ -43,9 +62,6 @@ &:hover { .actions { display: flex; - align-items: center; - justify-content: flex-start; - gap: 5px; } } diff --git a/src/pages/playground/style/think-content.less b/src/pages/playground/style/think-content.less index 227f5fc1..2561ce69 100644 --- a/src/pages/playground/style/think-content.less +++ b/src/pages/playground/style/think-content.less @@ -1,5 +1,4 @@ .think-wrapper { - margin-bottom: 16px; color: var(--ant-color-text-tertiary); .btn-collapse { @@ -10,10 +9,11 @@ } .think-content { - white-space: pre-wrap; + margin-bottom: 8px; padding: 12px; border-left: 2px solid var(--ant-color-fill-secondary); background-color: var(--ant-color-fill-tertiary); color: var(--ant-color-text-tertiary); + border-radius: var(--border-radius-base); } }