import { useState, useEffect } from 'react'; import { motion, AnimatePresence } from 'motion/react'; interface HandwrittenTextProps { text: string; className?: string; style?: React.CSSProperties; speed?: number; // Characters per second startDelay?: number; // Delay before animation starts (ms) onComplete?: () => void; autoStart?: boolean; } export function HandwrittenText({ text, className = "", style = {}, speed = 8, startDelay = 0, onComplete, autoStart = true }: HandwrittenTextProps) { const [displayedText, setDisplayedText] = useState(''); const [currentIndex, setCurrentIndex] = useState(0); const [isWriting, setIsWriting] = useState(false); const [showCursor, setShowCursor] = useState(false); // Split text into characters, preserving line breaks const characters = text.split(''); useEffect(() => { if (!autoStart) return; const startTimer = setTimeout(() => { setIsWriting(true); setShowCursor(true); }, startDelay); return () => clearTimeout(startTimer); }, [autoStart, startDelay]); useEffect(() => { if (!isWriting || currentIndex >= characters.length) { if (currentIndex >= characters.length) { // Hide cursor after a delay const cursorTimer = setTimeout(() => { setShowCursor(false); setIsWriting(false); onComplete?.(); }, 800); return () => clearTimeout(cursorTimer); } return; } const char = characters[currentIndex]; const baseDelay = 1000 / speed; // Variable delays for more natural writing let delay = baseDelay; if (char === ' ') { delay = baseDelay * 0.5; // Spaces are quicker } else if (char === '\n') { delay = baseDelay * 2; // Line breaks take longer } else if (['.', '!', '?'].includes(char)) { delay = baseDelay * 1.5; // Punctuation takes a bit longer } else if ([',', ';', ':'].includes(char)) { delay = baseDelay * 1.2; } else { // Add some randomness to letter timing delay = baseDelay * (0.8 + Math.random() * 0.4); } const timer = setTimeout(() => { setDisplayedText(prev => prev + char); setCurrentIndex(prev => prev + 1); }, delay); return () => clearTimeout(timer); }, [isWriting, currentIndex, characters, speed, onComplete]); // Reset function for external control const reset = () => { setDisplayedText(''); setCurrentIndex(0); setIsWriting(false); setShowCursor(false); }; const start = () => { reset(); setTimeout(() => { setIsWriting(true); setShowCursor(true); }, startDelay); }; // Expose control methods useEffect(() => { if (typeof window !== 'undefined') { (window as any).handwrittenTextControls = { reset, start }; } }, []); return (