mirror of
https://github.com/CherryHQ/cherry-studio.git
synced 2025-12-27 12:51:26 +08:00
Fix: bubble-style unnecessary menu background (Plan D) (#7026)
* fix: bubble-style unnecessary menu background * fix: show divider in message only in plain mode * fix: bubble user message style in dark mode * fix: action button hover style * refactor: The rendering position of the message menbar is determined by the settings * fix: bubble style assistant message token usage left align * fix: bubble style * fix: bubble style * fix: text color and bubble edit * fix: bubble editing * fix: bubble editing * fix: bubble editor * fix: editor width * fix: remove redundant tokens usage * fix: not unified token font size and color * fix: unexpected display behavior in plain mode * fix: info style * fix: bubble style * fix: Style fixes for better compatibility * fix: bubble style * fix: Move the menu of the last message to the outside * fix: bubble style * fix: why this happened? * feat: add description for messages divider in settings * fix: 谁想出来的上下margin不一样还是神秘数字 * fix: new context style
This commit is contained in:
parent
cf230c4f9b
commit
4ac9a556a8
@ -4,13 +4,3 @@
|
||||
border-top-left-radius: 10px;
|
||||
border-left: 0.5px solid var(--color-border);
|
||||
}
|
||||
|
||||
.group-container {
|
||||
.context-menu-container {
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
.context-menu-container {
|
||||
max-width: 100%;
|
||||
}
|
||||
|
||||
@ -129,22 +129,29 @@ ul {
|
||||
.message-content-container {
|
||||
margin: 5px 0;
|
||||
border-radius: 8px;
|
||||
padding: 10px 15px 0 15px;
|
||||
padding: 0.5rem 1rem;
|
||||
}
|
||||
|
||||
.block-wrapper {
|
||||
display: flow-root;
|
||||
}
|
||||
|
||||
.message-content-container > *:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.message-thought-container {
|
||||
margin-top: 8px;
|
||||
}
|
||||
|
||||
.message-user {
|
||||
color: var(--chat-text-user);
|
||||
.markdown,
|
||||
.anticon,
|
||||
.iconfont,
|
||||
.lucide,
|
||||
.message-tokens {
|
||||
.message-content-container-user .anticon {
|
||||
color: var(--chat-text-user) !important;
|
||||
}
|
||||
.message-action-button:hover {
|
||||
background-color: var(--color-white-soft);
|
||||
|
||||
.markdown {
|
||||
color: var(--chat-text-user);
|
||||
}
|
||||
}
|
||||
.group-grid-container.horizontal,
|
||||
@ -165,6 +172,12 @@ ul {
|
||||
code {
|
||||
color: var(--color-text);
|
||||
}
|
||||
.markdown {
|
||||
display: flow-root;
|
||||
*:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.lucide {
|
||||
|
||||
@ -6,9 +6,10 @@ import styled from 'styled-components'
|
||||
interface ContextMenuProps {
|
||||
children: React.ReactNode
|
||||
onContextMenu?: (e: React.MouseEvent) => void
|
||||
style?: React.CSSProperties
|
||||
}
|
||||
|
||||
const ContextMenu: React.FC<ContextMenuProps> = ({ children, onContextMenu }) => {
|
||||
const ContextMenu: React.FC<ContextMenuProps> = ({ children, onContextMenu, style }) => {
|
||||
const { t } = useTranslation()
|
||||
const [contextMenuPosition, setContextMenuPosition] = useState<{ x: number; y: number } | null>(null)
|
||||
const [selectedText, setSelectedText] = useState<string>('')
|
||||
@ -66,7 +67,7 @@ const ContextMenu: React.FC<ContextMenuProps> = ({ children, onContextMenu }) =>
|
||||
]
|
||||
|
||||
return (
|
||||
<ContextContainer onContextMenu={handleContextMenu} className="context-menu-container">
|
||||
<ContextContainer onContextMenu={handleContextMenu} className="context-menu-container" style={style}>
|
||||
{contextMenuPosition && (
|
||||
<Dropdown
|
||||
overlayStyle={{ position: 'fixed', left: contextMenuPosition.x, top: contextMenuPosition.y, zIndex: 1000 }}
|
||||
|
||||
@ -1512,6 +1512,7 @@
|
||||
"messages.prompt": "Show prompt",
|
||||
"messages.tokens": "Show token usage",
|
||||
"messages.divider": "Show divider between messages",
|
||||
"messages.divider.tooltip": "Not applicable to bubble-style message",
|
||||
"messages.grid_columns": "Message grid display columns",
|
||||
"messages.grid_popover_trigger": "Grid detail trigger",
|
||||
"messages.grid_popover_trigger.click": "Click to display",
|
||||
|
||||
@ -1506,6 +1506,7 @@
|
||||
"messages.prompt": "プロンプト表示",
|
||||
"messages.tokens": "トークン使用量を表示",
|
||||
"messages.divider": "メッセージ間に区切り線を表示",
|
||||
"messages.divider.tooltip": "バブルスタイルのメッセージには適用されません",
|
||||
"messages.grid_columns": "メッセージグリッドの表示列数",
|
||||
"messages.grid_popover_trigger": "グリッド詳細トリガー",
|
||||
"messages.grid_popover_trigger.click": "クリックで表示",
|
||||
|
||||
@ -1506,6 +1506,7 @@
|
||||
"messages.prompt": "Показывать подсказки",
|
||||
"messages.tokens": "Показать использование токенов",
|
||||
"messages.divider": "Показывать разделитель между сообщениями",
|
||||
"messages.divider.tooltip": "Не применимо к сообщениям в стиле пузырей",
|
||||
"messages.grid_columns": "Количество столбцов сетки сообщений",
|
||||
"messages.grid_popover_trigger": "Триггер для отображения подробной информации в сетке",
|
||||
"messages.grid_popover_trigger.click": "Нажатие для отображения",
|
||||
|
||||
@ -1512,6 +1512,7 @@
|
||||
"messages.prompt": "显示提示词",
|
||||
"messages.tokens": "显示Token用量",
|
||||
"messages.divider": "消息分割线",
|
||||
"messages.divider.tooltip": "不适用于气泡样式消息",
|
||||
"messages.grid_columns": "消息网格展示列数",
|
||||
"messages.grid_popover_trigger": "网格详情触发",
|
||||
"messages.grid_popover_trigger.click": "点击显示",
|
||||
|
||||
@ -1509,6 +1509,7 @@
|
||||
"messages.prompt": "提示詞顯示",
|
||||
"messages.tokens": "Token用量顯示",
|
||||
"messages.divider": "訊息間顯示分隔線",
|
||||
"messages.divider.tooltip": "不適用於氣泡樣式消息",
|
||||
"messages.grid_columns": "訊息網格展示列數",
|
||||
"messages.grid_popover_trigger": "網格詳細資訊觸發",
|
||||
"messages.grid_popover_trigger.click": "點選顯示",
|
||||
|
||||
@ -1300,6 +1300,7 @@
|
||||
"advancedSettings": "Προχωρημένες Ρυθμίσεις"
|
||||
},
|
||||
"messages.divider": "Διαχωριστική γραμμή μηνυμάτων",
|
||||
"messages.divider.tooltip": "Δεν ισχύει για μηνύματα με στυλ φυσαλίδας",
|
||||
"messages.grid_columns": "Αριθμός στήλων γριλ μηνυμάτων",
|
||||
"messages.grid_popover_trigger": "Καταγραφή στοιχείων στο grid",
|
||||
"messages.grid_popover_trigger.click": "Εμφάνιση κλικ",
|
||||
|
||||
@ -1299,6 +1299,7 @@
|
||||
"advancedSettings": "Configuración avanzada"
|
||||
},
|
||||
"messages.divider": "Separador de mensajes",
|
||||
"messages.divider.tooltip": "No aplicable para mensajes de estilo burbuja",
|
||||
"messages.grid_columns": "Número de columnas en la cuadrícula de mensajes",
|
||||
"messages.grid_popover_trigger": "Desencadenante de detalles de cuadrícula",
|
||||
"messages.grid_popover_trigger.click": "Mostrar al hacer clic",
|
||||
|
||||
@ -1300,6 +1300,7 @@
|
||||
"advancedSettings": "Расширенные настройки"
|
||||
},
|
||||
"messages.divider": "Séparateur de messages",
|
||||
"messages.divider.tooltip": "Non applicable aux messages de style bulle",
|
||||
"messages.grid_columns": "Nombre de colonnes de la grille de messages",
|
||||
"messages.grid_popover_trigger": "Déclencheur de popover de la grille",
|
||||
"messages.grid_popover_trigger.click": "Afficher au clic",
|
||||
|
||||
@ -1302,6 +1302,7 @@
|
||||
"advancedSettings": "Configurações Avançadas"
|
||||
},
|
||||
"messages.divider": "Divisor de mensagens",
|
||||
"messages.divider.tooltip": "Não aplicável a mensagens de estilo bolha",
|
||||
"messages.grid_columns": "Número de colunas da grade de mensagens",
|
||||
"messages.grid_popover_trigger": "Disparador de detalhes da grade",
|
||||
"messages.grid_popover_trigger.click": "Clique para mostrar",
|
||||
|
||||
@ -93,19 +93,15 @@ const Markdown: FC<Props> = ({ block }) => {
|
||||
} as Partial<Components>
|
||||
}, [onSaveCodeBlock])
|
||||
|
||||
if (messageContent.includes('<style>')) {
|
||||
components.style = MarkdownShadowDOMRenderer as any
|
||||
}
|
||||
|
||||
const urlTransform = useCallback((value: string) => {
|
||||
if (value.startsWith('data:image/png') || value.startsWith('data:image/jpeg')) return value
|
||||
return defaultUrlTransform(value)
|
||||
}, [])
|
||||
|
||||
// if (role === 'user' && !renderInputMessageAsMarkdown) {
|
||||
// return <p style={{ marginBottom: 5, whiteSpace: 'pre-wrap' }}>{messageContent}</p>
|
||||
// }
|
||||
|
||||
if (messageContent.includes('<style>')) {
|
||||
components.style = MarkdownShadowDOMRenderer as any
|
||||
}
|
||||
|
||||
return (
|
||||
<ReactMarkdown
|
||||
rehypePlugins={rehypePlugins}
|
||||
|
||||
@ -31,7 +31,7 @@ const MessageErrorInfo: React.FC<{ block: ErrorMessageBlock }> = ({ block }) =>
|
||||
}
|
||||
|
||||
const Alert = styled(AntdAlert)`
|
||||
margin: 15px 0 8px;
|
||||
margin: 0.5rem 0;
|
||||
padding: 10px;
|
||||
font-size: 12px;
|
||||
`
|
||||
|
||||
@ -151,7 +151,7 @@ const MainTextBlock: React.FC<Props> = ({ block, citationBlockId, role, mentions
|
||||
</Flex>
|
||||
)}
|
||||
{role === 'user' && !renderInputMessageAsMarkdown ? (
|
||||
<p className="markdown" style={{ marginBottom: 5, whiteSpace: 'pre-wrap' }}>
|
||||
<p className="markdown" style={{ whiteSpace: 'pre-wrap' }}>
|
||||
{block.content}
|
||||
</p>
|
||||
) : (
|
||||
|
||||
@ -42,6 +42,7 @@ const blockWrapperVariants = {
|
||||
const AnimatedBlockWrapper: React.FC<AnimatedBlockWrapperProps> = ({ children, enableAnimation }) => {
|
||||
return (
|
||||
<motion.div
|
||||
className="block-wrapper"
|
||||
variants={blockWrapperVariants}
|
||||
initial={enableAnimation ? 'hidden' : 'static'}
|
||||
animate={enableAnimation ? 'visible' : 'static'}>
|
||||
|
||||
@ -21,7 +21,6 @@ import MessageEditor from './MessageEditor'
|
||||
import MessageErrorBoundary from './MessageErrorBoundary'
|
||||
import MessageHeader from './MessageHeader'
|
||||
import MessageMenubar from './MessageMenubar'
|
||||
import MessageTokens from './MessageTokens'
|
||||
|
||||
interface Props {
|
||||
message: Message
|
||||
@ -99,7 +98,7 @@ const MessageItem: FC<Props> = ({
|
||||
const isAssistantMessage = message.role === 'assistant'
|
||||
const showMenubar = !hideMenuBar && !isStreaming && !message.status.includes('ing') && !isEditing
|
||||
|
||||
const messageBorder = showMessageDivider ? undefined : 'none'
|
||||
const messageBorder = !isBubbleStyle && showMessageDivider ? '1px dotted var(--color-border)' : 'none'
|
||||
const messageBackground = getMessageBackground(isBubbleStyle, isAssistantMessage)
|
||||
|
||||
const messageHighlightHandler = useCallback((highlight: boolean = true) => {
|
||||
@ -130,22 +129,6 @@ const MessageItem: FC<Props> = ({
|
||||
)
|
||||
}
|
||||
|
||||
if (isEditing) {
|
||||
return (
|
||||
<MessageContainer style={{ paddingTop: 15 }}>
|
||||
<MessageHeader message={message} assistant={assistant} model={model} key={getModelUniqId(model)} />
|
||||
<div style={{ paddingLeft: messageStyle === 'plain' ? 46 : undefined }}>
|
||||
<MessageEditor
|
||||
message={message}
|
||||
onSave={handleEditSave}
|
||||
onResend={handleEditResend}
|
||||
onCancel={handleEditCancel}
|
||||
/>
|
||||
</div>
|
||||
</MessageContainer>
|
||||
)
|
||||
}
|
||||
|
||||
return (
|
||||
<MessageContainer
|
||||
key={message.id}
|
||||
@ -155,35 +138,99 @@ const MessageItem: FC<Props> = ({
|
||||
'message-user': !isAssistantMessage
|
||||
})}
|
||||
ref={messageContainerRef}
|
||||
style={{ ...style, alignItems: isBubbleStyle ? (isAssistantMessage ? 'start' : 'end') : undefined }}>
|
||||
<ContextMenu>
|
||||
<MessageHeader message={message} assistant={assistant} model={model} key={getModelUniqId(model)} />
|
||||
<MessageContentContainer
|
||||
className={
|
||||
message.role === 'user'
|
||||
? 'message-content-container message-content-container-user'
|
||||
: message.role === 'assistant'
|
||||
? 'message-content-container message-content-container-assistant'
|
||||
: 'message-content-container'
|
||||
}
|
||||
style={{
|
||||
...style,
|
||||
justifyContent: isBubbleStyle ? (isAssistantMessage ? 'flex-start' : 'flex-end') : undefined,
|
||||
flex: isBubbleStyle ? undefined : 1
|
||||
}}>
|
||||
{isEditing && (
|
||||
<ContextMenu
|
||||
style={{
|
||||
fontFamily: messageFont === 'serif' ? 'var(--font-family-serif)' : 'var(--font-family)',
|
||||
fontSize,
|
||||
background: messageBackground,
|
||||
overflowY: 'visible',
|
||||
maxWidth: narrowMode ? 760 : undefined
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
alignSelf: isAssistantMessage ? 'flex-start' : 'flex-end',
|
||||
width: isBubbleStyle ? '70%' : '100%'
|
||||
}}>
|
||||
<MessageErrorBoundary>
|
||||
<MessageContent message={message} />
|
||||
</MessageErrorBoundary>
|
||||
{showMenubar && (
|
||||
<MessageHeader
|
||||
message={message}
|
||||
assistant={assistant}
|
||||
model={model}
|
||||
key={getModelUniqId(model)}
|
||||
index={index}
|
||||
/>
|
||||
<div style={{ paddingLeft: messageStyle === 'plain' ? 46 : undefined }}>
|
||||
<MessageEditor
|
||||
message={message}
|
||||
onSave={handleEditSave}
|
||||
onResend={handleEditResend}
|
||||
onCancel={handleEditCancel}
|
||||
/>
|
||||
</div>
|
||||
</ContextMenu>
|
||||
)}
|
||||
{!isEditing && (
|
||||
<ContextMenu
|
||||
style={{
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
alignSelf: isAssistantMessage ? 'flex-start' : 'flex-end',
|
||||
flex: isBubbleStyle ? undefined : 1
|
||||
}}>
|
||||
<MessageHeader
|
||||
message={message}
|
||||
assistant={assistant}
|
||||
model={model}
|
||||
key={getModelUniqId(model)}
|
||||
index={index}
|
||||
/>
|
||||
<MessageContentContainer
|
||||
className={
|
||||
message.role === 'user'
|
||||
? 'message-content-container message-content-container-user'
|
||||
: message.role === 'assistant'
|
||||
? 'message-content-container message-content-container-assistant'
|
||||
: 'message-content-container'
|
||||
}
|
||||
style={{
|
||||
fontFamily: messageFont === 'serif' ? 'var(--font-family-serif)' : 'var(--font-family)',
|
||||
fontSize,
|
||||
background: messageBackground,
|
||||
overflowY: 'visible',
|
||||
maxWidth: narrowMode ? 760 : undefined,
|
||||
alignSelf: isBubbleStyle ? (isAssistantMessage ? 'start' : 'end') : undefined
|
||||
}}>
|
||||
<MessageErrorBoundary>
|
||||
<MessageContent message={message} />
|
||||
</MessageErrorBoundary>
|
||||
{showMenubar && !isBubbleStyle && (
|
||||
<MessageFooter
|
||||
className="MessageFooter"
|
||||
style={{
|
||||
borderTop: messageBorder,
|
||||
flexDirection: !isLastMessage ? 'row-reverse' : undefined
|
||||
}}>
|
||||
<MessageMenubar
|
||||
message={message}
|
||||
assistant={assistant}
|
||||
model={model}
|
||||
index={index}
|
||||
topic={topic}
|
||||
isLastMessage={isLastMessage}
|
||||
isAssistantMessage={isAssistantMessage}
|
||||
isGrouped={isGrouped}
|
||||
messageContainerRef={messageContainerRef as React.RefObject<HTMLDivElement>}
|
||||
setModel={setModel}
|
||||
/>
|
||||
</MessageFooter>
|
||||
)}
|
||||
</MessageContentContainer>
|
||||
{showMenubar && isBubbleStyle && (
|
||||
<MessageFooter
|
||||
className="MessageFooter"
|
||||
style={{
|
||||
border: messageBorder,
|
||||
flexDirection: isLastMessage || isBubbleStyle ? 'row-reverse' : undefined
|
||||
borderTop: messageBorder,
|
||||
flexDirection: !isAssistantMessage ? 'row-reverse' : undefined
|
||||
}}>
|
||||
<MessageTokens message={message} isLastMessage={isLastMessage} />
|
||||
<MessageMenubar
|
||||
message={message}
|
||||
assistant={assistant}
|
||||
@ -198,8 +245,8 @@ const MessageItem: FC<Props> = ({
|
||||
/>
|
||||
</MessageFooter>
|
||||
)}
|
||||
</MessageContentContainer>
|
||||
</ContextMenu>
|
||||
</ContextMenu>
|
||||
)}
|
||||
</MessageContainer>
|
||||
)
|
||||
}
|
||||
@ -214,7 +261,7 @@ const getMessageBackground = (isBubbleStyle: boolean, isAssistantMessage: boolea
|
||||
|
||||
const MessageContainer = styled.div`
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
width: 100%;
|
||||
position: relative;
|
||||
transition: background-color 0.3s ease;
|
||||
padding: 0 20px;
|
||||
@ -257,12 +304,12 @@ const MessageFooter = styled.div`
|
||||
align-items: center;
|
||||
padding: 2px 0;
|
||||
margin-top: 2px;
|
||||
border-top: 1px dotted var(--color-border);
|
||||
gap: 20px;
|
||||
`
|
||||
|
||||
const NewContextMessage = styled.div`
|
||||
cursor: pointer;
|
||||
flex: 1;
|
||||
`
|
||||
|
||||
export default memo(MessageItem)
|
||||
|
||||
@ -14,7 +14,7 @@ const MessageContent: React.FC<Props> = ({ message }) => {
|
||||
return (
|
||||
<>
|
||||
{!isEmpty(message.mentions) && (
|
||||
<Flex gap="8px" wrap style={{ marginBottom: 10 }}>
|
||||
<Flex gap="8px" wrap>
|
||||
{message.mentions?.map((model) => <MentionTag key={getModelUniqId(model)}>{'@' + model.name}</MentionTag>)}
|
||||
</Flex>
|
||||
)}
|
||||
|
||||
@ -315,6 +315,7 @@ interface MessageWrapperProps {
|
||||
|
||||
const MessageWrapper = styled(Scrollbar)<MessageWrapperProps>`
|
||||
width: 100%;
|
||||
display: flex;
|
||||
|
||||
&.horizontal {
|
||||
display: inline-block;
|
||||
|
||||
@ -17,10 +17,13 @@ import { CSSProperties, FC, memo, useCallback, useMemo } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import styled from 'styled-components'
|
||||
|
||||
import MessageTokens from './MessageTokens'
|
||||
|
||||
interface Props {
|
||||
message: Message
|
||||
assistant: Assistant
|
||||
model?: Model
|
||||
index: number | undefined
|
||||
}
|
||||
|
||||
const getAvatarSource = (isLocalAi: boolean, modelId: string | undefined) => {
|
||||
@ -28,7 +31,7 @@ const getAvatarSource = (isLocalAi: boolean, modelId: string | undefined) => {
|
||||
return modelId ? getModelLogo(modelId) : undefined
|
||||
}
|
||||
|
||||
const MessageHeader: FC<Props> = memo(({ assistant, model, message }) => {
|
||||
const MessageHeader: FC<Props> = memo(({ assistant, model, message, index }) => {
|
||||
const avatar = useAvatar()
|
||||
const { theme } = useTheme()
|
||||
const { userName, sidebarIcons } = useSettings()
|
||||
@ -52,9 +55,11 @@ const MessageHeader: FC<Props> = memo(({ assistant, model, message }) => {
|
||||
|
||||
const isAssistantMessage = message.role === 'assistant'
|
||||
const showMinappIcon = sidebarIcons.visible.includes('minapp')
|
||||
const { showTokens } = useSettings()
|
||||
|
||||
const avatarName = useMemo(() => firstLetter(assistant?.name).toUpperCase(), [assistant?.name])
|
||||
const username = useMemo(() => removeLeadingEmoji(getUserName()), [getUserName])
|
||||
const isLastMessage = index === 0
|
||||
|
||||
const showMiniApp = useCallback(() => {
|
||||
showMinappIcon && model?.provider && openMinappById(model.provider)
|
||||
@ -111,7 +116,14 @@ const MessageHeader: FC<Props> = memo(({ assistant, model, message }) => {
|
||||
<UserName isBubbleStyle={isBubbleStyle} theme={theme}>
|
||||
{username}
|
||||
</UserName>
|
||||
<MessageTime>{dayjs(message?.updatedAt ?? message.createdAt).format('MM/DD HH:mm')}</MessageTime>
|
||||
<InfoWrap
|
||||
style={{
|
||||
flexDirection: !isAssistantMessage && isBubbleStyle ? 'row-reverse' : undefined
|
||||
}}>
|
||||
<MessageTime>{dayjs(message?.updatedAt ?? message.createdAt).format('MM/DD HH:mm')}</MessageTime>
|
||||
{showTokens && <DividerContainer style={{ color: 'var(--color-text-3)' }}> | </DividerContainer>}
|
||||
<MessageTokens message={message} isLastMessage={isLastMessage} />
|
||||
</InfoWrap>
|
||||
</UserWrap>
|
||||
</AvatarWrapper>
|
||||
</Container>
|
||||
@ -140,6 +152,19 @@ const UserWrap = styled.div`
|
||||
justify-content: space-between;
|
||||
`
|
||||
|
||||
const InfoWrap = styled.div`
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
gap: 4px;
|
||||
`
|
||||
|
||||
const DividerContainer = styled.div`
|
||||
font-size: 10px;
|
||||
color: var(--color-text-3);
|
||||
margin: 0 2px;
|
||||
`
|
||||
|
||||
const UserName = styled.div<{ isBubbleStyle?: boolean; theme?: string }>`
|
||||
font-size: 14px;
|
||||
font-weight: 600;
|
||||
|
||||
@ -5,6 +5,7 @@ import { TranslateLanguageOptions } from '@renderer/config/translate'
|
||||
import { useMessageEditing } from '@renderer/context/MessageEditingContext'
|
||||
import { useChatContext } from '@renderer/hooks/useChatContext'
|
||||
import { useMessageOperations, useTopicLoading } from '@renderer/hooks/useMessageOperations'
|
||||
import { useMessageStyle } from '@renderer/hooks/useSettings'
|
||||
import { EVENT_NAMES, EventEmitter } from '@renderer/services/EventService'
|
||||
import { getMessageTitle } from '@renderer/services/MessagesService'
|
||||
import { translateText } from '@renderer/services/TranslateService'
|
||||
@ -66,6 +67,9 @@ const MessageMenubar: FC<Props> = (props) => {
|
||||
appendAssistantResponse,
|
||||
removeMessageBlock
|
||||
} = useMessageOperations(topic)
|
||||
|
||||
const { isBubbleStyle } = useMessageStyle()
|
||||
|
||||
const loading = useTopicLoading(topic)
|
||||
|
||||
const isUserMessage = message.role === 'user'
|
||||
@ -332,24 +336,29 @@ const MessageMenubar: FC<Props> = (props) => {
|
||||
return translationBlocks.length > 0
|
||||
}, [message])
|
||||
|
||||
const softHoverBg = isBubbleStyle && !isLastMessage
|
||||
|
||||
return (
|
||||
<MenusBar className={`menubar ${isLastMessage && 'show'}`}>
|
||||
{message.role === 'user' && (
|
||||
<Tooltip title={t('common.regenerate')} mouseEnterDelay={0.8}>
|
||||
<ActionButton className="message-action-button" onClick={() => handleResendUserMessage()}>
|
||||
<ActionButton
|
||||
className="message-action-button"
|
||||
onClick={() => handleResendUserMessage()}
|
||||
$softHoverBg={isBubbleStyle}>
|
||||
<SyncOutlined />
|
||||
</ActionButton>
|
||||
</Tooltip>
|
||||
)}
|
||||
{message.role === 'user' && (
|
||||
<Tooltip title={t('common.edit')} mouseEnterDelay={0.8}>
|
||||
<ActionButton className="message-action-button" onClick={onEdit}>
|
||||
<ActionButton className="message-action-button" onClick={onEdit} $softHoverBg={softHoverBg}>
|
||||
<EditOutlined />
|
||||
</ActionButton>
|
||||
</Tooltip>
|
||||
)}
|
||||
<Tooltip title={t('common.copy')} mouseEnterDelay={0.8}>
|
||||
<ActionButton className="message-action-button" onClick={onCopy}>
|
||||
<ActionButton className="message-action-button" onClick={onCopy} $softHoverBg={softHoverBg}>
|
||||
{!copied && <Copy size={16} />}
|
||||
{copied && <CheckOutlined style={{ color: 'var(--color-primary)' }} />}
|
||||
</ActionButton>
|
||||
@ -366,7 +375,7 @@ const MessageMenubar: FC<Props> = (props) => {
|
||||
mouseEnterDelay={0.8}
|
||||
open={showRegenerateTooltip}
|
||||
onOpenChange={setShowRegenerateTooltip}>
|
||||
<ActionButton className="message-action-button">
|
||||
<ActionButton className="message-action-button" $softHoverBg={softHoverBg}>
|
||||
<RefreshCw size={16} />
|
||||
</ActionButton>
|
||||
</Tooltip>
|
||||
@ -374,7 +383,7 @@ const MessageMenubar: FC<Props> = (props) => {
|
||||
)}
|
||||
{isAssistantMessage && (
|
||||
<Tooltip title={t('message.mention.title')} mouseEnterDelay={0.8}>
|
||||
<ActionButton className="message-action-button" onClick={onMentionModel}>
|
||||
<ActionButton className="message-action-button" onClick={onMentionModel} $softHoverBg={softHoverBg}>
|
||||
<AtSign size={16} />
|
||||
</ActionButton>
|
||||
</Tooltip>
|
||||
@ -444,7 +453,10 @@ const MessageMenubar: FC<Props> = (props) => {
|
||||
placement="top"
|
||||
arrow>
|
||||
<Tooltip title={t('chat.translate')} mouseEnterDelay={1.2}>
|
||||
<ActionButton className="message-action-button" onClick={(e) => e.stopPropagation()}>
|
||||
<ActionButton
|
||||
className="message-action-button"
|
||||
onClick={(e) => e.stopPropagation()}
|
||||
$softHoverBg={softHoverBg}>
|
||||
<Languages size={16} />
|
||||
</ActionButton>
|
||||
</Tooltip>
|
||||
@ -452,7 +464,7 @@ const MessageMenubar: FC<Props> = (props) => {
|
||||
)}
|
||||
{isAssistantMessage && isGrouped && (
|
||||
<Tooltip title={t('chat.message.useful')} mouseEnterDelay={0.8}>
|
||||
<ActionButton className="message-action-button" onClick={onUseful}>
|
||||
<ActionButton className="message-action-button" onClick={onUseful} $softHoverBg={softHoverBg}>
|
||||
{message.useful ? (
|
||||
<ThumbsUp size={17.5} fill="var(--color-primary)" strokeWidth={0} />
|
||||
) : (
|
||||
@ -467,7 +479,7 @@ const MessageMenubar: FC<Props> = (props) => {
|
||||
icon={<QuestionCircleOutlined style={{ color: 'red' }} />}
|
||||
onOpenChange={(open) => open && setShowDeleteTooltip(false)}
|
||||
onConfirm={() => deleteMessage(message.id)}>
|
||||
<ActionButton className="message-action-button" onClick={(e) => e.stopPropagation()}>
|
||||
<ActionButton className="message-action-button" onClick={(e) => e.stopPropagation()} $softHoverBg={softHoverBg}>
|
||||
<Tooltip
|
||||
title={t('common.delete')}
|
||||
mouseEnterDelay={1}
|
||||
@ -483,7 +495,10 @@ const MessageMenubar: FC<Props> = (props) => {
|
||||
trigger={['click']}
|
||||
placement="topRight"
|
||||
arrow>
|
||||
<ActionButton className="message-action-button" onClick={(e) => e.stopPropagation()}>
|
||||
<ActionButton
|
||||
className="message-action-button"
|
||||
onClick={(e) => e.stopPropagation()}
|
||||
$softHoverBg={softHoverBg}>
|
||||
<Menu size={19} />
|
||||
</ActionButton>
|
||||
</Dropdown>
|
||||
@ -500,7 +515,7 @@ const MenusBar = styled.div`
|
||||
gap: 6px;
|
||||
`
|
||||
|
||||
const ActionButton = styled.div`
|
||||
const ActionButton = styled.div<{ $softHoverBg?: boolean }>`
|
||||
cursor: pointer;
|
||||
border-radius: 8px;
|
||||
display: flex;
|
||||
@ -511,8 +526,11 @@ const ActionButton = styled.div`
|
||||
height: 30px;
|
||||
transition: all 0.2s ease;
|
||||
&:hover {
|
||||
background-color: var(--color-background-mute);
|
||||
.anticon {
|
||||
background-color: ${(props) =>
|
||||
props.$softHoverBg ? 'var(--color-background-soft)' : 'var(--color-background-mute)'};
|
||||
color: var(--color-text-1);
|
||||
.anticon,
|
||||
.lucide {
|
||||
color: var(--color-text-1);
|
||||
}
|
||||
}
|
||||
@ -522,9 +540,6 @@ const ActionButton = styled.div`
|
||||
font-size: 14px;
|
||||
color: var(--color-icon);
|
||||
}
|
||||
&:hover {
|
||||
color: var(--color-text-1);
|
||||
}
|
||||
.icon-at {
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
@ -69,19 +69,14 @@ const MessgeTokens: React.FC<MessageTokensProps> = ({ message }) => {
|
||||
}
|
||||
|
||||
const MessageMetadata = styled.div`
|
||||
font-size: 11px;
|
||||
color: var(--color-text-2);
|
||||
font-size: 10px;
|
||||
color: var(--color-text-3);
|
||||
user-select: text;
|
||||
margin: 2px 0;
|
||||
cursor: pointer;
|
||||
text-align: right;
|
||||
|
||||
.tokens {
|
||||
display: block;
|
||||
|
||||
span {
|
||||
padding: 0 2px;
|
||||
}
|
||||
.tokens span {
|
||||
padding: 0 2px;
|
||||
}
|
||||
`
|
||||
|
||||
|
||||
@ -318,7 +318,12 @@ const SettingsTab: FC<Props> = (props) => {
|
||||
</SettingRow>
|
||||
<SettingDivider />
|
||||
<SettingRow>
|
||||
<SettingRowTitleSmall>{t('settings.messages.divider')}</SettingRowTitleSmall>
|
||||
<SettingRowTitleSmall>
|
||||
{t('settings.messages.divider')}
|
||||
<Tooltip title={t('settings.messages.divider.tooltip')}>
|
||||
<CircleHelp size={14} style={{ marginLeft: 4 }} color="var(--color-text-2)" />
|
||||
</Tooltip>
|
||||
</SettingRowTitleSmall>
|
||||
<Switch
|
||||
size="small"
|
||||
checked={showMessageDivider}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user