Add smooth animations to SessionsTab and Sessions components

- Replace static conditional rendering with Framer Motion animations for no-agent and session states
- Animate session list items with staggered entrance and exit transitions
- Add loading spinner animation with fade effect
- Apply motion to session creation button with delayed entrance
This commit is contained in:
suyao 2025-09-20 15:55:42 +08:00
parent 1c813aa6c3
commit 6c233fef9f
No known key found for this signature in database
2 changed files with 74 additions and 30 deletions

View File

@ -1,4 +1,5 @@
import { useRuntime } from '@renderer/hooks/useRuntime'
import { AnimatePresence,motion } from 'framer-motion'
import { FC, memo } from 'react'
import Sessions from './components/Sessions'
@ -9,14 +10,29 @@ const SessionsTab: FC<SessionsTabProps> = () => {
const { chat } = useRuntime()
const { activeAgentId } = chat
if (!activeAgentId) {
return <div> No active agent.</div>
}
return (
<>
<Sessions agentId={activeAgentId} />
</>
<AnimatePresence mode="wait">
{!activeAgentId ? (
<motion.div
key="no-agent"
initial={{ opacity: 0, y: 10 }}
animate={{ opacity: 1, y: 0 }}
exit={{ opacity: 0, y: -10 }}
transition={{ duration: 0.2 }}
className="flex h-full items-center justify-center text-foreground-500">
No active agent.
</motion.div>
) : (
<motion.div
key={activeAgentId}
initial={{ opacity: 0, x: 20 }}
animate={{ opacity: 1, x: 0 }}
exit={{ opacity: 0, x: -20 }}
transition={{ duration: 0.3, ease: 'easeOut' }}>
<Sessions agentId={activeAgentId} />
</motion.div>
)}
</AnimatePresence>
)
}

View File

@ -3,6 +3,7 @@ import { SessionModal } from '@renderer/components/Popups/agent/SessionModal'
import { useSessions } from '@renderer/hooks/agents/useSessions'
import { useAppDispatch } from '@renderer/store'
import { setActiveSessionIdAction, setActiveTopicOrSessionAction } from '@renderer/store/runtime'
import { AnimatePresence,motion } from 'framer-motion'
import { Plus } from 'lucide-react'
import { memo, useCallback } from 'react'
import { useTranslation } from 'react-i18next'
@ -28,36 +29,63 @@ const Sessions: React.FC<SessionsProps> = ({ agentId }) => {
[dispatch]
)
if (isLoading) return <Spinner />
if (isLoading) {
return (
<motion.div
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
exit={{ opacity: 0 }}
className="flex h-full items-center justify-center">
<Spinner size="lg" />
</motion.div>
)
}
// if (error) return
return (
<div className="agents-tab h-full w-full p-2">
<motion.div
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
transition={{ duration: 0.3 }}
className="agents-tab h-full w-full p-2">
{/* TODO: Add session button */}
<SessionModal
agentId={agentId}
trigger={{
content: (
<Button
onPress={(e) => e.continuePropagation()}
className="mb-2 w-full justify-start bg-transparent text-foreground-500 hover:bg-accent">
<Plus size={16} className="mr-1 shrink-0" />
{t('agent.session.add.title')}
</Button>
)
}}
/>
{sessions.map((session) => (
<SessionItem
key={session.id}
session={session}
<motion.div
initial={{ opacity: 0, y: -10 }}
animate={{ opacity: 1, y: 0 }}
transition={{ duration: 0.2, delay: 0.1 }}>
<SessionModal
agentId={agentId}
onDelete={() => deleteSession(session.id)}
onPress={() => setActiveSessionId(agentId, session.id)}
trigger={{
content: (
<Button
onPress={(e) => e.continuePropagation()}
className="mb-2 w-full justify-start bg-transparent text-foreground-500 hover:bg-accent">
<Plus size={16} className="mr-1 shrink-0" />
{t('agent.session.add.title')}
</Button>
)
}}
/>
))}
</div>
</motion.div>
<AnimatePresence>
{sessions.map((session, index) => (
<motion.div
key={session.id}
initial={{ opacity: 0, x: -20 }}
animate={{ opacity: 1, x: 0 }}
exit={{ opacity: 0, x: 20, transition: { duration: 0.2 } }}
transition={{ duration: 0.3, delay: index * 0.05 }}>
<SessionItem
session={session}
agentId={agentId}
onDelete={() => deleteSession(session.id)}
onPress={() => setActiveSessionId(agentId, session.id)}
/>
</motion.div>
))}
</AnimatePresence>
</motion.div>
)
}