fix: quick assistant bugs

This commit is contained in:
kangfenmao 2025-01-19 20:03:45 +08:00
parent 9c55b4516c
commit 88cc783a95
9 changed files with 64 additions and 28 deletions

View File

@ -305,7 +305,9 @@ export class WindowService {
if (is.dev && process.env['ELECTRON_RENDERER_URL']) { if (is.dev && process.env['ELECTRON_RENDERER_URL']) {
this.miniWindow.loadURL(process.env['ELECTRON_RENDERER_URL'] + '#/mini') this.miniWindow.loadURL(process.env['ELECTRON_RENDERER_URL'] + '#/mini')
} else { } else {
this.miniWindow.loadFile(join(__dirname, '../renderer/index.html') + '#/mini') this.miniWindow.loadFile(join(__dirname, '../renderer/index.html'), {
hash: '#/mini'
})
} }
} }

View File

@ -22,6 +22,7 @@ export function useSettings() {
}, },
setTray(isActive: boolean) { setTray(isActive: boolean) {
dispatch(setTray(isActive)) dispatch(setTray(isActive))
window.api.setTray(isActive)
}, },
setTheme(theme: ThemeMode) { setTheme(theme: ThemeMode) {
dispatch(setTheme(theme)) dispatch(setTheme(theme))

View File

@ -14,7 +14,7 @@ import { SettingContainer, SettingDivider, SettingGroup, SettingRow, SettingRowT
const QuickAssistantSettings: FC = () => { const QuickAssistantSettings: FC = () => {
const { t } = useTranslation() const { t } = useTranslation()
const { theme } = useTheme() const { theme } = useTheme()
const { enableQuickAssistant, clickTrayToShowQuickAssistant, setTray } = useSettings() const { enableQuickAssistant, clickTrayToShowQuickAssistant, tray, setTray } = useSettings()
const dispatch = useAppDispatch() const dispatch = useAppDispatch()
const handleEnableQuickAssistant = async (enable: boolean) => { const handleEnableQuickAssistant = async (enable: boolean) => {
@ -32,15 +32,16 @@ const QuickAssistantSettings: FC = () => {
key: 'quick-assistant-info' key: 'quick-assistant-info'
}) })
} }
if (enable && clickTrayToShowQuickAssistant) {
setTray(true)
}
} }
const handleClickTrayToShowQuickAssistant = async (checked: boolean) => { const handleClickTrayToShowQuickAssistant = async (checked: boolean) => {
dispatch(setClickTrayToShowQuickAssistant(checked)) dispatch(setClickTrayToShowQuickAssistant(checked))
await window.api.config.set('clickTrayToShowQuickAssistant', checked) await window.api.config.set('clickTrayToShowQuickAssistant', checked)
if (checked) { checked && setTray(true)
setTray(true)
window.api.setTray(true)
}
} }
return ( return (

View File

@ -847,6 +847,7 @@ const migrateConfig = {
}) })
state.settings.enableQuickAssistant = false state.settings.enableQuickAssistant = false
state.settings.clickTrayToShowQuickAssistant = true
return state return state
} }

View File

@ -12,7 +12,7 @@ import styled from 'styled-components'
interface Props { interface Props {
message: Message message: Message
index?: number index?: number
total?: number total: number
route: string route: string
onGetMessages?: () => Message[] onGetMessages?: () => Message[]
onSetMessages?: Dispatch<SetStateAction<Message[]>> onSetMessages?: Dispatch<SetStateAction<Message[]>>
@ -36,6 +36,8 @@ const MessageItem: FC<Props> = ({ message: _message, index, total, route, onSetM
const messageBackground = getMessageBackground(true, isAssistantMessage) const messageBackground = getMessageBackground(true, isAssistantMessage)
const maxWidth = window.location.hash === '#/mini' ? '480px' : '100%'
useEffect(() => { useEffect(() => {
if (onGetMessages && onSetMessages) { if (onGetMessages && onSetMessages) {
if (message.status === 'sending') { if (message.status === 'sending') {
@ -70,7 +72,7 @@ const MessageItem: FC<Props> = ({ message: _message, index, total, route, onSetM
<MessageContainer <MessageContainer
key={message.id} key={message.id}
ref={messageContainerRef} ref={messageContainerRef}
style={{ ...(isBubbleStyle ? { alignItems: isAssistantMessage ? 'start' : 'end' } : {}) }}> style={{ ...(isBubbleStyle ? { alignItems: isAssistantMessage ? 'start' : 'end' } : {}), maxWidth }}>
<MessageContentContainer <MessageContentContainer
className="message-content-container" className="message-content-container"
style={{ style={{

View File

@ -1,8 +1,11 @@
import Scrollbar from '@renderer/components/Scrollbar' import Scrollbar from '@renderer/components/Scrollbar'
import { useShortcut } from '@renderer/hooks/useShortcuts'
import { EVENT_NAMES, EventEmitter } from '@renderer/services/EventService' import { EVENT_NAMES, EventEmitter } from '@renderer/services/EventService'
import { getAssistantMessage } from '@renderer/services/MessagesService' import { getAssistantMessage } from '@renderer/services/MessagesService'
import { Assistant, Message } from '@renderer/types' import { Assistant, Message } from '@renderer/types'
import { last } from 'lodash'
import { FC, useCallback, useEffect, useRef, useState } from 'react' import { FC, useCallback, useEffect, useRef, useState } from 'react'
import { useTranslation } from 'react-i18next'
import styled from 'styled-components' import styled from 'styled-components'
import MessageItem from './Message' import MessageItem from './Message'
@ -22,6 +25,8 @@ const Messages: FC<Props> = ({ assistant, route }) => {
const containerRef = useRef<HTMLDivElement>(null) const containerRef = useRef<HTMLDivElement>(null)
const messagesRef = useRef(messages) const messagesRef = useRef(messages)
const { t } = useTranslation()
messagesRef.current = messages messagesRef.current = messages
const onSendMessage = useCallback( const onSendMessage = useCallback(
@ -44,6 +49,14 @@ const Messages: FC<Props> = ({ assistant, route }) => {
return () => unsubscribes.forEach((unsub) => unsub()) return () => unsubscribes.forEach((unsub) => unsub())
}, [assistant.id, onSendMessage]) }, [assistant.id, onSendMessage])
useShortcut('copy_last_message', () => {
const lastMessage = last(messages)
if (lastMessage) {
navigator.clipboard.writeText(lastMessage.content)
window.message.success(t('message.copy.success'))
}
})
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) => (

View File

@ -30,9 +30,9 @@ const HomeWindow: FC = () => {
const { t } = useTranslation() const { t } = useTranslation()
const textRef = useRef(text) const textRef = useRef(text)
const referenceText = selectedText || clipboardText const referenceText = selectedText || clipboardText || text
textRef.current = `${referenceText}\n\n${text}` textRef.current = referenceText === text ? text : `${referenceText}\n\n${text}`
const onReadClipboard = useCallback(async () => { const onReadClipboard = useCallback(async () => {
const text = await navigator.clipboard.readText() const text = await navigator.clipboard.readText()
@ -58,6 +58,7 @@ const HomeWindow: FC = () => {
} }
if (e.key === 'Enter') { if (e.key === 'Enter') {
e.preventDefault()
if (text.trim() === '') { if (text.trim() === '') {
return return
} }
@ -69,11 +70,12 @@ const HomeWindow: FC = () => {
const onSendMessage = useCallback( const onSendMessage = useCallback(
async (prompt?: string) => { async (prompt?: string) => {
const text = textRef.current.trim()
setTimeout(() => { setTimeout(() => {
const message = { const message = {
id: uuid(), id: uuid(),
role: 'user', role: 'user',
content: prompt ? `${prompt}\n\n${textRef.current}` : textRef.current, content: prompt ? `${prompt}\n\n${text}` : text,
assistantId: defaultAssistant.id, assistantId: defaultAssistant.id,
topicId: defaultAssistant.topics[0].id || uuid(), topicId: defaultAssistant.topics[0].id || uuid(),
createdAt: dayjs().format('YYYY-MM-DD HH:mm:ss'), createdAt: dayjs().format('YYYY-MM-DD HH:mm:ss'),

View File

@ -24,7 +24,7 @@ const InputBar: FC<InputBarProps> = ({ text, model, placeholder, handleKeyDown,
bordered={false} bordered={false}
autoFocus autoFocus
onKeyDown={handleKeyDown} onKeyDown={handleKeyDown}
onChange={(e) => setText(e.target.value)} onChange={(e) => setText(e.target.value.trim())}
disabled={generating} disabled={generating}
/> />
</InputWrapper> </InputWrapper>

View File

@ -9,7 +9,7 @@ import { Assistant, Message } from '@renderer/types'
import { runAsyncFunction, uuid } from '@renderer/utils' import { runAsyncFunction, uuid } from '@renderer/utils'
import { Select, Space } from 'antd' import { Select, Space } from 'antd'
import { isEmpty } from 'lodash' import { isEmpty } from 'lodash'
import { FC, useCallback, useEffect, useState } from 'react' import { FC, useCallback, useEffect, useRef, useState } from 'react'
import { useTranslation } from 'react-i18next' import { useTranslation } from 'react-i18next'
import styled from 'styled-components' import styled from 'styled-components'
@ -24,13 +24,20 @@ const Translate: FC<Props> = ({ text }) => {
const [targetLanguage, setTargetLanguage] = useState(_targetLanguage) const [targetLanguage, setTargetLanguage] = useState(_targetLanguage)
const { translateModel } = useDefaultModel() const { translateModel } = useDefaultModel()
const { t } = useTranslation() const { t } = useTranslation()
const translatingRef = useRef(false)
_targetLanguage = targetLanguage _targetLanguage = targetLanguage
const translate = useCallback(async () => { const translate = useCallback(async () => {
if (!text.trim() || !translateModel) return if (!text.trim() || !translateModel) return
const assistant: Assistant = getDefaultTranslateAssistant(targetLanguage, text) if (translatingRef.current) return
try {
translatingRef.current = true
const targetLang = await db.settings.get({ id: 'translate:target:language' })
const assistant: Assistant = getDefaultTranslateAssistant(targetLang?.value || targetLanguage, text)
const message: Message = { const message: Message = {
id: uuid(), id: uuid(),
role: 'user', role: 'user',
@ -44,6 +51,13 @@ const Translate: FC<Props> = ({ text }) => {
} }
await fetchTranslate({ message, assistant, onResponse: setResult }) await fetchTranslate({ message, assistant, onResponse: setResult })
translatingRef.current = false
} catch (error) {
console.error(error)
} finally {
translatingRef.current = false
}
}, [text, targetLanguage, translateModel]) }, [text, targetLanguage, translateModel])
useEffect(() => { useEffect(() => {
@ -75,9 +89,9 @@ const Translate: FC<Props> = ({ text }) => {
style={{ width: 200 }} style={{ width: 200 }}
optionFilterProp="label" optionFilterProp="label"
options={TranslateLanguageOptions} options={TranslateLanguageOptions}
onChange={(value) => { onChange={async (value) => {
await db.settings.put({ id: 'translate:target:language', value })
setTargetLanguage(value) setTargetLanguage(value)
db.settings.put({ id: 'translate:target:language', value })
}} }}
optionRender={(option) => ( optionRender={(option) => (
<Space> <Space>