revert: fold topics

This commit is contained in:
kangfenmao 2024-09-04 15:37:39 +08:00
parent 563472f3a9
commit ce830b692b
15 changed files with 302 additions and 218 deletions

View File

@ -36,8 +36,8 @@
--color-error: #f44336; --color-error: #f44336;
--color-link: #1677ff; --color-link: #1677ff;
--color-code-background: #323232; --color-code-background: #323232;
--color-scrollbar-thumb: rgba(255, 255, 255, 0.15); --color-scrollbar-thumb: rgba(255, 255, 255, 0.08);
--color-scrollbar-thumb-hover: rgba(255, 255, 255, 0.3); --color-scrollbar-thumb-hover: rgba(255, 255, 255, 0.15);
--navbar-background-mac: rgba(30, 30, 30, 0.8); --navbar-background-mac: rgba(30, 30, 30, 0.8);
--navbar-background: rgba(30, 30, 30); --navbar-background: rgba(30, 30, 30);
@ -48,8 +48,8 @@
--status-bar-height: 40px; --status-bar-height: 40px;
--input-bar-height: 85px; --input-bar-height: 85px;
--assistants-width: 280px; --assistants-width: 250px;
--topic-list-width: 280px; --topic-list-width: 250px;
--settings-width: var(--assistants-width); --settings-width: var(--assistants-width);
} }
@ -86,8 +86,8 @@ body[theme-mode='light'] {
--color-error: #f44336; --color-error: #f44336;
--color-link: #1677ff; --color-link: #1677ff;
--color-code-background: #e3e3e3; --color-code-background: #e3e3e3;
--color-scrollbar-thumb: rgba(0, 0, 0, 0.15); --color-scrollbar-thumb: rgba(0, 0, 0, 0.08);
--color-scrollbar-thumb-hover: rgba(0, 0, 0, 0.3); --color-scrollbar-thumb-hover: rgba(0, 0, 0, 0.15);
--navbar-background-mac: rgba(255, 255, 255, 0.75); --navbar-background-mac: rgba(255, 255, 255, 0.75);
--navbar-background: rgba(255, 255, 255); --navbar-background: rgba(255, 255, 255);

View File

@ -1,7 +1,7 @@
/* 全局初始化滚动条样式 */ /* 全局初始化滚动条样式 */
::-webkit-scrollbar { ::-webkit-scrollbar {
width: 3px; width: 2px;
height: 3px; height: 2px;
} }
::-webkit-scrollbar-track { ::-webkit-scrollbar-track {
@ -10,7 +10,6 @@
::-webkit-scrollbar-thumb { ::-webkit-scrollbar-thumb {
background: var(--color-scrollbar-thumb); background: var(--color-scrollbar-thumb);
border-radius: 5px;
&:hover { &:hover {
background: var(--color-scrollbar-thumb-hover); background: var(--color-scrollbar-thumb-hover);
} }

View File

@ -4,13 +4,14 @@ import { FC } from 'react'
interface Props<T> { interface Props<T> {
list: T[] list: T[]
style?: React.CSSProperties
children: (item: T, index: number) => React.ReactNode children: (item: T, index: number) => React.ReactNode
onUpdate: (list: T[]) => void onUpdate: (list: T[]) => void
onDragStart?: () => void onDragStart?: () => void
onDragEnd?: () => void onDragEnd?: () => void
} }
const DragableList: FC<Props<any>> = ({ children, list, onDragStart, onUpdate, onDragEnd }) => { const DragableList: FC<Props<any>> = ({ children, list, style, onDragStart, onUpdate, onDragEnd }) => {
const _onDragEnd = (result: DropResult) => { const _onDragEnd = (result: DropResult) => {
onDragEnd?.() onDragEnd?.()
if (result.destination) { if (result.destination) {
@ -33,7 +34,7 @@ const DragableList: FC<Props<any>> = ({ children, list, onDragStart, onUpdate, o
ref={provided.innerRef} ref={provided.innerRef}
{...provided.draggableProps} {...provided.draggableProps}
{...provided.dragHandleProps} {...provided.dragHandleProps}
style={{ ...provided.draggableProps.style, marginBottom: 8 }}> style={{ ...provided.draggableProps.style, marginBottom: 8, ...style }}>
{children(item, index)} {children(item, index)}
</div> </div>
)} )}

View File

@ -34,7 +34,14 @@ const AssistantSettingPopupContainer: React.FC<Props> = ({ assistant, resolve })
} }
return ( return (
<Modal title={assistant.name} open={open} onOk={onOk} onCancel={handleCancel} afterClose={onClose}> <Modal
title={assistant.name}
open={open}
onOk={onOk}
onCancel={handleCancel}
afterClose={onClose}
transitionName="ant-move-down"
maskTransitionName="ant-fade">
<Box mb={8}>{t('common.name')}</Box> <Box mb={8}>{t('common.name')}</Box>
<Input <Input
placeholder={t('common.assistant') + t('common.name')} placeholder={t('common.assistant') + t('common.name')}

View File

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

View File

@ -70,6 +70,7 @@ const resources = {
'topics.delete.all.content': 'Are you sure you want to delete all topics?', 'topics.delete.all.content': 'Are you sure you want to delete all topics?',
'topics.list': 'Topic List', 'topics.list': 'Topic List',
'input.new_topic': 'New Topic', 'input.new_topic': 'New Topic',
'input.topics': ' Topics ',
'input.clear': 'Clear', 'input.clear': 'Clear',
'input.expand': 'Expand', 'input.expand': 'Expand',
'input.collapse': 'Collapse', 'input.collapse': 'Collapse',
@ -315,6 +316,7 @@ const resources = {
'topics.delete.all.content': '确定要删除所有话题吗?', 'topics.delete.all.content': '确定要删除所有话题吗?',
'topics.list': '话题列表', 'topics.list': '话题列表',
'input.new_topic': '新话题', 'input.new_topic': '新话题',
'input.topics': ' 话题 ',
'input.clear': '清除', 'input.clear': '清除',
'input.expand': '展开', 'input.expand': '展开',
'input.collapse': '收起', 'input.collapse': '收起',

View File

@ -5,36 +5,22 @@ import { useAssistant, useAssistants } from '@renderer/hooks/useAssistant'
import { getDefaultTopic, syncAsistantToAgent } from '@renderer/services/assistant' import { getDefaultTopic, syncAsistantToAgent } from '@renderer/services/assistant'
import { EVENT_NAMES, EventEmitter } from '@renderer/services/event' import { EVENT_NAMES, EventEmitter } from '@renderer/services/event'
import { useAppSelector } from '@renderer/store' import { useAppSelector } from '@renderer/store'
import { Assistant, Topic } from '@renderer/types' import { Assistant } from '@renderer/types'
import { uuid } from '@renderer/utils' import { uuid } from '@renderer/utils'
import { Dropdown, Tooltip } from 'antd' import { Dropdown } from 'antd'
import { ItemType } from 'antd/es/menu/interface' import { ItemType } from 'antd/es/menu/interface'
import { last } from 'lodash' import { last } from 'lodash'
import { FC, useCallback } from 'react' import { FC, useCallback } from 'react'
import { useTranslation } from 'react-i18next' import { useTranslation } from 'react-i18next'
import styled from 'styled-components' import styled from 'styled-components'
import Topics from './Topics'
interface Props { interface Props {
activeAssistant: Assistant activeAssistant: Assistant
setActiveAssistant: (assistant: Assistant) => void setActiveAssistant: (assistant: Assistant) => void
activeTopic: Topic
setActiveTopic: (topic: Topic) => void
showTopics: boolean
setShowTopics: (showTopics: boolean) => void
onCreateAssistant: () => void onCreateAssistant: () => void
} }
const Assistants: FC<Props> = ({ const Assistants: FC<Props> = ({ activeAssistant, setActiveAssistant, onCreateAssistant }) => {
activeAssistant,
setActiveAssistant,
activeTopic,
setActiveTopic,
showTopics,
setShowTopics,
onCreateAssistant
}) => {
const { assistants, removeAssistant, addAssistant, updateAssistants } = useAssistants() const { assistants, removeAssistant, addAssistant, updateAssistants } = useAssistants()
const generating = useAppSelector((state) => state.runtime.generating) const generating = useAppSelector((state) => state.runtime.generating)
const { updateAssistant } = useAssistant(activeAssistant.id) const { updateAssistant } = useAssistant(activeAssistant.id)
@ -49,6 +35,15 @@ const Assistants: FC<Props> = ({
[assistants, onCreateAssistant, removeAssistant, setActiveAssistant] [assistants, onCreateAssistant, removeAssistant, setActiveAssistant]
) )
const onEditAssistant = useCallback(
async (assistant: Assistant) => {
const _assistant = await AssistantSettingPopup.show({ assistant })
updateAssistant(_assistant)
syncAsistantToAgent(_assistant)
},
[updateAssistant]
)
const getMenuItems = useCallback( const getMenuItems = useCallback(
(assistant: Assistant) => (assistant: Assistant) =>
[ [
@ -56,11 +51,7 @@ const Assistants: FC<Props> = ({
label: t('common.edit'), label: t('common.edit'),
key: 'edit', key: 'edit',
icon: <EditOutlined />, icon: <EditOutlined />,
async onClick() { onClick: () => onEditAssistant(assistant)
const _assistant = await AssistantSettingPopup.show({ assistant })
updateAssistant(_assistant)
syncAsistantToAgent(_assistant)
}
}, },
{ {
label: t('common.duplicate'), label: t('common.duplicate'),
@ -81,7 +72,7 @@ const Assistants: FC<Props> = ({
onClick: () => onDelete(assistant) onClick: () => onDelete(assistant)
} }
] as ItemType[], ] as ItemType[],
[addAssistant, onDelete, setActiveAssistant, t, updateAssistant] [addAssistant, onDelete, onEditAssistant, setActiveAssistant, t]
) )
const onSwitchAssistant = useCallback( const onSwitchAssistant = useCallback(
@ -93,25 +84,12 @@ const Assistants: FC<Props> = ({
}) })
} }
if (assistant.id === activeAssistant?.id) {
setShowTopics(true)
return
}
EventEmitter.emit(EVENT_NAMES.SWITCH_TOPIC_SIDEBAR) EventEmitter.emit(EVENT_NAMES.SWITCH_TOPIC_SIDEBAR)
setActiveAssistant(assistant) setActiveAssistant(assistant)
}, },
[activeAssistant?.id, generating, setActiveAssistant, setShowTopics, t] [generating, setActiveAssistant, t]
) )
if (showTopics) {
return (
<Container style={{ padding: 0 }}>
<Topics assistant={activeAssistant} activeTopic={activeTopic} setActiveTopic={setActiveTopic} />
</Container>
)
}
return ( return (
<Container> <Container>
<DragableList list={assistants} onUpdate={updateAssistants}> <DragableList list={assistants} onUpdate={updateAssistants}>
@ -121,11 +99,9 @@ const Assistants: FC<Props> = ({
onClick={() => onSwitchAssistant(assistant)} onClick={() => onSwitchAssistant(assistant)}
className={assistant.id === activeAssistant?.id ? 'active' : ''}> className={assistant.id === activeAssistant?.id ? 'active' : ''}>
<AssistantName className="name">{assistant.name || t('chat.default.name')}</AssistantName> <AssistantName className="name">{assistant.name || t('chat.default.name')}</AssistantName>
<Tooltip arrow title={t('chat.topics.list')} placement="bottom" mouseEnterDelay={0.5}> <ArrowRightButton className="arrow-button" onClick={() => onEditAssistant(assistant)}>
<ArrowRightButton className="arrow-button" onClick={() => setShowTopics(true)}>
<ArrowRightOutlined /> <ArrowRightOutlined />
</ArrowRightButton> </ArrowRightButton>
</Tooltip>
{false && <TopicCount className="topics-count">{assistant.topics.length}</TopicCount>} {false && <TopicCount className="topics-count">{assistant.topics.length}</TopicCount>}
</AssistantItem> </AssistantItem>
</Dropdown> </Dropdown>
@ -205,6 +181,7 @@ const ArrowRightButton = styled.div`
border-radius: 4px; border-radius: 4px;
position: absolute; position: absolute;
right: 10px; right: 10px;
top: 5px;
&:hover { &:hover {
background-color: var(--color-background); background-color: var(--color-background);
} }

View File

@ -1,12 +1,13 @@
import { useAssistant } from '@renderer/hooks/useAssistant' import { useAssistant } from '@renderer/hooks/useAssistant'
import { useShowRightSidebar } 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, useState } from 'react' import { FC, useEffect, useState } from 'react'
import styled from 'styled-components' import styled from 'styled-components'
import Inputbar from './Inputbar/Inputbar' import Inputbar from './Inputbar/Inputbar'
import Messages from './Messages/Messages' import Messages from './Messages/Messages'
import Settings from './Settings' import RightSidebar from './RightSidebar'
interface Props { interface Props {
assistant: Assistant assistant: Assistant
@ -17,19 +18,19 @@ interface Props {
const Chat: FC<Props> = (props) => { const Chat: FC<Props> = (props) => {
const { assistant } = useAssistant(props.assistant.id) const { assistant } = useAssistant(props.assistant.id)
const [showSetting, setShowSetting] = useState(false) const [showSetting, setShowSetting] = useState(false)
const { rightSidebarShown } = useShowRightSidebar()
useEffect(() => {
!rightSidebarShown && showSetting && setShowSetting(false)
}, [rightSidebarShown, showSetting])
return ( return (
<Container id="chat"> <Container id="chat">
<Main vertical flex={1} justify="space-between"> <Main vertical flex={1} justify="space-between">
<Messages assistant={assistant} topic={props.activeTopic} /> <Messages assistant={assistant} topic={props.activeTopic} />
<Inputbar <Inputbar assistant={assistant} setActiveTopic={props.setActiveTopic} />
assistant={assistant}
setActiveTopic={props.setActiveTopic}
showSetting={showSetting}
setShowSetting={setShowSetting}
/>
</Main> </Main>
{showSetting && <Settings assistant={assistant} onClose={() => setShowSetting(false)} />} <RightSidebar assistant={assistant} activeTopic={props.activeTopic} setActiveTopic={props.setActiveTopic} />
</Container> </Container>
) )
} }

View File

@ -1,41 +1,26 @@
import { ArrowLeftOutlined, UnorderedListOutlined } from '@ant-design/icons' import { useAssistants, useDefaultAssistant } from '@renderer/hooks/useAssistant'
import { Navbar, NavbarCenter, NavbarLeft } from '@renderer/components/app/Navbar'
import { HStack } from '@renderer/components/Layout'
import { isMac, isWindows } from '@renderer/config/constant'
import { useTheme } from '@renderer/context/ThemeProvider'
import { useAssistant, useAssistants, useDefaultAssistant } 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 { getDefaultTopic } from '@renderer/services/assistant'
import { Assistant, Topic } from '@renderer/types' import { Assistant, Topic } from '@renderer/types'
import { uuid } from '@renderer/utils' import { uuid } from '@renderer/utils'
import { Switch } from 'antd'
import { FC, useState } from 'react' import { FC, useState } from 'react'
import { useTranslation } from 'react-i18next'
import styled from 'styled-components' import styled from 'styled-components'
import AddAssistantPopup from '../../components/Popups/AddAssistantPopup'
import Assistants from './Assistants' import Assistants from './Assistants'
import Chat from './Chat' import Chat from './Chat'
import SelectModelButton from './components/SelectModelButton' import Navbar from './Navbar'
let _activeAssistant: Assistant let _activeAssistant: Assistant
let _showTopics = false
const HomePage: FC = () => { const HomePage: FC = () => {
const { assistants, addAssistant } = useAssistants() const { assistants, addAssistant } = useAssistants()
const [activeAssistant, setActiveAssistant] = useState(_activeAssistant || assistants[0]) const [activeAssistant, setActiveAssistant] = useState(_activeAssistant || assistants[0])
const { showAssistants } = useShowAssistants() const { showAssistants } = useShowAssistants()
const { defaultAssistant } = useDefaultAssistant() const { defaultAssistant } = useDefaultAssistant()
const { theme, toggleTheme } = useTheme()
const [showTopics, setShowTopics] = useState(_showTopics)
const { t } = useTranslation()
const { activeTopic, setActiveTopic } = useActiveTopic(activeAssistant) const { activeTopic, setActiveTopic } = useActiveTopic(activeAssistant)
const { addTopic } = useAssistant(activeAssistant.id)
_activeAssistant = activeAssistant _activeAssistant = activeAssistant
_showTopics = showTopics
const onCreateDefaultAssistant = () => { const onCreateDefaultAssistant = () => {
const assistant = { ...defaultAssistant, id: uuid() } const assistant = { ...defaultAssistant, id: uuid() }
@ -43,59 +28,18 @@ const HomePage: FC = () => {
setActiveAssistant(assistant) setActiveAssistant(assistant)
} }
const onCreate = async () => {
if (showTopics) {
const topic = getDefaultTopic()
addTopic(topic)
setActiveTopic(topic)
} else {
const assistant = await AddAssistantPopup.show()
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} />
{showAssistants && (
<NavbarLeft
style={{ justifyContent: 'space-between', alignItems: 'center', borderRight: 'none', padding: '0 8px' }}>
<NewButton onClick={() => setShowTopics(!showTopics)} className="back-button">
{showTopics ? <ArrowLeftOutlined /> : <UnorderedListOutlined />}
<BackText>{showTopics ? t('common.assistant') : t('chat.topics.title')}</BackText>
</NewButton>
<NewButton onClick={onCreate}>
<i className="iconfont icon-a-addchat"></i>
</NewButton>
</NavbarLeft>
)}
<NavbarCenter
style={{ justifyContent: 'space-between', paddingLeft: isMac ? 16 : 8, paddingRight: isWindows ? 135 : 12 }}>
<HStack alignItems="center">
<AssistantName>{activeAssistant?.name || t('chat.default.name')}</AssistantName>
<SelectModelButton assistant={activeAssistant} />
</HStack>
<ThemeSwitch
checkedChildren={<i className="iconfont icon-theme icon-dark1" />}
unCheckedChildren={<i className="iconfont icon-theme icon-theme-light" />}
checked={theme === 'dark'}
onChange={toggleTheme}
/>
</NavbarCenter>
</Navbar>
<ContentContainer> <ContentContainer>
{showAssistants && ( {showAssistants && (
<Assistants <Assistants
activeAssistant={activeAssistant} activeAssistant={activeAssistant}
setActiveAssistant={setActiveAssistant} setActiveAssistant={setActiveAssistant}
activeTopic={activeTopic}
setActiveTopic={setActiveTopic}
showTopics={showTopics}
setShowTopics={setShowTopics}
onCreateAssistant={onCreateDefaultAssistant} onCreateAssistant={onCreateDefaultAssistant}
/> />
)} )}
@ -118,67 +62,4 @@ const ContentContainer = styled.div`
background-color: var(--color-background); background-color: var(--color-background);
` `
const AssistantName = styled.span`
margin-left: 5px;
margin-right: 10px;
font-family: Ubuntu;
font-size: 13px;
font-weight: 500;
`
export const NewButton = styled.div`
-webkit-app-region: none;
border-radius: 4px;
padding: 0 5px;
height: 30px;
gap: 5px;
display: flex;
flex-direction: row;
justify-content: center;
align-items: center;
transition: all 0.2s ease-in-out;
color: var(--color-icon);
.icon-a-addchat {
font-size: 20px;
}
.anticon {
font-size: 19px;
}
.icon-showsidebarhoriz,
.icon-hidesidebarhoriz {
font-size: 17px;
}
&.back-button {
margin-left: ${isMac ? '8px' : 0};
.anticon {
font-size: 16px;
}
.anticon-arrow-left {
font-size: 14px;
}
&:hover {
background-color: var(--color-background-mute);
color: var(--color-icon-white);
}
}
&:hover {
background-color: var(--color-background-mute);
cursor: pointer;
color: var(--color-icon-white);
}
`
const BackText = styled.span`
font-size: 12px;
font-weight: 400;
`
const ThemeSwitch = styled(Switch)`
-webkit-app-region: none;
margin-right: 10px;
.icon-theme {
font-size: 14px;
}
`
export default HomePage export default HomePage

View File

@ -3,6 +3,7 @@ import {
ControlOutlined, ControlOutlined,
FullscreenExitOutlined, FullscreenExitOutlined,
FullscreenOutlined, FullscreenOutlined,
HistoryOutlined,
PauseCircleOutlined, PauseCircleOutlined,
PlusCircleOutlined, PlusCircleOutlined,
QuestionCircleOutlined QuestionCircleOutlined
@ -30,13 +31,11 @@ import SendMessageButton from './SendMessageButton'
interface Props { interface Props {
assistant: Assistant assistant: Assistant
setActiveTopic: (topic: Topic) => void setActiveTopic: (topic: Topic) => void
showSetting: boolean
setShowSetting: (show: boolean) => void
} }
let _text = '' let _text = ''
const Inputbar: FC<Props> = ({ assistant, setActiveTopic, showSetting, setShowSetting }) => { const Inputbar: FC<Props> = ({ assistant, setActiveTopic }) => {
const [text, setText] = useState(_text) const [text, setText] = useState(_text)
const [inputFocus, setInputFocus] = useState(false) const [inputFocus, setInputFocus] = useState(false)
const { addTopic } = useAssistant(assistant.id) const { addTopic } = useAssistant(assistant.id)
@ -221,8 +220,13 @@ const Inputbar: FC<Props> = ({ assistant, setActiveTopic, showSetting, setShowSe
</ToolbarButton> </ToolbarButton>
</Popconfirm> </Popconfirm>
</Tooltip> </Tooltip>
<Tooltip placement="top" title={t('chat.input.settings')} arrow className={showSetting ? 'active' : ''}> <Tooltip placement="top" title={t('chat.input.topics')} arrow>
<ToolbarButton type="text" onClick={() => setShowSetting(!showSetting)}> <ToolbarButton type="text" onClick={() => EventEmitter.emit(EVENT_NAMES.SHOW_TOPIC_SIDEBAR)}>
<HistoryOutlined />
</ToolbarButton>
</Tooltip>
<Tooltip placement="top" title={t('chat.input.settings')} arrow>
<ToolbarButton type="text" onClick={() => EventEmitter.emit(EVENT_NAMES.SHOW_CHAT_SETTINGS)}>
<ControlOutlined /> <ControlOutlined />
</ToolbarButton> </ToolbarButton>
</Tooltip> </Tooltip>
@ -236,7 +240,6 @@ const Inputbar: FC<Props> = ({ assistant, setActiveTopic, showSetting, setShowSe
<TextCount> <TextCount>
<Tooltip title={t('chat.input.context_count.tip') + ' | ' + t('chat.input.estimated_tokens.tip')}> <Tooltip title={t('chat.input.context_count.tip') + ' | ' + t('chat.input.estimated_tokens.tip')}>
<StyledTag> <StyledTag>
<i className="iconfont icon-history" style={{ marginRight: '3px' }} />
{assistant?.settings?.contextCount ?? DEFAULT_CONEXTCOUNT} {assistant?.settings?.contextCount ?? DEFAULT_CONEXTCOUNT}
<Divider type="vertical" style={{ marginTop: 2, marginLeft: 5, marginRight: 5 }} />{inputTokenCount} <Divider type="vertical" style={{ marginTop: 2, marginLeft: 5, marginRight: 5 }} />{inputTokenCount}
<span style={{ margin: '0 2px' }}>/</span> <span style={{ margin: '0 2px' }}>/</span>
@ -342,11 +345,12 @@ const TextCount = styled.div`
const StyledTag = styled(Tag)` const StyledTag = styled(Tag)`
cursor: pointer; cursor: pointer;
border-radius: 6px; border-radius: 20px;
display: flex; display: flex;
align-items: center; align-items: center;
padding: 2px 8px; padding: 2px 8px;
border-width: 0.5; border-width: 0.5;
margin: 0;
` `
export default Inputbar export default Inputbar

View File

@ -0,0 +1,111 @@
import { Navbar, NavbarCenter, NavbarLeft, NavbarRight } from '@renderer/components/app/Navbar'
import AddAssistantPopup from '@renderer/components/Popups/AddAssistantPopup'
import { isMac, isWindows } from '@renderer/config/constant'
import { useTheme } from '@renderer/context/ThemeProvider'
import { useAssistant } from '@renderer/hooks/useAssistant'
import { useShowAssistants, useShowRightSidebar } from '@renderer/hooks/useStore'
import { Assistant } from '@renderer/types'
import { removeLeadingEmoji } from '@renderer/utils'
import { Switch } from 'antd'
import { FC } from 'react'
import { useTranslation } from 'react-i18next'
import styled from 'styled-components'
import SelectModelButton from './components/SelectModelButton'
interface Props {
activeAssistant: Assistant
setActiveAssistant: (assistant: Assistant) => void
}
const HeaderNavbar: FC<Props> = ({ activeAssistant, setActiveAssistant }) => {
const { assistant } = useAssistant(activeAssistant.id)
const { t } = useTranslation()
const { showAssistants, toggleShowAssistants } = useShowAssistants()
const { rightSidebarShown, toggleRightSidebar } = useShowRightSidebar()
const { theme, toggleTheme } = useTheme()
const onCreateAssistant = async () => {
const assistant = await AddAssistantPopup.show()
assistant && setActiveAssistant(assistant)
}
return (
<Navbar>
{showAssistants && (
<NavbarLeft style={{ justifyContent: 'space-between', borderRight: 'none', padding: '0 8px' }}>
<NewButton onClick={toggleShowAssistants} style={{ marginLeft: isMac ? 8 : 0 }}>
<i className="iconfont icon-hidesidebarhoriz" />
</NewButton>
<NewButton onClick={onCreateAssistant}>
<i className="iconfont icon-a-addchat"></i>
</NewButton>
</NavbarLeft>
)}
<NavbarCenter style={{ paddingLeft: isMac ? 16 : 8 }}>
{!showAssistants && (
<NewButton onClick={toggleShowAssistants} style={{ marginRight: isMac ? 8 : 25 }}>
<i className="iconfont icon-showsidebarhoriz" />
</NewButton>
)}
<AssistantName>{removeLeadingEmoji(assistant?.name) || t('chat.default.name')}</AssistantName>
<SelectModelButton assistant={assistant} />
</NavbarCenter>
<NavbarRight style={{ justifyContent: 'flex-end', paddingRight: isWindows ? 140 : 12 }}>
<ThemeSwitch
checkedChildren={<i className="iconfont icon-theme icon-dark1" />}
unCheckedChildren={<i className="iconfont icon-theme icon-theme-light" />}
checked={theme === 'dark'}
onChange={toggleTheme}
/>
<NewButton onClick={toggleRightSidebar}>
<i className={`iconfont ${rightSidebarShown ? 'icon-showsidebarhoriz' : 'icon-hidesidebarhoriz'}`} />
</NewButton>
</NavbarRight>
</Navbar>
)
}
export const NewButton = styled.div`
-webkit-app-region: none;
border-radius: 4px;
width: 30px;
height: 30px;
display: flex;
flex-direction: row;
justify-content: center;
align-items: center;
transition: all 0.2s ease-in-out;
color: var(--color-icon);
cursor: pointer;
.icon-a-addchat {
font-size: 20px;
}
.anticon {
font-size: 19px;
}
.icon-showsidebarhoriz,
.icon-hidesidebarhoriz {
font-size: 17px;
}
&:hover {
background-color: var(--color-background-mute);
color: var(--color-icon-white);
}
`
const AssistantName = styled.span`
margin-left: 5px;
margin-right: 10px;
font-family: Ubuntu;
`
const ThemeSwitch = styled(Switch)`
-webkit-app-region: none;
margin-right: 10px;
.icon-theme {
font-size: 14px;
}
`
export default HeaderNavbar

View File

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

View File

@ -1,4 +1,4 @@
import { CheckOutlined, CloseOutlined, QuestionCircleOutlined, ReloadOutlined } from '@ant-design/icons' import { CheckOutlined, QuestionCircleOutlined, ReloadOutlined } from '@ant-design/icons'
import { HStack } from '@renderer/components/Layout' import { HStack } from '@renderer/components/Layout'
import { DEFAULT_CONEXTCOUNT, DEFAULT_MAX_TOKENS, DEFAULT_TEMPERATURE } from '@renderer/config/constant' import { DEFAULT_CONEXTCOUNT, DEFAULT_MAX_TOKENS, DEFAULT_TEMPERATURE } from '@renderer/config/constant'
import { useAssistant } from '@renderer/hooks/useAssistant' import { useAssistant } from '@renderer/hooks/useAssistant'
@ -19,7 +19,6 @@ import styled from 'styled-components'
interface Props { interface Props {
assistant: Assistant assistant: Assistant
onClose: () => void
} }
const SettingsTab: FC<Props> = (props) => { const SettingsTab: FC<Props> = (props) => {
@ -88,10 +87,6 @@ const SettingsTab: FC<Props> = (props) => {
return ( return (
<Container> <Container>
<SettingsHeader>
{t('settings.title')}
<CloseIcon onClick={props.onClose} />
</SettingsHeader>
<SettingSubtitle> <SettingSubtitle>
{t('settings.messages.model.title')}{' '} {t('settings.messages.model.title')}{' '}
<Tooltip title={t('chat.settings.reset')}> <Tooltip title={t('chat.settings.reset')}>
@ -264,21 +259,4 @@ const SettingRowTitleSmall = styled(SettingRowTitle)`
font-size: 13px; font-size: 13px;
` `
const SettingsHeader = styled.div`
display: flex;
flex-direction: row;
align-items: center;
justify-content: space-between;
padding: 10px 15px;
border-bottom: 0.5px solid var(--color-border);
margin-left: -15px;
margin-right: -15px;
`
const CloseIcon = styled(CloseOutlined)`
font-size: 14px;
cursor: pointer;
color: var(--color-text-3);
`
export default SettingsTab export default SettingsTab

View File

@ -125,10 +125,15 @@ 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);
border-left: 0.5px solid var(--color-border);
overflow-y: scroll;
height: calc(100vh - var(--navbar-height));
` `
const TopicListItem = styled.div` const TopicListItem = styled.div`
padding: 5px 10px; padding: 7px 10px;
margin: 0 10px; margin: 0 10px;
cursor: pointer; cursor: pointer;
border-radius: 4px; border-radius: 4px;

View File

@ -9,6 +9,7 @@ export enum ThemeMode {
} }
export interface SettingsState { export interface SettingsState {
showRightSidebar: boolean
showAssistants: boolean showAssistants: boolean
sendMessageShortcut: SendMessageShortcut sendMessageShortcut: SendMessageShortcut
language: string language: string
@ -23,6 +24,7 @@ export interface SettingsState {
} }
const initialState: SettingsState = { const initialState: SettingsState = {
showRightSidebar: true,
showAssistants: true, showAssistants: true,
sendMessageShortcut: 'Enter', sendMessageShortcut: 'Enter',
language: navigator.language, language: navigator.language,
@ -32,7 +34,7 @@ const initialState: SettingsState = {
messageFont: 'system', messageFont: 'system',
showInputEstimatedTokens: false, showInputEstimatedTokens: false,
theme: ThemeMode.light, theme: ThemeMode.light,
windowStyle: 'opaque', windowStyle: 'transparent',
fontSize: 14 fontSize: 14
} }
@ -40,6 +42,12 @@ const settingsSlice = createSlice({
name: 'settings', name: 'settings',
initialState, initialState,
reducers: { reducers: {
toggleRightSidebar: (state) => {
state.showRightSidebar = !state.showRightSidebar
},
setShowRightSidebar: (state, action: PayloadAction<boolean>) => {
state.showRightSidebar = action.payload
},
toggleShowAssistants: (state) => { toggleShowAssistants: (state) => {
state.showAssistants = !state.showAssistants state.showAssistants = !state.showAssistants
}, },
@ -78,6 +86,8 @@ const settingsSlice = createSlice({
}) })
export const { export const {
setShowRightSidebar,
toggleRightSidebar,
toggleShowAssistants, toggleShowAssistants,
setSendMessageShortcut, setSendMessageShortcut,
setLanguage, setLanguage,