import { useState, useRef, useEffect } from 'react' import { marked } from 'marked' import Dashboard from './Dashboard' import './App.css' import './Dashboard.css' marked.setOptions({ breaks: true, gfm: true }) const API_URL = window.location.hostname === 'localhost' ? 'http://localhost:4003' : 'https://diet-api.psbailey.uk' export default function App() { const [page, setPage] = useState('chat') const [messages, setMessages] = useState([]) const [input, setInput] = useState('') const [images, setImages] = useState([]) const [loading, setLoading] = useState(false) const [restarting, setRestarting] = useState(false) const messagesEnd = useRef(null) const inputRef = useRef(null) const fileRef = useRef(null) useEffect(() => { messagesEnd.current?.scrollIntoView({ behavior: 'smooth' }) }, [messages]) function handleImage(e) { const files = Array.from(e.target.files || []) for (const file of files) { const reader = new FileReader() reader.onload = () => { const base64 = reader.result.split(',')[1] setImages(prev => [...prev, { base64, preview: reader.result, mime: file.type }]) } reader.readAsDataURL(file) } } function removeImage(idx) { setImages(prev => prev.filter((_, i) => i !== idx)) if (fileRef.current) fileRef.current.value = '' } async function send(e) { e.preventDefault() const text = input.trim() if ((!text && !images.length) || loading) return setInput('') const userMsg = { role: 'user', content: text, images: images.map(i => i.preview) } setMessages(prev => [...prev, userMsg]) setLoading(true) const payload = { message: text || 'Analyse these images' } if (images.length) payload.images = images.map(i => ({ base64: i.base64, mime: i.mime })) setImages([]) if (fileRef.current) fileRef.current.value = '' try { const res = await fetch(`${API_URL}/chat`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(payload) }) if (!res.ok) throw new Error('Failed') const reader = res.body.getReader() const decoder = new TextDecoder() let assistantMsg = '' setMessages(prev => [...prev, { role: 'assistant', content: '' }]) while (true) { const { done, value } = await reader.read() if (done) break const chunk = decoder.decode(value, { stream: true }) for (const line of chunk.split('\n')) { if (line.startsWith('data: ')) { const data = line.slice(6) if (data === '[DONE]') break assistantMsg += data.replace(/\\n/g, '\n') setMessages(prev => { const updated = [...prev] updated[updated.length - 1] = { role: 'assistant', content: assistantMsg } return updated }) } } } } catch (err) { setMessages(prev => [...prev, { role: 'assistant', content: '⚠️ Connection error. Try again.' }]) } finally { setLoading(false) inputRef.current?.focus() } } if (page === 'dashboard') return setPage('chat')} /> async function restartApi() { setRestarting(true) try { await fetch(`${API_URL}/restart`, { method: 'POST', headers: { Authorization: 'my-health-data' } }) } catch {} setTimeout(() => setRestarting(false), 5000) } return (

🥗 Diet Coach

{messages.length === 0 && (
Ask me about your diet, log food, or send a screenshot of your Lose It report.
)} {messages.map((msg, i) => (
{msg.role === 'assistant' ?
:
{msg.images?.map((src, j) => )} {msg.content}
}
))}
{images.length > 0 && (
{images.map((img, i) => ( ))}
)}
setInput(e.target.value)} placeholder="What did you eat today?" disabled={loading} autoFocus />
) }