Vertical.ai / frontend /src /components /HeroAnimation.jsx
Abhisingh-18's picture
Mirror of github.com/Abhisingh18/Vertical.ai
1f7ead8 verified
Raw
History Blame Contribute Delete
13.8 kB
import React, { useState, useEffect } from 'react';
import { motion, AnimatePresence } from 'framer-motion';
import { Brain, FileText, Upload, Sparkles, MessageSquare, Plus, ChevronRight } from 'lucide-react';
const HeroAnimation = () => {
// Animation States: 'upload' | 'processing' | 'chat' | 'mindmap'
const [step, setStep] = useState('upload');
// Cycle through the animation loop
useEffect(() => {
const sequence = async () => {
while (true) {
setStep('upload');
await new Promise(r => setTimeout(r, 2000)); // Show upload
setStep('processing');
await new Promise(r => setTimeout(r, 1500)); // Show processing
setStep('chat');
await new Promise(r => setTimeout(r, 2000)); // Show chat typing
setStep('mindmap');
await new Promise(r => setTimeout(r, 4000)); // Show mind map
// Reset loop
setStep('reset');
await new Promise(r => setTimeout(r, 500));
}
};
sequence();
}, []);
return (
<div className="relative rounded-xl border border-slate-200 bg-white/50 backdrop-blur-sm shadow-2xl shadow-blue-900/10 overflow-hidden w-full max-w-4xl mx-auto aspect-[16/9]">
{/* Window Controls */}
<div className="bg-white border-b border-slate-100 p-3 flex items-center gap-2">
<div className="flex gap-1.5">
<div className="w-3 h-3 rounded-full bg-red-400/80"></div>
<div className="w-3 h-3 rounded-full bg-amber-400/80"></div>
<div className="w-3 h-3 rounded-full bg-green-400/80"></div>
</div>
<div className="flex-1 text-center">
<div className="bg-slate-100 text-slate-400 text-[10px] py-1 px-3 rounded-md inline-block font-mono">
vertical.ai / notebook
</div>
</div>
</div>
<div className="flex h-full pb-10">
{/* 1. Sidebar (Static) */}
<div className="w-1/4 border-r border-slate-100 bg-slate-50/50 p-4 hidden sm:flex flex-col gap-3">
<div className="w-full h-8 bg-blue-600/10 rounded-lg border border-blue-600/20 flex items-center px-3 gap-2">
<Plus size={14} className="text-blue-600" />
<span className="text-xs font-medium text-blue-700">New Note</span>
</div>
<div className="space-y-2 mt-2">
{[1, 2, 3].map((i) => (
<div key={i} className="flex items-center gap-2 px-2 py-1.5 rounded-md hover:bg-slate-200/50">
<FileText size={12} className="text-slate-400" />
<div className="h-2 w-16 bg-slate-200 rounded-full"></div>
</div>
))}
</div>
{/* Active Paper Upload */}
<AnimatePresence>
{step !== 'reset' && (
<motion.div
initial={{ opacity: 0, y: 20 }}
animate={{ opacity: 1, y: 0 }}
className="mt-auto bg-white border border-slate-200 p-3 rounded-lg shadow-sm"
>
<div className="flex items-center gap-2 mb-2">
<div className="p-1.5 bg-red-100 text-red-600 rounded">
<FileText size={14} />
</div>
<div className="text-[10px] font-medium text-slate-700 truncate">
Attention_Is_All_You_Need.pdf
</div>
</div>
<div className="h-1 w-full bg-slate-100 rounded-full overflow-hidden">
<motion.div
className="h-full bg-green-500"
initial={{ width: "0%" }}
animate={{ width: step === 'upload' ? "60%" : "100%" }}
transition={{ duration: 1 }}
/>
</div>
</motion.div>
)}
</AnimatePresence>
</div>
{/* 2. Main Area */}
<div className="flex-1 flex flex-col relative overflow-hidden">
{/* Chat Area */}
<div className="flex-1 p-6 space-y-6">
{/* User Message */}
<AnimatePresence>
{(step === 'processing' || step === 'chat' || step === 'mindmap') && (
<motion.div
initial={{ opacity: 0, x: 20 }}
animate={{ opacity: 1, x: 0 }}
className="flex justify-end"
>
<div className="bg-slate-100 text-slate-700 px-4 py-2.5 rounded-2xl rounded-tr-sm text-sm font-medium shadow-sm max-w-[80%]">
Explain the Transformer architecture logic.
</div>
</motion.div>
)}
</AnimatePresence>
{/* AI Response */}
<AnimatePresence>
{(step === 'chat' || step === 'mindmap') && (
<motion.div
initial={{ opacity: 0, x: -20 }}
animate={{ opacity: 1, x: 0 }}
transition={{ delay: 0.2 }}
className="flex gap-3"
>
<div className="w-8 h-8 rounded-lg bg-gradient-to-br from-blue-500 to-indigo-600 flex items-center justify-center text-white shadow-md shrink-0">
<Sparkles size={14} />
</div>
<div className="space-y-2 max-w-[90%]">
<div className="text-xs font-bold text-slate-800 flex items-center gap-2">
Vertical AI
<span className="text-[10px] px-1.5 py-0.5 bg-green-100 text-green-700 rounded-full font-medium">Verified Sources</span>
</div>
<div className="text-sm text-slate-600 leading-relaxed bg-white p-4 rounded-xl border border-slate-100 shadow-sm">
<p className="mb-2">The Transformer architecture replaces RNNs with self-attention mechanisms.</p>
<div className="flex gap-2 mt-3">
<div className="h-2 w-24 bg-slate-100 rounded animate-pulse"></div>
<div className="h-2 w-16 bg-slate-100 rounded animate-pulse"></div>
</div>
</div>
</div>
</motion.div>
)}
</AnimatePresence>
</div>
{/* Processing Overlay */}
<AnimatePresence>
{step === 'processing' && (
<motion.div
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
exit={{ opacity: 0 }}
className="absolute inset-0 bg-white/60 backdrop-blur-sm flex items-center justify-center z-10"
>
<div className="flex flex-col items-center gap-3">
<motion.div
animate={{ rotate: 360 }}
transition={{ repeat: Infinity, duration: 2, ease: "linear" }}
>
<Brain size={32} className="text-blue-500" />
</motion.div>
<div className="text-sm font-bold text-blue-900">Analyzing Knowledge Graph...</div>
</div>
</motion.div>
)}
</AnimatePresence>
{/* Mind Map Overlay (Pops up at end) */}
<AnimatePresence>
{step === 'mindmap' && (
<motion.div
initial={{ scale: 0.8, opacity: 0, y: 50 }}
animate={{ scale: 1, opacity: 1, y: 0 }}
transition={{ type: "spring", bounce: 0.4 }}
className="absolute bottom-6 right-6 bg-white border border-slate-200 shadow-2xl rounded-xl p-4 w-64 z-20"
>
<div className="flex items-center justify-between mb-3">
<div className="flex items-center gap-2">
<Brain size={16} className="text-purple-600" />
<span className="text-xs font-bold text-slate-700">Concept Map</span>
</div>
<ChevronRight size={14} className="text-slate-400" />
</div>
{/* Mock Graph Nodes */}
<div className="h-32 relative bg-slate-50 rounded-lg border border-slate-100 overflow-hidden">
<motion.div
initial={{ scale: 0 }} animate={{ scale: 1 }} transition={{ delay: 0.2 }}
className="absolute top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2 w-12 h-8 bg-blue-100 border border-blue-300 rounded flex items-center justify-center text-[8px] font-bold text-blue-700 z-10"
>
Attention
</motion.div>
{/* Satellite Nodes */}
{[0, 1, 2].map((i) => (
<motion.div
key={i}
initial={{ scale: 0, x: "-50%", y: "-50%" }}
animate={{
scale: 1,
left: i === 0 ? '20%' : i === 1 ? '80%' : '50%',
top: i === 0 ? '30%' : i === 1 ? '40%' : '80%'
}}
transition={{ delay: 0.4 + (i * 0.1) }}
className="absolute w-8 h-8 rounded-full bg-white border border-slate-200 shadow-sm flex items-center justify-center text-[10px]"
>
<div className="w-1.5 h-1.5 rounded-full bg-purple-400"></div>
</motion.div>
))}
{/* Connecting Lines */}
<svg className="absolute inset-0 w-full h-full pointer-events-none">
<motion.line
initial={{ pathLength: 0 }} animate={{ pathLength: 1 }}
x1="50%" y1="50%" x2="20%" y2="30%" stroke="#cbd5e1" strokeWidth="1"
/>
<motion.line
initial={{ pathLength: 0 }} animate={{ pathLength: 1 }}
x1="50%" y1="50%" x2="80%" y2="40%" stroke="#cbd5e1" strokeWidth="1"
/>
<motion.line
initial={{ pathLength: 0 }} animate={{ pathLength: 1 }}
x1="50%" y1="50%" x2="50%" y2="80%" stroke="#cbd5e1" strokeWidth="1"
/>
</svg>
</div>
</motion.div>
)}
</AnimatePresence>
</div>
</div>
</div>
);
};
export default HeroAnimation;