From 1ec7df9a7eeba8543391bcdf761cd86d416bab81 Mon Sep 17 00:00:00 2001 From: kangfenmao Date: Fri, 20 Sep 2024 15:11:50 +0800 Subject: [PATCH] feat: add new add topic button --- .../components/Popups/AddAssistantPopup.tsx | 76 ++++++++++++++----- src/renderer/src/components/app/Sidebar.tsx | 10 +-- src/renderer/src/context/AntdProvider.tsx | 3 +- src/renderer/src/i18n/index.ts | 18 ++++- src/renderer/src/pages/home/Assistants.tsx | 55 ++++++++++++-- src/renderer/src/pages/home/HomePage.tsx | 2 +- .../src/pages/home/Inputbar/Inputbar.tsx | 18 ++--- src/renderer/src/pages/home/Navbar.tsx | 29 ++++--- src/renderer/src/pages/home/RightSidebar.tsx | 12 ++- src/renderer/src/pages/home/Topics.tsx | 16 ++-- 10 files changed, 174 insertions(+), 65 deletions(-) diff --git a/src/renderer/src/components/Popups/AddAssistantPopup.tsx b/src/renderer/src/components/Popups/AddAssistantPopup.tsx index 80f3b3aa..5ceb4242 100644 --- a/src/renderer/src/components/Popups/AddAssistantPopup.tsx +++ b/src/renderer/src/components/Popups/AddAssistantPopup.tsx @@ -1,3 +1,4 @@ +import { PlusOutlined, SearchOutlined } from '@ant-design/icons' import { TopView } from '@renderer/components/TopView' import systemAgents from '@renderer/config/agents.json' import { useAgents } from '@renderer/hooks/useAgents' @@ -5,11 +6,13 @@ import { useAssistants, useDefaultAssistant } from '@renderer/hooks/useAssistant import { covertAgentToAssistant } from '@renderer/services/assistant' import { EVENT_NAMES, EventEmitter } from '@renderer/services/event' import { Agent, Assistant } from '@renderer/types' -import { Input, Modal, Tag } from 'antd' -import { useMemo, useState } from 'react' +import { Divider, Input, InputRef, Modal, Tag } from 'antd' +import { useEffect, useMemo, useRef, useState } from 'react' import { useTranslation } from 'react-i18next' import styled from 'styled-components' +import { HStack } from '../Layout' + interface Props { resolve: (value: Assistant | undefined) => void } @@ -21,6 +24,7 @@ const PopupContainer: React.FC = ({ resolve }) => { const [searchText, setSearchText] = useState('') const { defaultAssistant } = useDefaultAssistant() const { assistants, addAssistant } = useAssistants() + const inputRef = useRef(null) const defaultAgent: Agent = useMemo( () => ({ @@ -65,30 +69,52 @@ const PopupContainer: React.FC = ({ resolve }) => { AddAssistantPopup.hide() } + useEffect(() => { + open && setTimeout(() => inputRef.current?.focus(), 0) + }, [open]) + return ( - setSearchText(e.target.value)} - allowClear - autoFocus - 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" + /> + + {agents.map((agent) => ( - onCreateAssistant(agent)}> - {agent.emoji} {agent.name} - {agent.group === 'system' && {t('agents.tag.system')}} - {agent.group === 'user' && {t('agents.tag.user')}} + onCreateAssistant(agent)} + className={agent.id === 'default' ? 'default' : ''}> + + {agent.id === 'default' && } + {agent.emoji} {agent.name} + + {agent.group === 'system' && {t('agents.tag.system')}} + {agent.group === 'user' && {t('agents.tag.user')}} ))} @@ -97,7 +123,9 @@ const PopupContainer: React.FC = ({ resolve }) => { } const Container = styled.div` + padding: 0 12px; height: 50vh; + margin-top: 10px; overflow-y: auto; &::-webkit-scrollbar { display: none; @@ -109,12 +137,14 @@ const AgentItem = styled.div` flex-direction: row; align-items: center; justify-content: space-between; - padding: 8px; + padding: 10px 15px; border-radius: 8px; user-select: none; - background-color: var(--color-background-soft); margin-bottom: 8px; cursor: pointer; + &.default { + background-color: var(--color-background-soft); + } .anticon { font-size: 16px; 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 { static topviewId = 0 static hide() { diff --git a/src/renderer/src/components/app/Sidebar.tsx b/src/renderer/src/components/app/Sidebar.tsx index f86e012b..1c1dd50c 100644 --- a/src/renderer/src/components/app/Sidebar.tsx +++ b/src/renderer/src/components/app/Sidebar.tsx @@ -99,8 +99,8 @@ const Container = styled.div` ` const AvatarImg = styled(Avatar)` - width: 28px; - height: 28px; + width: 32px; + height: 32px; background-color: var(--color-background-soft); margin-bottom: ${isMac ? '12px' : '12px'}; margin-top: ${isMac ? '5px' : '2px'}; @@ -124,7 +124,7 @@ const Icon = styled.div` display: flex; justify-content: center; align-items: center; - border-radius: 6px; + border-radius: 50%; margin-bottom: 5px; transition: background-color 0.2s ease; -webkit-app-region: none; @@ -139,7 +139,7 @@ const Icon = styled.div` font-size: 17px; } &:hover { - background-color: var(--color-background-soft); + background-color: var(--color-background-mute); cursor: pointer; .iconfont, .anticon { @@ -147,7 +147,7 @@ const Icon = styled.div` } } &.active { - background-color: var(--color-background-mute); + background-color: var(--color-background-soft); .iconfont, .anticon { color: var(--color-icon-white); diff --git a/src/renderer/src/context/AntdProvider.tsx b/src/renderer/src/context/AntdProvider.tsx index 1a9e8e78..8ca9dc62 100644 --- a/src/renderer/src/context/AntdProvider.tsx +++ b/src/renderer/src/context/AntdProvider.tsx @@ -23,7 +23,8 @@ const AntdProvider: FC = ({ children }) => { } }, token: { - colorPrimary: '#00b96b' + colorPrimary: '#00b96b', + borderRadius: 6 } }}> {children} diff --git a/src/renderer/src/i18n/index.ts b/src/renderer/src/i18n/index.ts index 232c1674..39c33d90 100644 --- a/src/renderer/src/i18n/index.ts +++ b/src/renderer/src/i18n/index.ts @@ -61,7 +61,8 @@ const resources = { 'reset.double.confirm.content': 'All data will be lost, do you want to continue?', 'upgrade.success.title': 'Upgrade successfully', '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: { save: 'Save', @@ -108,6 +109,11 @@ const resources = { 'message.new.branch': 'New Branch', 'assistant.search.placeholder': 'Search' }, + assistants: { + title: 'Assistants', + abbr: 'Assistant', + search: 'Search assistants...' + }, files: { title: 'Files', file: 'File', @@ -333,7 +339,8 @@ const resources = { 'reset.double.confirm.content': '你的全部数据都会丢失,如果没有备份数据,将无法恢复,确定要继续吗?', 'upgrade.success.title': '升级成功', 'upgrade.success.content': '重启应用以完成升级', - 'upgrade.success.button': '重启' + 'upgrade.success.button': '重启', + 'topic.added': '话题添加成功' }, chat: { save: '保存', @@ -376,11 +383,16 @@ const resources = { 'settings.set_as_default': '应用到默认助手', 'settings.max': '不限', 'suggestions.title': '建议的问题', - 'add.assistant.title': '添加智能体', + 'add.assistant.title': '添加助手', 'message.new.context': '清除上下文', 'message.new.branch': '新分支', 'assistant.search.placeholder': '搜索' }, + assistants: { + title: '助手', + abbr: '助手', + search: '搜索助手' + }, files: { title: '文件', file: '文件', diff --git a/src/renderer/src/pages/home/Assistants.tsx b/src/renderer/src/pages/home/Assistants.tsx index 12e9e0c8..171d2cd0 100644 --- a/src/renderer/src/pages/home/Assistants.tsx +++ b/src/renderer/src/pages/home/Assistants.tsx @@ -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 CopyIcon from '@renderer/components/Icons/CopyIcon' import AssistantSettingPopup from '@renderer/components/Popups/AssistantSettingPopup' @@ -20,13 +20,20 @@ import styled from 'styled-components' interface Props { activeAssistant: Assistant setActiveAssistant: (assistant: Assistant) => void + onCreateDefaultAssistant: () => void onCreateAssistant: () => void } -const Assistants: FC = ({ activeAssistant, setActiveAssistant, onCreateAssistant }) => { +const Assistants: FC = ({ + activeAssistant, + setActiveAssistant, + onCreateAssistant, + onCreateDefaultAssistant +}) => { const { assistants, removeAssistant, addAssistant, updateAssistants } = useAssistants() const generating = useAppSelector((state) => state.runtime.generating) const [search, setSearch] = useState('') + const [dragging, setDragging] = useState(false) const { updateAssistant, removeAllTopics } = useAssistant(activeAssistant.id) const { clickAssistantToShowTopic, topicPosition } = useSettings() const searchRef = useRef(null) @@ -36,10 +43,10 @@ const Assistants: FC = ({ activeAssistant, setActiveAssistant, onCreateAs const onDelete = useCallback( (assistant: Assistant) => { const _assistant = last(assistants.filter((a) => a.id !== assistant.id)) - _assistant ? setActiveAssistant(_assistant) : onCreateAssistant() + _assistant ? setActiveAssistant(_assistant) : onCreateDefaultAssistant() removeAssistant(assistant.id) }, - [assistants, onCreateAssistant, removeAssistant, setActiveAssistant] + [assistants, onCreateDefaultAssistant, removeAssistant, setActiveAssistant] ) const onEditAssistant = useCallback( @@ -175,7 +182,12 @@ const Assistants: FC = ({ activeAssistant, setActiveAssistant, onCreateAs /> )} - + setDragging(true)} + onDragEnd={() => setDragging(false)}> {(assistant) => { const isCurrent = assistant.id === activeAssistant?.id return ( @@ -193,6 +205,12 @@ const Assistants: FC = ({ activeAssistant, setActiveAssistant, onCreateAs ) }} + {!dragging && ( + + {t('chat.add.assistant.title')} + + + )} ) } @@ -212,7 +230,7 @@ const AssistantItem = styled.div` justify-content: space-between; padding: 7px 10px; position: relative; - border-radius: 4px; + border-radius: 6px; margin: 0 10px; padding-right: 35px; cursor: pointer; @@ -293,4 +311,29 @@ const CommandKey = styled.div` 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 diff --git a/src/renderer/src/pages/home/HomePage.tsx b/src/renderer/src/pages/home/HomePage.tsx index 06c2e8c0..39173928 100644 --- a/src/renderer/src/pages/home/HomePage.tsx +++ b/src/renderer/src/pages/home/HomePage.tsx @@ -21,7 +21,7 @@ const HomePage: FC = () => { return ( - + {showAssistants && ( = ({ assistant, setActiveTopic }) => { - + @@ -305,16 +303,6 @@ const Inputbar: FC = ({ assistant, setActiveTopic }) => { - - { - !showTopics && toggleShowTopics() - setTimeout(() => EventEmitter.emit(EVENT_NAMES.SHOW_TOPIC_SIDEBAR), 0) - }}> - - - void + setActiveTopic: (topic: Topic) => void } -const HeaderNavbar: FC = ({ activeAssistant, setActiveAssistant }) => { - const { assistant, updateAssistant } = useAssistant(activeAssistant.id) +const HeaderNavbar: FC = ({ activeAssistant, setActiveTopic }) => { + const { assistant, updateAssistant, addTopic } = useAssistant(activeAssistant.id) const { showAssistants, toggleShowAssistants } = useShowAssistants() const { theme, toggleTheme } = useTheme() const { topicPosition } = useSettings() const { showTopics, toggleShowTopics } = useShowTopics() - - const onCreateAssistant = async () => { - const assistant = await AddAssistantPopup.show() - assistant && setActiveAssistant(assistant) - } + const { t } = useTranslation() const onEditAssistant = useCallback(async () => { const _assistant = await AssistantSettingPopup.show({ assistant }) @@ -39,6 +36,14 @@ const HeaderNavbar: FC = ({ activeAssistant, setActiveAssistant }) => { syncAsistantToAgent(_assistant) }, [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 ( {showAssistants && ( @@ -46,7 +51,7 @@ const HeaderNavbar: FC = ({ activeAssistant, setActiveAssistant }) => { - + @@ -85,7 +90,7 @@ const HeaderNavbar: FC = ({ activeAssistant, setActiveAssistant }) => { export const NewButton = styled.div` -webkit-app-region: none; - border-radius: 4px; + border-radius: 8px; height: 30px; padding: 0 7px; display: flex; diff --git a/src/renderer/src/pages/home/RightSidebar.tsx b/src/renderer/src/pages/home/RightSidebar.tsx index 7d43cbe6..9a4d4d57 100644 --- a/src/renderer/src/pages/home/RightSidebar.tsx +++ b/src/renderer/src/pages/home/RightSidebar.tsx @@ -1,4 +1,5 @@ import { BarsOutlined, SettingOutlined } from '@ant-design/icons' +import AddAssistantPopup from '@renderer/components/Popups/AddAssistantPopup' import { useAssistants, useDefaultAssistant } from '@renderer/hooks/useAssistant' import { useSettings } from '@renderer/hooks/useSettings' import { useShowTopics } from '@renderer/hooks/useStore' @@ -43,12 +44,18 @@ const RightSidebar: FC = ({ activeAssistant, activeTopic, setActiveAssist } const showTab = !(position === 'left' && topicPosition === 'right') + const assistantTab = { - label: t('common.assistant'), + label: t('assistants.abbr'), value: 'assistants', icon: } + const onCreateAssistant = async () => { + const assistant = await AddAssistantPopup.show() + assistant && setActiveAssistant(assistant) + } + const onCreateDefaultAssistant = () => { const assistant = { ...defaultAssistant, id: uuid() } addAssistant(assistant) @@ -108,7 +115,8 @@ const RightSidebar: FC = ({ activeAssistant, activeTopic, setActiveAssist )} {tab === 'topic' && ( diff --git a/src/renderer/src/pages/home/Topics.tsx b/src/renderer/src/pages/home/Topics.tsx index ff309381..61d3bc22 100644 --- a/src/renderer/src/pages/home/Topics.tsx +++ b/src/renderer/src/pages/home/Topics.tsx @@ -136,7 +136,7 @@ const Topics: FC = ({ assistant: _assistant, activeTopic, setActiveTopic return ( onSwitchTopic(topic)}> - {topic.name} + {topic.name} {assistant.topics.length > 1 && (