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.
This commit is contained in:
parent
462ac39897
commit
00d91ecf01
@ -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);
|
||||
}
|
||||
|
||||
@ -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<Props> = ({
|
||||
useState<MultiModelMessageStyle>(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<Props> = ({
|
||||
askId && onDeleteGroupMessages?.(askId)
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
setSelectedIndex(messageLength - 1)
|
||||
}, [messageLength])
|
||||
|
||||
const isHorizontal = multiModelMessageStyle === 'horizontal'
|
||||
|
||||
return (
|
||||
<GroupContainer $isGrouped={isGrouped} $layout={multiModelMessageStyle}>
|
||||
<GridContainer $count={messageLength} $layout={multiModelMessageStyle}>
|
||||
@ -54,7 +60,8 @@ const MessageGroup: FC<Props> = ({
|
||||
$layout={multiModelMessageStyle}
|
||||
$selected={index === selectedIndex}
|
||||
$isGrouped={isGrouped}
|
||||
key={message.id}>
|
||||
key={message.id}
|
||||
className={message.role === 'assistant' && isHorizontal && isGrouped ? 'group-message-wrapper' : ''}>
|
||||
<MessageItem
|
||||
message={message}
|
||||
topic={topic}
|
||||
@ -69,8 +76,8 @@ const MessageGroup: FC<Props> = ({
|
||||
))}
|
||||
</GridContainer>
|
||||
{isGrouped && (
|
||||
<GroupHeader>
|
||||
<HStack style={{ alignItems: 'center' }}>
|
||||
<GroupMenuBar className="group-menu-bar">
|
||||
<HStack style={{ alignItems: 'center', flex: 1, overflow: 'hidden' }}>
|
||||
<LayoutContainer>
|
||||
{['fold', 'horizontal', 'vertical'].map((layout) => (
|
||||
<LayoutOption
|
||||
@ -115,7 +122,7 @@ const MessageGroup: FC<Props> = ({
|
||||
icon={<DeleteOutlined style={{ color: 'var(--color-error)' }} />}
|
||||
onClick={onDelete}
|
||||
/>
|
||||
</GroupHeader>
|
||||
</GroupMenuBar>
|
||||
)}
|
||||
</GroupContainer>
|
||||
)
|
||||
@ -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)<MessageWrapperProps>`
|
||||
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`
|
||||
|
||||
@ -173,16 +173,23 @@ const MessageMenubar: FC<Props> = (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(() => {
|
||||
|
||||
@ -23,6 +23,7 @@ const MessageThought: FC<Props> = ({ message }) => {
|
||||
|
||||
return (
|
||||
<CollapseContainer
|
||||
className="message-thought-container"
|
||||
items={[
|
||||
{
|
||||
key: 'thought',
|
||||
|
||||
@ -68,6 +68,7 @@ const Messages: FC<Props> = ({ 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<Props> = ({ 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<Props> = ({ 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<Props> = ({ assistant, topic, setActiveTopic }) => {
|
||||
assistant,
|
||||
autoRenameTopic,
|
||||
messages,
|
||||
onAppendMessage,
|
||||
onDeleteMessage,
|
||||
onSendMessage,
|
||||
scrollToBottom,
|
||||
|
||||
@ -264,9 +264,9 @@ const SettingsTab: FC<Props> = (props) => {
|
||||
value={multiModelMessageStyle}
|
||||
onChange={(value) => dispatch(setMultiModelMessageStyle(value))}
|
||||
style={{ width: 135 }}>
|
||||
<Select.Option value="fold">{t('message.message.multi_model_style.fold')}</Select.Option>
|
||||
<Select.Option value="horizontal">{t('message.message.multi_model_style.horizontal')}</Select.Option>
|
||||
<Select.Option value="vertical">{t('message.message.multi_model_style.vertical')}</Select.Option>
|
||||
<Select.Option value="fold">{t('message.message.multi_model_style.fold')}</Select.Option>
|
||||
</Select>
|
||||
</SettingRow>
|
||||
<SettingDivider />
|
||||
|
||||
@ -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',
|
||||
|
||||
@ -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',
|
||||
|
||||
@ -879,7 +879,7 @@ const migrateConfig = {
|
||||
return state
|
||||
},
|
||||
'60': (state: RootState) => {
|
||||
state.settings.multiModelMessageStyle = 'vertical'
|
||||
state.settings.multiModelMessageStyle = 'fold'
|
||||
return state
|
||||
}
|
||||
}
|
||||
|
||||
@ -113,7 +113,7 @@ const initialState: SettingsState = {
|
||||
narrowMode: false,
|
||||
enableQuickAssistant: false,
|
||||
clickTrayToShowQuickAssistant: false,
|
||||
multiModelMessageStyle: 'horizontal'
|
||||
multiModelMessageStyle: 'fold'
|
||||
}
|
||||
|
||||
const settingsSlice = createSlice({
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user