refactor: Update message handling and improve Inputbar functionality

- Modified Inputbar to include a new resizeTextArea function for better text area management.
- Adjusted MessageGroup and MessageStream components to remove unnecessary props and streamline message handling.
- Enhanced message dispatching logic in the messages store for improved state management.
- Cleaned up unused imports and code in Message components to enhance readability and maintainability.
This commit is contained in:
kangfenmao 2025-03-14 13:40:48 +08:00
parent ca085a807e
commit 93ad07b44e
8 changed files with 37 additions and 80 deletions

View File

@ -1,6 +1,7 @@
import { ElectronAPI } from '@electron-toolkit/preload'
import type { FileMetadataResponse, ListFilesResponse, UploadFileResponse } from '@google/generative-ai/server'
import { ExtractChunkData } from '@llm-tools/embedjs-interfaces'
import type { MCPServer, MCPTool } from '@renderer/types'
import { AppInfo, FileType, KnowledgeBaseParams, KnowledgeItem, LanguageVarious, WebDavConfig } from '@renderer/types'
import type { LoaderReturn } from '@shared/config/types'
import type { OpenDialogOptions } from 'electron'
@ -129,7 +130,7 @@ declare global {
deleteServer: (serverName: string) => Promise<void>
setServerActive: (name: string, isActive: boolean) => Promise<void>
// tools
listTools: () => Promise<MCPTool>
listTools: () => Promise<MCPTool[]>
callTool: ({ client, name, args }: { client: string; name: string; args: any }) => Promise<any>
// status
cleanup: () => Promise<void>

View File

@ -132,6 +132,18 @@ const Inputbar: FC<Props> = ({ assistant: _assistant, setActiveTopic, topic }) =
_text = text
_files = files
const resizeTextArea = useCallback(() => {
const textArea = textareaRef.current?.resizableTextArea?.textArea
if (textArea) {
// 如果已经手动设置了高度,则不自动调整
if (textareaHeight) {
return
}
textArea.style.height = 'auto'
textArea.style.height = textArea?.scrollHeight > 400 ? '400px' : `${textArea?.scrollHeight}px`
}
}, [textareaHeight])
const sendMessage = useCallback(async () => {
if (inputEmpty || loading) {
return
@ -174,7 +186,19 @@ const Inputbar: FC<Props> = ({ assistant: _assistant, setActiveTopic, topic }) =
} catch (error) {
console.error('Failed to send message:', error)
}
}, [inputEmpty, files, dispatch, text, assistant, topic, selectedKnowledgeBases, mentionModels, enabledMCPs, loading])
}, [
assistant,
dispatch,
enabledMCPs,
files,
inputEmpty,
loading,
mentionModels,
resizeTextArea,
selectedKnowledgeBases,
text,
topic
])
const translate = async () => {
if (isTranslating) {
@ -318,18 +342,6 @@ const Inputbar: FC<Props> = ({ assistant: _assistant, setActiveTopic, topic }) =
EventEmitter.emit(EVENT_NAMES.NEW_CONTEXT)
}
const resizeTextArea = () => {
const textArea = textareaRef.current?.resizableTextArea?.textArea
if (textArea) {
// 如果已经手动设置了高度,则不自动调整
if (textareaHeight) {
return
}
textArea.style.height = 'auto'
textArea.style.height = textArea?.scrollHeight > 400 ? '400px' : `${textArea?.scrollHeight}px`
}
}
const onToggleExpended = () => {
const isExpended = !expended
setExpend(isExpended)

View File

@ -4,7 +4,6 @@ import { MultiModelMessageStyle } from '@renderer/store/settings'
import type { Message, Topic } from '@renderer/types'
import { classNames } from '@renderer/utils'
import { Popover } from 'antd'
import type { Dispatch, SetStateAction } from 'react'
import { memo, useCallback, useEffect, useState } from 'react'
import styled, { css } from 'styled-components'
@ -15,12 +14,10 @@ interface Props {
messages: (Message & { index: number })[]
topic: Topic
hidePresetMessages?: boolean
onSetMessages: Dispatch<SetStateAction<Message[]>>
}
const MessageGroup = ({ messages, topic, hidePresetMessages, onSetMessages }: Props) => {
const MessageGroup = ({ messages, topic, hidePresetMessages }: Props) => {
const { multiModelMessageStyle: multiModelMessageStyleSetting, gridColumns, gridPopoverTrigger } = useSettings()
// const { t } = useTranslation()
const [multiModelMessageStyle, setMultiModelMessageStyle] =
useState<MultiModelMessageStyle>(multiModelMessageStyleSetting)
@ -32,10 +29,6 @@ const MessageGroup = ({ messages, topic, hidePresetMessages, onSetMessages }: Pr
const isHorizontal = multiModelMessageStyle === 'horizontal'
const isGrid = multiModelMessageStyle === 'grid'
// const handleDeleteGroup = useCallback(async () => {
// }, [messages, t, deleteGroupMessages])
useEffect(() => {
setSelectedIndex(messageLength - 1)
}, [messageLength])
@ -51,10 +44,7 @@ const MessageGroup = ({ messages, topic, hidePresetMessages, onSetMessages }: Pr
hidePresetMessages,
style: {
paddingTop: isGrouped && ['horizontal', 'grid'].includes(multiModelMessageStyle) ? 0 : 15
},
onSetMessages
// onDeleteMessage,
// onGetMessages
}
}
const messageWrapper = (
@ -99,8 +89,6 @@ const MessageGroup = ({ messages, topic, hidePresetMessages, onSetMessages }: Pr
selectedIndex,
topic,
hidePresetMessages,
onSetMessages,
// onDeleteMessage,
gridPopoverTrigger
]
)

View File

@ -14,7 +14,6 @@ interface MessageStreamProps {
hidePresetMessages?: boolean
isGrouped?: boolean
style?: React.CSSProperties
onSetMessages?: React.Dispatch<React.SetStateAction<Message[]>>
}
const MessageStreamContainer = styled.div`
@ -30,8 +29,7 @@ const MessageStream: React.FC<MessageStreamProps> = ({
index,
hidePresetMessages,
isGrouped,
style,
onSetMessages
style
}) => {
// 获取流式消息
const streamMessage = useAppSelector((state) => selectStreamMessage(state, _message.topicId, _message.id))
@ -63,7 +61,6 @@ const MessageStream: React.FC<MessageStreamProps> = ({
isGrouped={isGrouped}
style={style}
isStreaming={isStreaming}
onSetMessages={onSetMessages}
/>
</MessageStreamContainer>
)

View File

@ -200,7 +200,6 @@ const Messages: React.FC<MessagesProps> = ({ assistant, topic, setActiveTopic })
messages={groupMessages}
topic={topic}
hidePresetMessages={assistant.settings?.hideMessages}
onSetMessages={setDisplayMessages}
/>
))}
</ScrollContainer>

View File

@ -243,6 +243,7 @@ export const sendMessage =
// Initialize topic messages if not exists
const initialState = getState()
if (!initialState.messages.messagesByTopic[topic.id]) {
dispatch(clearTopicMessages(topic.id))
}
@ -278,6 +279,7 @@ export const sendMessage =
assistantMessage.status = 'sending'
assistantMessages.push(assistantMessage)
}
dispatch(
addMessage({
topicId: topic.id,
@ -287,6 +289,7 @@ export const sendMessage =
}
const queue = getTopicQueue(topic.id)
for (const assistantMessage of assistantMessages) {
// Set as stream message instead of adding to messages
dispatch(setStreamMessage({ topicId: topic.id, message: assistantMessage }))
@ -298,6 +301,7 @@ export const sendMessage =
if (currentTopicMessages) {
await syncMessagesWithDB(topic.id, currentTopicMessages)
}
// 保证请求有序,防止请求静态,限制并发数量
await queue.add(async () => {
try {

View File

@ -3,12 +3,10 @@ import { useModel } from '@renderer/hooks/useModel'
import { useSettings } from '@renderer/hooks/useSettings'
import MessageContent from '@renderer/pages/home/Messages/MessageContent'
import MessageErrorBoundary from '@renderer/pages/home/Messages/MessageErrorBoundary'
import { fetchChatCompletion } from '@renderer/services/ApiService'
import { getDefaultAssistant, getDefaultModel } from '@renderer/services/AssistantService'
import { getMessageModelId } from '@renderer/services/MessagesService'
import { Message } from '@renderer/types'
import { isMiniWindow } from '@renderer/utils'
import { Dispatch, FC, memo, SetStateAction, useEffect, useMemo, useRef, useState } from 'react'
import { Dispatch, FC, memo, SetStateAction, useMemo, useRef } from 'react'
import styled from 'styled-components'
interface Props {
@ -23,8 +21,7 @@ interface Props {
const getMessageBackground = (isBubbleStyle: boolean, isAssistantMessage: boolean) =>
isBubbleStyle ? (isAssistantMessage ? 'transparent' : 'var(--chat-background-user)') : undefined
const MessageItem: FC<Props> = ({ message: _message, index, total, route, onSetMessages, onGetMessages }) => {
const [message, setMessage] = useState(_message)
const MessageItem: FC<Props> = ({ message, index, total, route }) => {
const model = useModel(getMessageModelId(message))
const isBubbleStyle = true
const { messageFont, fontSize } = useSettings()
@ -40,32 +37,6 @@ const MessageItem: FC<Props> = ({ message: _message, index, total, route, onSetM
const maxWidth = isMiniWindow() ? '480px' : '100%'
useEffect(() => {
if (onGetMessages && onSetMessages) {
if (message.status === 'sending') {
const messages = onGetMessages()
fetchChatCompletion({
message,
messages: messages
.filter((m) => !m.status.includes('ing'))
.slice(
0,
messages.findIndex((m) => m.id === message.id)
),
assistant: { ...getDefaultAssistant(), model: getDefaultModel() },
onResponse: (msg) => {
setMessage(msg)
if (msg.status !== 'pending') {
const _messages = messages.map((m) => (m.id === msg.id ? msg : m))
onSetMessages(_messages)
}
}
})
}
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [message.status])
if (['summary', 'explanation'].includes(route) && index === total - 1) {
return null
}

View File

@ -23,12 +23,9 @@ const Messages: FC<Props> = ({ assistant, route }) => {
const [messages, setMessages] = useState<Message[]>([])
const containerRef = useRef<HTMLDivElement>(null)
const messagesRef = useRef(messages)
const { t } = useTranslation()
messagesRef.current = messages
const onSendMessage = useCallback(
async (message: Message) => {
setMessages((prev) => {
@ -40,10 +37,6 @@ const Messages: FC<Props> = ({ assistant, route }) => {
[assistant]
)
const onGetMessages = useCallback(() => {
return messagesRef.current
}, [])
useEffect(() => {
const unsubscribes = [EventEmitter.on(EVENT_NAMES.SEND_MESSAGE, onSendMessage)]
return () => unsubscribes.forEach((unsub) => unsub())
@ -60,15 +53,7 @@ const Messages: FC<Props> = ({ assistant, route }) => {
return (
<Container id="messages" key={assistant.id} ref={containerRef}>
{[...messages].reverse().map((message, index) => (
<MessageItem
key={message.id}
message={message}
index={index}
total={messages.length}
onSetMessages={setMessages}
onGetMessages={onGetMessages}
route={route}
/>
<MessageItem key={message.id} message={message} index={index} total={messages.length} route={route} />
))}
</Container>
)