feat: Enhanced UI/UX with design updates, i18n, and feature enhancements.
- Updated design styles for segmented tabs and size adjustments for assistive elements. - Added internationalization translations for English and Chinese. - Removed unused import and functionality for switching topics sidebar. - Added functionality to hide or show the right sidebar in the Chat page. - Renamed Assistants component to RightSidebar. - Improved functionality for showing and toggling topics and settings in the input bar. - Removed unused imports and refactored Navbar component layout. - Updated existing right sidebar functionality to allow for custom position and show topic settings. - Removed inline styles for width from Settings component Container styles. - Added new features for managing topics in the home page, including drag and drop functionality, a "show all" button for viewing more topics, and improved handling of large topic lists.
This commit is contained in:
parent
9a502b5e47
commit
200d78a140
@ -48,8 +48,8 @@
|
|||||||
--status-bar-height: 40px;
|
--status-bar-height: 40px;
|
||||||
--input-bar-height: 85px;
|
--input-bar-height: 85px;
|
||||||
|
|
||||||
--assistants-width: 240px;
|
--assistants-width: 280px;
|
||||||
--topic-list-width: 270px;
|
--topic-list-width: 280px;
|
||||||
--settings-width: var(--assistants-width);
|
--settings-width: var(--assistants-width);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -208,3 +208,23 @@ body,
|
|||||||
.ant-drawer-header {
|
.ant-drawer-header {
|
||||||
-webkit-app-region: no-drag;
|
-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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@ -3,6 +3,7 @@ import systemAgents from '@renderer/config/agents.json'
|
|||||||
import { useAgents } from '@renderer/hooks/useAgents'
|
import { useAgents } from '@renderer/hooks/useAgents'
|
||||||
import { useAssistants, useDefaultAssistant } from '@renderer/hooks/useAssistant'
|
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 { Agent, Assistant } from '@renderer/types'
|
import { Agent, Assistant } from '@renderer/types'
|
||||||
import { Input, Modal, Tag } from 'antd'
|
import { Input, Modal, Tag } from 'antd'
|
||||||
import { useMemo, useState } from 'react'
|
import { useMemo, useState } from 'react'
|
||||||
@ -50,6 +51,7 @@ const PopupContainer: React.FC<Props> = ({ resolve }) => {
|
|||||||
const assistant = covertAgentToAssistant(agent)
|
const assistant = covertAgentToAssistant(agent)
|
||||||
|
|
||||||
addAssistant(assistant)
|
addAssistant(assistant)
|
||||||
|
setTimeout(() => EventEmitter.emit(EVENT_NAMES.SHOW_ASSISTANTS), 0)
|
||||||
resolve(assistant)
|
resolve(assistant)
|
||||||
setOpen(false)
|
setOpen(false)
|
||||||
}
|
}
|
||||||
|
|||||||
@ -37,7 +37,9 @@ const resources = {
|
|||||||
add: 'Add',
|
add: 'Add',
|
||||||
added: 'Added',
|
added: 'Added',
|
||||||
manage: 'Manage',
|
manage: 'Manage',
|
||||||
select_model: 'Select Model'
|
select_model: 'Select Model',
|
||||||
|
'show.all': 'Show All',
|
||||||
|
collapse: 'Collapse'
|
||||||
},
|
},
|
||||||
message: {
|
message: {
|
||||||
copied: 'Copied!',
|
copied: 'Copied!',
|
||||||
@ -288,7 +290,9 @@ const resources = {
|
|||||||
add: '添加',
|
add: '添加',
|
||||||
added: '已添加',
|
added: '已添加',
|
||||||
manage: '管理',
|
manage: '管理',
|
||||||
select_model: '选择模型'
|
select_model: '选择模型',
|
||||||
|
'show.all': '显示全部',
|
||||||
|
collapse: '收起'
|
||||||
},
|
},
|
||||||
message: {
|
message: {
|
||||||
copied: '已复制',
|
copied: '已复制',
|
||||||
|
|||||||
@ -3,7 +3,6 @@ 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'
|
||||||
import { useAssistant, useAssistants } from '@renderer/hooks/useAssistant'
|
import { useAssistant, useAssistants } from '@renderer/hooks/useAssistant'
|
||||||
import { useShowTopics } from '@renderer/hooks/useStore'
|
|
||||||
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'
|
||||||
@ -26,7 +25,6 @@ const Assistants: FC<Props> = ({ activeAssistant, setActiveAssistant, onCreateAs
|
|||||||
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, removeAllTopics } = useAssistant(activeAssistant.id)
|
const { updateAssistant, removeAllTopics } = useAssistant(activeAssistant.id)
|
||||||
const { showTopics, toggleShowTopics } = useShowTopics()
|
|
||||||
const { t } = useTranslation()
|
const { t } = useTranslation()
|
||||||
|
|
||||||
const onDelete = useCallback(
|
const onDelete = useCallback(
|
||||||
@ -100,7 +98,6 @@ const Assistants: FC<Props> = ({ activeAssistant, setActiveAssistant, onCreateAs
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
EventEmitter.emit(EVENT_NAMES.SWITCH_TOPIC_SIDEBAR)
|
|
||||||
setActiveAssistant(assistant)
|
setActiveAssistant(assistant)
|
||||||
},
|
},
|
||||||
[generating, setActiveAssistant, t]
|
[generating, setActiveAssistant, t]
|
||||||
@ -116,8 +113,8 @@ const Assistants: FC<Props> = ({ activeAssistant, setActiveAssistant, onCreateAs
|
|||||||
<AssistantItem onClick={() => onSwitchAssistant(assistant)} className={isCurrent ? 'active' : ''}>
|
<AssistantItem onClick={() => onSwitchAssistant(assistant)} className={isCurrent ? 'active' : ''}>
|
||||||
<AssistantName className="name">{assistant.name || t('chat.default.name')}</AssistantName>
|
<AssistantName className="name">{assistant.name || t('chat.default.name')}</AssistantName>
|
||||||
<ArrowRightButton
|
<ArrowRightButton
|
||||||
className={`arrow-button ${isCurrent && showTopics ? 'active' : ''}`}
|
className={`arrow-button ${isCurrent ? 'active' : ''}`}
|
||||||
onClick={() => isCurrent && toggleShowTopics()}>
|
onClick={() => EventEmitter.emit(EVENT_NAMES.SWITCH_TOPIC_SIDEBAR)}>
|
||||||
<i className="iconfont icon-gridlines" />
|
<i className="iconfont icon-gridlines" />
|
||||||
</ArrowRightButton>
|
</ArrowRightButton>
|
||||||
{false && <TopicCount className="topics-count">{assistant.topics.length}</TopicCount>}
|
{false && <TopicCount className="topics-count">{assistant.topics.length}</TopicCount>}
|
||||||
@ -133,9 +130,6 @@ const Assistants: FC<Props> = ({ activeAssistant, setActiveAssistant, onCreateAs
|
|||||||
const Container = styled.div`
|
const Container = styled.div`
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
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));
|
height: calc(100vh - var(--navbar-height));
|
||||||
overflow-y: auto;
|
overflow-y: auto;
|
||||||
padding-top: 10px;
|
padding-top: 10px;
|
||||||
|
|||||||
@ -1,5 +1,6 @@
|
|||||||
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 { useShowTopics } from '@renderer/hooks/useStore'
|
||||||
import { Assistant, Topic } from '@renderer/types'
|
import { Assistant, Topic } from '@renderer/types'
|
||||||
import { Flex } from 'antd'
|
import { Flex } from 'antd'
|
||||||
import { FC } from 'react'
|
import { FC } from 'react'
|
||||||
@ -13,23 +14,28 @@ interface Props {
|
|||||||
assistant: Assistant
|
assistant: Assistant
|
||||||
activeTopic: Topic
|
activeTopic: Topic
|
||||||
setActiveTopic: (topic: Topic) => void
|
setActiveTopic: (topic: Topic) => void
|
||||||
|
setActiveAssistant: (assistant: Assistant) => void
|
||||||
}
|
}
|
||||||
|
|
||||||
const Chat: FC<Props> = (props) => {
|
const Chat: FC<Props> = (props) => {
|
||||||
const { assistant } = useAssistant(props.assistant.id)
|
const { assistant } = useAssistant(props.assistant.id)
|
||||||
const { topicPosition } = useSettings()
|
const { topicPosition } = useSettings()
|
||||||
|
const { showTopics } = useShowTopics()
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Container id="chat">
|
<Container id="chat">
|
||||||
{topicPosition === 'left' && (
|
|
||||||
<RightSidebar assistant={assistant} activeTopic={props.activeTopic} setActiveTopic={props.setActiveTopic} />
|
|
||||||
)}
|
|
||||||
<Main vertical flex={1} justify="space-between">
|
<Main vertical flex={1} justify="space-between">
|
||||||
<Messages assistant={assistant} topic={props.activeTopic} setActiveTopic={props.setActiveTopic} />
|
<Messages assistant={assistant} topic={props.activeTopic} setActiveTopic={props.setActiveTopic} />
|
||||||
<Inputbar assistant={assistant} setActiveTopic={props.setActiveTopic} />
|
<Inputbar assistant={assistant} setActiveTopic={props.setActiveTopic} />
|
||||||
</Main>
|
</Main>
|
||||||
{topicPosition === 'right' && (
|
{topicPosition === 'right' && showTopics && (
|
||||||
<RightSidebar assistant={assistant} activeTopic={props.activeTopic} setActiveTopic={props.setActiveTopic} />
|
<RightSidebar
|
||||||
|
activeAssistant={assistant}
|
||||||
|
activeTopic={props.activeTopic}
|
||||||
|
setActiveAssistant={props.setActiveAssistant}
|
||||||
|
setActiveTopic={props.setActiveTopic}
|
||||||
|
position="right"
|
||||||
|
/>
|
||||||
)}
|
)}
|
||||||
</Container>
|
</Container>
|
||||||
)
|
)
|
||||||
|
|||||||
@ -1,54 +1,48 @@
|
|||||||
import { useAssistants, useDefaultAssistant } from '@renderer/hooks/useAssistant'
|
import { useAssistants } from '@renderer/hooks/useAssistant'
|
||||||
import { useShowAssistants } from '@renderer/hooks/useStore'
|
import { useShowAssistants } from '@renderer/hooks/useStore'
|
||||||
import { useActiveTopic } from '@renderer/hooks/useTopic'
|
import { useActiveTopic } from '@renderer/hooks/useTopic'
|
||||||
import { Assistant, Topic } from '@renderer/types'
|
import { Assistant, Topic } from '@renderer/types'
|
||||||
import { uuid } from '@renderer/utils'
|
|
||||||
import { FC, useState } from 'react'
|
import { FC, useState } from 'react'
|
||||||
import styled from 'styled-components'
|
import styled from 'styled-components'
|
||||||
|
|
||||||
import Assistants from './Assistants'
|
|
||||||
import Chat from './Chat'
|
import Chat from './Chat'
|
||||||
import Navbar from './Navbar'
|
import Navbar from './Navbar'
|
||||||
|
import RightSidebar from './RightSidebar'
|
||||||
|
|
||||||
let _activeAssistant: Assistant
|
let _activeAssistant: Assistant
|
||||||
|
|
||||||
const HomePage: FC = () => {
|
const HomePage: FC = () => {
|
||||||
const { assistants, addAssistant } = useAssistants()
|
const { assistants } = useAssistants()
|
||||||
const [activeAssistant, setActiveAssistant] = useState(_activeAssistant || assistants[0])
|
const [activeAssistant, setActiveAssistant] = useState(_activeAssistant || assistants[0])
|
||||||
const { showAssistants } = useShowAssistants()
|
const { showAssistants } = useShowAssistants()
|
||||||
const { defaultAssistant } = useDefaultAssistant()
|
|
||||||
|
|
||||||
const { activeTopic, setActiveTopic } = useActiveTopic(activeAssistant)
|
const { activeTopic, setActiveTopic } = useActiveTopic(activeAssistant)
|
||||||
|
|
||||||
_activeAssistant = activeAssistant
|
_activeAssistant = activeAssistant
|
||||||
|
|
||||||
const onCreateDefaultAssistant = () => {
|
|
||||||
const assistant = { ...defaultAssistant, id: uuid() }
|
|
||||||
addAssistant(assistant)
|
|
||||||
setActiveAssistant(assistant)
|
|
||||||
}
|
|
||||||
|
|
||||||
const onSetActiveTopic = (topic: Topic) => {
|
const onSetActiveTopic = (topic: Topic) => {
|
||||||
setActiveTopic(topic)
|
setActiveTopic(topic)
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Container>
|
<Container>
|
||||||
<Navbar
|
<Navbar activeAssistant={activeAssistant} setActiveAssistant={setActiveAssistant} activeTopic={activeTopic} />
|
||||||
activeAssistant={activeAssistant}
|
|
||||||
setActiveAssistant={setActiveAssistant}
|
|
||||||
activeTopic={activeTopic}
|
|
||||||
setActiveTopic={onSetActiveTopic}
|
|
||||||
/>
|
|
||||||
<ContentContainer>
|
<ContentContainer>
|
||||||
{showAssistants && (
|
{showAssistants && (
|
||||||
<Assistants
|
<RightSidebar
|
||||||
activeAssistant={activeAssistant}
|
activeAssistant={activeAssistant}
|
||||||
|
activeTopic={activeTopic}
|
||||||
setActiveAssistant={setActiveAssistant}
|
setActiveAssistant={setActiveAssistant}
|
||||||
onCreateAssistant={onCreateDefaultAssistant}
|
setActiveTopic={setActiveTopic}
|
||||||
|
position="left"
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
<Chat assistant={activeAssistant} activeTopic={activeTopic} setActiveTopic={onSetActiveTopic} />
|
<Chat
|
||||||
|
assistant={activeAssistant}
|
||||||
|
activeTopic={activeTopic}
|
||||||
|
setActiveTopic={onSetActiveTopic}
|
||||||
|
setActiveAssistant={setActiveAssistant}
|
||||||
|
/>
|
||||||
</ContentContainer>
|
</ContentContainer>
|
||||||
</Container>
|
</Container>
|
||||||
)
|
)
|
||||||
|
|||||||
@ -10,6 +10,7 @@ import {
|
|||||||
} from '@ant-design/icons'
|
} from '@ant-design/icons'
|
||||||
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 { useShowTopics } from '@renderer/hooks/useStore'
|
||||||
import { getDefaultTopic } from '@renderer/services/assistant'
|
import { getDefaultTopic } from '@renderer/services/assistant'
|
||||||
import { EVENT_NAMES, EventEmitter } from '@renderer/services/event'
|
import { EVENT_NAMES, EventEmitter } from '@renderer/services/event'
|
||||||
import { estimateInputTokenCount } from '@renderer/services/messages'
|
import { estimateInputTokenCount } from '@renderer/services/messages'
|
||||||
@ -48,6 +49,7 @@ const Inputbar: FC<Props> = ({ assistant, setActiveTopic }) => {
|
|||||||
const [files, setFiles] = useState<File[]>([])
|
const [files, setFiles] = useState<File[]>([])
|
||||||
const { t } = useTranslation()
|
const { t } = useTranslation()
|
||||||
const containerRef = useRef(null)
|
const containerRef = useRef(null)
|
||||||
|
const { showTopics, toggleShowTopics } = useShowTopics()
|
||||||
|
|
||||||
_text = text
|
_text = text
|
||||||
|
|
||||||
@ -235,12 +237,22 @@ const Inputbar: FC<Props> = ({ assistant, setActiveTopic }) => {
|
|||||||
</Popconfirm>
|
</Popconfirm>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
<Tooltip placement="top" title={t('chat.input.topics')} arrow>
|
<Tooltip placement="top" title={t('chat.input.topics')} arrow>
|
||||||
<ToolbarButton type="text" onClick={() => EventEmitter.emit(EVENT_NAMES.SHOW_TOPIC_SIDEBAR)}>
|
<ToolbarButton
|
||||||
|
type="text"
|
||||||
|
onClick={() => {
|
||||||
|
!showTopics && toggleShowTopics()
|
||||||
|
setTimeout(() => EventEmitter.emit(EVENT_NAMES.SHOW_TOPIC_SIDEBAR), 0)
|
||||||
|
}}>
|
||||||
<HistoryOutlined />
|
<HistoryOutlined />
|
||||||
</ToolbarButton>
|
</ToolbarButton>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
<Tooltip placement="top" title={t('chat.input.settings')} arrow>
|
<Tooltip placement="top" title={t('chat.input.settings')} arrow>
|
||||||
<ToolbarButton type="text" onClick={() => EventEmitter.emit(EVENT_NAMES.SHOW_CHAT_SETTINGS)}>
|
<ToolbarButton
|
||||||
|
type="text"
|
||||||
|
onClick={() => {
|
||||||
|
!showTopics && toggleShowTopics()
|
||||||
|
setTimeout(() => EventEmitter.emit(EVENT_NAMES.SHOW_CHAT_SETTINGS), 0)
|
||||||
|
}}>
|
||||||
<ControlOutlined />
|
<ControlOutlined />
|
||||||
</ToolbarButton>
|
</ToolbarButton>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
|
|||||||
@ -1,5 +1,4 @@
|
|||||||
import { FormOutlined } from '@ant-design/icons'
|
import { Navbar, NavbarLeft, NavbarRight } from '@renderer/components/app/Navbar'
|
||||||
import { Navbar, NavbarCenter, 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 AddAssistantPopup from '@renderer/components/Popups/AddAssistantPopup'
|
||||||
import AssistantSettingPopup from '@renderer/components/Popups/AssistantSettingPopup'
|
import AssistantSettingPopup from '@renderer/components/Popups/AssistantSettingPopup'
|
||||||
@ -8,11 +7,9 @@ import { useTheme } from '@renderer/context/ThemeProvider'
|
|||||||
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 { getDefaultTopic } 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 } 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'
|
||||||
@ -21,32 +18,30 @@ interface Props {
|
|||||||
activeAssistant: Assistant
|
activeAssistant: Assistant
|
||||||
activeTopic: Topic
|
activeTopic: Topic
|
||||||
setActiveAssistant: (assistant: Assistant) => void
|
setActiveAssistant: (assistant: Assistant) => void
|
||||||
setActiveTopic: (topic: Topic) => void
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const HeaderNavbar: FC<Props> = ({ activeAssistant, setActiveAssistant, setActiveTopic }) => {
|
const HeaderNavbar: FC<Props> = ({ activeAssistant, setActiveAssistant }) => {
|
||||||
const { assistant, addTopic } = useAssistant(activeAssistant.id)
|
const { assistant } = useAssistant(activeAssistant.id)
|
||||||
const { t } = useTranslation()
|
|
||||||
const { showAssistants, toggleShowAssistants } = useShowAssistants()
|
const { showAssistants, toggleShowAssistants } = useShowAssistants()
|
||||||
const { showTopics, toggleShowTopics } = useShowTopics()
|
|
||||||
const { theme, toggleTheme } = useTheme()
|
const { theme, toggleTheme } = useTheme()
|
||||||
const { topicPosition } = useSettings()
|
const { topicPosition } = useSettings()
|
||||||
|
const { showTopics, toggleShowTopics } = useShowTopics()
|
||||||
|
|
||||||
const onCreateAssistant = async () => {
|
const onCreateAssistant = async () => {
|
||||||
const assistant = await AddAssistantPopup.show()
|
const assistant = await AddAssistantPopup.show()
|
||||||
assistant && setActiveAssistant(assistant)
|
assistant && setActiveAssistant(assistant)
|
||||||
}
|
}
|
||||||
|
|
||||||
const addNewTopic = useCallback(() => {
|
|
||||||
const topic = getDefaultTopic()
|
|
||||||
addTopic(topic)
|
|
||||||
setActiveTopic(topic)
|
|
||||||
}, [addTopic, setActiveTopic])
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Navbar>
|
<Navbar>
|
||||||
{showAssistants && (
|
{showAssistants && (
|
||||||
<NavbarLeft style={{ justifyContent: 'space-between', borderRight: 'none', padding: '0 8px' }}>
|
<NavbarLeft
|
||||||
|
style={{
|
||||||
|
justifyContent: 'space-between',
|
||||||
|
borderRight: 'none',
|
||||||
|
padding: '0 8px',
|
||||||
|
width: topicPosition === 'left' ? '300px' : 'var(--assistants-width)'
|
||||||
|
}}>
|
||||||
<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>
|
||||||
@ -55,34 +50,9 @@ const HeaderNavbar: FC<Props> = ({ activeAssistant, setActiveAssistant, setActiv
|
|||||||
</NewButton>
|
</NewButton>
|
||||||
</NavbarLeft>
|
</NavbarLeft>
|
||||||
)}
|
)}
|
||||||
{showTopics && topicPosition === 'left' && (
|
|
||||||
<NavbarCenter
|
|
||||||
style={{
|
|
||||||
paddingLeft: isMac && !showAssistants ? 16 : 8,
|
|
||||||
paddingRight: 8,
|
|
||||||
maxWidth: 'var(--topic-list-width)',
|
|
||||||
justifyContent: 'space-between'
|
|
||||||
}}>
|
|
||||||
<HStack alignItems="center">
|
|
||||||
{!showAssistants && (
|
|
||||||
<NewButton onClick={toggleShowAssistants} style={{ marginRight: isMac ? 8 : 25 }}>
|
|
||||||
<i className="iconfont icon-show-sidebar" />
|
|
||||||
</NewButton>
|
|
||||||
)}
|
|
||||||
{showAssistants && (
|
|
||||||
<TitleText>
|
|
||||||
{t('chat.topics.title')} ({assistant.topics.length})
|
|
||||||
</TitleText>
|
|
||||||
)}
|
|
||||||
</HStack>
|
|
||||||
<NewButton onClick={addNewTopic}>
|
|
||||||
<FormOutlined />
|
|
||||||
</NewButton>
|
|
||||||
</NavbarCenter>
|
|
||||||
)}
|
|
||||||
<NavbarRight style={{ justifyContent: 'space-between', paddingRight: isWindows ? 140 : 12, flex: 1 }}>
|
<NavbarRight style={{ justifyContent: 'space-between', paddingRight: isWindows ? 140 : 12, flex: 1 }}>
|
||||||
<HStack alignItems="center">
|
<HStack alignItems="center">
|
||||||
{!showAssistants && (topicPosition === 'left' ? !showTopics : true) && (
|
{!showAssistants && (
|
||||||
<NewButton
|
<NewButton
|
||||||
onClick={() => toggleShowAssistants()}
|
onClick={() => toggleShowAssistants()}
|
||||||
style={{ marginRight: isMac ? 8 : 25, marginLeft: isMac ? 4 : 0 }}>
|
style={{ marginRight: isMac ? 8 : 25, marginLeft: isMac ? 4 : 0 }}>
|
||||||
|
|||||||
@ -1,78 +1,108 @@
|
|||||||
import { BarsOutlined, SettingOutlined } from '@ant-design/icons'
|
import { BarsOutlined, SettingOutlined } from '@ant-design/icons'
|
||||||
|
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'
|
||||||
import { EVENT_NAMES, EventEmitter } from '@renderer/services/event'
|
import { EVENT_NAMES, EventEmitter } from '@renderer/services/event'
|
||||||
import { Assistant, Topic } from '@renderer/types'
|
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 { FC, useEffect, useState } from 'react'
|
||||||
import { useTranslation } from 'react-i18next'
|
import { useTranslation } from 'react-i18next'
|
||||||
import styled from 'styled-components'
|
import styled from 'styled-components'
|
||||||
|
|
||||||
|
import Assistants from './Assistants'
|
||||||
import Settings from './Settings'
|
import Settings from './Settings'
|
||||||
import Topics from './Topics'
|
import Topics from './Topics'
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
assistant: Assistant
|
activeAssistant: Assistant
|
||||||
activeTopic: Topic
|
activeTopic: Topic
|
||||||
|
setActiveAssistant: (assistant: Assistant) => void
|
||||||
setActiveTopic: (topic: Topic) => void
|
setActiveTopic: (topic: Topic) => void
|
||||||
|
position: 'left' | 'right'
|
||||||
}
|
}
|
||||||
|
|
||||||
const RightSidebar: FC<Props> = (props) => {
|
const RightSidebar: FC<Props> = ({ activeAssistant, activeTopic, setActiveAssistant, setActiveTopic, position }) => {
|
||||||
const [tab, setTab] = useState<'topic' | 'settings'>('topic')
|
const { addAssistant } = useAssistants()
|
||||||
const { showTopics, setShowTopics } = useShowTopics()
|
const [tab, setTab] = useState<'assistants' | 'topic' | 'settings'>(position === 'left' ? 'assistants' : 'topic')
|
||||||
const { topicPosition } = useSettings()
|
const { topicPosition } = useSettings()
|
||||||
|
const { defaultAssistant } = useDefaultAssistant()
|
||||||
|
const { toggleShowTopics } = useShowTopics()
|
||||||
|
|
||||||
const { t } = useTranslation()
|
const { t } = useTranslation()
|
||||||
|
|
||||||
const isTopicTab = tab === 'topic'
|
|
||||||
const isSettingsTab = tab === 'settings'
|
|
||||||
const borderStyle = '0.5px solid var(--color-border)'
|
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: <i className="iconfont icon-business-smart-assistant" />
|
||||||
|
}
|
||||||
|
|
||||||
|
const onCreateDefaultAssistant = () => {
|
||||||
|
const assistant = { ...defaultAssistant, id: uuid() }
|
||||||
|
addAssistant(assistant)
|
||||||
|
setActiveAssistant(assistant)
|
||||||
|
}
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const unsubscribes = [
|
const unsubscribes = [
|
||||||
|
EventEmitter.on(EVENT_NAMES.SHOW_ASSISTANTS, (): any => {
|
||||||
|
showTab && setTab('assistants')
|
||||||
|
}),
|
||||||
EventEmitter.on(EVENT_NAMES.SHOW_TOPIC_SIDEBAR, (): any => {
|
EventEmitter.on(EVENT_NAMES.SHOW_TOPIC_SIDEBAR, (): any => {
|
||||||
if (showTopics && isTopicTab) {
|
showTab && setTab('topic')
|
||||||
return setShowTopics(false)
|
|
||||||
}
|
|
||||||
if (showTopics) {
|
|
||||||
return setTab('topic')
|
|
||||||
}
|
|
||||||
setShowTopics(true)
|
|
||||||
setTab('topic')
|
|
||||||
}),
|
}),
|
||||||
EventEmitter.on(EVENT_NAMES.SHOW_CHAT_SETTINGS, (): any => {
|
EventEmitter.on(EVENT_NAMES.SHOW_CHAT_SETTINGS, (): any => {
|
||||||
if (showTopics && isSettingsTab) {
|
showTab && setTab('settings')
|
||||||
return setShowTopics(false)
|
|
||||||
}
|
|
||||||
if (showTopics) {
|
|
||||||
return setTab('settings')
|
|
||||||
}
|
|
||||||
setShowTopics(true)
|
|
||||||
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())
|
return () => unsubscribes.forEach((unsub) => unsub())
|
||||||
}, [isSettingsTab, isTopicTab, showTopics, setShowTopics])
|
}, [position, showTab, tab, toggleShowTopics, topicPosition])
|
||||||
|
|
||||||
if (!showTopics) {
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Container style={topicPosition === 'left' ? { borderRight: borderStyle } : { borderLeft: borderStyle }}>
|
<Container style={{ ...border, width: topicPosition === 'left' ? '300px' : 'var(--assistants-width)' }}>
|
||||||
<Segmented
|
{showTab && (
|
||||||
value={tab}
|
<Segmented
|
||||||
style={{ borderRadius: 0, padding: '10px', gap: 5, borderBottom: '0.5px solid var(--color-border)' }}
|
value={tab}
|
||||||
options={[
|
className="segmented-tab"
|
||||||
{ label: t('common.topics'), value: 'topic', icon: <BarsOutlined /> },
|
style={{
|
||||||
{ label: t('settings.title'), value: 'settings', icon: <SettingOutlined /> }
|
borderRadius: 0,
|
||||||
]}
|
padding: '10px',
|
||||||
block
|
gap: 5,
|
||||||
onChange={(value) => setTab(value as 'topic' | 'settings')}
|
borderBottom: '0.5px solid var(--color-border)'
|
||||||
/>
|
}}
|
||||||
|
options={
|
||||||
|
[
|
||||||
|
position === 'left' && topicPosition === 'left' ? assistantTab : undefined,
|
||||||
|
{ label: t('common.topics'), value: 'topic', icon: <BarsOutlined /> },
|
||||||
|
{ label: t('settings.title'), value: 'settings', icon: <SettingOutlined /> }
|
||||||
|
].filter(Boolean) as SegmentedProps['options']
|
||||||
|
}
|
||||||
|
onChange={(value) => setTab(value as 'topic' | 'settings')}
|
||||||
|
block
|
||||||
|
/>
|
||||||
|
)}
|
||||||
<TabContent>
|
<TabContent>
|
||||||
{tab === 'topic' && <Topics {...props} />}
|
{tab === 'assistants' && (
|
||||||
{tab === 'settings' && <Settings assistant={props.assistant} />}
|
<Assistants
|
||||||
|
activeAssistant={activeAssistant}
|
||||||
|
setActiveAssistant={setActiveAssistant}
|
||||||
|
onCreateAssistant={onCreateDefaultAssistant}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
{tab === 'topic' && (
|
||||||
|
<Topics assistant={activeAssistant} activeTopic={activeTopic} setActiveTopic={setActiveTopic} />
|
||||||
|
)}
|
||||||
|
{tab === 'settings' && <Settings assistant={activeAssistant} />}
|
||||||
</TabContent>
|
</TabContent>
|
||||||
</Container>
|
</Container>
|
||||||
)
|
)
|
||||||
|
|||||||
@ -236,8 +236,6 @@ const Container = styled.div`
|
|||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
padding-bottom: 10px;
|
padding-bottom: 10px;
|
||||||
min-width: var(--topic-list-width);
|
|
||||||
max-width: var(--topic-list-width);
|
|
||||||
padding: 10px 15px;
|
padding: 10px 15px;
|
||||||
`
|
`
|
||||||
|
|
||||||
|
|||||||
@ -6,8 +6,9 @@ import { fetchMessagesSummary } from '@renderer/services/api'
|
|||||||
import LocalStorage from '@renderer/services/storage'
|
import LocalStorage from '@renderer/services/storage'
|
||||||
import { useAppSelector } from '@renderer/store'
|
import { useAppSelector } from '@renderer/store'
|
||||||
import { Assistant, Topic } from '@renderer/types'
|
import { Assistant, Topic } from '@renderer/types'
|
||||||
import { Dropdown, MenuProps } from 'antd'
|
import { Button, Dropdown, MenuProps } from 'antd'
|
||||||
import { FC, useCallback } from 'react'
|
import { take } from 'lodash'
|
||||||
|
import { FC, useCallback, useState } from 'react'
|
||||||
import { useTranslation } from 'react-i18next'
|
import { useTranslation } from 'react-i18next'
|
||||||
import styled from 'styled-components'
|
import styled from 'styled-components'
|
||||||
|
|
||||||
@ -19,6 +20,8 @@ interface Props {
|
|||||||
|
|
||||||
const Topics: 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 [showAll, setShowAll] = useState(false)
|
||||||
|
const [draging, setDraging] = useState(false)
|
||||||
const { t } = useTranslation()
|
const { t } = useTranslation()
|
||||||
const generating = useAppSelector((state) => state.runtime.generating)
|
const generating = useAppSelector((state) => state.runtime.generating)
|
||||||
|
|
||||||
@ -89,7 +92,11 @@ const Topics: FC<Props> = ({ assistant: _assistant, activeTopic, setActiveTopic
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<Container>
|
<Container>
|
||||||
<DragableList list={assistant.topics} onUpdate={updateTopics}>
|
<DragableList
|
||||||
|
list={take(assistant.topics, showAll ? assistant.topics.length : 15)}
|
||||||
|
onUpdate={updateTopics}
|
||||||
|
onDragStart={() => setDraging(true)}
|
||||||
|
onDragEnd={() => setDraging(false)}>
|
||||||
{(topic) => {
|
{(topic) => {
|
||||||
const isActive = topic.id === activeTopic?.id
|
const isActive = topic.id === activeTopic?.id
|
||||||
return (
|
return (
|
||||||
@ -101,6 +108,13 @@ const Topics: FC<Props> = ({ assistant: _assistant, activeTopic, setActiveTopic
|
|||||||
)
|
)
|
||||||
}}
|
}}
|
||||||
</DragableList>
|
</DragableList>
|
||||||
|
{!draging && assistant.topics.length > 15 && (
|
||||||
|
<Footer>
|
||||||
|
<Button type="link" onClick={() => setShowAll(!showAll)}>
|
||||||
|
{showAll ? t('button.collapse') : t('button.show.all')}
|
||||||
|
</Button>
|
||||||
|
</Footer>
|
||||||
|
)}
|
||||||
</Container>
|
</Container>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@ -110,8 +124,6 @@ const Container = styled.div`
|
|||||||
flex: 1;
|
flex: 1;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
padding-top: 10px;
|
padding-top: 10px;
|
||||||
min-width: var(--topic-list-width);
|
|
||||||
max-width: var(--topic-list-width);
|
|
||||||
overflow-y: scroll;
|
overflow-y: scroll;
|
||||||
height: calc(100vh - var(--navbar-height));
|
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
|
export default Topics
|
||||||
|
|||||||
@ -12,6 +12,7 @@ export const EVENT_NAMES = {
|
|||||||
REGENERATE_MESSAGE: 'REGENERATE_MESSAGE',
|
REGENERATE_MESSAGE: 'REGENERATE_MESSAGE',
|
||||||
CHAT_COMPLETION_PAUSED: 'CHAT_COMPLETION_PAUSED',
|
CHAT_COMPLETION_PAUSED: 'CHAT_COMPLETION_PAUSED',
|
||||||
ESTIMATED_TOKEN_COUNT: 'ESTIMATED_TOKEN_COUNT',
|
ESTIMATED_TOKEN_COUNT: 'ESTIMATED_TOKEN_COUNT',
|
||||||
|
SHOW_ASSISTANTS: 'SHOW_ASSISTANTS',
|
||||||
SHOW_CHAT_SETTINGS: 'SHOW_CHAT_SETTINGS',
|
SHOW_CHAT_SETTINGS: 'SHOW_CHAT_SETTINGS',
|
||||||
SHOW_TOPIC_SIDEBAR: 'SHOW_TOPIC_SIDEBAR',
|
SHOW_TOPIC_SIDEBAR: 'SHOW_TOPIC_SIDEBAR',
|
||||||
SWITCH_TOPIC_SIDEBAR: 'SWITCH_TOPIC_SIDEBAR',
|
SWITCH_TOPIC_SIDEBAR: 'SWITCH_TOPIC_SIDEBAR',
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user