feat: improved ui effects and rendering for components

- Added smooth all property transition effect to Icon component.
- Added hover effect and conditional rendering for Switch Topic Sidebar button on current assistant.
- Updated the existing conditional options array to consistently include both topic and settings options.
- Improved hover effects on topic list items.
This commit is contained in:
kangfenmao 2024-09-20 15:38:52 +08:00
parent f26ebf44c7
commit 62072e4f66
8 changed files with 69 additions and 66 deletions

View File

@ -22,7 +22,7 @@
--color-background: #181818; --color-background: #181818;
--color-background-soft: var(--color-black-soft); --color-background-soft: var(--color-black-soft);
--color-background-mute: var(--color-black-mute); --color-background-mute: var(--color-black-soft);
--color-primary: #00b96b; --color-primary: #00b96b;
--color-primary-soft: #00b96b99; --color-primary-soft: #00b96b99;

View File

@ -48,12 +48,12 @@ const Sidebar: FC = () => {
<Menus onClick={MinApp.onClose}> <Menus onClick={MinApp.onClose}>
<StyledLink onClick={() => to('/')}> <StyledLink onClick={() => to('/')}>
<Icon className={isRoute('/')}> <Icon className={isRoute('/')}>
<i className="iconfont icon-chat"></i> <i className="iconfont icon-chat" />
</Icon> </Icon>
</StyledLink> </StyledLink>
<StyledLink onClick={() => to('/agents')}> <StyledLink onClick={() => to('/agents')}>
<Icon className={isRoute('/agents')}> <Icon className={isRoute('/agents')}>
<i className="iconfont icon-business-smart-assistant"></i> <i className="iconfont icon-business-smart-assistant" />
</Icon> </Icon>
</StyledLink> </StyledLink>
<StyledLink onClick={() => to('/translate')}> <StyledLink onClick={() => to('/translate')}>
@ -63,7 +63,7 @@ const Sidebar: FC = () => {
</StyledLink> </StyledLink>
<StyledLink onClick={() => to('/apps')}> <StyledLink onClick={() => to('/apps')}>
<Icon className={isRoute('/apps')}> <Icon className={isRoute('/apps')}>
<i className="iconfont icon-appstore"></i> <i className="iconfont icon-appstore" />
</Icon> </Icon>
</StyledLink> </StyledLink>
<StyledLink onClick={() => to('/files')}> <StyledLink onClick={() => to('/files')}>
@ -76,7 +76,7 @@ const Sidebar: FC = () => {
<Menus onClick={MinApp.onClose}> <Menus onClick={MinApp.onClose}>
<StyledLink onClick={() => to(isLocalAi ? '/settings/assistant' : '/settings/provider')}> <StyledLink onClick={() => to(isLocalAi ? '/settings/assistant' : '/settings/provider')}>
<Icon className={pathname.startsWith('/settings') ? 'active' : ''}> <Icon className={pathname.startsWith('/settings') ? 'active' : ''}>
<i className="iconfont icon-setting"></i> <i className="iconfont icon-setting" />
</Icon> </Icon>
</StyledLink> </StyledLink>
</Menus> </Menus>
@ -128,6 +128,7 @@ const Icon = styled.div`
margin-bottom: 5px; margin-bottom: 5px;
transition: background-color 0.2s ease; transition: background-color 0.2s ease;
-webkit-app-region: none; -webkit-app-region: none;
transition: all 0.2s ease;
.iconfont, .iconfont,
.anticon { .anticon {
color: var(--color-icon); color: var(--color-icon);
@ -139,7 +140,7 @@ const Icon = styled.div`
font-size: 17px; font-size: 17px;
} }
&:hover { &:hover {
background-color: var(--color-background-mute); background-color: var(--color-background-soft);
cursor: pointer; cursor: pointer;
.iconfont, .iconfont,
.anticon { .anticon {
@ -147,7 +148,7 @@ const Icon = styled.div`
} }
} }
&.active { &.active {
background-color: var(--color-background-soft); background-color: var(--color-background-mute);
.iconfont, .iconfont,
.anticon { .anticon {
color: var(--color-icon-white); color: var(--color-icon-white);

View File

@ -66,7 +66,7 @@ const resources = {
}, },
chat: { chat: {
save: 'Save', save: 'Save',
'default.name': 'Default Assistant', 'default.name': '⭐️ Default Assistant',
'default.description': "Hello, I'm Default Assistant. You can start chatting with me right away", 'default.description': "Hello, I'm Default Assistant. You can start chatting with me right away",
'default.topic.name': 'Default Topic', 'default.topic.name': 'Default Topic',
'topics.title': 'Topics', 'topics.title': 'Topics',
@ -344,7 +344,7 @@ const resources = {
}, },
chat: { chat: {
save: '保存', save: '保存',
'default.name': '默认助手', 'default.name': '⭐️ 默认助手',
'default.description': '你好,我是默认助手。你可以立刻开始跟我聊天。', 'default.description': '你好,我是默认助手。你可以立刻开始跟我聊天。',
'default.topic.name': '默认话题', 'default.topic.name': '默认话题',
'topics.title': '话题', 'topics.title': '话题',

View File

@ -186,6 +186,7 @@ const Assistants: FC<Props> = ({
list={list} list={list}
onUpdate={updateAssistants} onUpdate={updateAssistants}
droppableProps={{ isDropDisabled: !isEmpty(search) }} droppableProps={{ isDropDisabled: !isEmpty(search) }}
style={{ paddingBottom: dragging ? '34px' : 0 }}
onDragStart={() => setDragging(true)} onDragStart={() => setDragging(true)}
onDragEnd={() => setDragging(false)}> onDragEnd={() => setDragging(false)}>
{(assistant) => { {(assistant) => {
@ -194,11 +195,11 @@ const Assistants: FC<Props> = ({
<Dropdown key={assistant.id} menu={{ items: getMenuItems(assistant) }} trigger={['contextMenu']}> <Dropdown key={assistant.id} menu={{ items: getMenuItems(assistant) }} trigger={['contextMenu']}>
<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 {isCurrent && (
className={`arrow-button ${isCurrent ? 'active' : ''}`} <ArrowRightButton onClick={() => EventEmitter.emit(EVENT_NAMES.SWITCH_TOPIC_SIDEBAR)}>
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>}
</AssistantItem> </AssistantItem>
</Dropdown> </Dropdown>
@ -206,10 +207,12 @@ const Assistants: FC<Props> = ({
}} }}
</DragableList> </DragableList>
{!dragging && ( {!dragging && (
<AddButton onClick={onCreateAssistant}> <AssistantItem onClick={onCreateAssistant}>
<AddButtonText>{t('chat.add.assistant.title')}</AddButtonText> <AssistantName>
<PlusOutlined /> <PlusOutlined style={{ color: 'var(--color-text-2)', marginRight: 4 }} />
</AddButton> {t('chat.add.assistant.title')}
</AssistantName>
</AssistantItem>
)} )}
</Container> </Container>
) )
@ -230,15 +233,18 @@ const AssistantItem = styled.div`
justify-content: space-between; justify-content: space-between;
padding: 7px 10px; padding: 7px 10px;
position: relative; position: relative;
border-radius: 6px; border-radius: 4px;
margin: 0 10px; margin: 0 10px;
padding-right: 35px; padding-right: 35px;
cursor: pointer;
font-family: Ubuntu; font-family: Ubuntu;
cursor: pointer;
.iconfont { .iconfont {
opacity: 0; opacity: 0;
color: var(--color-text-3); color: var(--color-text-3);
} }
&:hover {
background-color: var(--color-background-soft);
}
&.active { &.active {
background-color: var(--color-background-mute); background-color: var(--color-background-mute);
.name { .name {
@ -311,29 +317,4 @@ const CommandKey = styled.div`
margin-right: -4px; margin-right: -4px;
` `
const AddButton = styled.div`
height: 34px;
display: flex;
flex-direction: row;
justify-content: space-between;
align-items: center;
padding: 6px 10px;
margin: 0 10px;
margin-top: -2px;
color: var(--color-text-2);
transition: all 0.2s ease-in-out;
font-size: 13px;
cursor: pointer;
border-radius: 8px;
.anticon {
margin: 0 4px;
}
&:hover {
color: var(--color-text-1);
background-color: var(--color-background-soft);
}
`
const AddButtonText = styled.span``
export default Assistants export default Assistants

View File

@ -1,6 +1,7 @@
import { import {
ClearOutlined, ClearOutlined,
ControlOutlined, ControlOutlined,
FormOutlined,
FullscreenExitOutlined, FullscreenExitOutlined,
FullscreenOutlined, FullscreenOutlined,
PauseCircleOutlined, PauseCircleOutlined,
@ -256,11 +257,6 @@ const Inputbar: FC<Props> = ({ assistant, setActiveTopic }) => {
textareaRef.current?.focus() textareaRef.current?.focus()
}, [assistant]) }, [assistant])
useEffect(() => {
document.addEventListener('paste', onPaste)
return () => document.removeEventListener('paste', onPaste)
}, [onPaste])
return ( return (
<Container> <Container>
<AttachmentPreview files={files} setFiles={setFiles} /> <AttachmentPreview files={files} setFiles={setFiles} />
@ -281,13 +277,14 @@ const Inputbar: FC<Props> = ({ assistant, setActiveTopic }) => {
onBlur={() => setInputFocus(false)} onBlur={() => setInputFocus(false)}
onInput={onInput} onInput={onInput}
disabled={searching} disabled={searching}
onPaste={(e) => onPaste(e.nativeEvent)}
onClick={() => searching && dispatch(setSearching(false))} onClick={() => searching && dispatch(setSearching(false))}
/> />
<Toolbar> <Toolbar>
<ToolbarMenu> <ToolbarMenu>
<Tooltip placement="top" title={t('chat.input.new_topic')} arrow> <Tooltip placement="top" title={t('chat.input.new_topic')} arrow>
<ToolbarButton type="text" onClick={addNewTopic}> <ToolbarButton type="text" onClick={addNewTopic}>
<i className="iconfont icon-a-addchat" /> <FormOutlined />
</ToolbarButton> </ToolbarButton>
</Tooltip> </Tooltip>
<Tooltip placement="top" title={t('chat.input.clear')} arrow> <Tooltip placement="top" title={t('chat.input.clear')} arrow>

View File

@ -1,3 +1,4 @@
import { FormOutlined } from '@ant-design/icons'
import { Navbar, NavbarLeft, NavbarRight } from '@renderer/components/app/Navbar' import { Navbar, NavbarLeft, NavbarRight } from '@renderer/components/app/Navbar'
import { HStack } from '@renderer/components/Layout' import { HStack } from '@renderer/components/Layout'
import AssistantSettingPopup from '@renderer/components/Popups/AssistantSettingPopup' import AssistantSettingPopup from '@renderer/components/Popups/AssistantSettingPopup'
@ -8,6 +9,7 @@ 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, syncAsistantToAgent } from '@renderer/services/assistant' import { getDefaultTopic, syncAsistantToAgent } from '@renderer/services/assistant'
import { EVENT_NAMES, EventEmitter } from '@renderer/services/event'
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, useCallback } from 'react'
@ -41,7 +43,8 @@ const HeaderNavbar: FC<Props> = ({ activeAssistant, setActiveTopic }) => {
addTopic(topic) addTopic(topic)
setActiveTopic(topic) setActiveTopic(topic)
db.topics.add({ id: topic.id, messages: [] }) db.topics.add({ id: topic.id, messages: [] })
window.message.success({ content: t('message.topic.added') }) window.message.success({ content: t('message.topic.added'), key: 'topic-added' })
setTimeout(() => EventEmitter.emit(EVENT_NAMES.SHOW_TOPIC_SIDEBAR), 0)
}, [addTopic, setActiveTopic, t]) }, [addTopic, setActiveTopic, t])
return ( return (
@ -52,7 +55,7 @@ const HeaderNavbar: FC<Props> = ({ activeAssistant, setActiveTopic }) => {
<i className="iconfont icon-hide-sidebar" /> <i className="iconfont icon-hide-sidebar" />
</NewButton> </NewButton>
<NewButton onClick={addNewTopic}> <NewButton onClick={addNewTopic}>
<i className="iconfont icon-a-addchat" /> <FormOutlined />
</NewButton> </NewButton>
</NavbarLeft> </NavbarLeft>
)} )}

View File

@ -102,8 +102,16 @@ const RightSidebar: FC<Props> = ({ activeAssistant, activeTopic, setActiveAssist
options={ options={
[ [
position === 'left' && topicPosition === 'left' ? assistantTab : undefined, position === 'left' && topicPosition === 'left' ? assistantTab : undefined,
{ label: t('common.topics'), value: 'topic', icon: <BarsOutlined /> }, {
{ label: t('settings.title'), value: 'settings', icon: <SettingOutlined /> } label: t('common.topics'),
value: 'topic',
icon: <BarsOutlined />
},
{
label: t('settings.title'),
value: 'settings',
icon: <SettingOutlined />
}
].filter(Boolean) as SegmentedProps['options'] ].filter(Boolean) as SegmentedProps['options']
} }
onChange={(value) => setTab(value as 'topic' | 'settings')} onChange={(value) => setTab(value as 'topic' | 'settings')}

View File

@ -136,8 +136,11 @@ const Topics: FC<Props> = ({ assistant: _assistant, activeTopic, setActiveTopic
return ( return (
<Dropdown menu={{ items: getTopicMenuItems(topic) }} trigger={['contextMenu']} key={topic.id}> <Dropdown menu={{ items: getTopicMenuItems(topic) }} trigger={['contextMenu']} key={topic.id}>
<TopicListItem className={isActive ? 'active' : ''} onClick={() => onSwitchTopic(topic)}> <TopicListItem className={isActive ? 'active' : ''} onClick={() => onSwitchTopic(topic)}>
<TopicName className="name">{topic.name}</TopicName> <TopicName className="name">
{assistant.topics.length > 1 && ( <TopicHash>#</TopicHash>
{topic.name.replace('`', '')}
</TopicName>
{assistant.topics.length > 1 && isActive && (
<MenuButton <MenuButton
className="menu" className="menu"
onClick={(e) => { onClick={(e) => {
@ -171,8 +174,7 @@ const Container = styled.div`
const TopicListItem = styled.div` const TopicListItem = styled.div`
padding: 7px 10px; padding: 7px 10px;
margin: 0 10px; margin: 0 10px;
cursor: pointer; border-radius: 4px;
border-radius: 6px;
font-family: Ubuntu; font-family: Ubuntu;
font-size: 13px; font-size: 13px;
display: flex; display: flex;
@ -180,10 +182,18 @@ const TopicListItem = styled.div`
justify-content: space-between; justify-content: space-between;
align-items: center; align-items: center;
position: relative; position: relative;
font-family: Ubuntu;
cursor: pointer;
.menu { .menu {
opacity: 0; opacity: 0;
color: var(--color-text-3); color: var(--color-text-3);
} }
&:hover {
background-color: var(--color-background-soft);
.name {
opacity: 1;
}
}
&.active { &.active {
background-color: var(--color-background-mute); background-color: var(--color-background-mute);
.name { .name {
@ -214,17 +224,20 @@ const MenuButton = styled.div`
flex-direction: row; flex-direction: row;
justify-content: center; justify-content: center;
align-items: center; align-items: center;
width: 30px; min-width: 22px;
height: 24px; min-height: 22px;
min-width: 24px;
min-height: 24px;
border-radius: 4px;
position: absolute; position: absolute;
right: 10px; right: 8px;
top: 5px; top: 6px;
.anticon { .anticon {
font-size: 12px; font-size: 12px;
} }
` `
const TopicHash = styled.span`
font-size: 13px;
color: var(--color-text-3);
margin-right: 2px;
`
export default Topics export default Topics