import { IconDots as MoreHorizontal, IconPlus as Plus, IconSettings as Settings, IconUsers as Users, } from '@tabler/icons-react'; import { type ClassValue, clsx } from 'clsx'; import { AnimatePresence, motion } from 'framer-motion'; import { useCallback, useEffect, useState } from 'react'; import { useTranslation } from 'react-i18next'; import { twMerge } from 'tailwind-merge'; import { Nav } from './Nav'; import { ShimmerBadge } from './ShimmerBadge'; import { CommandBox } from './WorkflowSection'; function cn(...inputs: ClassValue[]) { return twMerge(clsx(inputs)); } // Animation phases for the CLI type Phase = | 'typing' | 'intro' | 'projectName' | 'packageManager' | 'git' | 'config' | 'spinner' | 'success' | 'nextSteps' | 'outro'; // Clack-style CLI animation const TerminalWindow = ({ onComplete }: { onComplete: () => void }) => { const [typed, setTyped] = useState(''); const [phase, setPhase] = useState('typing'); const [spinnerStep, setSpinnerStep] = useState(0); const [spinnerChar, setSpinnerChar] = useState(0); const [selectedPm, setSelectedPm] = useState(0); const command = 'vibecarbon create'; const spinnerChars = ['◒', '◐', '◓', '◑']; const spinnerSteps = [ 'Creating project structure', 'Generating configuration files', 'Generating Docker configuration', 'Generating backend files', 'Installing dependencies with npm', 'Initializing git repository', ]; const pmOptions = ['npm', 'pnpm', 'bun']; // Typing animation useEffect(() => { if (phase !== 'typing') return; if (typed.length < command.length) { const timeout = setTimeout( () => { setTyped(command.slice(0, typed.length + 1)); }, 30 + Math.random() * 20 ); return () => clearTimeout(timeout); } const timeout = setTimeout(() => setPhase('intro'), 400); return () => clearTimeout(timeout); }, [typed, phase]); // Phase progression useEffect(() => { if (phase === 'intro') { const timeout = setTimeout(() => setPhase('projectName'), 600); return () => clearTimeout(timeout); } if (phase === 'projectName') { const timeout = setTimeout(() => setPhase('packageManager'), 800); return () => clearTimeout(timeout); } if (phase === 'packageManager') { // Animate through package manager selection const selectTimeout = setTimeout(() => { setSelectedPm(0); setTimeout(() => setPhase('git'), 400); }, 600); return () => clearTimeout(selectTimeout); } if (phase === 'git') { const timeout = setTimeout(() => setPhase('config'), 600); return () => clearTimeout(timeout); } if (phase === 'config') { const timeout = setTimeout(() => setPhase('spinner'), 800); return () => clearTimeout(timeout); } if (phase === 'spinner') { if (spinnerStep < spinnerSteps.length) { const timeout = setTimeout(() => { setSpinnerStep((s) => s + 1); }, 350); return () => clearTimeout(timeout); } const timeout = setTimeout(() => setPhase('success'), 300); return () => clearTimeout(timeout); } if (phase === 'success') { const timeout = setTimeout(() => setPhase('nextSteps'), 400); return () => clearTimeout(timeout); } if (phase === 'nextSteps') { const timeout = setTimeout(() => setPhase('outro'), 600); return () => clearTimeout(timeout); } if (phase === 'outro') { const timeout = setTimeout(onComplete, 1200); return () => clearTimeout(timeout); } }, [phase, spinnerStep, onComplete, spinnerSteps.length]); // Spinner character animation useEffect(() => { if (phase !== 'spinner' || spinnerStep >= spinnerSteps.length) return; const interval = setInterval(() => { setSpinnerChar((c) => (c + 1) % 4); }, 80); return () => clearInterval(interval); }, [phase, spinnerStep, spinnerSteps.length]); const getPhaseIndex = (p: Phase) => { const phases: Phase[] = [ 'typing', 'intro', 'projectName', 'packageManager', 'git', 'config', 'spinner', 'success', 'nextSteps', 'outro', ]; return phases.indexOf(p); }; const isPastPhase = (p: Phase) => getPhaseIndex(phase) > getPhaseIndex(p); const isAtOrPastPhase = (p: Phase) => getPhaseIndex(phase) >= getPhaseIndex(p); return (
{/* Terminal Header */}
Terminal
{/* CLI Content */}
{/* Command line */}
$ {typed} {phase === 'typing' && typed.length < command.length && ( )}
{/* Intro */} {isAtOrPastPhase('intro') && ( ◆ vibecarbon create )} {/* Project name prompt */} {isAtOrPastPhase('projectName') && ( │
{isPastPhase('projectName') ? ( <> ◇{' '} What is your project name?{' '} my-app ) : ( <> ◆{' '} What is your project name? my-app )}
)} {/* Package manager select */} {isAtOrPastPhase('packageManager') && ( │
{isPastPhase('packageManager') ? ( <> ◇{' '} Which package manager?{' '} npm ) : ( <> ◆{' '} Which package manager?
{pmOptions.map((pm, i) => (
{i === selectedPm ? '●' : '○'} {pm} {i === 0 && ( (recommended) )}
))}
)}
)} {/* Git confirm */} {isAtOrPastPhase('git') && ( │
◇{' '} Initialize a git repository?{' '} Yes
)} {/* Configuration box */} {isAtOrPastPhase('config') && (
Configuration
Project: my-app
Package manager: npm
Git: yes
)} {/* Spinner steps */} {isAtOrPastPhase('spinner') && (
{spinnerSteps.map((step, i) => { if (i > spinnerStep) return null; const isComplete = i < spinnerStep; const isCurrent = i === spinnerStep && phase === 'spinner'; return ( {isComplete ? ( ✓ ) : isCurrent ? ( {spinnerChars[spinnerChar]} ) : ( ✓ )} {step} ); })}
)} {/* Success */} {isAtOrPastPhase('success') && ( ◇ Project created successfully )} {/* Next steps */} {isAtOrPastPhase('nextSteps') && (
Next steps
cd my-app
npm run dev:start
)} {/* Outro */} {isAtOrPastPhase('outro') && ( ◇ Happy building! )}
); }; // SaaS Dashboard Preview const DashboardPreview = () => { const projects = [ { name: 'my-app', env: 'production', status: 'healthy', users: '1.2k' }, { name: 'staging', env: 'staging', status: 'healthy', users: '45' }, ]; return (
{/* Sidebar */}
C
JD
{/* Main content */}
{/* Header */}

Projects

Manage your deployments

{/* Projects list */}
{projects.map((project) => (
{project.name[0].toUpperCase()}
{project.name}
{project.env}
{project.status}
{project.users} users
))}
{/* Stats row */}
{[ { label: 'Total Users', value: '1,247', change: '+12%' }, { label: 'API Calls', value: '847K', change: '+8%' }, { label: 'Avg Latency', value: '24ms', change: '-5%' }, ].map((stat) => (
{stat.label}
{stat.value}
{stat.change}
))}
); }; export default function Hero() { const { t } = useTranslation(); const [terminalComplete, setTerminalComplete] = useState(false); const [animationKey, setAnimationKey] = useState(0); const handleTerminalComplete = useCallback(() => { setTerminalComplete(true); }, []); useEffect(() => { if (terminalComplete) { const timeout = setTimeout(() => { setTerminalComplete(false); setAnimationKey((k) => k + 1); }, 6000); return () => clearTimeout(timeout); } }, [terminalComplete]); return (
); }