import React, { useState, useEffect } from 'react'; import { motion, AnimatePresence } from 'framer-motion'; const SEQUENCE = [ { type: 'user', text: "I messed up. Ate half a pizza. What now?", delay: 500 }, { type: 'thinking', duration: 1500 }, { type: 'ai', text: "No problem. That happens.", delay: 500 }, { type: 'ai', text: "I've adjusted tomorrow's plan: we'll drop fats slightly (-15g) to balance the weekly average. Enjoy your night! 🌙", delay: 1000 }, { type: 'reset', delay: 4000 } ]; export const ChatDemo: React.FC = () => { const [messages, setMessages] = useState([]); const [isTyping, setIsTyping] = useState(false); useEffect(() => { // Use ReturnType to ensure compatibility with both browser (number) and Node (Timeout) environments without requiring @types/node let timeoutIds: ReturnType[] = []; let mounted = true; const runSequence = async () => { if (!mounted) return; setMessages([]); setIsTyping(false); let accumulatedDelay = 0; SEQUENCE.forEach((step) => { accumulatedDelay += (step.delay || 0) + (step.duration || 0); const id = setTimeout(() => { if (!mounted) return; if (step.type === 'reset') { runSequence(); return; } if (step.type === 'thinking') { setIsTyping(true); setTimeout(() => { if(mounted) setIsTyping(false) }, step.duration); return; } if (step.type === 'user' || step.type === 'ai') { setMessages(prev => [...prev, { id: Date.now(), role: step.type, text: step.text }]); } }, accumulatedDelay); timeoutIds.push(id); }); }; runSequence(); return () => { mounted = false; timeoutIds.forEach(clearTimeout); }; }, []); return (
{/* Ambient Glow */}
{/* Copy */}
Judgment-Free Zone

A missed meal isn't a moral failure.
It's just data.

Life happens. FitMate's Rolling Adherence Buffer catches you when you fall. The system simply recalculates the optimal path forward using the remaining days in your week.

{/* Chat Simulation */}
{/* Header */}
FitMate Co-Pilot
{/* Messages Area */}
{messages.map((msg) => (
{msg.text}
))}
{/* Typing Indicator */} {isTyping && (
)}
); };