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:
parent
ca085a807e
commit
93ad07b44e
3
src/preload/index.d.ts
vendored
3
src/preload/index.d.ts
vendored
@ -1,6 +1,7 @@
|
|||||||
import { ElectronAPI } from '@electron-toolkit/preload'
|
import { ElectronAPI } from '@electron-toolkit/preload'
|
||||||
import type { FileMetadataResponse, ListFilesResponse, UploadFileResponse } from '@google/generative-ai/server'
|
import type { FileMetadataResponse, ListFilesResponse, UploadFileResponse } from '@google/generative-ai/server'
|
||||||
import { ExtractChunkData } from '@llm-tools/embedjs-interfaces'
|
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 { AppInfo, FileType, KnowledgeBaseParams, KnowledgeItem, LanguageVarious, WebDavConfig } from '@renderer/types'
|
||||||
import type { LoaderReturn } from '@shared/config/types'
|
import type { LoaderReturn } from '@shared/config/types'
|
||||||
import type { OpenDialogOptions } from 'electron'
|
import type { OpenDialogOptions } from 'electron'
|
||||||
@ -129,7 +130,7 @@ declare global {
|
|||||||
deleteServer: (serverName: string) => Promise<void>
|
deleteServer: (serverName: string) => Promise<void>
|
||||||
setServerActive: (name: string, isActive: boolean) => Promise<void>
|
setServerActive: (name: string, isActive: boolean) => Promise<void>
|
||||||
// tools
|
// tools
|
||||||
listTools: () => Promise<MCPTool>
|
listTools: () => Promise<MCPTool[]>
|
||||||
callTool: ({ client, name, args }: { client: string; name: string; args: any }) => Promise<any>
|
callTool: ({ client, name, args }: { client: string; name: string; args: any }) => Promise<any>
|
||||||
// status
|
// status
|
||||||
cleanup: () => Promise<void>
|
cleanup: () => Promise<void>
|
||||||
|
|||||||
@ -132,6 +132,18 @@ const Inputbar: FC<Props> = ({ assistant: _assistant, setActiveTopic, topic }) =
|
|||||||
_text = text
|
_text = text
|
||||||
_files = files
|
_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 () => {
|
const sendMessage = useCallback(async () => {
|
||||||
if (inputEmpty || loading) {
|
if (inputEmpty || loading) {
|
||||||
return
|
return
|
||||||
@ -174,7 +186,19 @@ const Inputbar: FC<Props> = ({ assistant: _assistant, setActiveTopic, topic }) =
|
|||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Failed to send message:', 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 () => {
|
const translate = async () => {
|
||||||
if (isTranslating) {
|
if (isTranslating) {
|
||||||
@ -318,18 +342,6 @@ const Inputbar: FC<Props> = ({ assistant: _assistant, setActiveTopic, topic }) =
|
|||||||
EventEmitter.emit(EVENT_NAMES.NEW_CONTEXT)
|
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 onToggleExpended = () => {
|
||||||
const isExpended = !expended
|
const isExpended = !expended
|
||||||
setExpend(isExpended)
|
setExpend(isExpended)
|
||||||
|
|||||||
@ -4,7 +4,6 @@ import { MultiModelMessageStyle } from '@renderer/store/settings'
|
|||||||
import type { Message, Topic } from '@renderer/types'
|
import type { Message, Topic } from '@renderer/types'
|
||||||
import { classNames } from '@renderer/utils'
|
import { classNames } from '@renderer/utils'
|
||||||
import { Popover } from 'antd'
|
import { Popover } from 'antd'
|
||||||
import type { Dispatch, SetStateAction } from 'react'
|
|
||||||
import { memo, useCallback, useEffect, useState } from 'react'
|
import { memo, useCallback, useEffect, useState } from 'react'
|
||||||
import styled, { css } from 'styled-components'
|
import styled, { css } from 'styled-components'
|
||||||
|
|
||||||
@ -15,12 +14,10 @@ interface Props {
|
|||||||
messages: (Message & { index: number })[]
|
messages: (Message & { index: number })[]
|
||||||
topic: Topic
|
topic: Topic
|
||||||
hidePresetMessages?: boolean
|
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 { multiModelMessageStyle: multiModelMessageStyleSetting, gridColumns, gridPopoverTrigger } = useSettings()
|
||||||
// const { t } = useTranslation()
|
|
||||||
|
|
||||||
const [multiModelMessageStyle, setMultiModelMessageStyle] =
|
const [multiModelMessageStyle, setMultiModelMessageStyle] =
|
||||||
useState<MultiModelMessageStyle>(multiModelMessageStyleSetting)
|
useState<MultiModelMessageStyle>(multiModelMessageStyleSetting)
|
||||||
@ -32,10 +29,6 @@ const MessageGroup = ({ messages, topic, hidePresetMessages, onSetMessages }: Pr
|
|||||||
const isHorizontal = multiModelMessageStyle === 'horizontal'
|
const isHorizontal = multiModelMessageStyle === 'horizontal'
|
||||||
const isGrid = multiModelMessageStyle === 'grid'
|
const isGrid = multiModelMessageStyle === 'grid'
|
||||||
|
|
||||||
// const handleDeleteGroup = useCallback(async () => {
|
|
||||||
|
|
||||||
// }, [messages, t, deleteGroupMessages])
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
setSelectedIndex(messageLength - 1)
|
setSelectedIndex(messageLength - 1)
|
||||||
}, [messageLength])
|
}, [messageLength])
|
||||||
@ -51,10 +44,7 @@ const MessageGroup = ({ messages, topic, hidePresetMessages, onSetMessages }: Pr
|
|||||||
hidePresetMessages,
|
hidePresetMessages,
|
||||||
style: {
|
style: {
|
||||||
paddingTop: isGrouped && ['horizontal', 'grid'].includes(multiModelMessageStyle) ? 0 : 15
|
paddingTop: isGrouped && ['horizontal', 'grid'].includes(multiModelMessageStyle) ? 0 : 15
|
||||||
},
|
}
|
||||||
onSetMessages
|
|
||||||
// onDeleteMessage,
|
|
||||||
// onGetMessages
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const messageWrapper = (
|
const messageWrapper = (
|
||||||
@ -99,8 +89,6 @@ const MessageGroup = ({ messages, topic, hidePresetMessages, onSetMessages }: Pr
|
|||||||
selectedIndex,
|
selectedIndex,
|
||||||
topic,
|
topic,
|
||||||
hidePresetMessages,
|
hidePresetMessages,
|
||||||
onSetMessages,
|
|
||||||
// onDeleteMessage,
|
|
||||||
gridPopoverTrigger
|
gridPopoverTrigger
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
|
|||||||
@ -14,7 +14,6 @@ interface MessageStreamProps {
|
|||||||
hidePresetMessages?: boolean
|
hidePresetMessages?: boolean
|
||||||
isGrouped?: boolean
|
isGrouped?: boolean
|
||||||
style?: React.CSSProperties
|
style?: React.CSSProperties
|
||||||
onSetMessages?: React.Dispatch<React.SetStateAction<Message[]>>
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const MessageStreamContainer = styled.div`
|
const MessageStreamContainer = styled.div`
|
||||||
@ -30,8 +29,7 @@ const MessageStream: React.FC<MessageStreamProps> = ({
|
|||||||
index,
|
index,
|
||||||
hidePresetMessages,
|
hidePresetMessages,
|
||||||
isGrouped,
|
isGrouped,
|
||||||
style,
|
style
|
||||||
onSetMessages
|
|
||||||
}) => {
|
}) => {
|
||||||
// 获取流式消息
|
// 获取流式消息
|
||||||
const streamMessage = useAppSelector((state) => selectStreamMessage(state, _message.topicId, _message.id))
|
const streamMessage = useAppSelector((state) => selectStreamMessage(state, _message.topicId, _message.id))
|
||||||
@ -63,7 +61,6 @@ const MessageStream: React.FC<MessageStreamProps> = ({
|
|||||||
isGrouped={isGrouped}
|
isGrouped={isGrouped}
|
||||||
style={style}
|
style={style}
|
||||||
isStreaming={isStreaming}
|
isStreaming={isStreaming}
|
||||||
onSetMessages={onSetMessages}
|
|
||||||
/>
|
/>
|
||||||
</MessageStreamContainer>
|
</MessageStreamContainer>
|
||||||
)
|
)
|
||||||
|
|||||||
@ -200,7 +200,6 @@ const Messages: React.FC<MessagesProps> = ({ assistant, topic, setActiveTopic })
|
|||||||
messages={groupMessages}
|
messages={groupMessages}
|
||||||
topic={topic}
|
topic={topic}
|
||||||
hidePresetMessages={assistant.settings?.hideMessages}
|
hidePresetMessages={assistant.settings?.hideMessages}
|
||||||
onSetMessages={setDisplayMessages}
|
|
||||||
/>
|
/>
|
||||||
))}
|
))}
|
||||||
</ScrollContainer>
|
</ScrollContainer>
|
||||||
|
|||||||
@ -243,6 +243,7 @@ export const sendMessage =
|
|||||||
|
|
||||||
// Initialize topic messages if not exists
|
// Initialize topic messages if not exists
|
||||||
const initialState = getState()
|
const initialState = getState()
|
||||||
|
|
||||||
if (!initialState.messages.messagesByTopic[topic.id]) {
|
if (!initialState.messages.messagesByTopic[topic.id]) {
|
||||||
dispatch(clearTopicMessages(topic.id))
|
dispatch(clearTopicMessages(topic.id))
|
||||||
}
|
}
|
||||||
@ -278,6 +279,7 @@ export const sendMessage =
|
|||||||
assistantMessage.status = 'sending'
|
assistantMessage.status = 'sending'
|
||||||
assistantMessages.push(assistantMessage)
|
assistantMessages.push(assistantMessage)
|
||||||
}
|
}
|
||||||
|
|
||||||
dispatch(
|
dispatch(
|
||||||
addMessage({
|
addMessage({
|
||||||
topicId: topic.id,
|
topicId: topic.id,
|
||||||
@ -287,6 +289,7 @@ export const sendMessage =
|
|||||||
}
|
}
|
||||||
|
|
||||||
const queue = getTopicQueue(topic.id)
|
const queue = getTopicQueue(topic.id)
|
||||||
|
|
||||||
for (const assistantMessage of assistantMessages) {
|
for (const assistantMessage of assistantMessages) {
|
||||||
// Set as stream message instead of adding to messages
|
// Set as stream message instead of adding to messages
|
||||||
dispatch(setStreamMessage({ topicId: topic.id, message: assistantMessage }))
|
dispatch(setStreamMessage({ topicId: topic.id, message: assistantMessage }))
|
||||||
@ -298,6 +301,7 @@ export const sendMessage =
|
|||||||
if (currentTopicMessages) {
|
if (currentTopicMessages) {
|
||||||
await syncMessagesWithDB(topic.id, currentTopicMessages)
|
await syncMessagesWithDB(topic.id, currentTopicMessages)
|
||||||
}
|
}
|
||||||
|
|
||||||
// 保证请求有序,防止请求静态,限制并发数量
|
// 保证请求有序,防止请求静态,限制并发数量
|
||||||
await queue.add(async () => {
|
await queue.add(async () => {
|
||||||
try {
|
try {
|
||||||
|
|||||||
@ -3,12 +3,10 @@ import { useModel } from '@renderer/hooks/useModel'
|
|||||||
import { useSettings } from '@renderer/hooks/useSettings'
|
import { useSettings } from '@renderer/hooks/useSettings'
|
||||||
import MessageContent from '@renderer/pages/home/Messages/MessageContent'
|
import MessageContent from '@renderer/pages/home/Messages/MessageContent'
|
||||||
import MessageErrorBoundary from '@renderer/pages/home/Messages/MessageErrorBoundary'
|
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 { getMessageModelId } from '@renderer/services/MessagesService'
|
||||||
import { Message } from '@renderer/types'
|
import { Message } from '@renderer/types'
|
||||||
import { isMiniWindow } from '@renderer/utils'
|
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'
|
import styled from 'styled-components'
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
@ -23,8 +21,7 @@ interface Props {
|
|||||||
const getMessageBackground = (isBubbleStyle: boolean, isAssistantMessage: boolean) =>
|
const getMessageBackground = (isBubbleStyle: boolean, isAssistantMessage: boolean) =>
|
||||||
isBubbleStyle ? (isAssistantMessage ? 'transparent' : 'var(--chat-background-user)') : undefined
|
isBubbleStyle ? (isAssistantMessage ? 'transparent' : 'var(--chat-background-user)') : undefined
|
||||||
|
|
||||||
const MessageItem: FC<Props> = ({ message: _message, index, total, route, onSetMessages, onGetMessages }) => {
|
const MessageItem: FC<Props> = ({ message, index, total, route }) => {
|
||||||
const [message, setMessage] = useState(_message)
|
|
||||||
const model = useModel(getMessageModelId(message))
|
const model = useModel(getMessageModelId(message))
|
||||||
const isBubbleStyle = true
|
const isBubbleStyle = true
|
||||||
const { messageFont, fontSize } = useSettings()
|
const { messageFont, fontSize } = useSettings()
|
||||||
@ -40,32 +37,6 @@ const MessageItem: FC<Props> = ({ message: _message, index, total, route, onSetM
|
|||||||
|
|
||||||
const maxWidth = isMiniWindow() ? '480px' : '100%'
|
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) {
|
if (['summary', 'explanation'].includes(route) && index === total - 1) {
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
|||||||
@ -23,12 +23,9 @@ const Messages: FC<Props> = ({ assistant, route }) => {
|
|||||||
const [messages, setMessages] = useState<Message[]>([])
|
const [messages, setMessages] = useState<Message[]>([])
|
||||||
|
|
||||||
const containerRef = useRef<HTMLDivElement>(null)
|
const containerRef = useRef<HTMLDivElement>(null)
|
||||||
const messagesRef = useRef(messages)
|
|
||||||
|
|
||||||
const { t } = useTranslation()
|
const { t } = useTranslation()
|
||||||
|
|
||||||
messagesRef.current = messages
|
|
||||||
|
|
||||||
const onSendMessage = useCallback(
|
const onSendMessage = useCallback(
|
||||||
async (message: Message) => {
|
async (message: Message) => {
|
||||||
setMessages((prev) => {
|
setMessages((prev) => {
|
||||||
@ -40,10 +37,6 @@ const Messages: FC<Props> = ({ assistant, route }) => {
|
|||||||
[assistant]
|
[assistant]
|
||||||
)
|
)
|
||||||
|
|
||||||
const onGetMessages = useCallback(() => {
|
|
||||||
return messagesRef.current
|
|
||||||
}, [])
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const unsubscribes = [EventEmitter.on(EVENT_NAMES.SEND_MESSAGE, onSendMessage)]
|
const unsubscribes = [EventEmitter.on(EVENT_NAMES.SEND_MESSAGE, onSendMessage)]
|
||||||
return () => unsubscribes.forEach((unsub) => unsub())
|
return () => unsubscribes.forEach((unsub) => unsub())
|
||||||
@ -60,15 +53,7 @@ const Messages: FC<Props> = ({ assistant, route }) => {
|
|||||||
return (
|
return (
|
||||||
<Container id="messages" key={assistant.id} ref={containerRef}>
|
<Container id="messages" key={assistant.id} ref={containerRef}>
|
||||||
{[...messages].reverse().map((message, index) => (
|
{[...messages].reverse().map((message, index) => (
|
||||||
<MessageItem
|
<MessageItem key={message.id} message={message} index={index} total={messages.length} route={route} />
|
||||||
key={message.id}
|
|
||||||
message={message}
|
|
||||||
index={index}
|
|
||||||
total={messages.length}
|
|
||||||
onSetMessages={setMessages}
|
|
||||||
onGetMessages={onGetMessages}
|
|
||||||
route={route}
|
|
||||||
/>
|
|
||||||
))}
|
))}
|
||||||
</Container>
|
</Container>
|
||||||
)
|
)
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user