From 00d91ecf01fe6b268d7d9b6be1fcc4abab4452d6 Mon Sep 17 00:00:00 2001 From: kangfenmao Date: Wed, 22 Jan 2025 12:03:08 +0800 Subject: [PATCH] feat: enhance message grouping and styling - Added new styles for message thought containers and group message wrappers to improve UI layout. - Updated MessageGroup component to dynamically set the selected message index based on message length. - Introduced a new event for appending messages, enhancing message handling capabilities. - Refactored MessageMenubar to support the new append message functionality. - Adjusted multi-model message style setting to 'fold' for better user experience. - Improved responsiveness of message grid layout for smaller screens. --- src/renderer/src/assets/styles/index.scss | 13 +++++ .../src/pages/home/Messages/MessageGroup.tsx | 51 ++++++++++++------- .../pages/home/Messages/MessageMenubar.tsx | 15 ++++-- .../pages/home/Messages/MessageThought.tsx | 1 + .../src/pages/home/Messages/Messages.tsx | 14 +++++ .../src/pages/home/Tabs/SettingsTab.tsx | 2 +- src/renderer/src/services/EventService.ts | 1 + src/renderer/src/services/MessagesService.ts | 1 + src/renderer/src/store/migrate.ts | 2 +- src/renderer/src/store/settings.ts | 2 +- 10 files changed, 77 insertions(+), 25 deletions(-) diff --git a/src/renderer/src/assets/styles/index.scss b/src/renderer/src/assets/styles/index.scss index 3e27beed..02b631db 100644 --- a/src/renderer/src/assets/styles/index.scss +++ b/src/renderer/src/assets/styles/index.scss @@ -234,6 +234,9 @@ body, border-radius: 8px; padding: 10px 15px 0 15px; } + .message-thought-container { + margin-top: 8px; + } .message-user { color: var(--chat-text-user); .markdown, @@ -246,6 +249,16 @@ body, background-color: var(--color-white-soft); } } + .group-message-wrapper { + background-color: var(--color-background); + .message-content-container { + width: 100%; + border: 1px solid var(--color-background-mute); + } + } + .group-menu-bar { + background-color: var(--color-background); + } code { color: var(--color-text); } diff --git a/src/renderer/src/pages/home/Messages/MessageGroup.tsx b/src/renderer/src/pages/home/Messages/MessageGroup.tsx index 3d0cc4f3..196c0e11 100644 --- a/src/renderer/src/pages/home/Messages/MessageGroup.tsx +++ b/src/renderer/src/pages/home/Messages/MessageGroup.tsx @@ -7,7 +7,7 @@ import { EVENT_NAMES, EventEmitter } from '@renderer/services/EventService' import { MultiModelMessageStyle } from '@renderer/store/settings' import { Message, Model, Topic } from '@renderer/types' import { Button, Segmented } from 'antd' -import { Dispatch, FC, SetStateAction, useState } from 'react' +import { Dispatch, FC, SetStateAction, useEffect, useState } from 'react' import styled, { css } from 'styled-components' import MessageItem from './Message' @@ -37,7 +37,7 @@ const MessageGroup: FC = ({ useState(multiModelMessageStyleSetting) const messageLength = messages.length - const [selectedIndex, setSelectedIndex] = useState(0) + const [selectedIndex, setSelectedIndex] = useState(messageLength - 1) const isGrouped = messageLength > 1 @@ -46,6 +46,12 @@ const MessageGroup: FC = ({ askId && onDeleteGroupMessages?.(askId) } + useEffect(() => { + setSelectedIndex(messageLength - 1) + }, [messageLength]) + + const isHorizontal = multiModelMessageStyle === 'horizontal' + return ( @@ -54,7 +60,8 @@ const MessageGroup: FC = ({ $layout={multiModelMessageStyle} $selected={index === selectedIndex} $isGrouped={isGrouped} - key={message.id}> + key={message.id} + className={message.role === 'assistant' && isHorizontal && isGrouped ? 'group-message-wrapper' : ''}> = ({ ))} {isGrouped && ( - - + + {['fold', 'horizontal', 'vertical'].map((layout) => ( = ({ icon={} onClick={onDelete} /> - + )} ) @@ -133,6 +140,12 @@ const GridContainer = styled(Scrollbar)<{ $count: number; $layout: MultiModelMes minmax(550px, 1fr) ); gap: ${({ $layout }) => ($layout === 'horizontal' ? '16px' : '0')}; + @media (max-width: 800px) { + grid-template-columns: repeat( + ${(props) => (['fold', 'vertical'].includes(props.$layout) ? 1 : props.$count)}, + minmax(400px, 1fr) + ); + } ` interface MessageWrapperProps { @@ -160,31 +173,24 @@ const MessageWrapper = styled(Scrollbar)` border-radius: 6px; max-height: 600px; overflow-y: auto; + margin-bottom: 10px; ` } return '' }} ` -const GroupHeader = styled.div` +const GroupMenuBar = styled.div` display: flex; flex-direction: row; align-items: center; gap: 10px; background-color: var(--color-background-soft); - padding: 8px 10px; + padding: 6px 10px; border-radius: 6px; margin-top: 10px; justify-content: space-between; -` - -const ModelsContainer = styled(Scrollbar)` - display: flex; - flex-direction: column; - justify-content: space-between; - &::-webkit-scrollbar { - display: none; - } + overflow: hidden; ` const LayoutContainer = styled.div` @@ -205,11 +211,20 @@ const LayoutOption = styled.div<{ active: boolean }>` } ` +const ModelsContainer = styled(Scrollbar)` + display: flex; + flex-direction: column; + justify-content: space-between; + &::-webkit-scrollbar { + display: none; + } +` + const SegmentedLabel = styled.div` display: flex; align-items: center; gap: 5px; - padding: 5px 0; + padding: 3px 0; ` const ModelName = styled.span` diff --git a/src/renderer/src/pages/home/Messages/MessageMenubar.tsx b/src/renderer/src/pages/home/Messages/MessageMenubar.tsx index 0a4b8445..0fcec8f7 100644 --- a/src/renderer/src/pages/home/Messages/MessageMenubar.tsx +++ b/src/renderer/src/pages/home/Messages/MessageMenubar.tsx @@ -173,16 +173,23 @@ const MessageMenubar: FC = (props) => { const selectedModel = await SelectModelPopup.show({ model }) if (!selectedModel) return - onEditMessage?.({ + const _message: Message = { ...message, content: '', reasoning_content: undefined, metrics: undefined, status: 'sending', - modelId: selectedModel.id || assistantModel?.id || model?.id, + modelId: selectedModel.id, model: selectedModel, - translatedContent: undefined - }) + translatedContent: undefined, + metadata: undefined + } + + if (message.askId && message.model) { + return EventEmitter.emit(EVENT_NAMES.APPEND_MESSAGE, { ..._message, id: uuid() }) + } + + onEditMessage?.(_message) } const onUseful = useCallback(() => { diff --git a/src/renderer/src/pages/home/Messages/MessageThought.tsx b/src/renderer/src/pages/home/Messages/MessageThought.tsx index 524ea2c8..e70cc869 100644 --- a/src/renderer/src/pages/home/Messages/MessageThought.tsx +++ b/src/renderer/src/pages/home/Messages/MessageThought.tsx @@ -23,6 +23,7 @@ const MessageThought: FC = ({ message }) => { return ( = ({ assistant, topic, setActiveTopic }) => { const onSendMessage = useCallback( async (message: Message) => { const assistantMessages: Message[] = [] + if (message.mentions?.length) { message.mentions.forEach((m) => { const assistantMessage = getAssistantMessage({ assistant: { ...assistant, model: m }, topic }) @@ -92,6 +93,17 @@ const Messages: FC = ({ assistant, topic, setActiveTopic }) => { [assistant, scrollToBottom, topic] ) + const onAppendMessage = useCallback( + (message: Message) => { + setMessages((prev) => { + const messages = prev.concat([message]) + db.topics.put({ id: topic.id, messages }) + return messages + }) + }, + [topic.id] + ) + const autoRenameTopic = useCallback(async () => { const _topic = getTopic(assistant, topic.id) @@ -146,6 +158,7 @@ const Messages: FC = ({ assistant, topic, setActiveTopic }) => { useEffect(() => { const unsubscribes = [ EventEmitter.on(EVENT_NAMES.SEND_MESSAGE, onSendMessage), + EventEmitter.on(EVENT_NAMES.APPEND_MESSAGE, onAppendMessage), EventEmitter.on(EVENT_NAMES.RECEIVE_MESSAGE, async () => { setTimeout(() => EventEmitter.emit(EVENT_NAMES.AI_AUTO_RENAME), 100) }), @@ -215,6 +228,7 @@ const Messages: FC = ({ assistant, topic, setActiveTopic }) => { assistant, autoRenameTopic, messages, + onAppendMessage, onDeleteMessage, onSendMessage, scrollToBottom, diff --git a/src/renderer/src/pages/home/Tabs/SettingsTab.tsx b/src/renderer/src/pages/home/Tabs/SettingsTab.tsx index 2db56400..658d6056 100644 --- a/src/renderer/src/pages/home/Tabs/SettingsTab.tsx +++ b/src/renderer/src/pages/home/Tabs/SettingsTab.tsx @@ -264,9 +264,9 @@ const SettingsTab: FC = (props) => { value={multiModelMessageStyle} onChange={(value) => dispatch(setMultiModelMessageStyle(value))} style={{ width: 135 }}> + {t('message.message.multi_model_style.fold')} {t('message.message.multi_model_style.horizontal')} {t('message.message.multi_model_style.vertical')} - {t('message.message.multi_model_style.fold')} diff --git a/src/renderer/src/services/EventService.ts b/src/renderer/src/services/EventService.ts index ee0f3318..f31c946a 100644 --- a/src/renderer/src/services/EventService.ts +++ b/src/renderer/src/services/EventService.ts @@ -4,6 +4,7 @@ export const EventEmitter = new Emittery() export const EVENT_NAMES = { SEND_MESSAGE: 'SEND_MESSAGE', + APPEND_MESSAGE: 'APPEND_MESSAGE', RECEIVE_MESSAGE: 'RECEIVE_MESSAGE', AI_AUTO_RENAME: 'AI_AUTO_RENAME', CLEAR_MESSAGES: 'CLEAR_MESSAGES', diff --git a/src/renderer/src/services/MessagesService.ts b/src/renderer/src/services/MessagesService.ts index 2f8d8e51..afaf0bfe 100644 --- a/src/renderer/src/services/MessagesService.ts +++ b/src/renderer/src/services/MessagesService.ts @@ -103,6 +103,7 @@ export function getAssistantMessage({ assistant, topic }: { assistant: Assistant content: '', assistantId: assistant.id, topicId: topic.id, + model, modelId: model.id, createdAt: new Date().toISOString(), type: 'text', diff --git a/src/renderer/src/store/migrate.ts b/src/renderer/src/store/migrate.ts index 33a9e6b5..061947a4 100644 --- a/src/renderer/src/store/migrate.ts +++ b/src/renderer/src/store/migrate.ts @@ -879,7 +879,7 @@ const migrateConfig = { return state }, '60': (state: RootState) => { - state.settings.multiModelMessageStyle = 'vertical' + state.settings.multiModelMessageStyle = 'fold' return state } } diff --git a/src/renderer/src/store/settings.ts b/src/renderer/src/store/settings.ts index 174b3b29..979c897f 100644 --- a/src/renderer/src/store/settings.ts +++ b/src/renderer/src/store/settings.ts @@ -113,7 +113,7 @@ const initialState: SettingsState = { narrowMode: false, enableQuickAssistant: false, clickTrayToShowQuickAssistant: false, - multiModelMessageStyle: 'horizontal' + multiModelMessageStyle: 'fold' } const settingsSlice = createSlice({