feat: 增加保持并发送的功能 #527

This commit is contained in:
kangfenmao 2024-12-30 14:09:59 +08:00
parent 96124cf58e
commit 47c455b125
9 changed files with 71 additions and 13 deletions

View File

@ -4,6 +4,7 @@ import { TextAreaProps } from 'antd/lib/input'
import { TextAreaRef } from 'antd/lib/input/TextArea'
import { useEffect, useRef, useState } from 'react'
import { useTranslation } from 'react-i18next'
import styled from 'styled-components'
import { TopView } from '../TopView'
@ -11,13 +12,14 @@ interface ShowParams {
text: string
textareaProps?: TextAreaProps
modalProps?: ModalProps
children?: (props: { onOk?: () => void; onCancel?: () => void }) => React.ReactNode
}
interface Props extends ShowParams {
resolve: (data: any) => void
}
const PopupContainer: React.FC<Props> = ({ text, textareaProps, modalProps, resolve }) => {
const PopupContainer: React.FC<Props> = ({ text, textareaProps, modalProps, resolve, children }) => {
const [open, setOpen] = useState(true)
const { t } = useTranslation()
const [textValue, setTextValue] = useState(text)
@ -73,12 +75,17 @@ const PopupContainer: React.FC<Props> = ({ text, textareaProps, modalProps, reso
onInput={resizeTextArea}
onChange={(e) => setTextValue(e.target.value)}
/>
<ChildrenContainer>{children && children({ onOk, onCancel })}</ChildrenContainer>
</Modal>
)
}
const TopViewKey = 'TextEditPopup'
const ChildrenContainer = styled.div`
position: relative;
`
export default class TextEditPopup {
static topviewId = 0
static hide() {

View File

@ -112,7 +112,8 @@
"topics.list": "Topic List",
"topics.move_to": "Move to",
"topics.title": "Topics",
"translate": "Translate"
"translate": "Translate",
"resend": "Resend"
},
"common": {
"and": "and",

View File

@ -112,7 +112,8 @@
"topics.list": "トピックリスト",
"topics.move_to": "移動先",
"topics.title": "トピック",
"translate": "翻訳"
"translate": "翻訳",
"resend": "再送信"
},
"common": {
"and": "と",

View File

@ -112,7 +112,8 @@
"topics.list": "Список топиков",
"topics.move_to": "Переместить в",
"topics.title": "Топики",
"translate": "Перевести"
"translate": "Перевести",
"resend": "Переотправить"
},
"common": {
"and": "и",

View File

@ -112,7 +112,8 @@
"topics.list": "话题列表",
"topics.move_to": "移动到",
"topics.title": "话题",
"translate": "翻译"
"translate": "翻译",
"resend": "重新发送"
},
"common": {
"and": "和",

View File

@ -112,7 +112,8 @@
"topics.list": "話題列表",
"topics.move_to": "移動到",
"topics.title": "話題",
"translate": "翻譯"
"translate": "翻譯",
"resend": "重新發送"
},
"common": {
"and": "與",

View File

@ -86,9 +86,12 @@ const MessageItem: FC<Props> = ({
}
useEffect(() => {
const unsubscribes = [EventEmitter.on(EVENT_NAMES.LOCATE_MESSAGE + ':' + message.id, messageHighlightHandler)]
const unsubscribes = [
EventEmitter.on(EVENT_NAMES.LOCATE_MESSAGE + ':' + message.id, messageHighlightHandler),
EventEmitter.on(EVENT_NAMES.RESEND_MESSAGE + ':' + message.id, onEditMessage)
]
return () => unsubscribes.forEach((unsub) => unsub())
}, [message])
}, [message, onEditMessage])
useEffect(() => {
if (message.role === 'user' && !message.usage) {
@ -178,6 +181,7 @@ const MessageItem: FC<Props> = ({
setModel={setModel}
onEditMessage={onEditMessage}
onDeleteMessage={onDeleteMessage}
onGetMessages={onGetMessages}
/>
</MessageFooter>
)}

View File

@ -15,7 +15,7 @@ import { EVENT_NAMES, EventEmitter } from '@renderer/services/EventService'
import { translateText } from '@renderer/services/TranslateService'
import { Message, Model } from '@renderer/types'
import { removeTrailingDoubleSpaces } from '@renderer/utils'
import { Dropdown, Popconfirm, Tooltip } from 'antd'
import { Button, Dropdown, Popconfirm, Tooltip } from 'antd'
import dayjs from 'dayjs'
import { FC, useCallback, useMemo, useState } from 'react'
import { useTranslation } from 'react-i18next'
@ -31,6 +31,7 @@ interface Props {
setModel: (model: Model) => void
onEditMessage?: (message: Message) => void
onDeleteMessage?: (message: Message) => void
onGetMessages?: () => Message[]
}
const MessageMenubar: FC<Props> = (props) => {
@ -43,7 +44,8 @@ const MessageMenubar: FC<Props> = (props) => {
assistantModel,
setModel,
onEditMessage,
onDeleteMessage
onDeleteMessage,
onGetMessages
} = props
const { t } = useTranslation()
const [copied, setCopied] = useState(false)
@ -75,10 +77,43 @@ const MessageMenubar: FC<Props> = (props) => {
})
}, [index, t])
const onResend = useCallback(() => {
const _messages = onGetMessages?.() || []
const index = _messages.findIndex((m) => m.id === message.id)
const nextIndex = index + 1
const nextMessage = _messages[nextIndex]
if (nextMessage && nextMessage.role === 'assistant') {
EventEmitter.emit(EVENT_NAMES.RESEND_MESSAGE + ':' + nextMessage.id, {
...nextMessage,
content: '',
status: 'sending',
modelId: assistantModel?.id || model?.id,
translatedContent: undefined
})
}
}, [assistantModel?.id, message.id, model?.id, onGetMessages])
const onEdit = useCallback(async () => {
const editedText = await TextEditPopup.show({ text: message.content })
let resendMessage = false
const editedText = await TextEditPopup.show({
text: message.content,
children: (props) => (
<ReSendButton
icon={<i className="iconfont icon-ic_send" style={{ color: 'var(--color-primary)' }} />}
onClick={() => {
props.onOk?.()
resendMessage = true
}}>
{t('chat.resend')}
</ReSendButton>
)
})
editedText && onEditMessage?.({ ...message, content: editedText })
}, [message, onEditMessage])
resendMessage && onResend()
}, [message, onEditMessage, onResend, t])
const handleTranslate = useCallback(
async (language: string) => {
@ -287,4 +322,10 @@ const ActionButton = styled.div`
}
`
const ReSendButton = styled(Button)`
position: absolute;
top: 10px;
left: 0;
`
export default MessageMenubar

View File

@ -20,5 +20,6 @@ export const EVENT_NAMES = {
NEW_BRANCH: 'NEW_BRANCH',
EXPORT_TOPIC_IMAGE: 'EXPORT_TOPIC_IMAGE',
LOCATE_MESSAGE: 'LOCATE_MESSAGE',
ADD_NEW_TOPIC: 'ADD_NEW_TOPIC'
ADD_NEW_TOPIC: 'ADD_NEW_TOPIC',
RESEND_MESSAGE: 'RESEND_MESSAGE'
}