Skip to content

Commit 04bdbcb

Browse files
committed
AgentStore: Reduce use of motion animation
1 parent b6ed8a2 commit 04bdbcb

File tree

1 file changed

+37
-34
lines changed

1 file changed

+37
-34
lines changed

web/src/app/store/page.tsx

Lines changed: 37 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,17 @@ const AgentStorePage = () => {
9696
}
9797
return await response.json()
9898
},
99+
select: (data) => {
100+
// Normalize data once to prevent reference changes and precompute expensive operations
101+
return data.map((agent) => ({
102+
...agent,
103+
// Precompute expensive operations
104+
createdAtMs: new Date(agent.created_at).getTime(),
105+
nameLower: agent.name.toLowerCase(),
106+
descriptionLower: agent.description?.toLowerCase() || '',
107+
tagsLower: agent.tags?.map((tag) => tag.toLowerCase()) || [],
108+
}))
109+
},
99110
})
100111

101112
// Fetch user's publishers if signed in
@@ -158,22 +169,17 @@ const AgentStorePage = () => {
158169
})
159170
}, [editorsChoice, searchQuery])
160171

161-
// Helper function to get agents for a specific row
172+
// Get agents for a specific row without pre-building all rows
162173
const getAgentsForRow = useCallback(
163-
(agents: AgentData[], rowIndex: number, cols: number) => {
164-
const startIndex = rowIndex * cols
165-
return agents.slice(startIndex, startIndex + cols)
174+
(rowIndex: number) => {
175+
const startIndex = rowIndex * columns
176+
return filteredAndSortedAgents.slice(startIndex, startIndex + columns)
166177
},
167-
[]
178+
[filteredAndSortedAgents, columns]
168179
)
169180

170-
// Create virtualized rows for All Agents only
171-
const allAgentsRows = useMemo(() => {
172-
const rowCount = Math.ceil(filteredAndSortedAgents.length / columns)
173-
return Array.from({ length: rowCount }, (_, i) =>
174-
getAgentsForRow(filteredAndSortedAgents, i, columns)
175-
)
176-
}, [filteredAndSortedAgents, columns, getAgentsForRow])
181+
// Calculate total rows needed
182+
const totalRows = Math.ceil(filteredAndSortedAgents.length / columns)
177183

178184
// Only create virtualizer when we have data and the component is mounted
179185
const [isMounted, setIsMounted] = useState(false)
@@ -184,14 +190,13 @@ const AgentStorePage = () => {
184190

185191
// Virtualizer for All Agents section only
186192
const allAgentsVirtualizer = useWindowVirtualizer({
187-
count: isMounted ? allAgentsRows.length : 0,
193+
count: isMounted ? totalRows : 0,
188194
estimateSize: () => 270, // Height for agent rows (card + gap)
189195
overscan: 6,
190-
useAnimationFrameWithResizeObserver: true,
191196
})
192197

193198
// Determine if we should use virtualization for All Agents section
194-
const shouldVirtualizeAllAgents = isMounted && allAgentsRows.length > 6
199+
const shouldVirtualizeAllAgents = isMounted && totalRows > 6
195200

196201
// Publisher button logic
197202
const renderPublisherButton = () => {
@@ -235,7 +240,6 @@ const AgentStorePage = () => {
235240
return count.toString()
236241
}
237242

238-
// Memoized AgentCard component to prevent unnecessary re-renders
239243
const AgentCard = memo(
240244
({
241245
agent,
@@ -251,10 +255,14 @@ const AgentStorePage = () => {
251255
>
252256
<Card
253257
className={cn(
254-
'relative h-full transition-all duration-200 cursor-pointer border bg-card/50 backdrop-blur-sm',
258+
'relative h-full transition-all duration-200 cursor-pointer border bg-card/50',
255259
'hover:border-accent/50 hover:bg-card/80',
256260
isEditorsChoice && 'ring-2 ring-amber-400/50 border-amber-400/30'
257261
)}
262+
style={{
263+
// Use CSS transforms for hover effects instead of Framer Motion
264+
transition: 'all 0.2s ease',
265+
}}
258266
>
259267
{/* Editor's Choice Badge - Positioned absolutely for better visual hierarchy */}
260268
{isEditorsChoice && (
@@ -285,12 +293,9 @@ const AgentStorePage = () => {
285293
v{agent.version}
286294
</Badge>
287295
</div>
288-
{/* Action buttons */}
289296
<div className="flex items-center gap-1">
290297
<div onClick={(e) => e.preventDefault()}>
291-
<motion.button
292-
whileHover={{ scale: 1.1 }}
293-
whileTap={{ scale: 0.95 }}
298+
<button
294299
onClick={() => {
295300
navigator.clipboard.writeText(
296301
`codebuff --agent ${agent.publisher.id}/${agent.id}@${agent.version}`
@@ -299,11 +304,11 @@ const AgentStorePage = () => {
299304
description: `Agent run command copied to clipboard!`,
300305
})
301306
}}
302-
className="p-2 hover:bg-muted/50 rounded-lg transition-all duration-200 opacity-60 group-hover:opacity-100"
307+
className="p-2 hover:bg-muted/50 rounded-lg transition-all duration-200 opacity-60 group-hover:opacity-100 hover:scale-110 active:scale-95"
303308
title={`Copy: codebuff --agent ${agent.publisher.id}/${agent.id}@${agent.version}`}
304309
>
305310
<Copy className="h-4 w-4 text-muted-foreground hover:text-foreground" />
306-
</motion.button>
311+
</button>
307312
</div>
308313
<ChevronRight className="h-5 w-5 text-muted-foreground transition-all duration-300 group-hover:text-primary group-hover:translate-x-1" />
309314
</div>
@@ -511,9 +516,9 @@ const AgentStorePage = () => {
511516
</p>
512517
</div>
513518

514-
{/* Non-virtualized Editor's Choice */}
515519
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6">
516520
{filteredEditorsChoice.map((agent) => (
521+
// Only use motion for small, non-virtualized sections
517522
<motion.div
518523
key={agent.id}
519524
whileHover={{ y: -4, transition: { duration: 0.2 } }}
@@ -547,7 +552,7 @@ const AgentStorePage = () => {
547552
{allAgentsVirtualizer
548553
.getVirtualItems()
549554
.map((virtualItem) => {
550-
const agents = allAgentsRows[virtualItem.index]
555+
const agents = getAgentsForRow(virtualItem.index)
551556
return (
552557
<div
553558
key={virtualItem.key}
@@ -562,20 +567,18 @@ const AgentStorePage = () => {
562567
>
563568
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6 mb-6">
564569
{agents?.map((agent) => (
565-
<motion.div
570+
// No motion for virtualized items - use CSS transitions instead
571+
<div
566572
key={agent.id}
567-
whileHover={{
568-
y: -4,
569-
transition: { duration: 0.2 },
570-
}}
573+
className="hover:-translate-y-1 transition-transform duration-200"
571574
>
572575
<AgentCard
573576
agent={agent}
574577
isEditorsChoice={EDITORS_CHOICE_AGENTS.includes(
575578
agent.id
576579
)}
577580
/>
578-
</motion.div>
581+
</div>
579582
))}
580583
</div>
581584
</div>
@@ -586,17 +589,17 @@ const AgentStorePage = () => {
586589
// Non-virtualized All Agents
587590
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6">
588591
{filteredAndSortedAgents.map((agent) => (
589-
<motion.div
592+
<div
590593
key={agent.id}
591-
whileHover={{ y: -4, transition: { duration: 0.2 } }}
594+
className="hover:-translate-y-1 transition-transform duration-200"
592595
>
593596
<AgentCard
594597
agent={agent}
595598
isEditorsChoice={EDITORS_CHOICE_AGENTS.includes(
596599
agent.id
597600
)}
598601
/>
599-
</motion.div>
602+
</div>
600603
))}
601604
</div>
602605
)}

0 commit comments

Comments
 (0)