feat: save file to disk
This commit is contained in:
@@ -22,3 +22,7 @@ export function useShowAssistants() {
|
||||
toggleShowAssistants: () => dispatch(toggleShowAssistants())
|
||||
}
|
||||
}
|
||||
|
||||
export function useRuntime() {
|
||||
return useAppSelector((state) => state.runtime)
|
||||
}
|
||||
|
||||
@@ -24,7 +24,8 @@ const resources = {
|
||||
copy: 'Copy',
|
||||
regenerate: 'Regenerate',
|
||||
provider: 'Provider',
|
||||
you: 'You'
|
||||
you: 'You',
|
||||
save: 'Save'
|
||||
},
|
||||
button: {
|
||||
add: 'Add',
|
||||
@@ -46,6 +47,9 @@ const resources = {
|
||||
'chat.completion.paused': 'Chat completion paused',
|
||||
'switch.disabled': 'Switching is disabled while the assistant is generating'
|
||||
},
|
||||
chat: {
|
||||
save: 'Save'
|
||||
},
|
||||
assistant: {
|
||||
'default.name': '😀 Default Assistant',
|
||||
'default.description': "Hello, I'm Default Assistant. You can start chatting with me right away",
|
||||
@@ -197,6 +201,9 @@ const resources = {
|
||||
'chat.completion.paused': '会话已停止',
|
||||
'switch.disabled': '模型回复完成后才能切换'
|
||||
},
|
||||
chat: {
|
||||
save: '保存'
|
||||
},
|
||||
assistant: {
|
||||
'default.name': '😃 默认助手 - Assistant',
|
||||
'default.description': '你好,我是默认助手。你可以立刻开始跟我聊天。',
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { CopyOutlined, DeleteOutlined, EditOutlined, SyncOutlined } from '@ant-design/icons'
|
||||
import { CopyOutlined, DeleteOutlined, EditOutlined, MenuOutlined, SaveOutlined, SyncOutlined } from '@ant-design/icons'
|
||||
import Logo from '@renderer/assets/images/logo.png'
|
||||
import { getModelLogo } from '@renderer/config/provider'
|
||||
import { useAssistant } from '@renderer/hooks/useAssistant'
|
||||
@@ -7,14 +7,15 @@ import { useSettings } from '@renderer/hooks/useSettings'
|
||||
import { EVENT_NAMES, EventEmitter } from '@renderer/services/event'
|
||||
import { Message } from '@renderer/types'
|
||||
import { firstLetter } from '@renderer/utils'
|
||||
import { Avatar, Tooltip } from 'antd'
|
||||
import { Avatar, Dropdown, Tooltip } from 'antd'
|
||||
import dayjs from 'dayjs'
|
||||
import { isEmpty, upperFirst } from 'lodash'
|
||||
import { FC } from 'react'
|
||||
import { FC, useCallback } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import Markdown from 'react-markdown'
|
||||
import styled from 'styled-components'
|
||||
import CodeBlock from './CodeBlock'
|
||||
import { useRuntime } from '@renderer/hooks/useStore'
|
||||
|
||||
interface Props {
|
||||
message: Message
|
||||
@@ -29,6 +30,7 @@ const MessageItem: FC<Props> = ({ message, index, showMenu, onDeleteMessage }) =
|
||||
const { t } = useTranslation()
|
||||
const { assistant } = useAssistant(message.assistantId)
|
||||
const { userName, showMessageDivider, messageFont } = useSettings()
|
||||
const { generating } = useRuntime()
|
||||
|
||||
const isLastMessage = index === 0
|
||||
const isUserMessage = message.role === 'user'
|
||||
@@ -66,7 +68,7 @@ const MessageItem: FC<Props> = ({ message, index, showMenu, onDeleteMessage }) =
|
||||
return message.content
|
||||
}
|
||||
|
||||
const getUserName = () => {
|
||||
const getUserName = useCallback(() => {
|
||||
if (message.id === 'assistant') {
|
||||
return assistant.name
|
||||
}
|
||||
@@ -76,7 +78,24 @@ const MessageItem: FC<Props> = ({ message, index, showMenu, onDeleteMessage }) =
|
||||
}
|
||||
|
||||
return userName || t('common.you')
|
||||
}
|
||||
}, [assistant.name, message.id, message.modelId, message.role, t, userName])
|
||||
|
||||
const getDropdownMenus = useCallback(
|
||||
(message: Message) => {
|
||||
return [
|
||||
{
|
||||
label: t('chat.save'),
|
||||
key: 'save',
|
||||
icon: <SaveOutlined />,
|
||||
onClick: () => {
|
||||
const fileName = message.createdAt + '.md'
|
||||
window.api.saveFile(fileName, message.content)
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
[t]
|
||||
)
|
||||
|
||||
const fontFamily = messageFont === 'serif' ? "Georgia, Cambria, 'Times New Roman', Times, serif" : undefined
|
||||
const messageBorder = showMessageDivider ? undefined : 'none'
|
||||
@@ -109,7 +128,7 @@ const MessageItem: FC<Props> = ({ message, index, showMenu, onDeleteMessage }) =
|
||||
{getMessageContent(message)}
|
||||
</Markdown>
|
||||
)}
|
||||
{message.usage && (
|
||||
{message.usage && !generating && (
|
||||
<MessageMetadata>
|
||||
Tokens: {message.usage.total_tokens} | ↑{message.usage.prompt_tokens}↓{message.usage.completion_tokens}
|
||||
</MessageMetadata>
|
||||
@@ -140,6 +159,13 @@ const MessageItem: FC<Props> = ({ message, index, showMenu, onDeleteMessage }) =
|
||||
</ActionButton>
|
||||
</Tooltip>
|
||||
)}
|
||||
{!isUserMessage && (
|
||||
<Dropdown menu={{ items: getDropdownMenus(message) }} trigger={['click']} placement="topRight" arrow>
|
||||
<ActionButton>
|
||||
<MenuOutlined />
|
||||
</ActionButton>
|
||||
</Dropdown>
|
||||
)}
|
||||
</MenusBar>
|
||||
)}
|
||||
</MessageContent>
|
||||
|
||||
@@ -93,7 +93,7 @@ const Tab = styled.div`
|
||||
color: #8a8a8a;
|
||||
border-bottom: 1px solid transparent;
|
||||
&.active {
|
||||
color: #a8a8a8;
|
||||
color: #bbb;
|
||||
font-weight: 600;
|
||||
}
|
||||
`
|
||||
|
||||
Reference in New Issue
Block a user