feat: quick regenerate with new model
This commit is contained in:
parent
cbd9f60cfc
commit
de41199f7e
@ -1,6 +1,5 @@
|
|||||||
import { TranslationOutlined } from '@ant-design/icons'
|
import { TranslationOutlined } from '@ant-design/icons'
|
||||||
import Logo from '@renderer/assets/images/logo.png'
|
import Logo from '@renderer/assets/images/logo.png'
|
||||||
import { isWindows } from '@renderer/config/constant'
|
|
||||||
import useAvatar from '@renderer/hooks/useAvatar'
|
import useAvatar from '@renderer/hooks/useAvatar'
|
||||||
import { FC } from 'react'
|
import { FC } from 'react'
|
||||||
import { Link, useLocation } from 'react-router-dom'
|
import { Link, useLocation } from 'react-router-dom'
|
||||||
@ -13,7 +12,7 @@ const Sidebar: FC = () => {
|
|||||||
const isRoute = (path: string): string => (pathname === path ? 'active' : '')
|
const isRoute = (path: string): string => (pathname === path ? 'active' : '')
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Container style={isWindows ? { paddingTop: 0 } : {}}>
|
<Container>
|
||||||
<StyledLink to="/">
|
<StyledLink to="/">
|
||||||
<AvatarImg src={avatar || Logo} draggable={false} />
|
<AvatarImg src={avatar || Logo} draggable={false} />
|
||||||
</StyledLink>
|
</StyledLink>
|
||||||
|
|||||||
@ -14,7 +14,7 @@ import useAvatar from '@renderer/hooks/useAvatar'
|
|||||||
import { useSettings } from '@renderer/hooks/useSettings'
|
import { useSettings } from '@renderer/hooks/useSettings'
|
||||||
import { useRuntime } from '@renderer/hooks/useStore'
|
import { useRuntime } from '@renderer/hooks/useStore'
|
||||||
import { EVENT_NAMES, EventEmitter } from '@renderer/services/event'
|
import { EVENT_NAMES, EventEmitter } from '@renderer/services/event'
|
||||||
import { Message } from '@renderer/types'
|
import { Message, Model } from '@renderer/types'
|
||||||
import { firstLetter, removeLeadingEmoji } from '@renderer/utils'
|
import { firstLetter, removeLeadingEmoji } from '@renderer/utils'
|
||||||
import { Avatar, Dropdown, Popconfirm, Tooltip } from 'antd'
|
import { Avatar, Dropdown, Popconfirm, Tooltip } from 'antd'
|
||||||
import dayjs from 'dayjs'
|
import dayjs from 'dayjs'
|
||||||
@ -24,6 +24,7 @@ import { useTranslation } from 'react-i18next'
|
|||||||
import styled from 'styled-components'
|
import styled from 'styled-components'
|
||||||
|
|
||||||
import Markdown from './markdown/Markdown'
|
import Markdown from './markdown/Markdown'
|
||||||
|
import SelectModelDropdown from './SelectModelDropdown'
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
message: Message
|
message: Message
|
||||||
@ -36,7 +37,7 @@ interface Props {
|
|||||||
const MessageItem: FC<Props> = ({ message, index, showMenu, onDeleteMessage }) => {
|
const MessageItem: FC<Props> = ({ message, index, showMenu, onDeleteMessage }) => {
|
||||||
const avatar = useAvatar()
|
const avatar = useAvatar()
|
||||||
const { t } = useTranslation()
|
const { t } = useTranslation()
|
||||||
const { assistant } = useAssistant(message.assistantId)
|
const { assistant, model, setModel } = useAssistant(message.assistantId)
|
||||||
const { userName, showMessageDivider, messageFont } = useSettings()
|
const { userName, showMessageDivider, messageFont } = useSettings()
|
||||||
const { generating } = useRuntime()
|
const { generating } = useRuntime()
|
||||||
const [copied, setCopied] = useState(false)
|
const [copied, setCopied] = useState(false)
|
||||||
@ -55,10 +56,14 @@ const MessageItem: FC<Props> = ({ message, index, showMenu, onDeleteMessage }) =
|
|||||||
|
|
||||||
const onEdit = useCallback(() => EventEmitter.emit(EVENT_NAMES.EDIT_MESSAGE, message), [message])
|
const onEdit = useCallback(() => EventEmitter.emit(EVENT_NAMES.EDIT_MESSAGE, message), [message])
|
||||||
|
|
||||||
const onRegenerate = useCallback(() => {
|
const onRegenerate = useCallback(
|
||||||
onDeleteMessage?.(message)
|
(model: Model) => {
|
||||||
setTimeout(() => EventEmitter.emit(EVENT_NAMES.REGENERATE_MESSAGE), 100)
|
setModel(model)
|
||||||
}, [message, onDeleteMessage])
|
onDeleteMessage?.(message)
|
||||||
|
setTimeout(() => EventEmitter.emit(EVENT_NAMES.REGENERATE_MESSAGE), 100)
|
||||||
|
},
|
||||||
|
[message, onDeleteMessage, setModel]
|
||||||
|
)
|
||||||
|
|
||||||
const getUserName = useCallback(() => {
|
const getUserName = useCallback(() => {
|
||||||
if (message.id === 'assistant') return assistant?.name
|
if (message.id === 'assistant') return assistant?.name
|
||||||
@ -144,11 +149,13 @@ const MessageItem: FC<Props> = ({ message, index, showMenu, onDeleteMessage }) =
|
|||||||
</Tooltip>
|
</Tooltip>
|
||||||
</Popconfirm>
|
</Popconfirm>
|
||||||
{canRegenerate && (
|
{canRegenerate && (
|
||||||
<Tooltip title={t('common.regenerate')} mouseEnterDelay={0.8}>
|
<SelectModelDropdown model={model} onSelect={onRegenerate} placement="topRight">
|
||||||
<ActionButton onClick={onRegenerate}>
|
<Tooltip title={t('common.regenerate')} mouseEnterDelay={0.8}>
|
||||||
<SyncOutlined />
|
<ActionButton>
|
||||||
</ActionButton>
|
<SyncOutlined />
|
||||||
</Tooltip>
|
</ActionButton>
|
||||||
|
</Tooltip>
|
||||||
|
</SelectModelDropdown>
|
||||||
)}
|
)}
|
||||||
{!isUserMessage && (
|
{!isUserMessage && (
|
||||||
<Dropdown menu={{ items: dropdownItems }} trigger={['click']} placement="topRight" arrow>
|
<Dropdown menu={{ items: dropdownItems }} trigger={['click']} placement="topRight" arrow>
|
||||||
|
|||||||
@ -1,19 +1,18 @@
|
|||||||
import { CodeSandboxOutlined } from '@ant-design/icons'
|
import { CodeSandboxOutlined } from '@ant-design/icons'
|
||||||
import { NavbarCenter } from '@renderer/components/app/Navbar'
|
import { NavbarCenter } from '@renderer/components/app/Navbar'
|
||||||
import { isMac } from '@renderer/config/constant'
|
import { isMac } from '@renderer/config/constant'
|
||||||
import { getModelLogo } from '@renderer/config/provider'
|
|
||||||
import { useAssistant } from '@renderer/hooks/useAssistant'
|
import { useAssistant } from '@renderer/hooks/useAssistant'
|
||||||
import { useProviders } from '@renderer/hooks/useProvider'
|
|
||||||
import { useShowAssistants } from '@renderer/hooks/useStore'
|
import { useShowAssistants } from '@renderer/hooks/useStore'
|
||||||
import { Assistant } from '@renderer/types'
|
import { Assistant } from '@renderer/types'
|
||||||
import { removeLeadingEmoji } from '@renderer/utils'
|
import { removeLeadingEmoji } from '@renderer/utils'
|
||||||
import { Avatar, Button, Dropdown, MenuProps } from 'antd'
|
import { Button } from 'antd'
|
||||||
import { first, upperFirst } from 'lodash'
|
import { upperFirst } from 'lodash'
|
||||||
import { FC } from 'react'
|
import { FC } from 'react'
|
||||||
import { useTranslation } from 'react-i18next'
|
import { useTranslation } from 'react-i18next'
|
||||||
import styled from 'styled-components'
|
import styled from 'styled-components'
|
||||||
|
|
||||||
import { NewButton } from '../HomePage'
|
import { NewButton } from '../HomePage'
|
||||||
|
import SelectModelDropdown from './SelectModelDropdown'
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
activeAssistant: Assistant
|
activeAssistant: Assistant
|
||||||
@ -22,29 +21,9 @@ interface Props {
|
|||||||
const NavigationCenter: FC<Props> = ({ activeAssistant }) => {
|
const NavigationCenter: FC<Props> = ({ activeAssistant }) => {
|
||||||
const { assistant } = useAssistant(activeAssistant.id)
|
const { assistant } = useAssistant(activeAssistant.id)
|
||||||
const { model, setModel } = useAssistant(activeAssistant.id)
|
const { model, setModel } = useAssistant(activeAssistant.id)
|
||||||
const { providers } = useProviders()
|
|
||||||
const { t } = useTranslation()
|
const { t } = useTranslation()
|
||||||
const { showAssistants, toggleShowAssistants } = useShowAssistants()
|
const { showAssistants, toggleShowAssistants } = useShowAssistants()
|
||||||
|
|
||||||
const items: MenuProps['items'] = providers
|
|
||||||
.filter((p) => p.models.length > 0)
|
|
||||||
.map((p) => ({
|
|
||||||
key: p.id,
|
|
||||||
label: p.isSystem ? t(`provider.${p.id}`) : p.name,
|
|
||||||
type: 'group',
|
|
||||||
children: p.models.map((m) => ({
|
|
||||||
key: m?.id,
|
|
||||||
label: upperFirst(m?.name),
|
|
||||||
style: m?.id === model?.id ? { color: 'var(--color-primary)' } : undefined,
|
|
||||||
icon: (
|
|
||||||
<Avatar src={getModelLogo(m?.id || '')} size={24}>
|
|
||||||
{first(m?.name)}
|
|
||||||
</Avatar>
|
|
||||||
),
|
|
||||||
onClick: () => m && setModel(m)
|
|
||||||
}))
|
|
||||||
}))
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<NavbarCenter style={{ paddingLeft: isMac ? 16 : 8 }}>
|
<NavbarCenter style={{ paddingLeft: isMac ? 16 : 8 }}>
|
||||||
{!showAssistants && (
|
{!showAssistants && (
|
||||||
@ -53,27 +32,20 @@ const NavigationCenter: FC<Props> = ({ activeAssistant }) => {
|
|||||||
</NewButton>
|
</NewButton>
|
||||||
)}
|
)}
|
||||||
<AssistantName>{removeLeadingEmoji(assistant?.name) || t('assistant.default.name')}</AssistantName>
|
<AssistantName>{removeLeadingEmoji(assistant?.name) || t('assistant.default.name')}</AssistantName>
|
||||||
<DropdownMenu
|
<SelectModelDropdown model={model} onSelect={setModel}>
|
||||||
menu={{ items, style: { maxHeight: '80vh', overflow: 'auto' } }}
|
|
||||||
trigger={['click']}
|
|
||||||
overlayClassName="chat-nav-dropdown">
|
|
||||||
<DropdownButton size="small" type="primary" ghost>
|
<DropdownButton size="small" type="primary" ghost>
|
||||||
<CodeSandboxOutlined />
|
<CodeSandboxOutlined />
|
||||||
<ModelName>{model ? upperFirst(model.name) : t('button.select_model')}</ModelName>
|
<ModelName>{model ? upperFirst(model.name) : t('button.select_model')}</ModelName>
|
||||||
</DropdownButton>
|
</DropdownButton>
|
||||||
</DropdownMenu>
|
</SelectModelDropdown>
|
||||||
</NavbarCenter>
|
</NavbarCenter>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
const DropdownMenu = styled(Dropdown)`
|
|
||||||
-webkit-app-region: none;
|
|
||||||
margin-left: 10px;
|
|
||||||
`
|
|
||||||
|
|
||||||
const AssistantName = styled.span`
|
const AssistantName = styled.span`
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
margin-left: 5px;
|
margin-left: 5px;
|
||||||
|
margin-right: 10px;
|
||||||
`
|
`
|
||||||
|
|
||||||
const DropdownButton = styled(Button)`
|
const DropdownButton = styled(Button)`
|
||||||
|
|||||||
@ -0,0 +1,55 @@
|
|||||||
|
import { getModelLogo } from '@renderer/config/provider'
|
||||||
|
import { useProviders } from '@renderer/hooks/useProvider'
|
||||||
|
import { Model } from '@renderer/types'
|
||||||
|
import { Avatar, Dropdown, DropdownProps, MenuProps } from 'antd'
|
||||||
|
import { first, upperFirst } from 'lodash'
|
||||||
|
import { FC, PropsWithChildren } from 'react'
|
||||||
|
import { useTranslation } from 'react-i18next'
|
||||||
|
import styled from 'styled-components'
|
||||||
|
|
||||||
|
interface Props extends DropdownProps {
|
||||||
|
model: Model
|
||||||
|
onSelect: (model: Model) => void
|
||||||
|
}
|
||||||
|
|
||||||
|
const SelectModelDropdown: FC<Props & PropsWithChildren> = ({ children, model, onSelect, ...props }) => {
|
||||||
|
const { t } = useTranslation()
|
||||||
|
const { providers } = useProviders()
|
||||||
|
|
||||||
|
const items: MenuProps['items'] = providers
|
||||||
|
.filter((p) => p.models.length > 0)
|
||||||
|
.map((p) => ({
|
||||||
|
key: p.id,
|
||||||
|
label: p.isSystem ? t(`provider.${p.id}`) : p.name,
|
||||||
|
type: 'group',
|
||||||
|
children: p.models.map((m) => ({
|
||||||
|
key: m?.id,
|
||||||
|
label: upperFirst(m?.name),
|
||||||
|
style: m?.id === model?.id ? { color: 'var(--color-primary)' } : undefined,
|
||||||
|
icon: (
|
||||||
|
<Avatar src={getModelLogo(m?.id || '')} size={24}>
|
||||||
|
{first(m?.name)}
|
||||||
|
</Avatar>
|
||||||
|
),
|
||||||
|
onClick: () => m && onSelect(m)
|
||||||
|
}))
|
||||||
|
}))
|
||||||
|
|
||||||
|
return (
|
||||||
|
<DropdownMenu
|
||||||
|
menu={{ items, style: { maxHeight: '80vh', overflow: 'auto' } }}
|
||||||
|
trigger={['click']}
|
||||||
|
arrow
|
||||||
|
placement="bottomCenter"
|
||||||
|
overlayClassName="chat-nav-dropdown"
|
||||||
|
{...props}>
|
||||||
|
{children}
|
||||||
|
</DropdownMenu>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
const DropdownMenu = styled(Dropdown)`
|
||||||
|
-webkit-app-region: none;
|
||||||
|
`
|
||||||
|
|
||||||
|
export default SelectModelDropdown
|
||||||
Loading…
x
Reference in New Issue
Block a user