feat: change topics position

This commit is contained in:
kangfenmao 2024-09-05 22:53:52 +08:00
parent d809f50c0e
commit ab0e7e1e07
11 changed files with 103 additions and 69 deletions

View File

@ -3,6 +3,7 @@ import {
SendMessageShortcut,
setSendMessageShortcut as _setSendMessageShortcut,
setTheme,
setTopicPosition,
setWindowStyle,
ThemeMode
} from '@renderer/store/settings'
@ -21,6 +22,9 @@ export function useSettings() {
},
setWindowStyle(windowStyle: 'transparent' | 'opaque') {
dispatch(setWindowStyle(windowStyle))
},
setTopicPosition(topicPosition: 'left' | 'right') {
dispatch(setTopicPosition(topicPosition))
}
}
}

View File

@ -1,23 +1,5 @@
import { useAppDispatch, useAppSelector } from '@renderer/store'
import {
setShowRightSidebar,
setShowTopics,
toggleRightSidebar,
toggleShowAssistants,
toggleShowTopics
} 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))
}
}
import { setShowTopics, toggleShowAssistants, toggleShowTopics } from '@renderer/store/settings'
export function useShowAssistants() {
const showAssistants = useAppSelector((state) => state.settings.showAssistants)

View File

@ -207,7 +207,10 @@ const resources = {
'theme.window.style.title': 'Window Style',
'theme.window.style.transparent': 'Transparent Window',
'theme.window.style.opaque': 'Opaque Window',
'font_size.title': 'Message Font Size'
'font_size.title': 'Message Font Size',
'topic.position': 'Topic Position',
'topic.position.left': 'Left',
'topic.position.right': 'Right'
},
translate: {
title: 'Translation',
@ -454,7 +457,10 @@ const resources = {
'theme.window.style.title': '窗口样式',
'theme.window.style.transparent': '透明窗口',
'theme.window.style.opaque': '不透明窗口',
'font_size.title': '消息字体大小'
'font_size.title': '消息字体大小',
'topic.position': '话题位置',
'topic.position.left': '左侧',
'topic.position.right': '右侧'
},
translate: {
title: '翻译',

View File

@ -1,8 +1,9 @@
import { useAssistant } from '@renderer/hooks/useAssistant'
import { useShowRightSidebar, useShowTopics } from '@renderer/hooks/useStore'
import { useSettings } from '@renderer/hooks/useSettings'
import { useShowTopics } from '@renderer/hooks/useStore'
import { Assistant, Topic } from '@renderer/types'
import { Flex } from 'antd'
import { FC, useEffect, useState } from 'react'
import { FC } from 'react'
import styled from 'styled-components'
import Inputbar from './Inputbar/Inputbar'
@ -17,23 +18,21 @@ interface Props {
const Chat: FC<Props> = (props) => {
const { assistant } = useAssistant(props.assistant.id)
const [showSetting, setShowSetting] = useState(false)
const { rightSidebarShown } = useShowRightSidebar()
const { showTopics } = useShowTopics()
useEffect(() => {
!rightSidebarShown && showSetting && setShowSetting(false)
}, [rightSidebarShown, showSetting])
const { topicPosition } = useSettings()
return (
<Container id="chat">
{showTopics && (
{showTopics && topicPosition === 'left' && (
<Topics assistant={assistant} activeTopic={props.activeTopic} setActiveTopic={props.setActiveTopic} />
)}
<Main vertical flex={1} justify="space-between">
<Messages assistant={assistant} topic={props.activeTopic} setActiveTopic={props.setActiveTopic} />
<Inputbar assistant={assistant} setActiveTopic={props.setActiveTopic} />
</Main>
{showTopics && topicPosition === 'right' && (
<Topics assistant={assistant} activeTopic={props.activeTopic} setActiveTopic={props.setActiveTopic} />
)}
</Container>
)
}

View File

@ -6,6 +6,7 @@ import AssistantSettingPopup from '@renderer/components/Popups/AssistantSettingP
import { isMac, isWindows } from '@renderer/config/constant'
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'
@ -27,8 +28,9 @@ const HeaderNavbar: FC<Props> = ({ activeAssistant, setActiveAssistant, setActiv
const { assistant, addTopic } = useAssistant(activeAssistant.id)
const { t } = useTranslation()
const { showAssistants, toggleShowAssistants } = useShowAssistants()
const { showTopics } = useShowTopics()
const { showTopics, toggleShowTopics } = useShowTopics()
const { theme, toggleTheme } = useTheme()
const { topicPosition } = useSettings()
const onCreateAssistant = async () => {
const assistant = await AddAssistantPopup.show()
@ -53,7 +55,7 @@ const HeaderNavbar: FC<Props> = ({ activeAssistant, setActiveAssistant, setActiv
</NewButton>
</NavbarLeft>
)}
{showTopics && (
{showTopics && topicPosition === 'left' && (
<NavbarCenter
style={{
paddingLeft: isMac && !showAssistants ? 16 : 8,
@ -80,7 +82,7 @@ const HeaderNavbar: FC<Props> = ({ activeAssistant, setActiveAssistant, setActiv
)}
<NavbarRight style={{ justifyContent: 'space-between', paddingRight: isWindows ? 130 : 12, flex: 1 }}>
<HStack alignItems="center">
{!showAssistants && !showTopics && (
{!showAssistants && (topicPosition === 'left' ? !showTopics : true) && (
<NewButton
onClick={() => toggleShowAssistants()}
style={{ marginRight: isMac ? 8 : 25, marginLeft: isMac ? 8 : 0 }}>
@ -102,6 +104,11 @@ const HeaderNavbar: FC<Props> = ({ activeAssistant, setActiveAssistant, setActiv
checked={theme === 'dark'}
onChange={toggleTheme}
/>
{topicPosition === 'right' && (
<NewButton onClick={toggleShowTopics}>
<i className={`iconfont icon-sidebar-${showTopics ? 'left' : 'right'}`} />
</NewButton>
)}
</HStack>
</NavbarRight>
</Navbar>

View File

@ -1,5 +1,5 @@
import { BarsOutlined, SettingOutlined } from '@ant-design/icons'
import { useShowRightSidebar } from '@renderer/hooks/useStore'
import { useShowTopics } from '@renderer/hooks/useStore'
import { EVENT_NAMES, EventEmitter } from '@renderer/services/event'
import { Assistant, Topic } from '@renderer/types'
import { Segmented } from 'antd'
@ -18,7 +18,7 @@ interface Props {
const RightSidebar: FC<Props> = (props) => {
const [tab, setTab] = useState<'topic' | 'settings'>('topic')
const { rightSidebarShown, showRightSidebar, hideRightSidebar } = useShowRightSidebar()
const { showTopics, setShowTopics } = useShowTopics()
const { t } = useTranslation()
const isTopicTab = tab === 'topic'
const isSettingsTab = tab === 'settings'
@ -26,31 +26,31 @@ const RightSidebar: FC<Props> = (props) => {
useEffect(() => {
const unsubscribes = [
EventEmitter.on(EVENT_NAMES.SHOW_TOPIC_SIDEBAR, (): any => {
if (rightSidebarShown && isTopicTab) {
return hideRightSidebar()
if (showTopics && isTopicTab) {
return setShowTopics(false)
}
if (rightSidebarShown) {
if (showTopics) {
return setTab('topic')
}
showRightSidebar()
setShowTopics(true)
setTab('topic')
}),
EventEmitter.on(EVENT_NAMES.SHOW_CHAT_SETTINGS, (): any => {
if (rightSidebarShown && isSettingsTab) {
return hideRightSidebar()
if (showTopics && isSettingsTab) {
return setShowTopics(false)
}
if (rightSidebarShown) {
if (showTopics) {
return setTab('settings')
}
showRightSidebar()
setShowTopics(true)
setTab('settings')
}),
EventEmitter.on(EVENT_NAMES.SWITCH_TOPIC_SIDEBAR, () => setTab('topic'))
]
return () => unsubscribes.forEach((unsub) => unsub())
}, [hideRightSidebar, isSettingsTab, isTopicTab, rightSidebarShown, showRightSidebar])
}, [isSettingsTab, isTopicTab, showTopics, setShowTopics])
if (!rightSidebarShown) {
if (!showTopics) {
return null
}

View File

@ -2,6 +2,7 @@ import { DeleteOutlined, EditOutlined, OpenAIOutlined } from '@ant-design/icons'
import DragableList from '@renderer/components/DragableList'
import PromptPopup from '@renderer/components/Popups/PromptPopup'
import { useAssistant } from '@renderer/hooks/useAssistant'
import { useSettings } from '@renderer/hooks/useSettings'
import { fetchMessagesSummary } from '@renderer/services/api'
import LocalStorage from '@renderer/services/storage'
import { useAppSelector } from '@renderer/store'
@ -21,6 +22,9 @@ const Topics: FC<Props> = ({ assistant: _assistant, activeTopic, setActiveTopic
const { assistant, removeTopic, updateTopic, updateTopics } = useAssistant(_assistant.id)
const { t } = useTranslation()
const generating = useAppSelector((state) => state.runtime.generating)
const { topicPosition } = useSettings()
const borderStyle = '0.5px solid var(--color-border)'
const getTopicMenuItems = useCallback(
(topic: Topic) => {
@ -88,17 +92,19 @@ const Topics: FC<Props> = ({ assistant: _assistant, activeTopic, setActiveTopic
)
return (
<Container>
<Container style={topicPosition === 'left' ? { borderRight: borderStyle } : { borderLeft: borderStyle }}>
<DragableList list={assistant.topics} onUpdate={updateTopics}>
{(topic) => (
<Dropdown menu={{ items: getTopicMenuItems(topic) }} trigger={['contextMenu']} key={topic.id}>
<TopicListItem
className={topic.id === activeTopic?.id ? 'active' : ''}
onClick={() => onSwitchTopic(topic)}>
{topic.name}
</TopicListItem>
</Dropdown>
)}
{(topic) => {
const isActive = topic.id === activeTopic?.id
const activeClass = topicPosition === 'left' ? 'active-left' : 'active-right'
return (
<Dropdown menu={{ items: getTopicMenuItems(topic) }} trigger={['contextMenu']} key={topic.id}>
<TopicListItem className={isActive ? activeClass : ''} onClick={() => onSwitchTopic(topic)}>
{topic.name}
</TopicListItem>
</Dropdown>
)
}}
</DragableList>
</Container>
)
@ -112,6 +118,7 @@ const Container = styled.div`
min-width: var(--topic-list-width);
max-width: var(--topic-list-width);
border-right: 0.5px solid var(--color-border);
border-left: 0.5px solid var(--color-border);
overflow-y: scroll;
height: calc(100vh - var(--navbar-height));
`
@ -129,11 +136,15 @@ const TopicListItem = styled.div`
&:hover {
background-color: var(--color-background-soft);
}
&.active {
&.active-left {
background-color: var(--color-primary);
color: white;
font-weight: 500;
}
&.active-right {
background-color: var(--color-background-mute);
font-weight: 500;
}
`
export default Topics

View File

@ -14,7 +14,17 @@ import { useTranslation } from 'react-i18next'
import { SettingContainer, SettingDivider, SettingRow, SettingRowTitle, SettingTitle } from '.'
const GeneralSettings: FC = () => {
const { language, proxyUrl: storeProxyUrl, userName, theme, windowStyle, setTheme, setWindowStyle } = useSettings()
const {
language,
proxyUrl: storeProxyUrl,
userName,
theme,
windowStyle,
topicPosition,
setTheme,
setWindowStyle,
setTopicPosition
} = useSettings()
const [proxyUrl, setProxyUrl] = useState<string | undefined>(storeProxyUrl)
const dispatch = useAppDispatch()
const { t } = useTranslation()
@ -79,6 +89,19 @@ const GeneralSettings: FC = () => {
/>
</SettingRow>
<SettingDivider />
<SettingRow>
<SettingRowTitle>{t('settings.topic.position')}</SettingRowTitle>
<Select
defaultValue={topicPosition || 'right'}
style={{ width: 180 }}
onChange={setTopicPosition}
options={[
{ value: 'left', label: t('settings.topic.position.left') },
{ value: 'right', label: t('settings.topic.position.right') }
]}
/>
</SettingRow>
<SettingDivider />
<SettingRow>
<SettingRowTitle>{t('settings.general.user_name')}</SettingRowTitle>
<Input

View File

@ -18,6 +18,8 @@ export function getDefaultAssistant(): Assistant {
export function getDefaultTopic(): Topic {
return {
id: uuid(),
createdAt: new Date().toISOString(),
updatedAt: new Date().toISOString(),
name: i18n.t('chat.default.topic.name'),
messages: []
}

View File

@ -393,6 +393,10 @@ const migrateConfig = {
updatedAt: new Date().toISOString()
}))
}))
},
settings: {
...state.settings,
topicPosition: 'right'
}
}
}

View File

@ -9,7 +9,6 @@ export enum ThemeMode {
}
export interface SettingsState {
showRightSidebar: boolean
showAssistants: boolean
showTopics: boolean
sendMessageShortcut: SendMessageShortcut
@ -22,10 +21,10 @@ export interface SettingsState {
theme: ThemeMode
windowStyle: 'transparent' | 'opaque'
fontSize: number
topicPosition: 'left' | 'right'
}
const initialState: SettingsState = {
showRightSidebar: true,
showAssistants: true,
showTopics: true,
sendMessageShortcut: 'Enter',
@ -37,19 +36,14 @@ const initialState: SettingsState = {
showInputEstimatedTokens: false,
theme: ThemeMode.light,
windowStyle: 'opaque',
fontSize: 14
fontSize: 14,
topicPosition: 'right'
}
const settingsSlice = createSlice({
name: 'settings',
initialState,
reducers: {
toggleRightSidebar: (state) => {
state.showRightSidebar = !state.showRightSidebar
},
setShowRightSidebar: (state, action: PayloadAction<boolean>) => {
state.showRightSidebar = action.payload
},
setShowAssistants: (state, action: PayloadAction<boolean>) => {
state.showAssistants = action.payload
},
@ -92,13 +86,14 @@ const settingsSlice = createSlice({
setWindowStyle: (state, action: PayloadAction<'transparent' | 'opaque'>) => {
state.windowStyle = action.payload
console.log(state.windowStyle)
},
setTopicPosition: (state, action: PayloadAction<'left' | 'right'>) => {
state.topicPosition = action.payload
}
}
})
export const {
setShowRightSidebar,
toggleRightSidebar,
setShowAssistants,
toggleShowAssistants,
setShowTopics,
@ -112,7 +107,8 @@ export const {
setShowInputEstimatedTokens,
setTheme,
setFontSize,
setWindowStyle
setWindowStyle,
setTopicPosition
} = settingsSlice.actions
export default settingsSlice.reducer