From 710d672307a3030359f673856ad32df644187648 Mon Sep 17 00:00:00 2001 From: AI Company Date: Tue, 21 Jul 2026 18:36:41 +0000 Subject: [PATCH] feat: initial MetaBloom cross-platform MVP --- .claude/settings.json | 5 + .gitignore | 5 + AGENTS.md | 3 + App.tsx | 56 + CLAUDE.md | 1 + LICENSE | 21 + README.md | 76 + app.json | 26 + assets/android-icon-background.png | Bin 0 -> 17549 bytes assets/android-icon-foreground.png | Bin 0 -> 78796 bytes assets/android-icon-monochrome.png | Bin 0 -> 4140 bytes assets/favicon.png | Bin 0 -> 1129 bytes assets/icon.png | Bin 0 -> 393493 bytes assets/splash-icon.png | Bin 0 -> 17547 bytes index.ts | 8 + package-lock.json | 6246 ++++++++++++++++++++++++++++ package.json | 28 + tsconfig.json | 6 + 18 files changed, 6481 insertions(+) create mode 100644 .claude/settings.json create mode 100644 .gitignore create mode 100644 AGENTS.md create mode 100644 App.tsx create mode 100644 CLAUDE.md create mode 100644 LICENSE create mode 100644 README.md create mode 100644 app.json create mode 100644 assets/android-icon-background.png create mode 100644 assets/android-icon-foreground.png create mode 100644 assets/android-icon-monochrome.png create mode 100644 assets/favicon.png create mode 100644 assets/icon.png create mode 100644 assets/splash-icon.png create mode 100644 index.ts create mode 100644 package-lock.json create mode 100644 package.json create mode 100644 tsconfig.json diff --git a/.claude/settings.json b/.claude/settings.json new file mode 100644 index 0000000..176e6a5 --- /dev/null +++ b/.claude/settings.json @@ -0,0 +1,5 @@ +{ + "enabledPlugins": { + "expo@claude-plugins-official": true + } +} diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..4774329 --- /dev/null +++ b/.gitignore @@ -0,0 +1,5 @@ +node_modules/ +dist/ +.expo/ +*.log +.DS_Store diff --git a/AGENTS.md b/AGENTS.md new file mode 100644 index 0000000..0e6bc80 --- /dev/null +++ b/AGENTS.md @@ -0,0 +1,3 @@ +# Expo HAS CHANGED + +Read the exact versioned docs at https://docs.expo.dev/versions/v57.0.0/ before writing any code. diff --git a/App.tsx b/App.tsx new file mode 100644 index 0000000..9270c97 --- /dev/null +++ b/App.tsx @@ -0,0 +1,56 @@ +import AsyncStorage from '@react-native-async-storage/async-storage'; +import { LinearGradient } from 'expo-linear-gradient'; +import { StatusBar } from 'expo-status-bar'; +import React, { useEffect, useMemo, useState } from 'react'; +import { Alert, Pressable, SafeAreaView, ScrollView, StyleSheet, Text, TextInput, View, useWindowDimensions } from 'react-native'; + +type Tab = 'today' | 'journal' | 'progress' | 'profile'; +type Entry = { date: string; weight: number; water: number; steps: number; glucose?: number }; +const C = { ink:'#15211c', muted:'#637169', cream:'#f6f7f2', white:'#fff', green:'#1d6b50', mint:'#dcefe5', coral:'#ef835f', yellow:'#f4d58d', line:'#e5e9e3' }; +const seed: Entry[] = [ + {date:'Lun',weight:91.4,water:1500,steps:6100,glucose:112},{date:'Mar',weight:91.2,water:1800,steps:7400,glucose:108}, + {date:'Mer',weight:91.1,water:1600,steps:6900,glucose:110},{date:'Jeu',weight:90.9,water:2000,steps:8200,glucose:105}, + {date:'Ven',weight:90.8,water:2100,steps:8600,glucose:104},{date:'Sam',weight:90.7,water:1750,steps:7200,glucose:107},{date:'Auj.',weight:90.6,water:1250,steps:6240,glucose:103}, +]; + +function Card({children, style}: any){return {children}} +function Pill({children, tone='green'}:any){return {children}} +function Metric({emoji,label,value,sub,onPress}:any){return [s.metric,pressed&&{opacity:.7}]}>{emoji}{label}{value}{sub}} +function ProgressBar({value,color=C.green}:any){return } +function Nav({tab,setTab}:any){const items:[Tab,string,string][]=[['today','⌂','Aujourd’hui'],['journal','+','Journal'],['progress','↗','Progrès'],['profile','○','Profil']];return {items.map(([id,icon,label])=>setTab(id)} style={s.navItem}>{icon}{label})}} + +export default function App(){ + const [tab,setTab]=useState('today'); const [entries,setEntries]=useState(seed); const [water,setWater]=useState(1250); const [steps,setSteps]=useState(6240); + const [weight,setWeight]=useState('90.6'); const [glucose,setGlucose]=useState('103'); const [saved,setSaved]=useState(false); const {width}=useWindowDimensions(); + useEffect(()=>{AsyncStorage.getItem('metabloom').then(v=>{if(v){const d=JSON.parse(v);setEntries(d.entries||seed);setWater(d.water||1250);setSteps(d.steps||6240)}}).catch(()=>{})},[]); + useEffect(()=>{AsyncStorage.setItem('metabloom',JSON.stringify({entries,water,steps})).catch(()=>{})},[entries,water,steps]); + const score=Math.round((Math.min(water/2000,1)+Math.min(steps/8000,1)+.82+.75)*25); + const save=()=>{const w=parseFloat(weight.replace(',','.')); const g=parseFloat(glucose); if(!w||w<30||w>300){Alert.alert('Valeur à vérifier','Saisis un poids compris entre 30 et 300 kg.');return} setEntries(x=>[...x.slice(0,6),{date:'Auj.',weight:w,water,steps,glucose:g||undefined}]);setSaved(true);setTimeout(()=>setSaved(false),2200)}; + const content=useMemo(()=>tab==='today'?:tab==='journal'?:tab==='progress'?:{setEntries(seed);setWater(1250);setSteps(6240);AsyncStorage.removeItem('metabloom')}}/>,[tab,score,water,steps,weight,glucose,saved,entries]); + return 900?1120:640}]}>{content}