feat: add new add topic button
This commit is contained in:
parent
83925832be
commit
1ec7df9a7e
@ -1,3 +1,4 @@
|
|||||||
|
import { PlusOutlined, SearchOutlined } from '@ant-design/icons'
|
||||||
import { TopView } from '@renderer/components/TopView'
|
import { TopView } from '@renderer/components/TopView'
|
||||||
import systemAgents from '@renderer/config/agents.json'
|
import systemAgents from '@renderer/config/agents.json'
|
||||||
import { useAgents } from '@renderer/hooks/useAgents'
|
import { useAgents } from '@renderer/hooks/useAgents'
|
||||||
@ -5,11 +6,13 @@ import { useAssistants, useDefaultAssistant } from '@renderer/hooks/useAssistant
|
|||||||
import { covertAgentToAssistant } from '@renderer/services/assistant'
|
import { covertAgentToAssistant } from '@renderer/services/assistant'
|
||||||
import { EVENT_NAMES, EventEmitter } from '@renderer/services/event'
|
import { EVENT_NAMES, EventEmitter } from '@renderer/services/event'
|
||||||
import { Agent, Assistant } from '@renderer/types'
|
import { Agent, Assistant } from '@renderer/types'
|
||||||
import { Input, Modal, Tag } from 'antd'
|
import { Divider, Input, InputRef, Modal, Tag } from 'antd'
|
||||||
import { useMemo, useState } from 'react'
|
import { useEffect, useMemo, useRef, useState } from 'react'
|
||||||
import { useTranslation } from 'react-i18next'
|
import { useTranslation } from 'react-i18next'
|
||||||
import styled from 'styled-components'
|
import styled from 'styled-components'
|
||||||
|
|
||||||
|
import { HStack } from '../Layout'
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
resolve: (value: Assistant | undefined) => void
|
resolve: (value: Assistant | undefined) => void
|
||||||
}
|
}
|
||||||
@ -21,6 +24,7 @@ const PopupContainer: React.FC<Props> = ({ resolve }) => {
|
|||||||
const [searchText, setSearchText] = useState('')
|
const [searchText, setSearchText] = useState('')
|
||||||
const { defaultAssistant } = useDefaultAssistant()
|
const { defaultAssistant } = useDefaultAssistant()
|
||||||
const { assistants, addAssistant } = useAssistants()
|
const { assistants, addAssistant } = useAssistants()
|
||||||
|
const inputRef = useRef<InputRef>(null)
|
||||||
|
|
||||||
const defaultAgent: Agent = useMemo(
|
const defaultAgent: Agent = useMemo(
|
||||||
() => ({
|
() => ({
|
||||||
@ -65,30 +69,52 @@ const PopupContainer: React.FC<Props> = ({ resolve }) => {
|
|||||||
AddAssistantPopup.hide()
|
AddAssistantPopup.hide()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
open && setTimeout(() => inputRef.current?.focus(), 0)
|
||||||
|
}, [open])
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Modal
|
<Modal
|
||||||
centered
|
centered
|
||||||
title={t('chat.add.assistant.title')}
|
|
||||||
open={open}
|
open={open}
|
||||||
onCancel={onCancel}
|
onCancel={onCancel}
|
||||||
afterClose={onClose}
|
afterClose={onClose}
|
||||||
transitionName="ant-move-down"
|
transitionName="ant-move-down"
|
||||||
maskTransitionName="ant-fade"
|
maskTransitionName="ant-fade"
|
||||||
|
styles={{ content: { borderRadius: 20, padding: 0, overflow: 'hidden', paddingBottom: 20 } }}
|
||||||
|
closeIcon={null}
|
||||||
footer={null}>
|
footer={null}>
|
||||||
<Input
|
<HStack style={{ padding: '0 12px', marginTop: 5 }}>
|
||||||
placeholder={t('common.search')}
|
<Input
|
||||||
value={searchText}
|
prefix={
|
||||||
onChange={(e) => setSearchText(e.target.value)}
|
<SearchIcon>
|
||||||
allowClear
|
<SearchOutlined />
|
||||||
autoFocus
|
</SearchIcon>
|
||||||
style={{ marginBottom: 16 }}
|
}
|
||||||
/>
|
ref={inputRef}
|
||||||
|
placeholder={t('assistants.search')}
|
||||||
|
value={searchText}
|
||||||
|
onChange={(e) => setSearchText(e.target.value)}
|
||||||
|
allowClear
|
||||||
|
autoFocus
|
||||||
|
style={{ paddingLeft: 0 }}
|
||||||
|
bordered={false}
|
||||||
|
size="large"
|
||||||
|
/>
|
||||||
|
</HStack>
|
||||||
|
<Divider style={{ margin: 0 }} />
|
||||||
<Container>
|
<Container>
|
||||||
{agents.map((agent) => (
|
{agents.map((agent) => (
|
||||||
<AgentItem key={agent.id} onClick={() => onCreateAssistant(agent)}>
|
<AgentItem
|
||||||
{agent.emoji} {agent.name}
|
key={agent.id}
|
||||||
{agent.group === 'system' && <Tag color="orange">{t('agents.tag.system')}</Tag>}
|
onClick={() => onCreateAssistant(agent)}
|
||||||
{agent.group === 'user' && <Tag color="green">{t('agents.tag.user')}</Tag>}
|
className={agent.id === 'default' ? 'default' : ''}>
|
||||||
|
<HStack alignItems="center" gap={5}>
|
||||||
|
{agent.id === 'default' && <PlusOutlined style={{ marginLeft: -2 }} />}
|
||||||
|
{agent.emoji} {agent.name}
|
||||||
|
</HStack>
|
||||||
|
{agent.group === 'system' && <Tag color="green">{t('agents.tag.system')}</Tag>}
|
||||||
|
{agent.group === 'user' && <Tag color="orange">{t('agents.tag.user')}</Tag>}
|
||||||
</AgentItem>
|
</AgentItem>
|
||||||
))}
|
))}
|
||||||
</Container>
|
</Container>
|
||||||
@ -97,7 +123,9 @@ const PopupContainer: React.FC<Props> = ({ resolve }) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const Container = styled.div`
|
const Container = styled.div`
|
||||||
|
padding: 0 12px;
|
||||||
height: 50vh;
|
height: 50vh;
|
||||||
|
margin-top: 10px;
|
||||||
overflow-y: auto;
|
overflow-y: auto;
|
||||||
&::-webkit-scrollbar {
|
&::-webkit-scrollbar {
|
||||||
display: none;
|
display: none;
|
||||||
@ -109,12 +137,14 @@ const AgentItem = styled.div`
|
|||||||
flex-direction: row;
|
flex-direction: row;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
padding: 8px;
|
padding: 10px 15px;
|
||||||
border-radius: 8px;
|
border-radius: 8px;
|
||||||
user-select: none;
|
user-select: none;
|
||||||
background-color: var(--color-background-soft);
|
|
||||||
margin-bottom: 8px;
|
margin-bottom: 8px;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
|
&.default {
|
||||||
|
background-color: var(--color-background-soft);
|
||||||
|
}
|
||||||
.anticon {
|
.anticon {
|
||||||
font-size: 16px;
|
font-size: 16px;
|
||||||
color: var(--color-icon);
|
color: var(--color-icon);
|
||||||
@ -124,6 +154,18 @@ const AgentItem = styled.div`
|
|||||||
}
|
}
|
||||||
`
|
`
|
||||||
|
|
||||||
|
const SearchIcon = styled.div`
|
||||||
|
width: 36px;
|
||||||
|
height: 36px;
|
||||||
|
border-radius: 50%;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
background-color: var(--color-background-soft);
|
||||||
|
margin-right: 6px;
|
||||||
|
`
|
||||||
|
|
||||||
export default class AddAssistantPopup {
|
export default class AddAssistantPopup {
|
||||||
static topviewId = 0
|
static topviewId = 0
|
||||||
static hide() {
|
static hide() {
|
||||||
|
|||||||
@ -99,8 +99,8 @@ const Container = styled.div`
|
|||||||
`
|
`
|
||||||
|
|
||||||
const AvatarImg = styled(Avatar)`
|
const AvatarImg = styled(Avatar)`
|
||||||
width: 28px;
|
width: 32px;
|
||||||
height: 28px;
|
height: 32px;
|
||||||
background-color: var(--color-background-soft);
|
background-color: var(--color-background-soft);
|
||||||
margin-bottom: ${isMac ? '12px' : '12px'};
|
margin-bottom: ${isMac ? '12px' : '12px'};
|
||||||
margin-top: ${isMac ? '5px' : '2px'};
|
margin-top: ${isMac ? '5px' : '2px'};
|
||||||
@ -124,7 +124,7 @@ const Icon = styled.div`
|
|||||||
display: flex;
|
display: flex;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
border-radius: 6px;
|
border-radius: 50%;
|
||||||
margin-bottom: 5px;
|
margin-bottom: 5px;
|
||||||
transition: background-color 0.2s ease;
|
transition: background-color 0.2s ease;
|
||||||
-webkit-app-region: none;
|
-webkit-app-region: none;
|
||||||
@ -139,7 +139,7 @@ const Icon = styled.div`
|
|||||||
font-size: 17px;
|
font-size: 17px;
|
||||||
}
|
}
|
||||||
&:hover {
|
&:hover {
|
||||||
background-color: var(--color-background-soft);
|
background-color: var(--color-background-mute);
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
.iconfont,
|
.iconfont,
|
||||||
.anticon {
|
.anticon {
|
||||||
@ -147,7 +147,7 @@ const Icon = styled.div`
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
&.active {
|
&.active {
|
||||||
background-color: var(--color-background-mute);
|
background-color: var(--color-background-soft);
|
||||||
.iconfont,
|
.iconfont,
|
||||||
.anticon {
|
.anticon {
|
||||||
color: var(--color-icon-white);
|
color: var(--color-icon-white);
|
||||||
|
|||||||
@ -23,7 +23,8 @@ const AntdProvider: FC<PropsWithChildren> = ({ children }) => {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
token: {
|
token: {
|
||||||
colorPrimary: '#00b96b'
|
colorPrimary: '#00b96b',
|
||||||
|
borderRadius: 6
|
||||||
}
|
}
|
||||||
}}>
|
}}>
|
||||||
{children}
|
{children}
|
||||||
|
|||||||
@ -61,7 +61,8 @@ const resources = {
|
|||||||
'reset.double.confirm.content': 'All data will be lost, do you want to continue?',
|
'reset.double.confirm.content': 'All data will be lost, do you want to continue?',
|
||||||
'upgrade.success.title': 'Upgrade successfully',
|
'upgrade.success.title': 'Upgrade successfully',
|
||||||
'upgrade.success.content': 'Please restart the application to complete the upgrade',
|
'upgrade.success.content': 'Please restart the application to complete the upgrade',
|
||||||
'upgrade.success.button': 'Restart'
|
'upgrade.success.button': 'Restart',
|
||||||
|
'topic.added': 'New topic added'
|
||||||
},
|
},
|
||||||
chat: {
|
chat: {
|
||||||
save: 'Save',
|
save: 'Save',
|
||||||
@ -108,6 +109,11 @@ const resources = {
|
|||||||
'message.new.branch': 'New Branch',
|
'message.new.branch': 'New Branch',
|
||||||
'assistant.search.placeholder': 'Search'
|
'assistant.search.placeholder': 'Search'
|
||||||
},
|
},
|
||||||
|
assistants: {
|
||||||
|
title: 'Assistants',
|
||||||
|
abbr: 'Assistant',
|
||||||
|
search: 'Search assistants...'
|
||||||
|
},
|
||||||
files: {
|
files: {
|
||||||
title: 'Files',
|
title: 'Files',
|
||||||
file: 'File',
|
file: 'File',
|
||||||
@ -333,7 +339,8 @@ const resources = {
|
|||||||
'reset.double.confirm.content': '你的全部数据都会丢失,如果没有备份数据,将无法恢复,确定要继续吗?',
|
'reset.double.confirm.content': '你的全部数据都会丢失,如果没有备份数据,将无法恢复,确定要继续吗?',
|
||||||
'upgrade.success.title': '升级成功',
|
'upgrade.success.title': '升级成功',
|
||||||
'upgrade.success.content': '重启应用以完成升级',
|
'upgrade.success.content': '重启应用以完成升级',
|
||||||
'upgrade.success.button': '重启'
|
'upgrade.success.button': '重启',
|
||||||
|
'topic.added': '话题添加成功'
|
||||||
},
|
},
|
||||||
chat: {
|
chat: {
|
||||||
save: '保存',
|
save: '保存',
|
||||||
@ -376,11 +383,16 @@ const resources = {
|
|||||||
'settings.set_as_default': '应用到默认助手',
|
'settings.set_as_default': '应用到默认助手',
|
||||||
'settings.max': '不限',
|
'settings.max': '不限',
|
||||||
'suggestions.title': '建议的问题',
|
'suggestions.title': '建议的问题',
|
||||||
'add.assistant.title': '添加智能体',
|
'add.assistant.title': '添加助手',
|
||||||
'message.new.context': '清除上下文',
|
'message.new.context': '清除上下文',
|
||||||
'message.new.branch': '新分支',
|
'message.new.branch': '新分支',
|
||||||
'assistant.search.placeholder': '搜索'
|
'assistant.search.placeholder': '搜索'
|
||||||
},
|
},
|
||||||
|
assistants: {
|
||||||
|
title: '助手',
|
||||||
|
abbr: '助手',
|
||||||
|
search: '搜索助手'
|
||||||
|
},
|
||||||
files: {
|
files: {
|
||||||
title: '文件',
|
title: '文件',
|
||||||
file: '文件',
|
file: '文件',
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
import { DeleteOutlined, EditOutlined, MinusCircleOutlined } from '@ant-design/icons'
|
import { DeleteOutlined, EditOutlined, MinusCircleOutlined, PlusOutlined } from '@ant-design/icons'
|
||||||
import DragableList from '@renderer/components/DragableList'
|
import DragableList from '@renderer/components/DragableList'
|
||||||
import CopyIcon from '@renderer/components/Icons/CopyIcon'
|
import CopyIcon from '@renderer/components/Icons/CopyIcon'
|
||||||
import AssistantSettingPopup from '@renderer/components/Popups/AssistantSettingPopup'
|
import AssistantSettingPopup from '@renderer/components/Popups/AssistantSettingPopup'
|
||||||
@ -20,13 +20,20 @@ import styled from 'styled-components'
|
|||||||
interface Props {
|
interface Props {
|
||||||
activeAssistant: Assistant
|
activeAssistant: Assistant
|
||||||
setActiveAssistant: (assistant: Assistant) => void
|
setActiveAssistant: (assistant: Assistant) => void
|
||||||
|
onCreateDefaultAssistant: () => void
|
||||||
onCreateAssistant: () => void
|
onCreateAssistant: () => void
|
||||||
}
|
}
|
||||||
|
|
||||||
const Assistants: FC<Props> = ({ activeAssistant, setActiveAssistant, onCreateAssistant }) => {
|
const Assistants: FC<Props> = ({
|
||||||
|
activeAssistant,
|
||||||
|
setActiveAssistant,
|
||||||
|
onCreateAssistant,
|
||||||
|
onCreateDefaultAssistant
|
||||||
|
}) => {
|
||||||
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 [search, setSearch] = useState('')
|
const [search, setSearch] = useState('')
|
||||||
|
const [dragging, setDragging] = useState(false)
|
||||||
const { updateAssistant, removeAllTopics } = useAssistant(activeAssistant.id)
|
const { updateAssistant, removeAllTopics } = useAssistant(activeAssistant.id)
|
||||||
const { clickAssistantToShowTopic, topicPosition } = useSettings()
|
const { clickAssistantToShowTopic, topicPosition } = useSettings()
|
||||||
const searchRef = useRef<InputRef>(null)
|
const searchRef = useRef<InputRef>(null)
|
||||||
@ -36,10 +43,10 @@ const Assistants: FC<Props> = ({ activeAssistant, setActiveAssistant, onCreateAs
|
|||||||
const onDelete = useCallback(
|
const onDelete = useCallback(
|
||||||
(assistant: Assistant) => {
|
(assistant: Assistant) => {
|
||||||
const _assistant = last(assistants.filter((a) => a.id !== assistant.id))
|
const _assistant = last(assistants.filter((a) => a.id !== assistant.id))
|
||||||
_assistant ? setActiveAssistant(_assistant) : onCreateAssistant()
|
_assistant ? setActiveAssistant(_assistant) : onCreateDefaultAssistant()
|
||||||
removeAssistant(assistant.id)
|
removeAssistant(assistant.id)
|
||||||
},
|
},
|
||||||
[assistants, onCreateAssistant, removeAssistant, setActiveAssistant]
|
[assistants, onCreateDefaultAssistant, removeAssistant, setActiveAssistant]
|
||||||
)
|
)
|
||||||
|
|
||||||
const onEditAssistant = useCallback(
|
const onEditAssistant = useCallback(
|
||||||
@ -175,7 +182,12 @@ const Assistants: FC<Props> = ({ activeAssistant, setActiveAssistant, onCreateAs
|
|||||||
/>
|
/>
|
||||||
</SearchContainer>
|
</SearchContainer>
|
||||||
)}
|
)}
|
||||||
<DragableList list={list} onUpdate={updateAssistants} droppableProps={{ isDropDisabled: !isEmpty(search) }}>
|
<DragableList
|
||||||
|
list={list}
|
||||||
|
onUpdate={updateAssistants}
|
||||||
|
droppableProps={{ isDropDisabled: !isEmpty(search) }}
|
||||||
|
onDragStart={() => setDragging(true)}
|
||||||
|
onDragEnd={() => setDragging(false)}>
|
||||||
{(assistant) => {
|
{(assistant) => {
|
||||||
const isCurrent = assistant.id === activeAssistant?.id
|
const isCurrent = assistant.id === activeAssistant?.id
|
||||||
return (
|
return (
|
||||||
@ -193,6 +205,12 @@ const Assistants: FC<Props> = ({ activeAssistant, setActiveAssistant, onCreateAs
|
|||||||
)
|
)
|
||||||
}}
|
}}
|
||||||
</DragableList>
|
</DragableList>
|
||||||
|
{!dragging && (
|
||||||
|
<AddButton onClick={onCreateAssistant}>
|
||||||
|
<AddButtonText>{t('chat.add.assistant.title')}</AddButtonText>
|
||||||
|
<PlusOutlined />
|
||||||
|
</AddButton>
|
||||||
|
)}
|
||||||
</Container>
|
</Container>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@ -212,7 +230,7 @@ const AssistantItem = styled.div`
|
|||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
padding: 7px 10px;
|
padding: 7px 10px;
|
||||||
position: relative;
|
position: relative;
|
||||||
border-radius: 4px;
|
border-radius: 6px;
|
||||||
margin: 0 10px;
|
margin: 0 10px;
|
||||||
padding-right: 35px;
|
padding-right: 35px;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
@ -293,4 +311,29 @@ const CommandKey = styled.div`
|
|||||||
margin-right: -4px;
|
margin-right: -4px;
|
||||||
`
|
`
|
||||||
|
|
||||||
|
const AddButton = styled.div`
|
||||||
|
height: 34px;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
padding: 6px 10px;
|
||||||
|
margin: 0 10px;
|
||||||
|
margin-top: -2px;
|
||||||
|
color: var(--color-text-2);
|
||||||
|
transition: all 0.2s ease-in-out;
|
||||||
|
font-size: 13px;
|
||||||
|
cursor: pointer;
|
||||||
|
border-radius: 8px;
|
||||||
|
.anticon {
|
||||||
|
margin: 0 4px;
|
||||||
|
}
|
||||||
|
&:hover {
|
||||||
|
color: var(--color-text-1);
|
||||||
|
background-color: var(--color-background-soft);
|
||||||
|
}
|
||||||
|
`
|
||||||
|
|
||||||
|
const AddButtonText = styled.span``
|
||||||
|
|
||||||
export default Assistants
|
export default Assistants
|
||||||
|
|||||||
@ -21,7 +21,7 @@ const HomePage: FC = () => {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<Container>
|
<Container>
|
||||||
<Navbar activeAssistant={activeAssistant} setActiveAssistant={setActiveAssistant} activeTopic={activeTopic} />
|
<Navbar activeAssistant={activeAssistant} activeTopic={activeTopic} setActiveTopic={setActiveTopic} />
|
||||||
<ContentContainer>
|
<ContentContainer>
|
||||||
{showAssistants && (
|
{showAssistants && (
|
||||||
<RightSidebar
|
<RightSidebar
|
||||||
|
|||||||
@ -1,10 +1,8 @@
|
|||||||
import {
|
import {
|
||||||
ClearOutlined,
|
ClearOutlined,
|
||||||
ControlOutlined,
|
ControlOutlined,
|
||||||
FormOutlined,
|
|
||||||
FullscreenExitOutlined,
|
FullscreenExitOutlined,
|
||||||
FullscreenOutlined,
|
FullscreenOutlined,
|
||||||
HistoryOutlined,
|
|
||||||
PauseCircleOutlined,
|
PauseCircleOutlined,
|
||||||
QuestionCircleOutlined
|
QuestionCircleOutlined
|
||||||
} from '@ant-design/icons'
|
} from '@ant-design/icons'
|
||||||
@ -289,7 +287,7 @@ const Inputbar: FC<Props> = ({ assistant, setActiveTopic }) => {
|
|||||||
<ToolbarMenu>
|
<ToolbarMenu>
|
||||||
<Tooltip placement="top" title={t('chat.input.new_topic')} arrow>
|
<Tooltip placement="top" title={t('chat.input.new_topic')} arrow>
|
||||||
<ToolbarButton type="text" onClick={addNewTopic}>
|
<ToolbarButton type="text" onClick={addNewTopic}>
|
||||||
<FormOutlined />
|
<i className="iconfont icon-a-addchat" />
|
||||||
</ToolbarButton>
|
</ToolbarButton>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
<Tooltip placement="top" title={t('chat.input.clear')} arrow>
|
<Tooltip placement="top" title={t('chat.input.clear')} arrow>
|
||||||
@ -305,16 +303,6 @@ const Inputbar: FC<Props> = ({ assistant, setActiveTopic }) => {
|
|||||||
</ToolbarButton>
|
</ToolbarButton>
|
||||||
</Popconfirm>
|
</Popconfirm>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
<Tooltip placement="top" title={t('chat.input.topics')} arrow>
|
|
||||||
<ToolbarButton
|
|
||||||
type="text"
|
|
||||||
onClick={() => {
|
|
||||||
!showTopics && toggleShowTopics()
|
|
||||||
setTimeout(() => EventEmitter.emit(EVENT_NAMES.SHOW_TOPIC_SIDEBAR), 0)
|
|
||||||
}}>
|
|
||||||
<HistoryOutlined />
|
|
||||||
</ToolbarButton>
|
|
||||||
</Tooltip>
|
|
||||||
<Tooltip placement="top" title={t('chat.input.settings')} arrow>
|
<Tooltip placement="top" title={t('chat.input.settings')} arrow>
|
||||||
<ToolbarButton
|
<ToolbarButton
|
||||||
type="text"
|
type="text"
|
||||||
@ -421,6 +409,10 @@ const ToolbarButton = styled(Button)`
|
|||||||
transition: all 0.3s ease;
|
transition: all 0.3s ease;
|
||||||
color: var(--color-icon);
|
color: var(--color-icon);
|
||||||
}
|
}
|
||||||
|
.icon-a-addchat {
|
||||||
|
font-size: 19px;
|
||||||
|
margin-bottom: -2px;
|
||||||
|
}
|
||||||
&:hover {
|
&:hover {
|
||||||
background-color: var(--color-background-soft);
|
background-color: var(--color-background-soft);
|
||||||
.anticon,
|
.anticon,
|
||||||
|
|||||||
@ -1,16 +1,17 @@
|
|||||||
import { Navbar, NavbarLeft, NavbarRight } from '@renderer/components/app/Navbar'
|
import { Navbar, NavbarLeft, NavbarRight } from '@renderer/components/app/Navbar'
|
||||||
import { HStack } from '@renderer/components/Layout'
|
import { HStack } from '@renderer/components/Layout'
|
||||||
import AddAssistantPopup from '@renderer/components/Popups/AddAssistantPopup'
|
|
||||||
import AssistantSettingPopup from '@renderer/components/Popups/AssistantSettingPopup'
|
import AssistantSettingPopup from '@renderer/components/Popups/AssistantSettingPopup'
|
||||||
import { isMac, isWindows } from '@renderer/config/constant'
|
import { isMac, isWindows } from '@renderer/config/constant'
|
||||||
import { useTheme } from '@renderer/context/ThemeProvider'
|
import { useTheme } from '@renderer/context/ThemeProvider'
|
||||||
|
import db from '@renderer/databases'
|
||||||
import { useAssistant } from '@renderer/hooks/useAssistant'
|
import { useAssistant } from '@renderer/hooks/useAssistant'
|
||||||
import { useSettings } from '@renderer/hooks/useSettings'
|
import { useSettings } from '@renderer/hooks/useSettings'
|
||||||
import { useShowAssistants, useShowTopics } from '@renderer/hooks/useStore'
|
import { useShowAssistants, useShowTopics } from '@renderer/hooks/useStore'
|
||||||
import { syncAsistantToAgent } from '@renderer/services/assistant'
|
import { getDefaultTopic, syncAsistantToAgent } from '@renderer/services/assistant'
|
||||||
import { Assistant, Topic } from '@renderer/types'
|
import { Assistant, Topic } from '@renderer/types'
|
||||||
import { Switch } from 'antd'
|
import { Switch } from 'antd'
|
||||||
import { FC, useCallback } from 'react'
|
import { FC, useCallback } from 'react'
|
||||||
|
import { useTranslation } from 'react-i18next'
|
||||||
import styled from 'styled-components'
|
import styled from 'styled-components'
|
||||||
|
|
||||||
import SelectModelButton from './components/SelectModelButton'
|
import SelectModelButton from './components/SelectModelButton'
|
||||||
@ -18,20 +19,16 @@ import SelectModelButton from './components/SelectModelButton'
|
|||||||
interface Props {
|
interface Props {
|
||||||
activeAssistant: Assistant
|
activeAssistant: Assistant
|
||||||
activeTopic: Topic
|
activeTopic: Topic
|
||||||
setActiveAssistant: (assistant: Assistant) => void
|
setActiveTopic: (topic: Topic) => void
|
||||||
}
|
}
|
||||||
|
|
||||||
const HeaderNavbar: FC<Props> = ({ activeAssistant, setActiveAssistant }) => {
|
const HeaderNavbar: FC<Props> = ({ activeAssistant, setActiveTopic }) => {
|
||||||
const { assistant, updateAssistant } = useAssistant(activeAssistant.id)
|
const { assistant, updateAssistant, addTopic } = useAssistant(activeAssistant.id)
|
||||||
const { showAssistants, toggleShowAssistants } = useShowAssistants()
|
const { showAssistants, toggleShowAssistants } = useShowAssistants()
|
||||||
const { theme, toggleTheme } = useTheme()
|
const { theme, toggleTheme } = useTheme()
|
||||||
const { topicPosition } = useSettings()
|
const { topicPosition } = useSettings()
|
||||||
const { showTopics, toggleShowTopics } = useShowTopics()
|
const { showTopics, toggleShowTopics } = useShowTopics()
|
||||||
|
const { t } = useTranslation()
|
||||||
const onCreateAssistant = async () => {
|
|
||||||
const assistant = await AddAssistantPopup.show()
|
|
||||||
assistant && setActiveAssistant(assistant)
|
|
||||||
}
|
|
||||||
|
|
||||||
const onEditAssistant = useCallback(async () => {
|
const onEditAssistant = useCallback(async () => {
|
||||||
const _assistant = await AssistantSettingPopup.show({ assistant })
|
const _assistant = await AssistantSettingPopup.show({ assistant })
|
||||||
@ -39,6 +36,14 @@ const HeaderNavbar: FC<Props> = ({ activeAssistant, setActiveAssistant }) => {
|
|||||||
syncAsistantToAgent(_assistant)
|
syncAsistantToAgent(_assistant)
|
||||||
}, [assistant, updateAssistant])
|
}, [assistant, updateAssistant])
|
||||||
|
|
||||||
|
const addNewTopic = useCallback(() => {
|
||||||
|
const topic = getDefaultTopic()
|
||||||
|
addTopic(topic)
|
||||||
|
setActiveTopic(topic)
|
||||||
|
db.topics.add({ id: topic.id, messages: [] })
|
||||||
|
window.message.success({ content: t('message.topic.added') })
|
||||||
|
}, [addTopic, setActiveTopic, t])
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Navbar>
|
<Navbar>
|
||||||
{showAssistants && (
|
{showAssistants && (
|
||||||
@ -46,7 +51,7 @@ const HeaderNavbar: FC<Props> = ({ activeAssistant, setActiveAssistant }) => {
|
|||||||
<NewButton onClick={toggleShowAssistants} style={{ marginLeft: isMac ? 8 : 0 }}>
|
<NewButton onClick={toggleShowAssistants} style={{ marginLeft: isMac ? 8 : 0 }}>
|
||||||
<i className="iconfont icon-hide-sidebar" />
|
<i className="iconfont icon-hide-sidebar" />
|
||||||
</NewButton>
|
</NewButton>
|
||||||
<NewButton onClick={onCreateAssistant}>
|
<NewButton onClick={addNewTopic}>
|
||||||
<i className="iconfont icon-a-addchat" />
|
<i className="iconfont icon-a-addchat" />
|
||||||
</NewButton>
|
</NewButton>
|
||||||
</NavbarLeft>
|
</NavbarLeft>
|
||||||
@ -85,7 +90,7 @@ const HeaderNavbar: FC<Props> = ({ activeAssistant, setActiveAssistant }) => {
|
|||||||
|
|
||||||
export const NewButton = styled.div`
|
export const NewButton = styled.div`
|
||||||
-webkit-app-region: none;
|
-webkit-app-region: none;
|
||||||
border-radius: 4px;
|
border-radius: 8px;
|
||||||
height: 30px;
|
height: 30px;
|
||||||
padding: 0 7px;
|
padding: 0 7px;
|
||||||
display: flex;
|
display: flex;
|
||||||
|
|||||||
@ -1,4 +1,5 @@
|
|||||||
import { BarsOutlined, SettingOutlined } from '@ant-design/icons'
|
import { BarsOutlined, SettingOutlined } from '@ant-design/icons'
|
||||||
|
import AddAssistantPopup from '@renderer/components/Popups/AddAssistantPopup'
|
||||||
import { useAssistants, useDefaultAssistant } from '@renderer/hooks/useAssistant'
|
import { useAssistants, useDefaultAssistant } from '@renderer/hooks/useAssistant'
|
||||||
import { useSettings } from '@renderer/hooks/useSettings'
|
import { useSettings } from '@renderer/hooks/useSettings'
|
||||||
import { useShowTopics } from '@renderer/hooks/useStore'
|
import { useShowTopics } from '@renderer/hooks/useStore'
|
||||||
@ -43,12 +44,18 @@ const RightSidebar: FC<Props> = ({ activeAssistant, activeTopic, setActiveAssist
|
|||||||
}
|
}
|
||||||
|
|
||||||
const showTab = !(position === 'left' && topicPosition === 'right')
|
const showTab = !(position === 'left' && topicPosition === 'right')
|
||||||
|
|
||||||
const assistantTab = {
|
const assistantTab = {
|
||||||
label: t('common.assistant'),
|
label: t('assistants.abbr'),
|
||||||
value: 'assistants',
|
value: 'assistants',
|
||||||
icon: <i className="iconfont icon-business-smart-assistant" />
|
icon: <i className="iconfont icon-business-smart-assistant" />
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const onCreateAssistant = async () => {
|
||||||
|
const assistant = await AddAssistantPopup.show()
|
||||||
|
assistant && setActiveAssistant(assistant)
|
||||||
|
}
|
||||||
|
|
||||||
const onCreateDefaultAssistant = () => {
|
const onCreateDefaultAssistant = () => {
|
||||||
const assistant = { ...defaultAssistant, id: uuid() }
|
const assistant = { ...defaultAssistant, id: uuid() }
|
||||||
addAssistant(assistant)
|
addAssistant(assistant)
|
||||||
@ -108,7 +115,8 @@ const RightSidebar: FC<Props> = ({ activeAssistant, activeTopic, setActiveAssist
|
|||||||
<Assistants
|
<Assistants
|
||||||
activeAssistant={activeAssistant}
|
activeAssistant={activeAssistant}
|
||||||
setActiveAssistant={setActiveAssistant}
|
setActiveAssistant={setActiveAssistant}
|
||||||
onCreateAssistant={onCreateDefaultAssistant}
|
onCreateAssistant={onCreateAssistant}
|
||||||
|
onCreateDefaultAssistant={onCreateDefaultAssistant}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
{tab === 'topic' && (
|
{tab === 'topic' && (
|
||||||
|
|||||||
@ -136,7 +136,7 @@ const Topics: FC<Props> = ({ assistant: _assistant, activeTopic, setActiveTopic
|
|||||||
return (
|
return (
|
||||||
<Dropdown menu={{ items: getTopicMenuItems(topic) }} trigger={['contextMenu']} key={topic.id}>
|
<Dropdown menu={{ items: getTopicMenuItems(topic) }} trigger={['contextMenu']} key={topic.id}>
|
||||||
<TopicListItem className={isActive ? 'active' : ''} onClick={() => onSwitchTopic(topic)}>
|
<TopicListItem className={isActive ? 'active' : ''} onClick={() => onSwitchTopic(topic)}>
|
||||||
<TopicName>{topic.name}</TopicName>
|
<TopicName className="name">{topic.name}</TopicName>
|
||||||
{assistant.topics.length > 1 && (
|
{assistant.topics.length > 1 && (
|
||||||
<MenuButton
|
<MenuButton
|
||||||
className="menu"
|
className="menu"
|
||||||
@ -162,14 +162,17 @@ const Container = styled.div`
|
|||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
padding-top: 10px;
|
padding-top: 10px;
|
||||||
overflow-y: scroll;
|
overflow-y: scroll;
|
||||||
max-height: calc(100vh - var(--navbar-height) - 140px);
|
max-height: calc(100vh - var(--navbar-height) - 70px);
|
||||||
|
&::-webkit-scrollbar {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
`
|
`
|
||||||
|
|
||||||
const TopicListItem = styled.div`
|
const TopicListItem = styled.div`
|
||||||
padding: 7px 10px;
|
padding: 7px 10px;
|
||||||
margin: 0 10px;
|
margin: 0 10px;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
border-radius: 4px;
|
border-radius: 6px;
|
||||||
font-family: Ubuntu;
|
font-family: Ubuntu;
|
||||||
font-size: 13px;
|
font-size: 13px;
|
||||||
display: flex;
|
display: flex;
|
||||||
@ -183,7 +186,10 @@ const TopicListItem = styled.div`
|
|||||||
}
|
}
|
||||||
&.active {
|
&.active {
|
||||||
background-color: var(--color-background-mute);
|
background-color: var(--color-background-mute);
|
||||||
font-weight: 500;
|
.name {
|
||||||
|
opacity: 1;
|
||||||
|
font-weight: 500;
|
||||||
|
}
|
||||||
.menu {
|
.menu {
|
||||||
opacity: 1;
|
opacity: 1;
|
||||||
background-color: var(--color-background-mute);
|
background-color: var(--color-background-mute);
|
||||||
@ -195,12 +201,12 @@ const TopicListItem = styled.div`
|
|||||||
`
|
`
|
||||||
|
|
||||||
const TopicName = styled.div`
|
const TopicName = styled.div`
|
||||||
color: var(--color-text);
|
|
||||||
display: -webkit-box;
|
display: -webkit-box;
|
||||||
-webkit-line-clamp: 1;
|
-webkit-line-clamp: 1;
|
||||||
-webkit-box-orient: vertical;
|
-webkit-box-orient: vertical;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
font-size: 13px;
|
font-size: 13px;
|
||||||
|
opacity: 0.6;
|
||||||
`
|
`
|
||||||
|
|
||||||
const MenuButton = styled.div`
|
const MenuButton = styled.div`
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user