feat: added copy last message feature and translations
This commit is contained in:
parent
c510f5dcce
commit
749353f460
@ -667,7 +667,8 @@
|
||||
"footer": {
|
||||
"esc": "Press ESC {{action}}",
|
||||
"esc_close": "close the window",
|
||||
"esc_back": "back"
|
||||
"esc_back": "back",
|
||||
"copy_last_message": "Press C to copy"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -652,7 +652,8 @@
|
||||
"footer": {
|
||||
"esc": "ESC キーを押して{{action}}",
|
||||
"esc_close": "ウィンドウを閉じる",
|
||||
"esc_back": "戻る"
|
||||
"esc_back": "戻る",
|
||||
"copy_last_message": "C キーを押してコピー"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -666,7 +666,8 @@
|
||||
"footer": {
|
||||
"esc": "Нажмите ESC {{action}}",
|
||||
"esc_close": "закрытия окна",
|
||||
"esc_back": "возвращения"
|
||||
"esc_back": "возвращения",
|
||||
"copy_last_message": "Нажмите C для копирования"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -655,7 +655,8 @@
|
||||
"footer": {
|
||||
"esc": "按 ESC {{action}}",
|
||||
"esc_close": "关闭窗口",
|
||||
"esc_back": "返回"
|
||||
"esc_back": "返回",
|
||||
"copy_last_message": "按 C 键复制"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -654,7 +654,8 @@
|
||||
"footer": {
|
||||
"esc": "按 ESC {{action}}",
|
||||
"esc_close": "關閉窗口",
|
||||
"esc_back": "返回"
|
||||
"esc_back": "返回",
|
||||
"copy_last_message": "按 C 鍵複製"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -133,6 +133,7 @@ const settingsSlice = createSlice({
|
||||
},
|
||||
setLanguage: (state, action: PayloadAction<LanguageVarious>) => {
|
||||
state.language = action.payload
|
||||
window.electron.ipcRenderer.send('miniwindow-reload')
|
||||
},
|
||||
setProxyMode: (state, action: PayloadAction<'system' | 'custom' | 'none'>) => {
|
||||
state.proxyMode = action.payload
|
||||
|
||||
@ -1,10 +1,10 @@
|
||||
import Scrollbar from '@renderer/components/Scrollbar'
|
||||
import { useShortcut } from '@renderer/hooks/useShortcuts'
|
||||
import { EVENT_NAMES, EventEmitter } from '@renderer/services/EventService'
|
||||
import { getAssistantMessage } from '@renderer/services/MessagesService'
|
||||
import { Assistant, Message } from '@renderer/types'
|
||||
import { last } from 'lodash'
|
||||
import { FC, useCallback, useEffect, useRef, useState } from 'react'
|
||||
import { useHotkeys } from 'react-hotkeys-hook'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import styled from 'styled-components'
|
||||
|
||||
@ -49,7 +49,7 @@ const Messages: FC<Props> = ({ assistant, route }) => {
|
||||
return () => unsubscribes.forEach((unsub) => unsub())
|
||||
}, [assistant.id, onSendMessage])
|
||||
|
||||
useShortcut('copy_last_message', () => {
|
||||
useHotkeys('c', () => {
|
||||
const lastMessage = last(messages)
|
||||
if (lastMessage) {
|
||||
navigator.clipboard.writeText(lastMessage.content)
|
||||
|
||||
@ -7,7 +7,8 @@ import { EventEmitter } from '@renderer/services/EventService'
|
||||
import { uuid } from '@renderer/utils'
|
||||
import { Divider } from 'antd'
|
||||
import dayjs from 'dayjs'
|
||||
import { FC, useCallback, useEffect, useRef, useState } from 'react'
|
||||
import { isEmpty } from 'lodash'
|
||||
import { FC, useCallback, useEffect, useState } from 'react'
|
||||
import { useHotkeys } from 'react-hotkeys-hook'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import styled from 'styled-components'
|
||||
@ -28,11 +29,10 @@ const HomeWindow: FC = () => {
|
||||
const { defaultModel: model } = useDefaultModel()
|
||||
const { language } = useSettings()
|
||||
const { t } = useTranslation()
|
||||
const textRef = useRef(text)
|
||||
|
||||
const referenceText = selectedText || clipboardText || text
|
||||
|
||||
textRef.current = referenceText === text ? text : `${referenceText}\n\n${text}`
|
||||
const content = (referenceText === text ? text : `${referenceText}\n\n${text}`).trim()
|
||||
|
||||
const onReadClipboard = useCallback(async () => {
|
||||
const text = await navigator.clipboard.readText()
|
||||
@ -59,23 +59,25 @@ const HomeWindow: FC = () => {
|
||||
|
||||
if (e.key === 'Enter') {
|
||||
e.preventDefault()
|
||||
if (textRef.current.trim() === '') {
|
||||
return
|
||||
}
|
||||
if (content) {
|
||||
setRoute('chat')
|
||||
onSendMessage()
|
||||
setTimeout(() => setText(''), 100)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const onSendMessage = useCallback(
|
||||
async (prompt?: string) => {
|
||||
const text = textRef.current.trim()
|
||||
if (isEmpty(content)) {
|
||||
return
|
||||
}
|
||||
|
||||
setTimeout(() => {
|
||||
const message = {
|
||||
id: uuid(),
|
||||
role: 'user',
|
||||
content: prompt ? `${prompt}\n\n${text}` : text,
|
||||
content: prompt ? `${prompt}\n\n${content}` : content,
|
||||
assistantId: defaultAssistant.id,
|
||||
topicId: defaultAssistant.topics[0].id || uuid(),
|
||||
createdAt: dayjs().format('YYYY-MM-DD HH:mm:ss'),
|
||||
@ -85,7 +87,7 @@ const HomeWindow: FC = () => {
|
||||
EventEmitter.emit(EVENT_NAMES.SEND_MESSAGE, message)
|
||||
}, 0)
|
||||
},
|
||||
[defaultAssistant]
|
||||
[content, defaultAssistant.id, defaultAssistant.topics]
|
||||
)
|
||||
|
||||
const clearClipboard = () => {
|
||||
@ -104,12 +106,8 @@ const HomeWindow: FC = () => {
|
||||
})
|
||||
|
||||
useEffect(() => {
|
||||
window.electron.ipcRenderer.on('show-mini-window', () => {
|
||||
onReadClipboard()
|
||||
})
|
||||
|
||||
window.electron.ipcRenderer.on('show-mini-window', onReadClipboard)
|
||||
window.electron.ipcRenderer.on('selection-action', (_, { action, selectedText }) => {
|
||||
console.debug('[HomeWindow] selection-action', action, selectedText)
|
||||
selectedText && setSelectedText(selectedText)
|
||||
action && setRoute(action)
|
||||
action === 'chat' && onSendMessage()
|
||||
@ -176,7 +174,7 @@ const HomeWindow: FC = () => {
|
||||
<Divider style={{ margin: '10px 0' }} />
|
||||
<ClipboardPreview referenceText={referenceText} clearClipboard={clearClipboard} t={t} />
|
||||
<Main>
|
||||
<FeatureMenus setRoute={setRoute} onSendMessage={onSendMessage} />
|
||||
<FeatureMenus setRoute={setRoute} onSendMessage={onSendMessage} text={content} />
|
||||
</Main>
|
||||
<Divider style={{ margin: '10px 0' }} />
|
||||
<Footer
|
||||
|
||||
@ -6,11 +6,12 @@ import { useTranslation } from 'react-i18next'
|
||||
import styled from 'styled-components'
|
||||
|
||||
interface FeatureMenusProps {
|
||||
text: string
|
||||
setRoute: Dispatch<SetStateAction<'translate' | 'summary' | 'chat' | 'explanation' | 'home'>>
|
||||
onSendMessage: (prompt?: string) => void
|
||||
}
|
||||
|
||||
const FeatureMenus: FC<FeatureMenusProps> = ({ setRoute, onSendMessage }) => {
|
||||
const FeatureMenus: FC<FeatureMenusProps> = ({ text, setRoute, onSendMessage }) => {
|
||||
const { t } = useTranslation()
|
||||
|
||||
const features = [
|
||||
@ -19,31 +20,37 @@ const FeatureMenus: FC<FeatureMenusProps> = ({ setRoute, onSendMessage }) => {
|
||||
title: t('miniwindow.feature.chat'),
|
||||
active: true,
|
||||
onClick: () => {
|
||||
if (text) {
|
||||
setRoute('chat')
|
||||
onSendMessage()
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
icon: <TranslationOutlined style={{ fontSize: '16px', color: 'var(--color-text)' }} />,
|
||||
title: t('miniwindow.feature.translate'),
|
||||
onClick: () => setRoute('translate')
|
||||
onClick: () => text && setRoute('translate')
|
||||
},
|
||||
{
|
||||
icon: <FileTextOutlined style={{ fontSize: '16px', color: 'var(--color-text)' }} />,
|
||||
title: t('miniwindow.feature.summary'),
|
||||
onClick: () => {
|
||||
if (text) {
|
||||
setRoute('summary')
|
||||
onSendMessage(t('prompts.summarize'))
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
icon: <BulbOutlined style={{ fontSize: '16px', color: 'var(--color-text)' }} />,
|
||||
title: t('miniwindow.feature.explanation'),
|
||||
onClick: () => {
|
||||
if (text) {
|
||||
setRoute('explanation')
|
||||
onSendMessage(t('prompts.explanation'))
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
|
||||
return (
|
||||
|
||||
@ -1,3 +1,5 @@
|
||||
import { CopyOutlined, LoginOutlined } from '@ant-design/icons'
|
||||
import { Tag } from 'antd'
|
||||
import { FC } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import styled from 'styled-components'
|
||||
@ -13,9 +15,16 @@ const Footer: FC<FooterProps> = ({ route, onExit }) => {
|
||||
return (
|
||||
<WindowFooter onClick={() => onExit()}>
|
||||
<FooterText className="nodrag">
|
||||
<Tag bordered={false} icon={<LoginOutlined />}>
|
||||
{t('miniwindow.footer.esc', {
|
||||
action: route === 'home' ? t('miniwindow.footer.esc_close') : t('miniwindow.footer.esc_back')
|
||||
})}
|
||||
</Tag>
|
||||
{route !== 'home' && (
|
||||
<Tag bordered={false} icon={<CopyOutlined />}>
|
||||
{t('miniwindow.footer.copy_last_message')}
|
||||
</Tag>
|
||||
)}
|
||||
</FooterText>
|
||||
</WindowFooter>
|
||||
)
|
||||
@ -29,7 +38,11 @@ const WindowFooter = styled.div`
|
||||
cursor: pointer;
|
||||
`
|
||||
|
||||
const FooterText = styled.span`
|
||||
const FooterText = styled.div`
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: 5px;
|
||||
color: var(--color-text-secondary);
|
||||
font-size: 12px;
|
||||
`
|
||||
|
||||
@ -10,6 +10,7 @@ import { runAsyncFunction, uuid } from '@renderer/utils'
|
||||
import { Select, Space } from 'antd'
|
||||
import { isEmpty } from 'lodash'
|
||||
import { FC, useCallback, useEffect, useRef, useState } from 'react'
|
||||
import { useHotkeys } from 'react-hotkeys-hook'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import styled from 'styled-components'
|
||||
|
||||
@ -71,6 +72,10 @@ const Translate: FC<Props> = ({ text }) => {
|
||||
translate()
|
||||
}, [translate])
|
||||
|
||||
useHotkeys('c', () => {
|
||||
navigator.clipboard.writeText(result)
|
||||
})
|
||||
|
||||
return (
|
||||
<Container>
|
||||
<MenuContainer>
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user