feat: Add animation for sidebar (#9768)

This commit is contained in:
RieN 7z 2025-09-02 09:26:56 +08:00 committed by GitHub
parent 1295d37ff6
commit 16b9f49cc8
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 93 additions and 46 deletions

View File

@ -13,6 +13,7 @@ import { Assistant, Topic } from '@renderer/types'
import { classNames } from '@renderer/utils' import { classNames } from '@renderer/utils'
import { Flex } from 'antd' import { Flex } from 'antd'
import { debounce } from 'lodash' import { debounce } from 'lodash'
import { AnimatePresence, motion } from 'motion/react'
import React, { FC, useState } from 'react' import React, { FC, useState } from 'react'
import { useHotkeys } from 'react-hotkeys-hook' import { useHotkeys } from 'react-hotkeys-hook'
import styled from 'styled-components' import styled from 'styled-components'
@ -43,7 +44,6 @@ const Chat: FC<Props> = (props) => {
const contentSearchRef = React.useRef<ContentSearchRef>(null) const contentSearchRef = React.useRef<ContentSearchRef>(null)
const [filterIncludeUser, setFilterIncludeUser] = useState(false) const [filterIncludeUser, setFilterIncludeUser] = useState(false)
const maxWidth = useChatMaxWidth()
const { setTimeoutTimer } = useTimer() const { setTimeoutTimer } = useTimer()
useHotkeys('esc', () => { useHotkeys('esc', () => {
@ -131,7 +131,7 @@ const Chat: FC<Props> = (props) => {
vertical vertical
flex={1} flex={1}
justify="space-between" justify="space-between"
style={{ maxWidth, height: mainHeight }}> style={{ maxWidth: '100%', height: mainHeight }}>
<Messages <Messages
key={props.activeTopic.id} key={props.activeTopic.id}
assistant={assistant} assistant={assistant}
@ -153,15 +153,24 @@ const Chat: FC<Props> = (props) => {
{isMultiSelectMode && <MultiSelectActionPopup topic={props.activeTopic} />} {isMultiSelectMode && <MultiSelectActionPopup topic={props.activeTopic} />}
</QuickPanelProvider> </QuickPanelProvider>
</Main> </Main>
{topicPosition === 'right' && showTopics && ( <AnimatePresence initial={false}>
<Tabs {topicPosition === 'right' && showTopics && (
activeAssistant={assistant} <motion.div
activeTopic={props.activeTopic} initial={{ width: 0, opacity: 0 }}
setActiveAssistant={props.setActiveAssistant} animate={{ width: 'auto', opacity: 1 }}
setActiveTopic={props.setActiveTopic} exit={{ width: 0, opacity: 0 }}
position="right" transition={{ duration: 0.3, ease: 'easeInOut' }}
/> style={{ overflow: 'hidden' }}>
)} <Tabs
activeAssistant={assistant}
activeTopic={props.activeTopic}
setActiveAssistant={props.setActiveAssistant}
setActiveTopic={props.setActiveTopic}
position="right"
/>
</motion.div>
)}
</AnimatePresence>
</HStack> </HStack>
</Container> </Container>
) )

View File

@ -13,6 +13,7 @@ import { Assistant, Topic } from '@renderer/types'
import { Tooltip } from 'antd' import { Tooltip } from 'antd'
import { t } from 'i18next' import { t } from 'i18next'
import { Menu, PanelLeftClose, PanelRightClose, Search } from 'lucide-react' import { Menu, PanelLeftClose, PanelRightClose, Search } from 'lucide-react'
import { AnimatePresence, motion } from 'motion/react'
import { FC } from 'react' import { FC } from 'react'
import styled from 'styled-components' import styled from 'styled-components'
@ -80,11 +81,19 @@ const HeaderNavbar: FC<Props> = ({ activeAssistant, setActiveAssistant, activeTo
</NavbarIcon> </NavbarIcon>
</Tooltip> </Tooltip>
)} )}
{!showAssistants && ( <AnimatePresence initial={false}>
<NavbarIcon onClick={onShowAssistantsDrawer} style={{ marginRight: 8 }}> {!showAssistants && (
<Menu size={18} /> <motion.div
</NavbarIcon> initial={{ width: 0, opacity: 0 }}
)} animate={{ width: 'auto', opacity: 1 }}
exit={{ width: 0, opacity: 0 }}
transition={{ duration: 0.3, ease: 'easeInOut' }}>
<NavbarIcon onClick={onShowAssistantsDrawer} style={{ marginRight: 8 }}>
<Menu size={18} />
</NavbarIcon>
</motion.div>
)}
</AnimatePresence>
<SelectModelButton assistant={assistant} /> <SelectModelButton assistant={assistant} />
</HStack> </HStack>
<HStack alignItems="center" gap={8}> <HStack alignItems="center" gap={8}>

View File

@ -7,6 +7,7 @@ import NavigationService from '@renderer/services/NavigationService'
import { newMessagesActions } from '@renderer/store/newMessage' import { newMessagesActions } from '@renderer/store/newMessage'
import { Assistant, Topic } from '@renderer/types' import { Assistant, Topic } from '@renderer/types'
import { MIN_WINDOW_HEIGHT, MIN_WINDOW_WIDTH, SECOND_MIN_WINDOW_WIDTH } from '@shared/config/constant' import { MIN_WINDOW_HEIGHT, MIN_WINDOW_WIDTH, SECOND_MIN_WINDOW_WIDTH } from '@shared/config/constant'
import { AnimatePresence, motion } from 'motion/react'
import { FC, startTransition, useCallback, useEffect, useState } from 'react' import { FC, startTransition, useCallback, useEffect, useState } from 'react'
import { useDispatch } from 'react-redux' import { useDispatch } from 'react-redux'
import { useLocation, useNavigate } from 'react-router-dom' import { useLocation, useNavigate } from 'react-router-dom'
@ -100,17 +101,26 @@ const HomePage: FC = () => {
/> />
)} )}
<ContentContainer id={isLeftNavbar ? 'content-container' : undefined}> <ContentContainer id={isLeftNavbar ? 'content-container' : undefined}>
{showAssistants && ( <AnimatePresence initial={false}>
<ErrorBoundary> {showAssistants && (
<HomeTabs <ErrorBoundary>
activeAssistant={activeAssistant} <motion.div
activeTopic={activeTopic} initial={{ width: 0, opacity: 0 }}
setActiveAssistant={setActiveAssistant} animate={{ width: 'var(--assistants-width)', opacity: 1 }}
setActiveTopic={setActiveTopic} exit={{ width: 0, opacity: 0 }}
position="left" transition={{ duration: 0.3, ease: 'easeInOut' }}
/> style={{ overflow: 'hidden' }}>
</ErrorBoundary> <HomeTabs
)} activeAssistant={activeAssistant}
activeTopic={activeTopic}
setActiveAssistant={setActiveAssistant}
setActiveTopic={setActiveTopic}
position="left"
/>
</motion.div>
</ErrorBoundary>
)}
</AnimatePresence>
<ErrorBoundary> <ErrorBoundary>
<Chat <Chat
assistant={activeAssistant} assistant={activeAssistant}

View File

@ -15,6 +15,7 @@ import { Assistant, Topic } from '@renderer/types'
import { Tooltip } from 'antd' import { Tooltip } from 'antd'
import { t } from 'i18next' import { t } from 'i18next'
import { Menu, MessageSquareDiff, PanelLeftClose, PanelRightClose, Search } from 'lucide-react' import { Menu, MessageSquareDiff, PanelLeftClose, PanelRightClose, Search } from 'lucide-react'
import { AnimatePresence, motion } from 'motion/react'
import { FC } from 'react' import { FC } from 'react'
import styled from 'styled-components' import styled from 'styled-components'
@ -68,20 +69,29 @@ const HeaderNavbar: FC<Props> = ({ activeAssistant, setActiveAssistant, activeTo
return ( return (
<Navbar className="home-navbar"> <Navbar className="home-navbar">
{showAssistants && ( <AnimatePresence initial={false}>
<NavbarLeft style={{ justifyContent: 'space-between', borderRight: 'none', padding: 0 }}> {showAssistants && (
<Tooltip title={t('navbar.hide_sidebar')} mouseEnterDelay={0.8}> <motion.div
<NavbarIcon onClick={toggleShowAssistants} style={{ marginLeft: isMac && !isFullscreen ? 16 : 0 }}> initial={{ width: 0, opacity: 0 }}
<PanelLeftClose size={18} /> animate={{ width: 'auto', opacity: 1 }}
</NavbarIcon> exit={{ width: 0, opacity: 0 }}
</Tooltip> transition={{ duration: 0.3, ease: 'easeInOut' }}
<Tooltip title={t('settings.shortcuts.new_topic')} mouseEnterDelay={0.8}> style={{ overflow: 'hidden', display: 'flex', flexDirection: 'row' }}>
<NavbarIcon onClick={() => EventEmitter.emit(EVENT_NAMES.ADD_NEW_TOPIC)} style={{ marginRight: 5 }}> <NavbarLeft style={{ justifyContent: 'space-between', borderRight: 'none', padding: 0 }}>
<MessageSquareDiff size={18} /> <Tooltip title={t('navbar.hide_sidebar')} mouseEnterDelay={0.8}>
</NavbarIcon> <NavbarIcon onClick={toggleShowAssistants} style={{ marginLeft: isMac && !isFullscreen ? 16 : 0 }}>
</Tooltip> <PanelLeftClose size={18} />
</NavbarLeft> </NavbarIcon>
)} </Tooltip>
<Tooltip title={t('settings.shortcuts.new_topic')} mouseEnterDelay={0.8}>
<NavbarIcon onClick={() => EventEmitter.emit(EVENT_NAMES.ADD_NEW_TOPIC)} style={{ marginRight: 5 }}>
<MessageSquareDiff size={18} />
</NavbarIcon>
</Tooltip>
</NavbarLeft>
</motion.div>
)}
</AnimatePresence>
<NavbarRight style={{ justifyContent: 'space-between', flex: 1 }} className="home-navbar-right"> <NavbarRight style={{ justifyContent: 'space-between', flex: 1 }} className="home-navbar-right">
<HStack alignItems="center"> <HStack alignItems="center">
{!showAssistants && ( {!showAssistants && (
@ -93,11 +103,20 @@ const HeaderNavbar: FC<Props> = ({ activeAssistant, setActiveAssistant, activeTo
</NavbarIcon> </NavbarIcon>
</Tooltip> </Tooltip>
)} )}
{!showAssistants && ( <AnimatePresence initial={false}>
<NavbarIcon onClick={onShowAssistantsDrawer} style={{ marginRight: 8 }}> {!showAssistants && (
<Menu size={18} /> <motion.div
</NavbarIcon> initial={{ width: 0, opacity: 0 }}
)} animate={{ width: 'auto', opacity: 1 }}
exit={{ width: 0, opacity: 0 }}
transition={{ duration: 0.3, ease: 'easeInOut' }}
style={{ overflow: 'hidden' }}>
<NavbarIcon onClick={onShowAssistantsDrawer} style={{ marginRight: 8 }}>
<Menu size={18} />
</NavbarIcon>
</motion.div>
)}
</AnimatePresence>
<SelectModelButton assistant={assistant} /> <SelectModelButton assistant={assistant} />
</HStack> </HStack>
<HStack alignItems="center" gap={8}> <HStack alignItems="center" gap={8}>