import { useEffect, useState } from 'react'
import { AnimatePresence, motion } from 'motion/react'
import ToolResult, { type ToolResultProps } from '@/components/ui/tool-result'
type Call = Pick<ToolResultProps, 'status' | 'summary'> & { duration?: string }
const phases: { calls: Call[]; ms: number }[] = [
{ ms: 1300, calls: [{ status: 'running', summary: 'Searching 576 registry components' }] },
{
ms: 1000,
calls: [
{ status: 'complete', summary: 'Found 33 matches across 7 groups', duration: '1.3s' },
{ status: 'running', summary: 'Reading src/components/ui' },
],
},
{
ms: 1500,
calls: [
{ status: 'complete', summary: 'Found 33 matches across 7 groups', duration: '1.3s' },
{ status: 'complete', summary: 'Read 214 lines from 3 files', duration: '1.0s' },
{ status: 'running', summary: 'Building preview deployment' },
],
},
{
ms: 1600,
calls: [
{ status: 'complete', summary: 'Found 33 matches across 7 groups', duration: '1.3s' },
{ status: 'complete', summary: 'Read 214 lines from 3 files', duration: '1.0s' },
{ status: 'error', summary: 'Build timed out after 30s', duration: '1.5s' },
],
},
{
ms: 1400,
calls: [
{ status: 'complete', summary: 'Found 33 matches across 7 groups', duration: '1.3s' },
{ status: 'complete', summary: 'Read 214 lines from 3 files', duration: '1.0s' },
{ status: 'running', summary: 'Retrying with a clean cache' },
],
},
{
ms: 4000,
calls: [
{ status: 'complete', summary: 'Found 33 matches across 7 groups', duration: '1.3s' },
{ status: 'complete', summary: 'Read 214 lines from 3 files', duration: '1.0s' },
{ status: 'complete', summary: 'Live at preview.pastaui.dev', duration: '2.9s' },
],
},
]
const names = ['Search component catalog', 'Read source files', 'Deploy preview']
const matches = [
['tool-result.tsx', 'ai-agents'],
['tool-group.tsx', 'ai-agents'],
['tool-approval.tsx', 'ai-agents'],
]
export default function Demo() {
const [phase, setPhase] = useState(2)
const [elapsed, setElapsed] = useState(0)
const [open, setOpen] = useState(true)
useEffect(() => {
setElapsed(0)
if (phase <= 1) setOpen(phase === 1)
const timer = setTimeout(
() => setPhase((value) => (value + 1) % phases.length),
phases[phase].ms,
)
const tick = setInterval(() => setElapsed((value) => value + 0.1), 100)
return () => {
clearTimeout(timer)
clearInterval(tick)
}
}, [phase])
return (
<div className="flex min-h-[22rem] w-full max-w-lg flex-col gap-3">
<AnimatePresence initial={false}>
{phases[phase].calls.map((call, index) => (
<motion.div
animate={{ opacity: 1, y: 0 }}
exit={{ opacity: 0 }}
initial={{ opacity: 0, y: 10 }}
key={names[index]}
transition={{ duration: 0.3, ease: [0.23, 1, 0.32, 1] }}
>
<ToolResult
duration={call.status === 'running' ? `${elapsed.toFixed(1)}s` : call.duration}
copyText={index === 0 ? matches.map(([file]) => file).join('\n') : undefined}
error={
call.status === 'error'
? 'vite build exceeded the 30s limit while bundling 1,284 modules.'
: undefined
}
input={
index === 0 ? '{ "query": "tool", "group": "ai-agents", "limit": 40 }' : undefined
}
name={names[index]}
onRetry={call.status === 'error' ? () => setPhase(4) : undefined}
onOpenChange={index === 0 ? setOpen : undefined}
open={index === 0 ? open : undefined}
output={
index === 0 ? (
<ul className="space-y-1">
{matches.map(([file, group], row) => (
<motion.li
animate={{ opacity: 1, x: 0 }}
className="flex justify-between"
initial={{ opacity: 0, x: -6 }}
key={file}
transition={{ delay: 0.15 + row * 0.08 }}
>
<span>{file}</span>
<span>{group}</span>
</motion.li>
))}
<li className="pt-1">+ 30 more</li>
</ul>
) : undefined
}
status={call.status}
summary={call.summary}
/>
</motion.div>
))}
</AnimatePresence>
</div>
)
}