diff --git a/src/renderer/src/assets/styles/index.scss b/src/renderer/src/assets/styles/index.scss index 766c9e6b3a..1e9865eccd 100644 --- a/src/renderer/src/assets/styles/index.scss +++ b/src/renderer/src/assets/styles/index.scss @@ -48,8 +48,8 @@ --status-bar-height: 40px; --input-bar-height: 85px; - --assistants-width: 240px; - --topic-list-width: 270px; + --assistants-width: 280px; + --topic-list-width: 280px; --settings-width: var(--assistants-width); } @@ -208,3 +208,23 @@ body, .ant-drawer-header { -webkit-app-region: no-drag; } + +.segmented-tab { + .ant-segmented-item-label { + align-items: center; + display: flex; + flex-direction: row; + justify-content: center; + font-size: 13px; + } + .iconfont { + font-size: 13px; + margin-left: -2px; + } + .anticon-setting { + font-size: 12px; + } + .ant-segmented-item-icon + * { + margin-left: 4px; + } +} diff --git a/src/renderer/src/components/Popups/AddAssistantPopup.tsx b/src/renderer/src/components/Popups/AddAssistantPopup.tsx index 0a2e64a73d..11dc2387ee 100644 --- a/src/renderer/src/components/Popups/AddAssistantPopup.tsx +++ b/src/renderer/src/components/Popups/AddAssistantPopup.tsx @@ -3,6 +3,7 @@ import systemAgents from '@renderer/config/agents.json' import { useAgents } from '@renderer/hooks/useAgents' 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' @@ -50,6 +51,7 @@ const PopupContainer: React.FC = ({ resolve }) => { const assistant = covertAgentToAssistant(agent) addAssistant(assistant) + setTimeout(() => EventEmitter.emit(EVENT_NAMES.SHOW_ASSISTANTS), 0) resolve(assistant) setOpen(false) } diff --git a/src/renderer/src/i18n/index.ts b/src/renderer/src/i18n/index.ts index 5baf455522..3606c8c446 100644 --- a/src/renderer/src/i18n/index.ts +++ b/src/renderer/src/i18n/index.ts @@ -37,7 +37,9 @@ const resources = { add: 'Add', added: 'Added', manage: 'Manage', - select_model: 'Select Model' + select_model: 'Select Model', + 'show.all': 'Show All', + collapse: 'Collapse' }, message: { copied: 'Copied!', @@ -288,7 +290,9 @@ const resources = { add: '添加', added: '已添加', manage: '管理', - select_model: '选择模型' + select_model: '选择模型', + 'show.all': '显示全部', + collapse: '收起' }, message: { copied: '已复制', diff --git a/src/renderer/src/pages/home/Assistants.tsx b/src/renderer/src/pages/home/Assistants.tsx index 34eec2380b..ecd2de91bc 100644 --- a/src/renderer/src/pages/home/Assistants.tsx +++ b/src/renderer/src/pages/home/Assistants.tsx @@ -3,7 +3,6 @@ import DragableList from '@renderer/components/DragableList' import CopyIcon from '@renderer/components/Icons/CopyIcon' import AssistantSettingPopup from '@renderer/components/Popups/AssistantSettingPopup' import { useAssistant, useAssistants } from '@renderer/hooks/useAssistant' -import { useShowTopics } from '@renderer/hooks/useStore' import { getDefaultTopic, syncAsistantToAgent } from '@renderer/services/assistant' import { EVENT_NAMES, EventEmitter } from '@renderer/services/event' import { useAppSelector } from '@renderer/store' @@ -26,7 +25,6 @@ const Assistants: FC = ({ activeAssistant, setActiveAssistant, onCreateAs const { assistants, removeAssistant, addAssistant, updateAssistants } = useAssistants() const generating = useAppSelector((state) => state.runtime.generating) const { updateAssistant, removeAllTopics } = useAssistant(activeAssistant.id) - const { showTopics, toggleShowTopics } = useShowTopics() const { t } = useTranslation() const onDelete = useCallback( @@ -100,7 +98,6 @@ const Assistants: FC = ({ activeAssistant, setActiveAssistant, onCreateAs }) } - EventEmitter.emit(EVENT_NAMES.SWITCH_TOPIC_SIDEBAR) setActiveAssistant(assistant) }, [generating, setActiveAssistant, t] @@ -116,8 +113,8 @@ const Assistants: FC = ({ activeAssistant, setActiveAssistant, onCreateAs onSwitchAssistant(assistant)} className={isCurrent ? 'active' : ''}> {assistant.name || t('chat.default.name')} isCurrent && toggleShowTopics()}> + className={`arrow-button ${isCurrent ? 'active' : ''}`} + onClick={() => EventEmitter.emit(EVENT_NAMES.SWITCH_TOPIC_SIDEBAR)}> {false && {assistant.topics.length}} @@ -133,9 +130,6 @@ const Assistants: FC = ({ activeAssistant, setActiveAssistant, onCreateAs const Container = styled.div` display: flex; flex-direction: column; - min-width: var(--assistants-width); - max-width: var(--assistants-width); - border-right: 0.5px solid var(--color-border); height: calc(100vh - var(--navbar-height)); overflow-y: auto; padding-top: 10px; diff --git a/src/renderer/src/pages/home/Chat.tsx b/src/renderer/src/pages/home/Chat.tsx index a7127330f5..c3da4d35b1 100644 --- a/src/renderer/src/pages/home/Chat.tsx +++ b/src/renderer/src/pages/home/Chat.tsx @@ -1,5 +1,6 @@ import { useAssistant } from '@renderer/hooks/useAssistant' import { useSettings } from '@renderer/hooks/useSettings' +import { useShowTopics } from '@renderer/hooks/useStore' import { Assistant, Topic } from '@renderer/types' import { Flex } from 'antd' import { FC } from 'react' @@ -13,23 +14,28 @@ interface Props { assistant: Assistant activeTopic: Topic setActiveTopic: (topic: Topic) => void + setActiveAssistant: (assistant: Assistant) => void } const Chat: FC = (props) => { const { assistant } = useAssistant(props.assistant.id) const { topicPosition } = useSettings() + const { showTopics } = useShowTopics() return ( - {topicPosition === 'left' && ( - - )}
- {topicPosition === 'right' && ( - + {topicPosition === 'right' && showTopics && ( + )}
) diff --git a/src/renderer/src/pages/home/HomePage.tsx b/src/renderer/src/pages/home/HomePage.tsx index bcd1885dfa..8a41df4f26 100644 --- a/src/renderer/src/pages/home/HomePage.tsx +++ b/src/renderer/src/pages/home/HomePage.tsx @@ -1,54 +1,48 @@ -import { useAssistants, useDefaultAssistant } from '@renderer/hooks/useAssistant' +import { useAssistants } from '@renderer/hooks/useAssistant' import { useShowAssistants } from '@renderer/hooks/useStore' import { useActiveTopic } from '@renderer/hooks/useTopic' import { Assistant, Topic } from '@renderer/types' -import { uuid } from '@renderer/utils' import { FC, useState } from 'react' import styled from 'styled-components' -import Assistants from './Assistants' import Chat from './Chat' import Navbar from './Navbar' +import RightSidebar from './RightSidebar' let _activeAssistant: Assistant const HomePage: FC = () => { - const { assistants, addAssistant } = useAssistants() + const { assistants } = useAssistants() const [activeAssistant, setActiveAssistant] = useState(_activeAssistant || assistants[0]) const { showAssistants } = useShowAssistants() - const { defaultAssistant } = useDefaultAssistant() const { activeTopic, setActiveTopic } = useActiveTopic(activeAssistant) _activeAssistant = activeAssistant - const onCreateDefaultAssistant = () => { - const assistant = { ...defaultAssistant, id: uuid() } - addAssistant(assistant) - setActiveAssistant(assistant) - } - const onSetActiveTopic = (topic: Topic) => { setActiveTopic(topic) } return ( - + {showAssistants && ( - )} - + ) diff --git a/src/renderer/src/pages/home/Inputbar/Inputbar.tsx b/src/renderer/src/pages/home/Inputbar/Inputbar.tsx index f00a5da9eb..c772878825 100644 --- a/src/renderer/src/pages/home/Inputbar/Inputbar.tsx +++ b/src/renderer/src/pages/home/Inputbar/Inputbar.tsx @@ -10,6 +10,7 @@ import { } from '@ant-design/icons' import { useAssistant } from '@renderer/hooks/useAssistant' import { useSettings } from '@renderer/hooks/useSettings' +import { useShowTopics } from '@renderer/hooks/useStore' import { getDefaultTopic } from '@renderer/services/assistant' import { EVENT_NAMES, EventEmitter } from '@renderer/services/event' import { estimateInputTokenCount } from '@renderer/services/messages' @@ -48,6 +49,7 @@ const Inputbar: FC = ({ assistant, setActiveTopic }) => { const [files, setFiles] = useState([]) const { t } = useTranslation() const containerRef = useRef(null) + const { showTopics, toggleShowTopics } = useShowTopics() _text = text @@ -235,12 +237,22 @@ const Inputbar: FC = ({ assistant, setActiveTopic }) => { - EventEmitter.emit(EVENT_NAMES.SHOW_TOPIC_SIDEBAR)}> + { + !showTopics && toggleShowTopics() + setTimeout(() => EventEmitter.emit(EVENT_NAMES.SHOW_TOPIC_SIDEBAR), 0) + }}> - EventEmitter.emit(EVENT_NAMES.SHOW_CHAT_SETTINGS)}> + { + !showTopics && toggleShowTopics() + setTimeout(() => EventEmitter.emit(EVENT_NAMES.SHOW_CHAT_SETTINGS), 0) + }}> diff --git a/src/renderer/src/pages/home/Navbar.tsx b/src/renderer/src/pages/home/Navbar.tsx index a7b585e4b4..70705698ce 100644 --- a/src/renderer/src/pages/home/Navbar.tsx +++ b/src/renderer/src/pages/home/Navbar.tsx @@ -1,5 +1,4 @@ -import { FormOutlined } from '@ant-design/icons' -import { Navbar, NavbarCenter, NavbarLeft, NavbarRight } from '@renderer/components/app/Navbar' +import { Navbar, NavbarLeft, NavbarRight } from '@renderer/components/app/Navbar' import { HStack } from '@renderer/components/Layout' import AddAssistantPopup from '@renderer/components/Popups/AddAssistantPopup' import AssistantSettingPopup from '@renderer/components/Popups/AssistantSettingPopup' @@ -8,11 +7,9 @@ import { useTheme } from '@renderer/context/ThemeProvider' import { useAssistant } from '@renderer/hooks/useAssistant' import { useSettings } from '@renderer/hooks/useSettings' import { useShowAssistants, useShowTopics } from '@renderer/hooks/useStore' -import { getDefaultTopic } from '@renderer/services/assistant' import { Assistant, Topic } from '@renderer/types' import { Switch } from 'antd' -import { FC, useCallback } from 'react' -import { useTranslation } from 'react-i18next' +import { FC } from 'react' import styled from 'styled-components' import SelectModelButton from './components/SelectModelButton' @@ -21,32 +18,30 @@ interface Props { activeAssistant: Assistant activeTopic: Topic setActiveAssistant: (assistant: Assistant) => void - setActiveTopic: (topic: Topic) => void } -const HeaderNavbar: FC = ({ activeAssistant, setActiveAssistant, setActiveTopic }) => { - const { assistant, addTopic } = useAssistant(activeAssistant.id) - const { t } = useTranslation() +const HeaderNavbar: FC = ({ activeAssistant, setActiveAssistant }) => { + const { assistant } = useAssistant(activeAssistant.id) const { showAssistants, toggleShowAssistants } = useShowAssistants() - const { showTopics, toggleShowTopics } = useShowTopics() const { theme, toggleTheme } = useTheme() const { topicPosition } = useSettings() + const { showTopics, toggleShowTopics } = useShowTopics() const onCreateAssistant = async () => { const assistant = await AddAssistantPopup.show() assistant && setActiveAssistant(assistant) } - const addNewTopic = useCallback(() => { - const topic = getDefaultTopic() - addTopic(topic) - setActiveTopic(topic) - }, [addTopic, setActiveTopic]) - return ( {showAssistants && ( - + @@ -55,34 +50,9 @@ const HeaderNavbar: FC = ({ activeAssistant, setActiveAssistant, setActiv )} - {showTopics && topicPosition === 'left' && ( - - - {!showAssistants && ( - - - - )} - {showAssistants && ( - - {t('chat.topics.title')} ({assistant.topics.length}) - - )} - - - - - - )} - {!showAssistants && (topicPosition === 'left' ? !showTopics : true) && ( + {!showAssistants && ( toggleShowAssistants()} style={{ marginRight: isMac ? 8 : 25, marginLeft: isMac ? 4 : 0 }}> diff --git a/src/renderer/src/pages/home/RightSidebar.tsx b/src/renderer/src/pages/home/RightSidebar.tsx index 84419d177f..edc35d8b24 100644 --- a/src/renderer/src/pages/home/RightSidebar.tsx +++ b/src/renderer/src/pages/home/RightSidebar.tsx @@ -1,78 +1,108 @@ import { BarsOutlined, SettingOutlined } from '@ant-design/icons' +import { useAssistants, useDefaultAssistant } from '@renderer/hooks/useAssistant' import { useSettings } from '@renderer/hooks/useSettings' import { useShowTopics } from '@renderer/hooks/useStore' import { EVENT_NAMES, EventEmitter } from '@renderer/services/event' import { Assistant, Topic } from '@renderer/types' -import { Segmented } from 'antd' +import { uuid } from '@renderer/utils' +import { Segmented, SegmentedProps } from 'antd' import { FC, useEffect, useState } from 'react' import { useTranslation } from 'react-i18next' import styled from 'styled-components' +import Assistants from './Assistants' import Settings from './Settings' import Topics from './Topics' interface Props { - assistant: Assistant + activeAssistant: Assistant activeTopic: Topic + setActiveAssistant: (assistant: Assistant) => void setActiveTopic: (topic: Topic) => void + position: 'left' | 'right' } -const RightSidebar: FC = (props) => { - const [tab, setTab] = useState<'topic' | 'settings'>('topic') - const { showTopics, setShowTopics } = useShowTopics() +const RightSidebar: FC = ({ activeAssistant, activeTopic, setActiveAssistant, setActiveTopic, position }) => { + const { addAssistant } = useAssistants() + const [tab, setTab] = useState<'assistants' | 'topic' | 'settings'>(position === 'left' ? 'assistants' : 'topic') const { topicPosition } = useSettings() + const { defaultAssistant } = useDefaultAssistant() + const { toggleShowTopics } = useShowTopics() + const { t } = useTranslation() - const isTopicTab = tab === 'topic' - const isSettingsTab = tab === 'settings' const borderStyle = '0.5px solid var(--color-border)' + const border = position === 'left' ? { borderRight: borderStyle } : { borderLeft: borderStyle } + + const showTab = !(position === 'left' && topicPosition === 'right') + const assistantTab = { + label: t('common.assistant'), + value: 'assistants', + icon: + } + + const onCreateDefaultAssistant = () => { + const assistant = { ...defaultAssistant, id: uuid() } + addAssistant(assistant) + setActiveAssistant(assistant) + } useEffect(() => { const unsubscribes = [ + EventEmitter.on(EVENT_NAMES.SHOW_ASSISTANTS, (): any => { + showTab && setTab('assistants') + }), EventEmitter.on(EVENT_NAMES.SHOW_TOPIC_SIDEBAR, (): any => { - if (showTopics && isTopicTab) { - return setShowTopics(false) - } - if (showTopics) { - return setTab('topic') - } - setShowTopics(true) - setTab('topic') + showTab && setTab('topic') }), EventEmitter.on(EVENT_NAMES.SHOW_CHAT_SETTINGS, (): any => { - if (showTopics && isSettingsTab) { - return setShowTopics(false) - } - if (showTopics) { - return setTab('settings') - } - setShowTopics(true) - setTab('settings') + showTab && setTab('settings') }), - EventEmitter.on(EVENT_NAMES.SWITCH_TOPIC_SIDEBAR, () => setTab('topic')) + EventEmitter.on(EVENT_NAMES.SWITCH_TOPIC_SIDEBAR, () => { + showTab && setTab('topic') + if (position === 'left' && topicPosition === 'right') { + toggleShowTopics() + } + }) ] return () => unsubscribes.forEach((unsub) => unsub()) - }, [isSettingsTab, isTopicTab, showTopics, setShowTopics]) - - if (!showTopics) { - return null - } + }, [position, showTab, tab, toggleShowTopics, topicPosition]) return ( - - }, - { label: t('settings.title'), value: 'settings', icon: } - ]} - block - onChange={(value) => setTab(value as 'topic' | 'settings')} - /> + + {showTab && ( + }, + { label: t('settings.title'), value: 'settings', icon: } + ].filter(Boolean) as SegmentedProps['options'] + } + onChange={(value) => setTab(value as 'topic' | 'settings')} + block + /> + )} - {tab === 'topic' && } - {tab === 'settings' && } + {tab === 'assistants' && ( + + )} + {tab === 'topic' && ( + + )} + {tab === 'settings' && } ) diff --git a/src/renderer/src/pages/home/Settings.tsx b/src/renderer/src/pages/home/Settings.tsx index 72cd94345c..ae263f4af6 100644 --- a/src/renderer/src/pages/home/Settings.tsx +++ b/src/renderer/src/pages/home/Settings.tsx @@ -236,8 +236,6 @@ const Container = styled.div` flex-direction: column; overflow: hidden; padding-bottom: 10px; - min-width: var(--topic-list-width); - max-width: var(--topic-list-width); padding: 10px 15px; ` diff --git a/src/renderer/src/pages/home/Topics.tsx b/src/renderer/src/pages/home/Topics.tsx index ec404c3cca..bc7403388e 100644 --- a/src/renderer/src/pages/home/Topics.tsx +++ b/src/renderer/src/pages/home/Topics.tsx @@ -6,8 +6,9 @@ import { fetchMessagesSummary } from '@renderer/services/api' import LocalStorage from '@renderer/services/storage' import { useAppSelector } from '@renderer/store' import { Assistant, Topic } from '@renderer/types' -import { Dropdown, MenuProps } from 'antd' -import { FC, useCallback } from 'react' +import { Button, Dropdown, MenuProps } from 'antd' +import { take } from 'lodash' +import { FC, useCallback, useState } from 'react' import { useTranslation } from 'react-i18next' import styled from 'styled-components' @@ -19,6 +20,8 @@ interface Props { const Topics: FC = ({ assistant: _assistant, activeTopic, setActiveTopic }) => { const { assistant, removeTopic, updateTopic, updateTopics } = useAssistant(_assistant.id) + const [showAll, setShowAll] = useState(false) + const [draging, setDraging] = useState(false) const { t } = useTranslation() const generating = useAppSelector((state) => state.runtime.generating) @@ -89,7 +92,11 @@ const Topics: FC = ({ assistant: _assistant, activeTopic, setActiveTopic return ( - + setDraging(true)} + onDragEnd={() => setDraging(false)}> {(topic) => { const isActive = topic.id === activeTopic?.id return ( @@ -101,6 +108,13 @@ const Topics: FC = ({ assistant: _assistant, activeTopic, setActiveTopic ) }} + {!draging && assistant.topics.length > 15 && ( + + )} ) } @@ -110,8 +124,6 @@ const Container = styled.div` flex: 1; flex-direction: column; padding-top: 10px; - min-width: var(--topic-list-width); - max-width: var(--topic-list-width); overflow-y: scroll; height: calc(100vh - var(--navbar-height)); ` @@ -135,4 +147,9 @@ const TopicListItem = styled.div` } ` +const Footer = styled.div` + margin: 0 4px; + margin-bottom: 10px; +` + export default Topics diff --git a/src/renderer/src/services/event.ts b/src/renderer/src/services/event.ts index b7eee8c861..8417545494 100644 --- a/src/renderer/src/services/event.ts +++ b/src/renderer/src/services/event.ts @@ -12,6 +12,7 @@ export const EVENT_NAMES = { REGENERATE_MESSAGE: 'REGENERATE_MESSAGE', CHAT_COMPLETION_PAUSED: 'CHAT_COMPLETION_PAUSED', ESTIMATED_TOKEN_COUNT: 'ESTIMATED_TOKEN_COUNT', + SHOW_ASSISTANTS: 'SHOW_ASSISTANTS', SHOW_CHAT_SETTINGS: 'SHOW_CHAT_SETTINGS', SHOW_TOPIC_SIDEBAR: 'SHOW_TOPIC_SIDEBAR', SWITCH_TOPIC_SIDEBAR: 'SWITCH_TOPIC_SIDEBAR',