mirror of
https://github.com/CherryHQ/cherry-studio.git
synced 2026-01-02 10:29:02 +08:00
fix: keep bubble metrics visible
This commit is contained in:
parent
f1a2e15dac
commit
36eaf5229d
@ -10,6 +10,7 @@ import type { ToolQuickPanelApi } from '@renderer/pages/home/Inputbar/types'
|
||||
import FileManager from '@renderer/services/FileManager'
|
||||
import PasteService from '@renderer/services/PasteService'
|
||||
import { useAppSelector } from '@renderer/store'
|
||||
import { messageBlocksSelectors } from '@renderer/store/messageBlock'
|
||||
import { selectMessagesForTopic } from '@renderer/store/newMessage'
|
||||
import type { FileMetadata } from '@renderer/types'
|
||||
import { FileTypes } from '@renderer/types'
|
||||
@ -43,7 +44,15 @@ interface Props {
|
||||
const logger = loggerService.withContext('MessageBlockEditor')
|
||||
|
||||
const MessageBlockEditor: FC<Props> = ({ message, topicId, onSave, onResend, onCancel }) => {
|
||||
const allBlocks = findAllBlocks(message)
|
||||
const blockEntities = useAppSelector(messageBlocksSelectors.selectEntities)
|
||||
const allBlocks = useMemo(() => {
|
||||
if (!message?.blocks || message.blocks.length === 0) {
|
||||
return []
|
||||
}
|
||||
return message.blocks
|
||||
.map((blockId) => blockEntities[blockId])
|
||||
.filter((block): block is MessageBlock => Boolean(block))
|
||||
}, [blockEntities, message?.blocks])
|
||||
const [editedBlocks, setEditedBlocks] = useState<MessageBlock[]>(allBlocks)
|
||||
const [files, setFiles] = useState<FileMetadata[]>([])
|
||||
const [isProcessing, setIsProcessing] = useState(false)
|
||||
@ -120,6 +129,12 @@ const MessageBlockEditor: FC<Props> = ({ message, topicId, onSave, onResend, onC
|
||||
return () => clearTimeout(timer)
|
||||
}, [])
|
||||
|
||||
useEffect(() => {
|
||||
if (editedBlocks.length === 0 && allBlocks.length > 0) {
|
||||
setEditedBlocks(allBlocks)
|
||||
}
|
||||
}, [allBlocks, editedBlocks.length])
|
||||
|
||||
// 仅在打开时执行一次
|
||||
useEffect(() => {
|
||||
if (textareaRef.current) {
|
||||
@ -202,7 +217,16 @@ const MessageBlockEditor: FC<Props> = ({ message, topicId, onSave, onResend, onC
|
||||
|
||||
// 处理编辑区块并上传文件
|
||||
const processEditedBlocks = async () => {
|
||||
const updatedBlocks = [...editedBlocks]
|
||||
let updatedBlocks = [...editedBlocks]
|
||||
|
||||
if (!updatedBlocks.some((block) => block.type === MessageBlockType.MAIN_TEXT)) {
|
||||
const originalMainTextBlocks = findAllBlocks(message).filter(
|
||||
(block): block is MessageBlock => block.type === MessageBlockType.MAIN_TEXT
|
||||
)
|
||||
if (originalMainTextBlocks.length > 0) {
|
||||
updatedBlocks = [...originalMainTextBlocks, ...updatedBlocks]
|
||||
}
|
||||
}
|
||||
if (files && files.length) {
|
||||
const uploadedFiles = await FileManager.uploadFiles(files)
|
||||
uploadedFiles.forEach((file) => {
|
||||
|
||||
@ -524,7 +524,7 @@ const MessageMenubar: FC<Props> = (props) => {
|
||||
const softHoverBg = isBubbleStyle && !isLastMessage
|
||||
const isUserBubbleStyleMessage = isBubbleStyle && isUserMessage
|
||||
const bubbleAlignment: 'flex-start' | 'flex-end' = isAssistantMessage ? 'flex-start' : 'flex-end'
|
||||
const messageTokensAlignment: 'left' | 'right' = isBubbleStyle ? 'right' : 'left'
|
||||
const messageTokensAlignment: 'left' | 'right' = isBubbleStyle || isAssistantMessage ? 'right' : 'left'
|
||||
|
||||
const tokensElement = <MessageTokens message={message} align={messageTokensAlignment} />
|
||||
|
||||
@ -563,7 +563,11 @@ const MessageMenubar: FC<Props> = (props) => {
|
||||
|
||||
if (isBubbleStyle) {
|
||||
return (
|
||||
<BubbleMenubarWrapper $align={bubbleAlignment}>
|
||||
<div
|
||||
className={classNames(
|
||||
'flex w-full flex-row items-center gap-2',
|
||||
bubbleAlignment === 'flex-start' ? 'justify-start' : 'justify-end'
|
||||
)}>
|
||||
<MenusBar
|
||||
className={classNames({
|
||||
menubar: true,
|
||||
@ -583,8 +587,8 @@ const MessageMenubar: FC<Props> = (props) => {
|
||||
return <Fragment key={buttonId}>{element}</Fragment>
|
||||
})}
|
||||
</MenusBar>
|
||||
<BubbleTokens>{tokensElement}</BubbleTokens>
|
||||
</BubbleMenubarWrapper>
|
||||
<div className="ml-auto flex min-w-0 flex-none justify-end">{tokensElement}</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
@ -610,23 +614,6 @@ const MessageMenubar: FC<Props> = (props) => {
|
||||
)
|
||||
}
|
||||
|
||||
const BubbleMenubarWrapper = styled.div<{ $align: 'flex-start' | 'flex-end' }>`
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
justify-content: ${(props) => props.$align};
|
||||
gap: 8px;
|
||||
width: 100%;
|
||||
`
|
||||
|
||||
const BubbleTokens = styled.div`
|
||||
margin-left: auto;
|
||||
flex: 0 0 auto;
|
||||
min-width: 0;
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
`
|
||||
|
||||
const MenusBar = styled.div`
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
|
||||
@ -1,9 +1,9 @@
|
||||
// import { useRuntime } from '@renderer/hooks/useRuntime'
|
||||
import { classNames } from '@renderer/utils'
|
||||
import { EVENT_NAMES, EventEmitter } from '@renderer/services/EventService'
|
||||
import type { Message } from '@renderer/types/newMessage'
|
||||
import { Popover } from 'antd'
|
||||
import { t } from 'i18next'
|
||||
import styled from 'styled-components'
|
||||
|
||||
interface MessageTokensProps {
|
||||
message: Message
|
||||
@ -54,11 +54,16 @@ const MessageTokens: React.FC<MessageTokensProps> = ({ message, align = 'left' }
|
||||
return <div />
|
||||
}
|
||||
|
||||
const metadataClassName = classNames(
|
||||
'message-tokens flex min-w-0 cursor-pointer select-text text-[10px] text-[var(--color-text-3)]',
|
||||
align === 'right' ? 'ml-auto justify-end' : 'mr-auto justify-start'
|
||||
)
|
||||
|
||||
if (message.role === 'user') {
|
||||
return (
|
||||
<MessageMetadata className="message-tokens" onClick={locateMessage} $align={align}>
|
||||
<div className={metadataClassName} onClick={locateMessage}>
|
||||
{`Tokens: ${message?.usage?.total_tokens}`}
|
||||
</MessageMetadata>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
@ -78,15 +83,15 @@ const MessageTokens: React.FC<MessageTokensProps> = ({ message, align = 'left' }
|
||||
const tokensInfo = (
|
||||
<span className="tokens">
|
||||
Tokens:
|
||||
<span>{message?.usage?.total_tokens}</span>
|
||||
<span>↑{message?.usage?.prompt_tokens}</span>
|
||||
<span>↓{message?.usage?.completion_tokens}</span>
|
||||
<span>{getPriceString()}</span>
|
||||
<span className="px-0.5">{message?.usage?.total_tokens}</span>
|
||||
<span className="px-0.5">↑{message?.usage?.prompt_tokens}</span>
|
||||
<span className="px-0.5">↓{message?.usage?.completion_tokens}</span>
|
||||
<span className="px-0.5">{getPriceString()}</span>
|
||||
</span>
|
||||
)
|
||||
|
||||
return (
|
||||
<MessageMetadata className="message-tokens" onClick={locateMessage} $align={align}>
|
||||
<div className={metadataClassName} onClick={locateMessage}>
|
||||
{hasMetrics ? (
|
||||
<Popover content={metrixs} placement="top" trigger="hover" styles={{ root: { fontSize: 11 } }}>
|
||||
{tokensInfo}
|
||||
@ -94,28 +99,11 @@ const MessageTokens: React.FC<MessageTokensProps> = ({ message, align = 'left' }
|
||||
) : (
|
||||
tokensInfo
|
||||
)}
|
||||
</MessageMetadata>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
return null
|
||||
}
|
||||
|
||||
const MessageMetadata = styled.div<{ $align: 'left' | 'right' }>`
|
||||
font-size: 10px;
|
||||
color: var(--color-text-3);
|
||||
user-select: text;
|
||||
cursor: pointer;
|
||||
display: flex;
|
||||
flex: 0 1 auto;
|
||||
min-width: 0;
|
||||
justify-content: ${(props) => (props.$align === 'right' ? 'flex-end' : 'flex-start')};
|
||||
margin-left: ${(props) => (props.$align === 'right' ? 'auto' : '0')};
|
||||
margin-right: ${(props) => (props.$align === 'left' ? 'auto' : '0')};
|
||||
|
||||
.tokens span {
|
||||
padding: 0 2px;
|
||||
}
|
||||
`
|
||||
|
||||
export default MessageTokens
|
||||
|
||||
Loading…
Reference in New Issue
Block a user