feat: merge assistant and topics

This commit is contained in:
kangfenmao 2024-09-03 10:21:48 +08:00
parent 8feab6a49d
commit 5ce380c37a
21 changed files with 200 additions and 246 deletions

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 21 KiB

View File

@ -2,9 +2,10 @@ import { TranslationOutlined } from '@ant-design/icons'
import { isMac } from '@renderer/config/constant' import { isMac } from '@renderer/config/constant'
import { AppLogo, isLocalAi } from '@renderer/config/env' import { AppLogo, isLocalAi } from '@renderer/config/env'
import useAvatar from '@renderer/hooks/useAvatar' import useAvatar from '@renderer/hooks/useAvatar'
import { useRuntime } from '@renderer/hooks/useStore' import { useRuntime, useShowAssistants } from '@renderer/hooks/useStore'
import { Avatar } from 'antd' import { Avatar } from 'antd'
import { FC } from 'react' import { FC } from 'react'
import { useTranslation } from 'react-i18next'
import { Link, useLocation } from 'react-router-dom' import { Link, useLocation } from 'react-router-dom'
import styled from 'styled-components' import styled from 'styled-components'
@ -16,11 +17,26 @@ const Sidebar: FC = () => {
const { pathname } = useLocation() const { pathname } = useLocation()
const avatar = useAvatar() const avatar = useAvatar()
const { minappShow } = useRuntime() const { minappShow } = useRuntime()
const { toggleShowAssistants } = useShowAssistants()
const { generating } = useRuntime()
const { t } = useTranslation()
const isRoute = (path: string): string => (pathname === path ? 'active' : '') const isRoute = (path: string): string => (pathname === path ? 'active' : '')
const onEditUser = () => { const onEditUser = () => UserPopup.show()
UserPopup.show()
const to = (path: string) => {
if (generating) {
window.message.warning({ content: t('message.switch.disabled'), key: 'switch-assistant' })
return '/'
}
return path
}
const onToggleShowAssistants = () => {
if (pathname === '/') {
toggleShowAssistants()
}
} }
return ( return (
@ -28,22 +44,22 @@ const Sidebar: FC = () => {
<AvatarImg src={avatar || AppLogo} draggable={false} className="nodrag" onClick={onEditUser} /> <AvatarImg src={avatar || AppLogo} draggable={false} className="nodrag" onClick={onEditUser} />
<MainMenus> <MainMenus>
<Menus> <Menus>
<StyledLink to="/"> <StyledLink to={to('/')} onClick={onToggleShowAssistants}>
<Icon className={isRoute('/')}> <Icon className={isRoute('/')}>
<i className="iconfont icon-chat"></i> <i className="iconfont icon-chat"></i>
</Icon> </Icon>
</StyledLink> </StyledLink>
<StyledLink to="/agents"> <StyledLink to={to('/agents')}>
<Icon className={isRoute('/agents')}> <Icon className={isRoute('/agents')}>
<i className="iconfont icon-business-smart-assistant"></i> <i className="iconfont icon-business-smart-assistant"></i>
</Icon> </Icon>
</StyledLink> </StyledLink>
<StyledLink to="/translate"> <StyledLink to={to('/translate')}>
<Icon className={isRoute('/translate')}> <Icon className={isRoute('/translate')}>
<TranslationOutlined /> <TranslationOutlined />
</Icon> </Icon>
</StyledLink> </StyledLink>
<StyledLink to="/apps"> <StyledLink to={to('/apps')}>
<Icon className={isRoute('/apps')}> <Icon className={isRoute('/apps')}>
<i className="iconfont icon-appstore"></i> <i className="iconfont icon-appstore"></i>
</Icon> </Icon>
@ -51,7 +67,7 @@ const Sidebar: FC = () => {
</Menus> </Menus>
</MainMenus> </MainMenus>
<Menus> <Menus>
<StyledLink to={isLocalAi ? '/settings/assistant' : '/settings/provider'}> <StyledLink to={to(isLocalAi ? '/settings/assistant' : '/settings/provider')}>
<Icon className={pathname.startsWith('/settings') ? 'active' : ''}> <Icon className={pathname.startsWith('/settings') ? 'active' : ''}>
<i className="iconfont icon-setting"></i> <i className="iconfont icon-setting"></i>
</Icon> </Icon>

View File

@ -255,46 +255,60 @@ export const SYSTEM_MODELS: Record<string, SystemModel[]> = {
} }
], ],
zhipu: [ zhipu: [
{
id: 'glm-4-0520',
provider: 'zhipu',
name: 'GLM-4-0520',
group: 'GLM',
enabled: true
},
{ {
id: 'glm-4', id: 'glm-4',
provider: 'zhipu', provider: 'zhipu',
name: 'GLM-4', name: 'GLM-4',
group: 'GLM', group: 'GLM-4',
enabled: false enabled: false
}, },
{ {
id: 'glm-4-airx', id: 'glm-4-plus',
provider: 'zhipu', provider: 'zhipu',
name: 'GLM-4-AirX', name: 'GLM-4-Plus',
group: 'GLM', group: 'GLM-4',
enabled: false enabled: false
}, },
{ {
id: 'glm-4-air', id: 'glm-4-air',
provider: 'zhipu', provider: 'zhipu',
name: 'GLM-4-Air', name: 'GLM-4-Air',
group: 'GLM', group: 'GLM-4',
enabled: true enabled: true
}, },
{
id: 'glm-4-airx',
provider: 'zhipu',
name: 'GLM-4-AirX',
group: 'GLM-4',
enabled: false
},
{
id: 'glm-4-flash',
provider: 'zhipu',
name: 'GLM-4-Flash',
group: 'GLM-4',
enabled: false
},
{ {
id: 'glm-4v', id: 'glm-4v',
provider: 'zhipu', provider: 'zhipu',
name: 'GLM-4V', name: 'GLM 4V',
group: 'GLM', group: 'GLM-4v',
enabled: false
},
{
id: 'glm-4v-plus',
provider: 'zhipu',
name: 'GLM-4V-Plus',
group: 'GLM-4v',
enabled: false enabled: false
}, },
{ {
id: 'glm-4-alltools', id: 'glm-4-alltools',
provider: 'zhipu', provider: 'zhipu',
name: 'GLM-4-AllTools', name: 'GLM-4-AllTools',
group: 'GLM', group: 'GLM-4-AllTools',
enabled: false enabled: false
} }
], ],

View File

@ -2,7 +2,7 @@ import BaicuanAppLogo from '@renderer/assets/images/apps/baixiaoying.webp'
import KimiAppLogo from '@renderer/assets/images/apps/kimi.jpg' import KimiAppLogo from '@renderer/assets/images/apps/kimi.jpg'
import YuewenAppLogo from '@renderer/assets/images/apps/yuewen.png' import YuewenAppLogo from '@renderer/assets/images/apps/yuewen.png'
import BaichuanModelLogo from '@renderer/assets/images/models/baichuan.png' import BaichuanModelLogo from '@renderer/assets/images/models/baichuan.png'
import ChatGLMModelLogo from '@renderer/assets/images/models/chatglm.jpeg' import ChatGLMModelLogo from '@renderer/assets/images/models/chatglm.png'
import ChatGPTModelLogo from '@renderer/assets/images/models/chatgpt.jpeg' import ChatGPTModelLogo from '@renderer/assets/images/models/chatgpt.jpeg'
import ClaudeModelLogo from '@renderer/assets/images/models/claude.png' import ClaudeModelLogo from '@renderer/assets/images/models/claude.png'
import DeepSeekModelLogo from '@renderer/assets/images/models/deepseek.png' import DeepSeekModelLogo from '@renderer/assets/images/models/deepseek.png'

View File

@ -1,17 +1,5 @@
import { useAppDispatch, useAppSelector } from '@renderer/store' import { useAppDispatch, useAppSelector } from '@renderer/store'
import { setShowRightSidebar, toggleRightSidebar, toggleShowAssistants } from '@renderer/store/settings' import { toggleShowAssistants } from '@renderer/store/settings'
export function useShowRightSidebar() {
const showRightSidebar = useAppSelector((state) => state.settings.showRightSidebar)
const dispatch = useAppDispatch()
return {
rightSidebarShown: showRightSidebar,
toggleRightSidebar: () => dispatch(toggleRightSidebar()),
showRightSidebar: () => dispatch(setShowRightSidebar(true)),
hideRightSidebar: () => dispatch(setShowRightSidebar(false))
}
}
export function useShowAssistants() { export function useShowAssistants() {
const showAssistants = useAppSelector((state) => state.settings.showAssistants) const showAssistants = useAppSelector((state) => state.settings.showAssistants)

View File

@ -2,9 +2,12 @@ import { Assistant, Topic } from '@renderer/types'
import { find } from 'lodash' import { find } from 'lodash'
import { useEffect, useState } from 'react' import { useEffect, useState } from 'react'
import { useAssistant } from './useAssistant'
let _activeTopic: Topic let _activeTopic: Topic
export function useActiveTopic(assistant: Assistant) { export function useActiveTopic(_assistant: Assistant) {
const { assistant } = useAssistant(_assistant.id)
const [activeTopic, setActiveTopic] = useState(_activeTopic || assistant?.topics[0]) const [activeTopic, setActiveTopic] = useState(_activeTopic || assistant?.topics[0])
useEffect(() => { useEffect(() => {

View File

@ -29,7 +29,8 @@ const resources = {
select: 'Select', select: 'Select',
search: 'Search', search: 'Search',
default: 'Default', default: 'Default',
warning: 'Warning' warning: 'Warning',
back: 'Back'
}, },
button: { button: {
add: 'Add', add: 'Add',
@ -67,7 +68,6 @@ const resources = {
'topics.delete.all.title': 'Delete all topics', 'topics.delete.all.title': 'Delete all topics',
'topics.delete.all.content': 'Are you sure you want to delete all topics?', 'topics.delete.all.content': 'Are you sure you want to delete all topics?',
'input.new_topic': 'New Topic', 'input.new_topic': 'New Topic',
'input.topics': ' Topics ',
'input.clear': 'Clear', 'input.clear': 'Clear',
'input.expand': 'Expand', 'input.expand': 'Expand',
'input.collapse': 'Collapse', 'input.collapse': 'Collapse',
@ -267,7 +267,8 @@ const resources = {
select: '选择', select: '选择',
search: '搜索', search: '搜索',
default: '默认', default: '默认',
warning: '警告' warning: '警告',
back: '返回'
}, },
button: { button: {
add: '添加', add: '添加',
@ -305,7 +306,6 @@ const resources = {
'topics.delete.all.title': '删除所有话题', 'topics.delete.all.title': '删除所有话题',
'topics.delete.all.content': '确定要删除所有话题吗?', 'topics.delete.all.content': '确定要删除所有话题吗?',
'input.new_topic': '新话题', 'input.new_topic': '新话题',
'input.topics': ' 话题 ',
'input.clear': '清除', 'input.clear': '清除',
'input.expand': '展开', 'input.expand': '展开',
'input.collapse': '收起', 'input.collapse': '收起',

View File

@ -1,11 +1,13 @@
import { CopyOutlined, DeleteOutlined, EditOutlined } from '@ant-design/icons' import { ArrowRightOutlined, CopyOutlined, DeleteOutlined, EditOutlined } from '@ant-design/icons'
import { ArrowLeftOutlined } from '@ant-design/icons'
import DragableList from '@renderer/components/DragableList' import DragableList from '@renderer/components/DragableList'
import { HStack } from '@renderer/components/Layout'
import AssistantSettingPopup from '@renderer/components/Popups/AssistantSettingPopup' import AssistantSettingPopup from '@renderer/components/Popups/AssistantSettingPopup'
import { useAssistant, useAssistants } from '@renderer/hooks/useAssistant' import { useAssistant, useAssistants } from '@renderer/hooks/useAssistant'
import { getDefaultTopic, syncAsistantToAgent } from '@renderer/services/assistant' import { getDefaultTopic, syncAsistantToAgent } from '@renderer/services/assistant'
import { EVENT_NAMES, EventEmitter } from '@renderer/services/event' import { EVENT_NAMES, EventEmitter } from '@renderer/services/event'
import { useAppSelector } from '@renderer/store' import { useAppSelector } from '@renderer/store'
import { Assistant } from '@renderer/types' import { Assistant, Topic } from '@renderer/types'
import { uuid } from '@renderer/utils' import { uuid } from '@renderer/utils'
import { Dropdown } from 'antd' import { Dropdown } from 'antd'
import { ItemType } from 'antd/es/menu/interface' import { ItemType } from 'antd/es/menu/interface'
@ -14,13 +16,27 @@ import { FC, useCallback } from 'react'
import { useTranslation } from 'react-i18next' import { useTranslation } from 'react-i18next'
import styled from 'styled-components' import styled from 'styled-components'
import Topics from './Topics'
interface Props { interface Props {
activeAssistant: Assistant activeAssistant: Assistant
setActiveAssistant: (assistant: Assistant) => void setActiveAssistant: (assistant: Assistant) => void
activeTopic: Topic
setActiveTopic: (topic: Topic) => void
showTopics: boolean
setShowTopics: (showTopics: boolean) => void
onCreateAssistant: () => void onCreateAssistant: () => void
} }
const Assistants: FC<Props> = ({ activeAssistant, setActiveAssistant, onCreateAssistant }) => { const Assistants: FC<Props> = ({
activeAssistant,
setActiveAssistant,
activeTopic,
setActiveTopic,
showTopics,
setShowTopics,
onCreateAssistant
}) => {
const { assistants, removeAssistant, addAssistant, updateAssistants } = useAssistants() const { assistants, removeAssistant, addAssistant, updateAssistants } = useAssistants()
const generating = useAppSelector((state) => state.runtime.generating) const generating = useAppSelector((state) => state.runtime.generating)
const { updateAssistant } = useAssistant(activeAssistant.id) const { updateAssistant } = useAssistant(activeAssistant.id)
@ -80,10 +96,23 @@ const Assistants: FC<Props> = ({ activeAssistant, setActiveAssistant, onCreateAs
} }
EventEmitter.emit(EVENT_NAMES.SWITCH_TOPIC_SIDEBAR) EventEmitter.emit(EVENT_NAMES.SWITCH_TOPIC_SIDEBAR)
setActiveAssistant(assistant) setActiveAssistant(assistant)
setShowTopics(true)
}, },
[generating, setActiveAssistant, t] [generating, setActiveAssistant, setShowTopics, t]
) )
if (showTopics) {
return (
<Container>
<NavigtaionHeader onClick={() => setShowTopics(false)}>
<ArrowLeftOutlined />
{t('common.back')}
</NavigtaionHeader>
<Topics assistant={activeAssistant} activeTopic={activeTopic} setActiveTopic={setActiveTopic} />
</Container>
)
}
return ( return (
<Container> <Container>
<DragableList list={assistants} onUpdate={updateAssistants}> <DragableList list={assistants} onUpdate={updateAssistants}>
@ -93,6 +122,9 @@ const Assistants: FC<Props> = ({ activeAssistant, setActiveAssistant, onCreateAs
onClick={() => onSwitchAssistant(assistant)} onClick={() => onSwitchAssistant(assistant)}
className={assistant.id === activeAssistant?.id ? 'active' : ''}> className={assistant.id === activeAssistant?.id ? 'active' : ''}>
<AssistantName className="name">{assistant.name || t('chat.default.name')}</AssistantName> <AssistantName className="name">{assistant.name || t('chat.default.name')}</AssistantName>
<HStack alignItems="center">
<ArrowRightOutlined />
</HStack>
</AssistantItem> </AssistantItem>
</Dropdown> </Dropdown>
)} )}
@ -108,26 +140,31 @@ const Container = styled.div`
max-width: var(--assistants-width); max-width: var(--assistants-width);
border-right: 0.5px solid var(--color-border); border-right: 0.5px solid var(--color-border);
height: calc(100vh - var(--navbar-height)); height: calc(100vh - var(--navbar-height));
padding: 10px;
overflow-y: auto; overflow-y: auto;
padding: 10px 0;
` `
const AssistantItem = styled.div` const AssistantItem = styled.div`
display: flex; display: flex;
flex-direction: column; flex-direction: row;
justify-content: space-between;
padding: 7px 10px; padding: 7px 10px;
position: relative; position: relative;
border-radius: 4px; border-radius: 4px;
margin: 0 10px;
cursor: pointer; cursor: pointer;
font-family: Ubuntu; font-family: Ubuntu;
.anticon { .anticon {
display: none; display: none;
color: var(--color-text-3);
} }
&:hover { &:hover {
background-color: var(--color-background-soft); background-color: var(--color-background-soft);
.count {
display: none;
}
.anticon { .anticon {
display: block; display: block;
color: var(--color-text-1);
} }
} }
&.active { &.active {
@ -139,6 +176,19 @@ const AssistantItem = styled.div`
} }
` `
const NavigtaionHeader = styled.div`
display: flex;
flex-direction: row;
align-items: center;
justify-content: flex-start;
gap: 10px;
padding: 0 5px;
cursor: pointer;
color: var(--color-text-3);
margin: 10px;
margin-top: 0;
`
const AssistantName = styled.div` const AssistantName = styled.div`
color: var(--color-text); color: var(--color-text);
display: -webkit-box; display: -webkit-box;

View File

@ -1,29 +1,35 @@
import { useAssistant } from '@renderer/hooks/useAssistant' import { useAssistant } from '@renderer/hooks/useAssistant'
import { useActiveTopic } from '@renderer/hooks/useTopic' import { Assistant, Topic } from '@renderer/types'
import { Assistant } from '@renderer/types'
import { Flex } from 'antd' import { Flex } from 'antd'
import { FC } from 'react' import { FC, useState } from 'react'
import styled from 'styled-components' import styled from 'styled-components'
import Inputbar from './Inputbar/Inputbar' import Inputbar from './Inputbar/Inputbar'
import Messages from './Messages/Messages' import Messages from './Messages/Messages'
import RightSidebar from './RightSidebar' import Settings from './Settings'
interface Props { interface Props {
assistant: Assistant assistant: Assistant
activeTopic: Topic
setActiveTopic: (topic: Topic) => void
} }
const Chat: FC<Props> = (props) => { const Chat: FC<Props> = (props) => {
const { assistant } = useAssistant(props.assistant.id) const { assistant } = useAssistant(props.assistant.id)
const { activeTopic, setActiveTopic } = useActiveTopic(assistant) const [showSetting, setShowSetting] = useState(false)
return ( return (
<Container id="chat"> <Container id="chat">
<Main vertical flex={1} justify="space-between"> <Main vertical flex={1} justify="space-between">
<Messages assistant={assistant} topic={activeTopic} /> <Messages assistant={assistant} topic={props.activeTopic} />
<Inputbar assistant={assistant} setActiveTopic={setActiveTopic} /> <Inputbar
assistant={assistant}
setActiveTopic={props.setActiveTopic}
showSetting={showSetting}
setShowSetting={setShowSetting}
/>
</Main> </Main>
<RightSidebar assistant={assistant} activeTopic={activeTopic} setActiveTopic={setActiveTopic} /> {showSetting && <Settings assistant={assistant} />}
</Container> </Container>
) )
} }

View File

@ -1,42 +0,0 @@
import { NavbarCenter } from '@renderer/components/app/Navbar'
import { isMac } from '@renderer/config/constant'
import { useAssistant } from '@renderer/hooks/useAssistant'
import { useShowAssistants } from '@renderer/hooks/useStore'
import { Assistant } from '@renderer/types'
import { removeLeadingEmoji } from '@renderer/utils'
import { FC } from 'react'
import { useTranslation } from 'react-i18next'
import styled from 'styled-components'
import SelectModelButton from './components/SelectModelButton'
import { NewButton } from './HomePage'
interface Props {
activeAssistant: Assistant
}
const HomeHeader: FC<Props> = ({ activeAssistant }) => {
const { assistant } = useAssistant(activeAssistant.id)
const { t } = useTranslation()
const { showAssistants, toggleShowAssistants } = useShowAssistants()
return (
<NavbarCenter style={{ paddingLeft: isMac ? 16 : 8 }}>
{!showAssistants && (
<NewButton onClick={toggleShowAssistants} style={{ marginRight: isMac ? 8 : 25 }}>
<i className="iconfont icon-showsidebarhoriz" />
</NewButton>
)}
<AssistantName>{removeLeadingEmoji(assistant?.name) || t('chat.default.name')}</AssistantName>
<SelectModelButton assistant={assistant} />
</NavbarCenter>
)
}
const AssistantName = styled.span`
margin-left: 5px;
margin-right: 10px;
font-family: Ubuntu;
`
export default HomeHeader

View File

@ -1,30 +1,37 @@
import { Navbar, NavbarLeft, NavbarRight } from '@renderer/components/app/Navbar' import { Navbar, NavbarCenter, NavbarLeft, NavbarRight } from '@renderer/components/app/Navbar'
import { isMac, isWindows } from '@renderer/config/constant' import { isMac, isWindows } from '@renderer/config/constant'
import { useAssistants, useDefaultAssistant } from '@renderer/hooks/useAssistant' import { useAssistants, useDefaultAssistant } from '@renderer/hooks/useAssistant'
import { useShowAssistants, useShowRightSidebar } from '@renderer/hooks/useStore' import { useShowAssistants } from '@renderer/hooks/useStore'
import { useActiveTopic } from '@renderer/hooks/useTopic'
import { useTheme } from '@renderer/providers/ThemeProvider' import { useTheme } from '@renderer/providers/ThemeProvider'
import { Assistant } from '@renderer/types' import { Assistant, Topic } from '@renderer/types'
import { uuid } from '@renderer/utils' import { uuid } from '@renderer/utils'
import { Switch } from 'antd' import { Switch } from 'antd'
import { FC, useState } from 'react' import { FC, useState } from 'react'
import { useTranslation } from 'react-i18next'
import styled from 'styled-components' import styled from 'styled-components'
import AddAssistantPopup from '../../components/Popups/AddAssistantPopup' import AddAssistantPopup from '../../components/Popups/AddAssistantPopup'
import Assistants from './Assistants' import Assistants from './Assistants'
import Chat from './Chat' import Chat from './Chat'
import Navigation from './Header' import SelectModelButton from './components/SelectModelButton'
let _activeAssistant: Assistant let _activeAssistant: Assistant
let _showTopics = false
const HomePage: FC = () => { const HomePage: FC = () => {
const { assistants, addAssistant } = useAssistants() const { assistants, addAssistant } = useAssistants()
const [activeAssistant, setActiveAssistant] = useState(_activeAssistant || assistants[0]) const [activeAssistant, setActiveAssistant] = useState(_activeAssistant || assistants[0])
const { rightSidebarShown, toggleRightSidebar } = useShowRightSidebar() const { showAssistants } = useShowAssistants()
const { showAssistants, toggleShowAssistants } = useShowAssistants()
const { defaultAssistant } = useDefaultAssistant() const { defaultAssistant } = useDefaultAssistant()
const { theme, toggleTheme } = useTheme() const { theme, toggleTheme } = useTheme()
const [showTopics, setShowTopics] = useState(_showTopics)
const { t } = useTranslation()
const { activeTopic, setActiveTopic } = useActiveTopic(activeAssistant)
_activeAssistant = activeAssistant _activeAssistant = activeAssistant
_showTopics = showTopics
const onCreateDefaultAssistant = () => { const onCreateDefaultAssistant = () => {
const assistant = { ...defaultAssistant, id: uuid() } const assistant = { ...defaultAssistant, id: uuid() }
@ -37,20 +44,25 @@ const HomePage: FC = () => {
assistant && setActiveAssistant(assistant) assistant && setActiveAssistant(assistant)
} }
const onSetActiveTopic = (topic: Topic) => {
setActiveTopic(topic)
setShowTopics(true)
}
return ( return (
<Container> <Container>
<Navbar> <Navbar>
{showAssistants && ( {showAssistants && (
<NavbarLeft style={{ justifyContent: 'space-between', borderRight: 'none', padding: '0 8px' }}> <NavbarLeft style={{ justifyContent: 'flex-end', borderRight: 'none', padding: '0 8px' }}>
<NewButton onClick={toggleShowAssistants} style={{ marginLeft: isMac ? 8 : 0 }}>
<i className="iconfont icon-hidesidebarhoriz" />
</NewButton>
<NewButton onClick={onCreateAssistant}> <NewButton onClick={onCreateAssistant}>
<i className="iconfont icon-a-addchat"></i> <i className="iconfont icon-a-addchat"></i>
</NewButton> </NewButton>
</NavbarLeft> </NavbarLeft>
)} )}
<Navigation activeAssistant={activeAssistant} /> <NavbarCenter style={{ paddingLeft: isMac ? 16 : 8 }}>
<AssistantName>{activeAssistant?.name || t('chat.default.name')}</AssistantName>
<SelectModelButton assistant={activeAssistant} />
</NavbarCenter>
<NavbarRight style={{ justifyContent: 'flex-end', paddingRight: isWindows ? 140 : 12 }}> <NavbarRight style={{ justifyContent: 'flex-end', paddingRight: isWindows ? 140 : 12 }}>
<ThemeSwitch <ThemeSwitch
checkedChildren={<i className="iconfont icon-theme icon-dark1" />} checkedChildren={<i className="iconfont icon-theme icon-dark1" />}
@ -58,9 +70,6 @@ const HomePage: FC = () => {
checked={theme === 'dark'} checked={theme === 'dark'}
onChange={toggleTheme} onChange={toggleTheme}
/> />
<NewButton onClick={toggleRightSidebar}>
<i className={`iconfont ${rightSidebarShown ? 'icon-showsidebarhoriz' : 'icon-hidesidebarhoriz'}`} />
</NewButton>
</NavbarRight> </NavbarRight>
</Navbar> </Navbar>
<ContentContainer> <ContentContainer>
@ -68,10 +77,14 @@ const HomePage: FC = () => {
<Assistants <Assistants
activeAssistant={activeAssistant} activeAssistant={activeAssistant}
setActiveAssistant={setActiveAssistant} setActiveAssistant={setActiveAssistant}
activeTopic={activeTopic}
setActiveTopic={setActiveTopic}
showTopics={showTopics}
setShowTopics={setShowTopics}
onCreateAssistant={onCreateDefaultAssistant} onCreateAssistant={onCreateDefaultAssistant}
/> />
)} )}
<Chat assistant={activeAssistant} /> <Chat assistant={activeAssistant} activeTopic={activeTopic} setActiveTopic={onSetActiveTopic} />
</ContentContainer> </ContentContainer>
</Container> </Container>
) )
@ -90,6 +103,12 @@ const ContentContainer = styled.div`
background-color: var(--color-background); background-color: var(--color-background);
` `
const AssistantName = styled.span`
margin-left: 5px;
margin-right: 10px;
font-family: Ubuntu;
`
export const NewButton = styled.div` export const NewButton = styled.div`
-webkit-app-region: none; -webkit-app-region: none;
border-radius: 4px; border-radius: 4px;

View File

@ -3,7 +3,6 @@ import {
ControlOutlined, ControlOutlined,
FullscreenExitOutlined, FullscreenExitOutlined,
FullscreenOutlined, FullscreenOutlined,
HistoryOutlined,
PauseCircleOutlined, PauseCircleOutlined,
PlusCircleOutlined, PlusCircleOutlined,
QuestionCircleOutlined QuestionCircleOutlined
@ -32,11 +31,13 @@ import SendMessageButton from './SendMessageButton'
interface Props { interface Props {
assistant: Assistant assistant: Assistant
setActiveTopic: (topic: Topic) => void setActiveTopic: (topic: Topic) => void
showSetting: boolean
setShowSetting: (show: boolean) => void
} }
let _text = '' let _text = ''
const Inputbar: FC<Props> = ({ assistant, setActiveTopic }) => { const Inputbar: FC<Props> = ({ assistant, setActiveTopic, showSetting, setShowSetting }) => {
const [text, setText] = useState(_text) const [text, setText] = useState(_text)
const [inputFocus, setInputFocus] = useState(false) const [inputFocus, setInputFocus] = useState(false)
const { addTopic } = useAssistant(assistant.id) const { addTopic } = useAssistant(assistant.id)
@ -194,6 +195,7 @@ const Inputbar: FC<Props> = ({ assistant, setActiveTopic }) => {
variant="borderless" variant="borderless"
rows={1} rows={1}
ref={textareaRef} ref={textareaRef}
style={{ fontSize }}
styles={{ textarea: TextareaStyle }} styles={{ textarea: TextareaStyle }}
onFocus={() => setInputFocus(true)} onFocus={() => setInputFocus(true)}
onBlur={() => setInputFocus(false)} onBlur={() => setInputFocus(false)}
@ -219,13 +221,8 @@ const Inputbar: FC<Props> = ({ assistant, setActiveTopic }) => {
</ToolbarButton> </ToolbarButton>
</Popconfirm> </Popconfirm>
</Tooltip> </Tooltip>
<Tooltip placement="top" title={t('chat.input.topics')} arrow> <Tooltip placement="top" title={t('chat.input.settings')} arrow className={showSetting ? 'active' : ''}>
<ToolbarButton type="text" onClick={() => EventEmitter.emit(EVENT_NAMES.SHOW_TOPIC_SIDEBAR)}> <ToolbarButton type="text" onClick={() => setShowSetting(!showSetting)}>
<HistoryOutlined />
</ToolbarButton>
</Tooltip>
<Tooltip placement="top" title={t('chat.input.settings')} arrow>
<ToolbarButton type="text" onClick={() => EventEmitter.emit(EVENT_NAMES.SHOW_CHAT_SETTINGS)}>
<ControlOutlined /> <ControlOutlined />
</ToolbarButton> </ToolbarButton>
</Tooltip> </Tooltip>

View File

@ -162,7 +162,7 @@ const MessageItem: FC<Props> = ({ message, index, showMenu, onDeleteMessage }) =
</ActionButton> </ActionButton>
</Tooltip> </Tooltip>
{canRegenerate && ( {canRegenerate && (
<SelectModelDropdown model={model} onSelect={onRegenerate} placement="topRight"> <SelectModelDropdown model={model} onSelect={onRegenerate} placement="topLeft">
<Tooltip title={t('common.regenerate')} mouseEnterDelay={0.8}> <Tooltip title={t('common.regenerate')} mouseEnterDelay={0.8}>
<ActionButton> <ActionButton>
<SyncOutlined /> <SyncOutlined />

View File

@ -1,96 +0,0 @@
import { BarsOutlined, SettingOutlined } from '@ant-design/icons'
import { useShowRightSidebar } from '@renderer/hooks/useStore'
import { EVENT_NAMES, EventEmitter } from '@renderer/services/event'
import { Assistant, Topic } from '@renderer/types'
import { Segmented } from 'antd'
import { FC, useEffect, useState } from 'react'
import { useTranslation } from 'react-i18next'
import styled from 'styled-components'
import SettingsTab from './SettingsTab'
import TopicsTab from './TopicsTab'
interface Props {
assistant: Assistant
activeTopic: Topic
setActiveTopic: (topic: Topic) => void
}
const RightSidebar: FC<Props> = (props) => {
const [tab, setTab] = useState<'topic' | 'settings'>('topic')
const { rightSidebarShown, showRightSidebar, hideRightSidebar } = useShowRightSidebar()
const { t } = useTranslation()
const isTopicTab = tab === 'topic'
const isSettingsTab = tab === 'settings'
useEffect(() => {
const unsubscribes = [
EventEmitter.on(EVENT_NAMES.SHOW_TOPIC_SIDEBAR, (): any => {
if (rightSidebarShown && isTopicTab) {
return hideRightSidebar()
}
if (rightSidebarShown) {
return setTab('topic')
}
showRightSidebar()
setTab('topic')
}),
EventEmitter.on(EVENT_NAMES.SHOW_CHAT_SETTINGS, (): any => {
if (rightSidebarShown && isSettingsTab) {
return hideRightSidebar()
}
if (rightSidebarShown) {
return setTab('settings')
}
showRightSidebar()
setTab('settings')
}),
EventEmitter.on(EVENT_NAMES.SWITCH_TOPIC_SIDEBAR, () => setTab('topic'))
]
return () => unsubscribes.forEach((unsub) => unsub())
}, [hideRightSidebar, isSettingsTab, isTopicTab, rightSidebarShown, showRightSidebar])
if (!rightSidebarShown) {
return null
}
return (
<Container>
<Segmented
value={tab}
style={{ borderRadius: 0, padding: '10px', gap: 5, borderBottom: '0.5px solid var(--color-border)' }}
options={[
{ label: t('common.topics'), value: 'topic', icon: <BarsOutlined /> },
{ label: t('settings.title'), value: 'settings', icon: <SettingOutlined /> }
]}
block
onChange={(value) => setTab(value as 'topic' | 'settings')}
/>
<TabContent>
{tab === 'topic' && <TopicsTab {...props} />}
{tab === 'settings' && <SettingsTab assistant={props.assistant} />}
</TabContent>
</Container>
)
}
const Container = styled.div`
display: flex;
flex-direction: column;
width: var(--topic-list-width);
height: calc(100vh - var(--navbar-height));
border-left: 0.5px solid var(--color-border);
.collapsed {
width: 0;
border-left: none;
}
`
const TabContent = styled.div`
display: flex;
flex: 1;
flex-direction: column;
overflow-y: auto;
`
export default RightSidebar

View File

@ -234,7 +234,13 @@ const Container = styled.div`
display: flex; display: flex;
flex: 1; flex: 1;
flex-direction: column; flex-direction: column;
width: var(--topic-list-width);
max-width: var(--topic-list-width);
height: calc(100vh - var(--navbar-height));
border-left: 0.5px solid var(--color-border);
padding: 0 15px; padding: 0 15px;
padding-bottom: 20px;
overflow-y: auto;
` `
const Label = styled.p` const Label = styled.p`

View File

@ -18,7 +18,7 @@ interface Props {
setActiveTopic: (topic: Topic) => void setActiveTopic: (topic: Topic) => void
} }
const TopicsTab: FC<Props> = ({ assistant: _assistant, activeTopic, setActiveTopic }) => { const Topics: FC<Props> = ({ assistant: _assistant, activeTopic, setActiveTopic }) => {
const { assistant, removeTopic, updateTopic, updateTopics } = useAssistant(_assistant.id) const { assistant, removeTopic, updateTopic, updateTopics } = useAssistant(_assistant.id)
const { t } = useTranslation() const { t } = useTranslation()
const generating = useAppSelector((state) => state.runtime.generating) const generating = useAppSelector((state) => state.runtime.generating)
@ -136,11 +136,12 @@ const Container = styled.div`
display: flex; display: flex;
flex: 1; flex: 1;
flex-direction: column; flex-direction: column;
padding: 10px 10px; overflow-y: scroll;
` `
const TopicListItem = styled.div` const TopicListItem = styled.div`
padding: 7px 10px; padding: 7px 10px;
margin: 0 10px;
cursor: pointer; cursor: pointer;
border-radius: 4px; border-radius: 4px;
white-space: nowrap; white-space: nowrap;
@ -155,4 +156,4 @@ const TopicListItem = styled.div`
} }
` `
export default TopicsTab export default Topics

View File

@ -1,5 +1,4 @@
import { PlusOutlined } from '@ant-design/icons' import { DeleteOutlined, EditOutlined, PlusOutlined } from '@ant-design/icons'
import { DeleteOutlined, EditOutlined } from '@ant-design/icons'
import { DragDropContext, Draggable, Droppable, DropResult } from '@hello-pangea/dnd' import { DragDropContext, Draggable, Droppable, DropResult } from '@hello-pangea/dnd'
import { getProviderLogo } from '@renderer/config/provider' import { getProviderLogo } from '@renderer/config/provider'
import { useAllProviders, useProviders } from '@renderer/hooks/useProvider' import { useAllProviders, useProviders } from '@renderer/hooks/useProvider'

View File

@ -2,8 +2,6 @@ import { Divider } from 'antd'
import Link from 'antd/es/typography/Link' import Link from 'antd/es/typography/Link'
import styled from 'styled-components' import styled from 'styled-components'
import SettingsPage from './SettingsPage'
export const SettingContainer = styled.div` export const SettingContainer = styled.div`
display: flex; display: flex;
flex-direction: column; flex-direction: column;
@ -69,5 +67,3 @@ export const SettingHelpLink = styled(Link)`
font-size: 11px; font-size: 11px;
padding: 0 5px; padding: 0 5px;
` `
export default SettingsPage

View File

@ -54,12 +54,7 @@ export default class ProviderSDK {
const userMessages = takeRight(messages, contextCount + 1).map((message) => { const userMessages = takeRight(messages, contextCount + 1).map((message) => {
return { return {
role: message.role, role: message.role,
content: message.images content: message.content
? [
{ type: 'text', text: message.content },
...message.images!.map((image) => ({ type: 'image_url', image_url: image }))
]
: message.content
} }
}) })
@ -132,10 +127,22 @@ export default class ProviderSDK {
return return
} }
const _userMessages = takeRight(messages, contextCount + 1).map((message) => {
return {
role: message.role,
content: message.images
? [
{ type: 'text', text: message.content },
...message.images!.map((image) => ({ type: 'image_url', image_url: image }))
]
: message.content
}
})
// @ts-ignore key is not typed // @ts-ignore key is not typed
const stream = await this.openaiSdk.chat.completions.create({ const stream = await this.openaiSdk.chat.completions.create({
model: model.id, model: model.id,
messages: [systemMessage, ...userMessages].filter(Boolean) as ChatCompletionMessageParam[], messages: [systemMessage, ..._userMessages].filter(Boolean) as ChatCompletionMessageParam[],
stream: true, stream: true,
temperature: assistant?.settings?.temperature, temperature: assistant?.settings?.temperature,
max_tokens: maxTokens, max_tokens: maxTokens,

View File

@ -9,7 +9,6 @@ export enum ThemeMode {
} }
export interface SettingsState { export interface SettingsState {
showRightSidebar: boolean
showAssistants: boolean showAssistants: boolean
sendMessageShortcut: SendMessageShortcut sendMessageShortcut: SendMessageShortcut
language: string language: string
@ -23,7 +22,6 @@ export interface SettingsState {
} }
const initialState: SettingsState = { const initialState: SettingsState = {
showRightSidebar: true,
showAssistants: true, showAssistants: true,
sendMessageShortcut: 'Enter', sendMessageShortcut: 'Enter',
language: navigator.language, language: navigator.language,
@ -40,12 +38,6 @@ const settingsSlice = createSlice({
name: 'settings', name: 'settings',
initialState, initialState,
reducers: { reducers: {
toggleRightSidebar: (state) => {
state.showRightSidebar = !state.showRightSidebar
},
setShowRightSidebar: (state, action: PayloadAction<boolean>) => {
state.showRightSidebar = action.payload
},
toggleShowAssistants: (state) => { toggleShowAssistants: (state) => {
state.showAssistants = !state.showAssistants state.showAssistants = !state.showAssistants
}, },
@ -80,8 +72,6 @@ const settingsSlice = createSlice({
}) })
export const { export const {
setShowRightSidebar,
toggleRightSidebar,
toggleShowAssistants, toggleShowAssistants,
setSendMessageShortcut, setSendMessageShortcut,
setLanguage, setLanguage,