// Chat — native iOS chat with the Life OS LLM. Rich inline cards. // Wired to POST /api/chat — local messages state, typing indicator while waiting. function ChatScreen({ go }) { const d = LifeData; const [showMemory, setShowMemory] = React.useState(false); const [draft, setDraft] = React.useState(''); const [messages, setMessages] = React.useState([]); const [loading, setLoading] = React.useState(false); const [initialized, setInitialized] = React.useState(false); const scrollRef = React.useRef(null); const inputRef = React.useRef(null); const pollRef = React.useRef(null); const atBottomRef = React.useRef(true); const fetchHistory = async () => { try { const res = await fetch(`/api/chat/history?user=${d.user}&limit=40`); if (res.ok) { const { messages: msgs } = await res.json(); setMessages(msgs); setInitialized(true); } } catch {} }; React.useEffect(() => { fetchHistory(); pollRef.current = setInterval(fetchHistory, 5000); return () => clearInterval(pollRef.current); }, []); React.useEffect(() => { if (atBottomRef.current && scrollRef.current) { scrollRef.current.scrollTop = scrollRef.current.scrollHeight; } }, [messages, loading]); const onScroll = () => { if (!scrollRef.current) return; const { scrollTop, scrollHeight, clientHeight } = scrollRef.current; atBottomRef.current = scrollHeight - scrollTop - clientHeight < 60; }; const fmtTime = () => new Date().toLocaleTimeString('en-US', { hour: '2-digit', minute: '2-digit', hour12: false }); const send = async () => { const text = draft.trim(); if (!text || loading) return; setDraft(''); atBottomRef.current = true; const userMsg = { id: `u${Date.now()}`, role: 'user', time: fmtTime(), text }; setMessages(msgs => [...msgs, userMsg]); setLoading(true); try { const res = await fetch('/api/chat', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ message: text, user: d.user }), }); if (res.ok) { await fetchHistory(); } else { const asstMsg = { id: `a${Date.now()}`, role: 'assistant', time: fmtTime(), text: 'Sorry, something went wrong. Is the backend running?' }; setMessages(msgs => [...msgs, asstMsg]); } } catch (err) { const asstMsg = { id: `a${Date.now()}`, role: 'assistant', time: fmtTime(), text: 'Cannot reach Life OS backend. Check that the server is running.' }; setMessages(msgs => [...msgs, asstMsg]); } finally { setLoading(false); } }; const onKeyDown = (e) => { if (e.key === 'Enter' && !e.shiftKey) { e.preventDefault(); send(); } }; const fillDraft = (s) => { setDraft(s); if (inputRef.current) inputRef.current.focus(); }; return (