# Daroms Speed App : documentation complète > Framework mobile Expo + Tamagui + Supabase piloté par Claude : le cœur fournit le répétitif, l'app n'écrit que son spécifique, pour sortir une app par jour. # Introduction > Ce qu'est Daroms Speed App, à qui il s'adresse, ses principes et ce qu'il fournit, en une page. Daroms Speed App est un framework mobile **Expo SDK 58 + Tamagui v3 + Supabase** conçu pour être piloté par Claude Code. Son objectif : partir d'une idée le matin et installer l'app sur un téléphone le soir, avec un code propre et vérifié. ## Le principe : un cœur, des apps Le dépôt `daroms-speed-app` est un **cœur versionné**. Chaque app l'embarque en sous-module git dans `core/` et n'écrit que son spécifique : | Fourni par le cœur (`core/`) | Écrit dans l'app | |---|---| | Thème, 32 composants, icônes (`@daroms/ui`, `@daroms/theme`) | `brand.ts` : 5 valeurs de marque | | Client Supabase, erreurs, requêtes (`@daroms/data`) | `src/features//` : écrans, hooks, `api.ts` | | Modules : auth, paiements, push, chat IA, cartes... | `app.manifest.ts` : modules activés, onglets | | Générateurs, CLI `daroms`, couche IA (`_ai/`) | `locales/*.json`, `supabase/migrations/` | Mettre à jour le cœur dans une app = changer de version du sous-module. Voir [l'architecture](https://daromsartof.github.io/daroms-speedapp/explications/architecture.md). ## Les cinq idées du framework 1. **Générer avant d'écrire.** Écran, table, module : `bun daroms g ...` produit le répétitif à partir de gabarits, sans IA. Claude remplit le reste. 2. **Des règles vérifiées, pas des conseils.** Couleurs en dur, Supabase dans un composant, texte hors i18n : un hook refuse l'écriture et explique quoi faire. Voir [les règles](https://daromsartof.github.io/daroms-speedapp/reference/regles.md). 3. **Une marque, un thème.** `brand.ts` (couleur principale, accent, police, rayon, ton) génère palettes, thèmes clair et sombre et sous-thèmes. Voir [la marque](https://daromsartof.github.io/daroms-speedapp/reference/marque.md). 4. **Une fonctionnalité optionnelle est un module.** `bun daroms module add payments` ajoute dépendances, routes, migrations et configuration. Voir [les modules](https://daromsartof.github.io/daroms-speedapp/reference/modules/auth.md). 5. **Peu de tokens.** Un noyau court toujours chargé, des règles chargées selon les fichiers touchés, un catalogue de composants à la place des sources. Voir [la couche IA](https://daromsartof.github.io/daroms-speedapp/explications/couche-ia.md). ## Pour qui - Un développeur ou une petite équipe qui sort beaucoup d'apps mobiles et veut le même socle partout. - Un agent (Claude Code) qui travaille dans une app daroms : cette documentation existe aussi en Markdown, voir [Utiliser avec un agent](https://daromsartof.github.io/daroms-speedapp/agents/utiliser-avec-un-agent.md). ## Choix figés - **Expo** (Expo Router, New Architecture, React Native 0.88), **EAS** pour build, soumission et mises à jour OTA. - **Tamagui v3** pour le thème et l'UI : aucune autre librairie UI, un seul jeu d'icônes (Lucide). - **Supabase** : authentification, Postgres avec RLS, Storage, edge functions. - **Bun** pour les workspaces, la CLI et les scripts ; TypeScript strict. ## Par où commencer - Installer le cœur et créer une première app : [Installation](https://daromsartof.github.io/daroms-speedapp/demarrer/installation.md). - Faire une app complète, étape par étape : [Tutoriel : ton app en un jour](https://daromsartof.github.io/daroms-speedapp/demarrer/tutoriel.md). - Comprendre ce que contient une app : [Structure d'une app](https://daromsartof.github.io/daroms-speedapp/demarrer/structure-app.md). --- Page HTML : https://daromsartof.github.io/daroms-speedapp/demarrer/introduction/ Index de la documentation : https://daromsartof.github.io/daroms-speedapp/llms.txt # Installation > Prérequis, installation du cœur, commande daroms globale et création d'une première app lancée sur téléphone. ## Prérequis | Outil | Version | Pourquoi | |---|---|---| | Node | >= 22.18 | `app.config.ts` charge le manifest TypeScript directement | | Bun | >= 1.3 | workspaces, CLI `daroms`, tests de la CLI | | Python 3 | - | hooks de la couche IA (`_ai/hooks/*.py`) | | Git | - | le cœur est un sous-module de chaque app | | Expo Go (téléphone) | SDK 58 | aperçu immédiat par QR | | Android Studio ou Xcode | facultatif | émulateur, simulateur, builds locaux | Comptes : Expo (EAS) et Supabase pour développer ; Apple Developer et Google Play pour publier. ## Créer une première app Le framework est publié sur npm (`@daromsart/daroms-speed-app`, organisation `daromsart`). Depuis le dossier qui contiendra tes projets : ```bash npx @daromsart/daroms-speed-app new mon-app --name "Mon app" --variant list-detail cd mon-app cp .env.example .env # URL et clé publique Supabase bun daroms dev # scanner le QR avec Expo Go ``` Commande globale facultative : `npm install -g @daromsart/daroms-speed-app`, puis `daroms new mon-app` partout. La CLI s'exécute avec Bun : `npx` sans Bun installé ne fonctionne pas. `daroms new` copie le template, ajoute le cœur en sous-module `core/`, écrit `brand.ts` et `app.manifest.ts`, active les modules de la variante, génère tables et écrans, les icônes, installe les dépendances, synchronise la couche IA dans `.claude/` et fait le commit initial. Toutes les options : [CLI, `new`](https://daromsartof.github.io/daroms-speedapp/reference/cli.md#new). D'où vient `core/` : | Cas | Résultat | |---|---| | CLI installée depuis npm, dépôt du cœur accessible | sous-module git fixé sur le tag de la version installée (`v0.2.0`...) | | CLI installée depuis npm, dépôt inaccessible | copie du cœur contenu dans le paquet (sans sous-module) | | CLI lancée depuis un clone du cœur | sous-module du remote du clone, branche par défaut | | `--copy`, `--link`, `--core ` | copie, lien symbolique, sous-module de l'URL donnée | ## Travailler sur le cœur lui-même Pour modifier le framework (pas pour créer une app) : ```bash git clone https://github.com/daromsartof/daroms-speedapp.git daroms-speed-app cd daroms-speed-app bun install bun run check # tsc, eslint, jest, tests CLI, tests des hooks ``` `bun link` dans le clone rend la commande `daroms` globale et pointée sur tes sources. Voir [Développer le cœur](https://daromsartof.github.io/daroms-speedapp/guides/developper-le-coeur.md). :::tip[Sans Supabase pour commencer] `--local` garde les tables de la variante sur l'appareil (mêmes hooks). Sans `.env`, l'app démarre et Supabase reste désactivé. ::: ## Vérifier l'installation ```bash bun daroms doctor # Node, .env, manifest, i18n, règles, types, lint, expo-doctor bun daroms shot / # rendu web de l'accueil : erreurs et débordements ``` Écran blanc, QR qui ne charge pas, module natif absent dans Expo Go : voir [Dépanner](https://daromsartof.github.io/daroms-speedapp/guides/depanner.md). ## Avec Claude Code Ouvrir Claude Code dans le dossier de l'app : au démarrage de session, le hook `daroms-sync` copie règles, skills et hooks de `core/_ai/` dans `.claude/`. Ensuite : ```text /daroms-day Une app pour suivre ses habitudes de lecture ``` La journée complète est décrite dans le [tutoriel](https://daromsartof.github.io/daroms-speedapp/demarrer/tutoriel.md). --- Page HTML : https://daromsartof.github.io/daroms-speedapp/demarrer/installation/ Index de la documentation : https://daromsartof.github.io/daroms-speedapp/llms.txt # Tutoriel : ton app en un jour > Une journée complète, de l'idée à l'app installée sur téléphone puis soumise aux stores, avec les skills Claude et les commandes daroms. Objectif : partir d'une idée le matin et installer l'app sur un téléphone le soir, en laissant Claude faire le répétitif. Les durées sont celles de l'app pilote (Lecture, un suivi de lecture). :::note[En une commande] `/daroms-day ` enchaîne toutes les étapes ci-dessous et s'arrête pour ta validation entre chacune. Ce tutoriel détaille ce qu'il fait, pour pouvoir lancer une étape seule. ::: ## Prérequis (une fois) - Node >= 22.18, Bun >= 1.3, Python 3, Git ; Android Studio (émulateur) ou Xcode (simulateur) facultatifs. - Comptes : Expo (EAS), Supabase ; Apple Developer et Google Play pour publier. - La CLI : rien à installer, `npx @daromsart/daroms-speed-app` la télécharge depuis npm ; ou `npm install -g @daromsart/daroms-speed-app` pour avoir `daroms` partout. Détail : [Installation](https://daromsartof.github.io/daroms-speedapp/demarrer/installation.md). ## Matin : cadrer et créer (1 h) 1. Ouvrir Claude Code dans le dossier des projets et lancer `/daroms-day Une app pour suivre ses habitudes de lecture`. 2. **Cadrage** (`/daroms-idea`) : Claude écrit `app.spec.md` (cible, boucle principale, 3 à 6 écrans, tables, modules, monétisation, nom, ton, variante). Relire et valider : c'est la source unique des étapes suivantes. 3. **Création** (`/daroms-new`) : une commande crée l'app complète : ```bash npx @daromsart/daroms-speed-app new lecture --name "Lecture" --variant tracker --primary "#0F766E" --accent "#F59E0B" --tone minimal ``` Template, sous-module `core/`, `brand.ts`, `app.manifest.ts`, modules de la variante, icônes générées, dépendances, couche IA (`.claude/`), commit initial. 4. `cd lecture && cp .env.example .env` (URL et clé publique Supabase), puis `bun daroms dev` : scanner le QR avec Expo Go. ## Midi : inspiration, marque, design, données (2 h) 5. **Inspiration** (`/daroms-inspire`) : Claude cherche des maquettes et des apps publiées sur Dribbble, Pinterest, Behance, Mobbin et Screens Design, regarde les aperçus (`bun daroms inspire `), propose 2 ou 3 directions et écrit celle que tu choisis dans `app.spec.md > Inspiration`. Il s'en inspire sans rien copier. 6. **Marque** (`/daroms-brand`) : 5 valeurs dans `brand.ts`, icône SVG dans `assets/source/icon.svg`, puis `bun daroms assets`. Vérifier `/_dev/kitchen-sink` en clair et en sombre. Voir [Changer la marque](https://daromsartof.github.io/daroms-speedapp/guides/changer-la-marque.md). 7. **Design** (`/daroms-design`) : wireframes texte par écran dans `app.spec.md`, uniquement avec les composants du [catalogue](https://daromsartof.github.io/daroms-speedapp/reference/composants.md). 8. **Données** (`/daroms-data`) : `bun daroms g table books --fields "title:text, author:text?, pages:int=0"` crée la migration avec RLS, le schéma zod, les hooks `useBooks`, `useCreateBook`... puis `bun daroms db push`. Voir [Ajouter une table](https://daromsartof.github.io/daroms-speedapp/guides/ajouter-une-table.md). ## Après-midi : écrans et modules (3 h) 9. **Écrans** (`/daroms-screen books --tab --icon BookOpen`) : route mince, feature, 4 états, textes FR/EN, test ; Claude remplit avec `@daroms/ui` et vérifie avec `bun daroms shot /books`. Voir [Ajouter un écran](https://daromsartof.github.io/daroms-speedapp/guides/ajouter-un-ecran.md). 10. **Modules** (`/daroms-module payments`) : paywall RevenueCat, statut Pro, restauration ; idem `push`, `ai-chat`, `analytics`, `camera` (photos vers Supabase Storage), `maps` (carte, position). Voir [Ajouter un module](https://daromsartof.github.io/daroms-speedapp/guides/ajouter-un-module.md). 11. **Vérification** (`/daroms-check`) puis **finition** (`/daroms-polish`) : revue visuelle par l'agent `daroms-designer`. Voir [Vérifier](https://daromsartof.github.io/daroms-speedapp/guides/verifier.md). ## Soir : publier (1 h) 12. **Fiche store** (`/daroms-store`) : titres, mots-clés, descriptions FR/EN, captures, politique de confidentialité. 13. **Build** (`/daroms-build preview`) : installation interne par QR ; puis `production` et `/daroms-submit` (TestFlight, piste interne). Voir [Publier](https://daromsartof.github.io/daroms-speedapp/guides/publier.md). 14. **Bilan** : `/daroms-learn` (zones de contexte), puis `bun daroms cost` écrit `app.cost.md` : tokens et prix de l'app par étape, par modèle et par session, au tarif de l'API Anthropic. C'est la référence à faire baisser à l'app suivante. Enfin `/daroms-commit`. ## Et ensuite - Une correction JavaScript sans changement natif : `bun daroms update production --message "..."`. - Une nouvelle version du cœur : [Mettre à jour le cœur](https://daromsartof.github.io/daroms-speedapp/guides/mettre-a-jour-le-coeur.md). --- Page HTML : https://daromsartof.github.io/daroms-speedapp/demarrer/tutoriel/ Index de la documentation : https://daromsartof.github.io/daroms-speedapp/llms.txt # Structure d'une app > Arborescence d'une app créée par daroms new, rôle de chaque fichier et ce qui est généré ou écrit à la main. Une app daroms ne contient que son spécifique ; le reste vient de `core/`. ```text mon-app/ core/ sous-module daroms-speed-app (ne jamais modifier depuis l'app) app/ Expo Router : routes minces uniquement _layout.tsx (tabs)/ (auth)/ +not-found.tsx _dev/kitchen-sink.tsx src/ features// XScreen.tsx, hooks.ts, api.ts, schema.ts, components/, __tests__/ lib/ fonctions pures de l'app, testées types/db.ts types Supabase générés (bun daroms db types) generated/ registre des modules, généré (ne pas éditer) brand.ts 5 valeurs de marque app.manifest.ts nom, identifiants, modules, onglets, écrans, monétisation app.config.ts configuration Expo dérivée du manifest (withDaroms) app.spec.md cahier de l'app (écrit par /daroms-idea) : source unique pour Claude locales/fr.json en.json textes de l'app, mêmes clés partout supabase/migrations/ structure de la base, RLS assets/source/icon.svg source de l'icône, du splash et du favicon e2e/*.yaml flows Maestro CLAUDE.md @core/_ai/CLAUDE.core.md + spécificités de l'app .claude/ règles, skills, hooks synchronisés (daroms-*) + fichiers app-* eas.json .env.example ``` ## Une feature Chaque écran a une route d'une ligne et un dossier de feature : ```tsx // app/(tabs)/books.tsx export { default } from '@/features/books/BooksScreen' ``` | Fichier | Contient | Règle | |---|---|---| | `BooksScreen.tsx` | l'UI, uniquement `@daroms/ui` et tokens | 4 états, <= 200 lignes | | `hooks.ts` | la logique de l'écran (requêtes, état local) | pas de JSX | | `api.ts` | hooks TanStack Query, seul endroit où l'on parle à Supabase | sorties validées zod | | `schema.ts` | schémas zod (ligne, saisie) et types | - | | `components/` | composants propres à la feature | tokens seulement | ## Généré, synchronisé, écrit à la main | Généré par la CLI | Synchronisé depuis le cœur | Écrit à la main | |---|---|---| | `src/generated/`, `src/types/db.ts`, `assets/*.png` | `.claude/rules/daroms-*`, `.claude/skills/daroms-*`, hooks | `src/features/`, `src/lib/` | | squelettes `g screen`, `g table` (puis complétés) | `CATALOG.md`, `digest.md` (dans `core/`) | `locales/*.json`, `app.spec.md` | | `app.manifest.ts` (réécrit par `g screen`, `module add`) | | `brand.ts`, migrations spécifiques | Les fichiers `.claude/` préfixés `daroms-` sont écrasés à chaque synchronisation ; ce qui est propre à l'app porte le préfixe `app-` et n'est jamais touché. ## Imports - `@/` pointe vers `src/` (`@/features/books/api`), `~/` vers la racine (`~/brand`). - Paquets du cœur : `@daroms/ui`, `@daroms/ui/icons`, `@daroms/data`, `@daroms/lib`, `@daroms/i18n`, `@daroms/app`, et `@daroms/module-` pour un module actif. Détail : [Paquets](https://daromsartof.github.io/daroms-speedapp/reference/paquets.md). --- Page HTML : https://daromsartof.github.io/daroms-speedapp/demarrer/structure-app/ Index de la documentation : https://daromsartof.github.io/daroms-speedapp/llms.txt # Ajouter un écran > Créer un écran complet avec daroms g screen (route mince, feature, i18n, test, manifest), le remplir avec @daroms/ui et le vérifier. ## 1. Générer ```bash bun daroms g screen books --tab --icon BookOpen --title "Livres" --title-en "Books" ``` | Créé ou modifié | Rôle | |---|---| | `app/(tabs)/books.tsx` | route mince : `export { default } from '@/features/books/BooksScreen'` | | `src/features/books/BooksScreen.tsx` | l'écran, déjà branché sur `QueryView` (4 états) | | `src/features/books/hooks.ts`, `api.ts`, `schema.ts` | logique, données, schémas zod | | `src/features/books/__tests__/BooksScreen.test.tsx` | test de rendu | | `locales/fr.json`, `locales/en.json` | clés `books.title`, `books.emptyTitle`, `books.emptyDescription` | | `app.manifest.ts` | écran ajouté à `screens`, onglet ajouté à `tabs` (avec `--tab`) | Options : `--tab` (onglet), `--icon NomLucide` (icône d'onglet et d'état vide), `--route chemin` (défaut `(tabs)/` avec `--tab`, sinon ``), `--force` (réécrit). Feature sans route : `bun daroms g feature `. ## 2. Brancher les données Si l'écran affiche une table, la créer d'abord ([Ajouter une table](https://daromsartof.github.io/daroms-speedapp/guides/ajouter-une-table.md)) : l'écran se branche sur `useBooks()`. ```ts // src/features/books/hooks.ts import { useBooks } from './api' export function useBooksScreen() { const query = useBooks() return { query } } ``` ## 3. Remplir avec @daroms/ui Lire le [catalogue](https://daromsartof.github.io/daroms-speedapp/reference/composants.md) et composer. Les 4 états (chargement, vide, erreur avec réessai, données) sont gérés par `QueryView` ou `List` : ```tsx import { useT } from '@daroms/i18n' import { Header, List, ListItem, Screen } from '@daroms/ui' import { BookOpen } from '@daroms/ui/icons' import { useCallback } from 'react' import { useBooksScreen } from './hooks' import type { Book } from './schema' export default function BooksScreen() { const t = useT() const { query } = useBooksScreen() const renderItem = useCallback(({ item }: { item: Book }) => , []) return ( }> ) } ``` Ce que les hooks refusent ici : couleur hex, `p={14}`, `StyleSheet.create`, texte en dur dans le JSX, import de Supabase. Voir [les règles](https://daromsartof.github.io/daroms-speedapp/reference/regles.md). ## 4. Vérifier ```bash bun daroms shot /books # erreurs, débordements, capture si utile bun daroms shot /books --dark # mode sombre bun daroms test books # test de la feature ``` Détail : [Vérifier](https://daromsartof.github.io/daroms-speedapp/guides/verifier.md). ## Avec Claude ```text /daroms-screen books --tab --icon BookOpen ``` Le skill lit le wireframe de l'écran dans `app.spec.md`, lance `g screen`, remplit l'écran, ajoute les textes FR/EN, écrit le test et contrôle le rendu clair et sombre. --- Page HTML : https://daromsartof.github.io/daroms-speedapp/guides/ajouter-un-ecran/ Index de la documentation : https://daromsartof.github.io/daroms-speedapp/llms.txt # Ajouter une table > Créer une table Supabase avec RLS, schéma zod et hooks TanStack Query en une commande, ou une table locale sur l'appareil avec les mêmes hooks. ## Table Supabase ```bash bun daroms g table books --fields "title:text, author:text?, pages:int=0, finished_on:date?" ``` | Créé | Contenu | |---|---| | `supabase/migrations/_books.sql` | table, RLS propriétaire sur les 4 opérations, index `user_id`, trigger `updated_at` | | `src/features/books/schema.ts` | `BookSchema` (ligne) et `BookInputSchema` (saisie), types `Book`, `BookInput` | | `src/features/books/api.ts` | `useBooks`, `useBook(id)`, `useCreateBook`, `useUpdateBook`, `useDeleteBook` | | types hors ligne | en attendant `db types` | ### Champs Format : `nom:type`, `?` pour facultatif, `=valeur` pour un défaut, séparés par des virgules. | Type | SQL | zod | |---|---|---| | `text` | `text` | `z.string()` | | `int` | `integer` | `z.number().int()` | | `bigint` | `bigint` | `z.number().int()` | | `numeric`, `float` | `numeric`, `double precision` | `z.number()` | | `bool` | `boolean` | `z.boolean()` | | `date`, `timestamptz` | `date`, `timestamptz` | `z.string()` | | `uuid` | `uuid` | `z.string().uuid()` | | `jsonb` | `jsonb` | `z.unknown()` | | `text[]` | `text[]` | `z.array(z.string())` | Options : `--feature ` (dossier de feature, défaut : nom de la table avec tirets), `--singular ` (défaut : nom sans le `s` final). ### Appliquer Projet Supabase lié (`manifest.supabase.projectRef`) : ```bash bun daroms db push # applique les migrations et régénère src/types/db.ts ``` ### La migration générée Chaque utilisateur ne voit que ses lignes : ```sql create table public.books ( id uuid primary key default gen_random_uuid(), user_id uuid not null default auth.uid() references auth.users (id) on delete cascade, title text not null, author text, pages integer not null default 0, created_at timestamptz not null default now(), updated_at timestamptz not null default now() ); alter table public.books enable row level security; create policy "books lecture propriétaire" on public.books for select to authenticated using ((select auth.uid()) = user_id); -- idem insert, update, delete ``` Relations, lecture publique, partage, Storage, realtime : compléter la migration, voir [Données et sécurité](https://daromsartof.github.io/daroms-speedapp/explications/donnees-et-securite.md). ### Les hooks générés ```ts const books = useBooks() // useQuery, liste la plus récente d'abord const create = useCreateBook() // useMutation, invalide la liste create.mutate({ title: 'Dune', pages: 0 }) // entrée validée par BookInputSchema ``` Toute réponse passe par `parseOrThrow(schema, ...)` : une donnée inattendue devient une `AppError` `validation` affichable par `ErrorState`. ## Table locale (sans Supabase) App sans compte ni synchronisation (suivi perso, carnet) : ```bash bun daroms g table books --fields "title:text, pages:int=0" --local ``` - `api.ts` utilise `localTable()` de `@daroms/data` (MMKV, sinon kv-store dans Expo Go) : mêmes hooks (`useBooks`, `useCreateBook`...), mêmes lignes (`id`, `created_at`, `updated_at`), défauts appliqués par zod. - Aucune migration ; la table n'apparaît pas dans `src/types/db.ts`. - Passer à Supabase plus tard : relancer la même commande sans `--local` (régénère `schema.ts` et `api.ts`, ajoute la migration) ; les écrans ne changent pas, les données déjà sur l'appareil ne sont pas migrées. - Une ligne stockée devenue invalide (champ obligatoire ajouté sans défaut) lève une erreur `validation` au lieu d'être effacée : ajouter un défaut (`=valeur`) au nouveau champ. Toute une app en local dès la création : `daroms new --variant tracker --local`. ## Avec Claude `/daroms-data` lit les tables prévues dans `app.spec.md`, lance `g table` pour chacune, puis `db push` si un projet est lié. --- Page HTML : https://daromsartof.github.io/daroms-speedapp/guides/ajouter-une-table/ Index de la documentation : https://daromsartof.github.io/daroms-speedapp/llms.txt # Ajouter un module > Activer un module du cœur (auth, onboarding, settings, payments, push, analytics, ai-chat, camera, maps), le configurer et faire les étapes hors code. Un module est une fonctionnalité optionnelle complète : provider, écrans, migrations, edge functions, textes, configuration validée par zod. Rien n'est importé tant qu'il n'est pas activé. ## 1. Activer ```bash bun daroms module list # modules disponibles et actifs bun daroms module add payments ``` `module add` ajoute les modules requis (`requires`), les routes, les migrations, les edge functions, les variables de `.env.example`, les textes d'app, la configuration par défaut dans `app.manifest.ts > modules`, régénère le registre `src/generated/modules.ts` et installe les dépendances natives (`expo install`). `--no-install` saute l'installation. ## 2. Configurer La configuration vit dans le manifest et est validée au démarrage par `core/modules//config.ts` : ```ts // app.manifest.ts modules: { auth: { providers: ['email', 'apple'], requireAuth: true }, payments: { entitlement: 'pro', mode: 'soft' }, }, ``` Chaque page module liste ses clés, schémas et valeurs par défaut : | Module | Rôle | Étape hors code | |---|---|---| | [auth](https://daromsartof.github.io/daroms-speedapp/reference/modules/auth.md) | e-mail, Apple, Google, profil, suppression de compte | fournisseurs dans Supabase Auth, `://**` en URL de redirection, `functions deploy delete-account` | | [onboarding](https://daromsartof.github.io/daroms-speedapp/reference/modules/onboarding.md) | pages d'accueil au premier lancement | - | | [settings](https://daromsartof.github.io/daroms-speedapp/reference/modules/settings.md) | onglet Réglages (apparence, langue, haptique, sections des modules) | - | | [payments](https://daromsartof.github.io/daroms-speedapp/reference/modules/payments.md) | paywall RevenueCat, statut Pro | clés publiques RevenueCat dans `.env`, entitlement et offering | | [push](https://daromsartof.github.io/daroms-speedapp/reference/modules/push.md) | notifications Expo, jeton par utilisateur | build de dev, projet EAS, `functions deploy send-push` | | [analytics](https://daromsartof.github.io/daroms-speedapp/reference/modules/analytics.md) | événements par lots dans Supabase | - | | [ai-chat](https://daromsartof.github.io/daroms-speedapp/reference/modules/ai-chat.md) | chat Claude en streaming | `bunx supabase secrets set ANTHROPIC_API_KEY=...`, `functions deploy ai-chat` | | [camera](https://daromsartof.github.io/daroms-speedapp/reference/modules/camera.md) | photos vers Supabase Storage, `PhotoPicker` | - | | [maps](https://daromsartof.github.io/daroms-speedapp/reference/modules/maps.md) | carte, marqueurs, position, distances | `GOOGLE_MAPS_ANDROID_API_KEY` pour les builds Android | ## 3. Appliquer la base Si le module a des migrations : `bun daroms db push`. Edge functions : `bunx supabase functions deploy `. ## 4. Modules natifs hors Expo Go `payments` (achats réels) et `push` ont besoin d'un build de développement : ```bash bun daroms build development bun daroms dev --dev-client ``` ## Retirer ```bash bun daroms module remove payments ``` Les migrations déjà appliquées restent dans la base. ## Avec Claude `/daroms-module payments` active le module, règle le manifest selon `app.spec.md`, branche les écrans (ex. ``, `router.push('/paywall')`) et liste les étapes manuelles restantes. Créer un nouveau module dans le cœur : [Développer le cœur](https://daromsartof.github.io/daroms-speedapp/guides/developper-le-coeur.md). --- Page HTML : https://daromsartof.github.io/daroms-speedapp/guides/ajouter-un-module/ Index de la documentation : https://daromsartof.github.io/daroms-speedapp/llms.txt # Formulaires > Construire un formulaire avec FormField, react-hook-form et zod, avec messages d'erreur traduits et envoi par une mutation TanStack Query. Un formulaire daroms = un schéma zod, `useForm` avec `zodResolver`, des `FormField`, et une mutation de `api.ts`. ## Exemple complet ```tsx // src/features/books/components/BookForm.tsx import { useT } from '@daroms/i18n' import { zRequired } from '@daroms/lib/zod' import { Button, FormField, notify, YStack } from '@daroms/ui' import { zodResolver } from '@hookform/resolvers/zod' import { useForm } from 'react-hook-form' import { z } from 'zod' import { useCreateBook } from '../api' const schema = z.object({ title: zRequired, pages: z.coerce.number().int().positive('daroms:validation.positive'), }) type Values = z.input export function BookForm({ onDone }: { onDone: () => void }) { const t = useT() const create = useCreateBook() const form = useForm>({ resolver: zodResolver(schema), defaultValues: { title: '', pages: '' } }) const submit = form.handleSubmit((values) => create.mutate(values, { onSuccess: () => { notify.success(t('books.saved')) onDone() }, }), ) return ( ) } ``` ## Ce qu'il faut savoir - **Messages d'erreur = clés i18n.** Un message zod est traduit par `FormField` : `'daroms:validation.required'` (textes du cœur) ou une clé de l'app (`'books.errors.title'`). - **Schémas prêts** dans `@daroms/lib/zod` : `zEmail`, `zPassword` (8 caractères min.), `zRequired`, `zUuid`, `zUrl`, `zIsoDate`, `zMoney`, `zPositiveInt`, `zRow` (champs communs des lignes). - **Sortie transformée.** `FormField` accepte un formulaire dont la sortie zod diffère de l'entrée : un champ nombre se saisit en texte (`z.coerce.number()`, défaut `''`) et sort en nombre. Typer `useForm, unknown, z.output<...>>`. - **Clavier.** Les props d'`Input` passent par `FormField` : `keyboardType`, `secureTextEntry`, `autoComplete`, `autoCapitalize`. Écran avec formulaire : ``. - **Tests e2e.** Chaque champ porte `testID="field-"` pour Maestro. - **Dans une feuille.** Formulaire de création dans `` ; fermer dans `onSuccess`. - **Erreur serveur.** `create.error` est une `AppError` : `notify.error(...)` ou ``. --- Page HTML : https://daromsartof.github.io/daroms-speedapp/guides/formulaires/ Index de la documentation : https://daromsartof.github.io/daroms-speedapp/llms.txt # Textes et langues > Afficher tout texte avec t('cle'), garder les mêmes clés en FR et EN, utiliser les textes du cœur et des modules, ajouter une langue. Aucun texte affiché n'est écrit en dur : le hook `daroms-check-rules` refuse un texte dans le JSX, et `daroms doctor` vérifie que chaque langue a les mêmes clés. ## Écrire un texte ```json // locales/fr.json { "books": { "title": "Livres", "count": "{{count}} livres" } } ``` ```json // locales/en.json { "books": { "title": "Books", "count": "{{count}} books" } } ``` ```tsx import { useT } from '@daroms/i18n' const t = useT() {t('books.title')} {t('books.count', { count: books.length })} ``` `useT()` est typé par les clés de l'app : une clé absente est une erreur TypeScript. ## Règles des textes - Mêmes clés dans chaque fichier de `locales/` (vérifié par `daroms doctor`). - Ni emoji, ni tiret long dans les textes. - Textes d'interface courts, en voix active ; un bouton dit ce qu'il fait (« Ajouter un livre »). ## Namespaces | Namespace | Contenu | Accès | |---|---|---| | `app` (défaut) | `locales/*.json` de l'app | `useT()` | | `daroms` | textes du cœur : boutons standard, états, erreurs, validation | `useDaromsT()`, ou clé préfixée `daroms:validation.email` | | `` | textes d'un module (`auth`, `payments`, `aiChat`...) | hook du module ; clé préfixée `aiChat:title` dans le manifest | `useTranslateKey()` traduit une clé éventuellement préfixée (`daroms:validation.required`) : c'est ce qu'utilisent `FormField` et `ErrorState`. ## Langues - Le manifest déclare `locales` (`fr`, `en`, `es`, `de`, `it`, `pt`) et `defaultLocale`. - La langue de l'appareil est détectée (expo-localization) ; l'utilisateur peut la changer dans le module `settings`. - Ajouter une langue : l'ajouter à `locales` dans `app.manifest.ts`, créer `locales/.json` avec toutes les clés, puis `bun daroms doctor`. - Contrôle visuel dans une langue : `bun daroms shot /books --locale en`. ## Formats Dates, nombres et prix suivent la langue : `formatDate`, `formatRelative`, `formatNumber`, `formatPrice`, `formatPercent`, `formatWeekday` de `@daroms/lib`. Voir [Paquets](https://daromsartof.github.io/daroms-speedapp/reference/paquets.md#daromslib). --- Page HTML : https://daromsartof.github.io/daroms-speedapp/guides/textes-et-langues/ Index de la documentation : https://daromsartof.github.io/daroms-speedapp/llms.txt # Changer la marque > Changer l'identité visuelle d'une app en modifiant les 5 valeurs de brand.ts, puis régénérer icône, splash et favicon. ## 1. Les 5 valeurs ```ts // brand.ts import { defineBrand } from '@daroms/theme/brand' export default defineBrand({ primary: '#0F766E', // boutons, liens, éléments actifs accent: '#F59E0B', // mises en avant, badges font: 'inter', // 'inter' (embarquée) | 'system' radius: 'lg', // 'none' | 'sm' | 'md' | 'lg' | 'xl' | 'full' tone: 'minimal', // 'playful' | 'pro' | 'minimal' }) ``` Palettes 11 nuances, thèmes clair et sombre, sous-thèmes (`brand`, `soft`, `accent`, `highlight`, `success`, `warning`, `danger`, `destructive`), rayons, espacements, graisses et ressorts sont recalculés. Le détail du calcul : [Marque](https://daromsartof.github.io/daroms-speedapp/reference/marque.md). Essayer sans app : l'éditeur de la [page d'accueil](https://daromsartof.github.io/daroms-speedapp/llms.txt). ## 2. L'icône 1. Dessiner `assets/source/icon.svg` (1024 x 1024, fond inclus) ; facultatif : `assets/source/foreground.svg` pour l'icône adaptative Android. 2. Générer : ```bash bun daroms assets # icon.png, adaptive-icon.png, splash-icon.png, favicon.png ``` ## 3. Vérifier ```bash bun daroms shot /_dev/kitchen-sink --capture bun daroms shot /_dev/kitchen-sink --dark --capture ``` L'écran `/_dev/kitchen-sink` (absent en production) montre tous les composants avec le thème courant. :::caution[Contraste] Une couleur principale très claire (jaune, vert d'eau) donne des boutons peu lisibles. Préférer une nuance moyenne ou foncée pour `primary`, et garder les couleurs claires pour `accent`. ::: ## Avec Claude `/daroms-brand` propose palette et ton depuis `app.spec.md` (et la direction retenue par `/daroms-inspire` si elle existe), dessine l'icône en SVG, lance `daroms assets` et vérifie le kitchen-sink. --- Page HTML : https://daromsartof.github.io/daroms-speedapp/guides/changer-la-marque/ Index de la documentation : https://daromsartof.github.io/daroms-speedapp/llms.txt # Vérifier > Contrôler une app daroms avant de rendre - doctor, contrôle visuel daroms shot, tests jest-expo, flows Maestro, et le skill /daroms-check. ## Diagnostic ```bash bun daroms doctor # Node, .env, manifest, i18n, règles, TypeScript, ESLint, expo-doctor bun daroms doctor --quick # sans les étapes lentes, avant chaque rendu ``` `doctor` régénère aussi les types de routes s'ils sont périmés. ## Contrôle visuel : `daroms shot` `shot` lance (ou réutilise) le serveur web d'Expo, ouvre la route dans Chromium et rend d'abord **un rapport texte** : erreurs de console, exceptions, débordements horizontaux. La capture n'est faite que si elle est utile ou demandée. ```bash bun daroms shot /books # rapport, capture si un problème est vu bun daroms shot /books --capture # capture toujours bun daroms shot /books --dark # mode sombre bun daroms shot /books --locale en # autre langue bun daroms shot /books --width 430 # largeur (défaut 390) bun daroms shot /books --storage cle=valeur # valeurs de stockage injectées avant le rendu bun daroms shot /books --storage-file e2e/state.json bun daroms shot /books --android # capture sur l'émulateur Android bun daroms shot --stop # arrête le serveur web ``` Le rapport texte coûte quelques lignes ; une capture coûte beaucoup plus de tokens à un agent. D'où l'ordre : texte d'abord. ## Tests unitaires ```bash bun daroms test # jest-expo + Testing Library bun daroms test books # motif ``` Les tests vivent à côté du code (`src/features//__tests__/`), sans réseau. `@daroms/app/testing` fournit le rendu avec providers. ## Tests de bout en bout (Maestro) ```bash bun daroms e2e # flows e2e/*.yaml sur le build installé bun daroms e2e --go # app chargée dans Expo Go bun daroms e2e main --go --driver # pré-lance le pilote Android (API récentes) ``` Le flow du template est paramétré par `APP_ID` et `APP_URL`. Les champs de formulaire portent `testID="field-"`. ## Tout d'un coup `/daroms-check` enchaîne typecheck, lint, tests, règles, `shot` clair et sombre sur les écrans touchés et le flow Maestro principal, puis rend un rapport court. `/daroms-polish` fait ensuite relire l'UX par l'agent `daroms-designer`. Dans le dépôt du cœur : `bun run check` (tsc, eslint, jest, tests CLI, tests Python des hooks). --- Page HTML : https://daromsartof.github.io/daroms-speedapp/guides/verifier/ Index de la documentation : https://daromsartof.github.io/daroms-speedapp/llms.txt # Publier > Builds EAS preview et production, soumission TestFlight et piste interne Google Play, mises à jour OTA, et la checklist de conformité. ## Avant le premier build 1. `bun daroms doctor` vert. 2. `manifest.store.privacyUrl` et `manifest.store.supportEmail` renseignés (requis par les stores). 3. `bunx eas-cli login` (une fois). Le projet EAS est créé au premier build et son identifiant rangé dans `manifest.eas.projectId`. ## Les trois profils | Profil | Commande | Usage | |---|---|---| | `development` | `bun daroms build development` | client de dev, requis pour les modules natifs hors Expo Go (`payments`, `push`) ; puis `bun daroms dev --dev-client` | | `preview` | `bun daroms build preview` | APK Android et build iOS ad hoc, installation interne par lien et QR | | `production` | `bun daroms build production` | build signé pour les stores | Option commune : `--platform ios|android|all` (défaut `all`). ## Soumettre ```bash bun daroms build production bun daroms submit # dernier build production : TestFlight et piste interne Google Play ``` Avant chaque soumission, relire `.claude/rules-ondemand/daroms-store-compliance.md`. Les refus fréquents : - inscription possible sans suppression du compte dans l'app (le module `auth` l'ajoute aux réglages) ; - Google proposé sur iOS sans Sign in with Apple ; - achat numérique hors achats intégrés, ou paywall sans bouton Restaurer, prix, période, CGU et confidentialité ; - pas d'URL de confidentialité publique, ou étiquettes de confidentialité incohérentes avec les SDK ; - permission demandée trop tôt, sans texte clair, ou inutilisée ; - écran vide ou « bientôt » ; pas de compte de test dans les notes de revue. ## Mettre à jour sans store (OTA) ```bash bun daroms update production --message "Corrige le tri des livres" ``` Uniquement JavaScript et assets. Un changement natif (module, plugin, permission) demande un nouveau build et une `version` incrémentée dans le manifest : la version runtime est celle de l'app. :::caution[Sous-module core/] EAS envoie l'arbre de travail : vérifier que `core/` est bien présent (`git submodule update --init`) avant un build. ::: ## Avec Claude `/daroms-store` (fiche FR/EN, mots-clés, captures, confidentialité, via l'agent `daroms-store`), `/daroms-build`, `/daroms-submit`, `/daroms-update`. `/daroms-submit` ne se lance que sur demande explicite. --- Page HTML : https://daromsartof.github.io/daroms-speedapp/guides/publier/ Index de la documentation : https://daromsartof.github.io/daroms-speedapp/llms.txt # Mettre à jour le cœur > Passer une app à une nouvelle version du cœur daroms (sous-module core/), resynchroniser la couche IA et vérifier. Le cœur est un sous-module git : une app reste sur sa version tant qu'on ne la change pas. ```bash cd core && git fetch --tags && git checkout v0.2.0 && cd .. bun install bun daroms sync-ai # règles, skills, hooks à jour dans .claude/ bun daroms doctor git add core && git commit -m "chore(core): passe à v0.2.0" ``` - Lire `core/CHANGELOG.md` avant : les ruptures n'arrivent qu'en version majeure. - Les fichiers `app-*` de `.claude/` ne sont jamais touchés ; les `daroms-*` sont écrasés. - `daroms-sync` (hook de début de session) resynchronise aussi la couche IA automatiquement quand `core/_ai` a changé. - App créée avec un cœur copié (sans sous-module) : remplacer `core/` par la nouvelle version, ou le transformer en sous-module (`git rm -r core && git submodule add https://github.com/daromsartof/daroms-speedapp.git core`), puis les mêmes commandes. --- Page HTML : https://daromsartof.github.io/daroms-speedapp/guides/mettre-a-jour-le-coeur/ Index de la documentation : https://daromsartof.github.io/daroms-speedapp/llms.txt # Développer le cœur > Travailler dans le dépôt daroms-speed-app - vérifications, essai dans une vraie app, nouveau composant, icône, module, règle ou skill, publication d'une version. On développe le cœur dans son propre dépôt, jamais dans le `core/` d'une app. ## Vérifier ```bash bun run check # tsc, eslint, jest, tests CLI (bun test ./cli), tests Python des hooks ``` ## Essayer dans une vraie app ```bash bun cli/daroms.ts new essai --dir /tmp --copy # instantané du cœur, fiable bun cli/daroms.ts new essai --dir /tmp --link # cœur lié, modifications visibles immédiatement ``` Avec `--link`, les workspaces sont écrits en chemins explicites (Bun ne suit pas un lien dans `core/packages/*`) et `tsc` dans l'app peut signaler des types en double si une dépendance n'a pas la même version dans `node_modules` du cœur et de l'app (Metro n'est pas concerné). ## Ajouter | Quoi | Comment | |---|---| | Composant UI | fichier dans `packages/ui/src/components/` avec un bloc JSDoc `@catalog / @props / @example`, export dans `index.ts`, puis `bun daroms catalog` (régénère `CATALOG.md`, donc la [page Composants](https://daromsartof.github.io/daroms-speedapp/reference/composants.md)) | | Icône du jeu de base | `bunx tamagui icons add Nom --out packages/ui/src/icons/lucide` | | Module | `bun daroms g module --description "..."` puis compléter `module.json` (routes, dépendances natives, migrations, fonctions, textes d'app) et `index.ts` (`defineModule`). Sa page de référence est générée depuis `README.md`, `module.json` et `config.ts`. | | Règle | `_ai/rules/daroms-.md` avec frontmatter `paths:` et une ligne `> résumé` ; `bun daroms catalog` régénère le digest | | Skill | `_ai/skills/daroms-/SKILL.md` (ou `/daroms-skill-new`) | ## Documentation Le site vit dans `docs/` (Astro + Starlight) : ```bash cd docs && bun install bun run dev # génère les pages de référence puis lance le site bun run check # build complet + vérification des fichiers pour agents et des liens ``` Les pages Composants, Modules, Règles et Skills sont générées depuis les sources par `docs/scripts/sync-content.ts` : modifier la source, pas la page. ## Publier une version ```bash bun daroms release-core minor --dry # aperçu : version et section de changelog bun daroms release-core minor # VERSION, package.json, CHANGELOG.md, commit et tag vX.Y.Z git push --follow-tags # le tag déclenche .github/workflows/publish.yml ``` Le workflow `publish` vérifie que tag, `VERSION` et `package.json` concordent, installe, lance `bun run check` (via `prepublishOnly`) puis `npm publish` du paquet `@daromsart/daroms-speed-app`. Si la version est déjà sur npm, il ne fait rien. Ensuite, chaque app suit [Mettre à jour le cœur](https://daromsartof.github.io/daroms-speedapp/guides/mettre-a-jour-le-coeur.md). Le paquet contient `cli/`, `packages/`, `modules/`, `template/`, `generators/`, `_ai/`, `VERSION`, `CHANGELOG.md`, sans tests ni docs. Vérifier son contenu : `npm pack --dry-run`. ### Configurer npm (une fois) 1. Compte sur npmjs.com, membre de l'organisation `daromsart`, puis `npm login` dans le terminal. 2. Première publication, depuis `main` après `release-core` : `npm publish` (lance `bun run check`), puis `git push --follow-tags`. 3. Sur npmjs.com, paquet `@daromsart/daroms-speed-app` > Settings > Trusted publishing : GitHub Actions, dépôt `daromsartof/daroms-speedapp`, workflow `publish.yml`. Les versions suivantes partent du CI sans jeton. Sans publication de confiance : créer un jeton npm d'automatisation et l'ajouter au dépôt comme secret `NPM_TOKEN` (`gh secret set NPM_TOKEN`). ## Documentation en ligne Le workflow `docs` publie le site sur GitHub Pages (https://daromsartof.github.io/daroms-speedapp/) à chaque push sur `main` qui touche la doc ou ses sources. Source Pages du dépôt : GitHub Actions. --- Page HTML : https://daromsartof.github.io/daroms-speedapp/guides/developper-le-coeur/ Index de la documentation : https://daromsartof.github.io/daroms-speedapp/llms.txt # Dépanner > Symptômes fréquents (QR, écran blanc, module natif, types, RLS, cache Metro, hooks, Maestro) avec leur cause probable et l'action à mener. Un symptôme, sa cause probable, l'action. Si rien ne correspond : `/daroms-fix` lit les erreurs (doctor, tsc, Metro, shot), corrige et relance, trois essais au plus, puis rend un rapport. | Symptôme | Cause probable | Action | |---|---|---| | iPhone : « aucune donnée utilisable » au scan du QR | QR de build de dev (`exp+://`) sans build installé | `bun daroms dev` (Expo Go) ; build de dev : `bun daroms build development` puis `--dev-client` | | Le QR s'ouvre mais l'app ne charge pas | téléphone sur un autre réseau | même Wi-Fi, ou `bun daroms dev --tunnel` | | Écran blanc, erreur rouge | exception au rendu | `bun daroms shot /route` (message), `/daroms-fix` | | `Unable to resolve module` | paquet absent ou cache | `bunx expo install ` ; `bun daroms dev --clear` | | Module natif introuvable dans Expo Go | MMKV, RevenueCat, push | `bun daroms build development` (client de dev) | | Erreur de type sur une route | types de routes périmés | `bun daroms doctor` (les régénère) | | Données vides alors que la table est remplie | RLS | vérifier les politiques et `auth.uid()` ; utilisateur connecté ? | | Manifest ignoré sur le web | cache Metro | `bun daroms shot` redémarre le serveur ; sinon `bun daroms dev --web --clear` | | Hook qui refuse une écriture | règle daroms | lire le message, corriger (pas de contournement) | | `.env` refusé | protection des secrets | lire `.env.example` ; demander les valeurs à l'humain | | Maestro : « driver did not start » ou gRPC UNAVAILABLE | Android très récent (API 37) ou émulateur lent | Maestro >= 2.11 puis `bun daroms e2e --go --driver` (pré-lance l'instrumentation) | | Expo Go ne s'installe pas (`Fetching Expo Go` bloqué) | téléchargement lent | télécharger l'APK (adresse via `https://api.expo.dev/v2/versions`) puis `adb install` | --- Page HTML : https://daromsartof.github.io/daroms-speedapp/guides/depanner/ Index de la documentation : https://daromsartof.github.io/daroms-speedapp/llms.txt # CLI daroms > Toutes les commandes de la CLI daroms (new, dev, doctor, g, icons, module, shot, test, e2e, assets, inspire, db, build, submit, update, cost, sync-ai, catalog, release-core) avec leurs options. Dans une app : `bun daroms `. Hors app : `npx @daromsart/daroms-speed-app ` (paquet npm), `daroms ` après `npm install -g @daromsart/daroms-speed-app`, ou `bun /cli/daroms.ts `. `bun daroms --help` liste les commandes. ## Résumé | Commande | Rôle | |---|---| | [`new`](#new) | crée une app complète | | [`dev`](#dev) | serveur Expo et QR | | [`doctor`](#doctor) | diagnostic complet | | [`g`](#g) | génère écran, feature, table, module | | [`icons`](#icons) | ajoute des icônes Lucide à l'app | | [`module`](#module) | active, retire, liste les modules | | [`shot`](#shot) | contrôle visuel d'une route | | [`test`, `e2e`](#test-et-e2e) | tests unitaires, flows Maestro | | [`assets`](#assets) | icônes, splash, favicon depuis un SVG | | [`inspire`](#inspire) | aperçus d'inspiration à regarder avant le design | | [`db`](#db) | migrations et types Supabase | | [`build`, `submit`, `update`](#build-submit-update) | EAS | | [`cost`](#cost) | ce que l'app a coûté en tokens et en dollars | | [`sync-ai`, `catalog`](#sync-ai-et-catalog) | couche IA, catalogue | | [`release-core`](#release-core) | version du cœur | ## new ```bash npx @daromsart/daroms-speed-app new [options] ``` | Option | Défaut | Effet | |---|---|---| | `--name "Nom"` | slug capitalisé | nom affiché | | `--variant` | `blank` | `blank`, `list-detail`, `tracker`, `ai-chat`, `social`, `shop` ([Variantes](https://daromsartof.github.io/daroms-speedapp/reference/variantes.md)) | | `--local` | - | tables de la variante sur l'appareil (`localTable`) | | `--primary`, `--accent` | `#4F46E5`, `#F97316` | couleurs de `brand.ts` | | `--font` | `inter` | `inter` ou `system` | | `--radius` | `lg` | `none`, `sm`, `md`, `lg`, `xl`, `full` | | `--tone` | `pro` | `playful`, `pro`, `minimal` | | `--bundle` | `com.daroms.` | identifiant iOS et Android | | `--modules a,b` | - | modules en plus de ceux de la variante | | `--supabase ` | - | `manifest.supabase.projectRef` | | `--dir ` | dossier courant | où créer l'app | | `--core ` | dépôt du cœur | URL du sous-module (depuis npm : sous-module fixé sur le tag de la version, ou copie si le dépôt est inaccessible) | | `--copy` | - | copie le cœur (instantané, sans sous-module) | | `--link` | - | lien symbolique vers le cœur (développement du cœur) | | `--no-git` | - | ni `git init` ni commit initial | Étapes : copie du template, variante, substitution des valeurs, `git init`, cœur (sous-module, copie ou lien), `bun install`, manifest, modules, registre, tables et écrans de la variante, icônes, types de routes, couche IA, commit initial. ## dev ```bash bun daroms dev [--dev-client] [--web] [--android] [--ios] [--tunnel] [--clear] ``` Expo Go par défaut (aperçu immédiat). `--dev-client` : build de développement déjà installé. `--tunnel` si le téléphone n'est pas sur le même réseau, `--clear` vide le cache Metro. Avertit si `.env` manque ou si `payments` ou `push` sont actifs sans `--dev-client`. ## doctor ```bash bun daroms doctor [--quick] ``` Node, `.env`, manifest, cohérence de l'app (écrans, onglets, clés i18n FR = EN), règles, TypeScript, ESLint, expo-doctor. `--quick` saute les étapes lentes. ## g ```bash bun daroms g screen [--tab] [--icon NomLucide] [--title "Titre"] [--title-en "Title"] [--route chemin] [--force] bun daroms g feature bun daroms g table --fields "titre:text, fait:bool=false" [--feature nom] [--singular nom] [--local] bun daroms g module [--description "..."] # dans le dépôt du cœur ``` Détail : [Ajouter un écran](https://daromsartof.github.io/daroms-speedapp/guides/ajouter-un-ecran.md), [Ajouter une table](https://daromsartof.github.io/daroms-speedapp/guides/ajouter-une-table.md), [Développer le cœur](https://daromsartof.github.io/daroms-speedapp/guides/developper-le-coeur.md). ## icons ```bash bun daroms icons add Rocket Sprout ``` Génère les icônes Lucide demandées dans l'app (`@/icons`). Le jeu de base (102 icônes) est dans `@daroms/ui/icons`. Jamais d'autre jeu d'icônes. ## module ```bash bun daroms module list bun daroms module add [--no-install] bun daroms module remove ``` Détail : [Ajouter un module](https://daromsartof.github.io/daroms-speedapp/guides/ajouter-un-module.md). ## shot ```bash bun daroms shot [--dark] [--capture] [--width 390] [--locale en] [--storage cle=val,...] [--storage-file f.json] [--android] [--port 8099] bun daroms shot --stop ``` Rapport texte (erreurs, exceptions, débordements) puis capture si utile ou si `--capture`. Le serveur web est réutilisé tant que manifest et marque ne changent pas. Détail : [Vérifier](https://daromsartof.github.io/daroms-speedapp/guides/verifier.md). ## test et e2e ```bash bun daroms test [motif] bun daroms e2e [flow] [--go] [--driver] ``` `test` : jest-expo. `e2e` : Maestro sur l'appareil connecté ; `--go` charge l'app dans Expo Go ; `--driver` pré-lance le pilote Android. ## assets ```bash bun daroms assets ``` Depuis `assets/source/icon.svg` (et `foreground.svg` facultatif) : `icon.png` (1024), `adaptive-icon.png`, `splash-icon.png` (512), `favicon.png` (48). ## inspire ```bash bun daroms inspire [--clear] bun daroms inspire ``` Télécharge dans `.daroms/inspiration/` (non versionné) l'image de chaque URL : image directe, ou page dont il prend l'aperçu (`og:image`). Claude regarde ensuite ces fichiers pendant `/daroms-inspire`. 12 adresses au plus, 8 Mo au plus par image, formats png, jpg, webp et gif ; `index.json` garde la source de chaque fichier. Sans argument : liste les aperçus ; `--clear` vide le dossier d'abord. La commande s'annonce comme `daroms-inspire` et ne contourne rien : une page qui refuse la lecture automatique (Dribbble et Behance le font souvent) est signalée puis sautée ; garder alors le lien et l'extrait de la recherche. ## db ```bash bun daroms db new # migration vide horodatée bun daroms db push # applique les migrations au projet lié, régénère src/types/db.ts bun daroms db types # src/types/db.ts depuis Supabase (hors ligne sans projet lié) bun daroms db reset # supabase db reset (base locale) ``` ## build, submit, update ```bash bun daroms build [preview|production|development] [--platform ios|android|all] bun daroms submit [--platform ios|android|all] bun daroms update [canal] --message "..." ``` Détail : [Publier](https://daromsartof.github.io/daroms-speedapp/guides/publier.md). ## cost ```bash bun daroms cost [--print] [--out fichier] ``` Lit les transcripts de Claude Code (`~/.claude/projects`, ou `$CLAUDE_CONFIG_DIR/projects`) et chiffre chaque réponse de Claude au tarif de l'API Anthropic : entrée, sortie, cache lu, cache écrit (5 min et 1 h), mode rapide, recherches web. Écrit `app.cost.md` : total, coût par étape (`/daroms-idea`, `/daroms-brand`, `/daroms-screen`...), par modèle et par session, et ce qu'il faut retenir (étape la plus chère, effet du cache, part des sous-agents). `--print` affiche sans écrire. - Une session compte pour l'app où elle a passé la majorité de son temps : `/daroms-day` lancé depuis une autre app compte pour la nouvelle. - Les sous-agents (designer, store...) sont inclus et rattachés à l'étape en cours. - Avec un abonnement Claude Pro ou Max, ces montants ne sont pas facturés ainsi : ils mesurent l'usage, à comparer d'une app à l'autre. - Claude Code efface ses transcripts après 30 jours (`cleanupPeriodDays`) : `/daroms-day` écrit le rapport en fin de journée et le versionne. - Tarifs dans `cli/lib/pricing.ts` du cœur, avec leur date de relevé ; un modèle inconnu est signalé et compté 0 $. ## sync-ai et catalog - `sync-ai` : copie `core/_ai` vers `.claude/` (règles, skills, agents, hooks, contexte) et fusionne `settings.json`. Le hook `daroms-sync` le fait au début de chaque session. - `catalog` : régénère `packages/ui/CATALOG.md` et `_ai/digest.md` depuis les sources du cœur. ## release-core ```bash bun daroms release-core [--dry] ``` Dans le dépôt du cœur : nouvelle version dans `VERSION` et tous les `package.json`, section de `CHANGELOG.md` depuis les commits conventionnels, commit et tag. L'arbre git doit être propre ; `--dry` affiche le résultat sans rien écrire. `git push --follow-tags` déclenche ensuite la publication npm ([Développer le cœur](https://daromsartof.github.io/daroms-speedapp/guides/developper-le-coeur.md#publier-une-version)). --- Page HTML : https://daromsartof.github.io/daroms-speedapp/reference/cli/ Index de la documentation : https://daromsartof.github.io/daroms-speedapp/llms.txt # Manifest > Tous les champs de app.manifest.ts (validé par zod), leurs types et valeurs par défaut, et comment ils pilotent la configuration Expo et l'app. `app.manifest.ts` décrit l'app : identité, langues, écrans, onglets, modules, monétisation, services. Il est validé par zod (`defineManifest`), lu par `app.config.ts` au build et par `getManifest()` au runtime. La CLI le réécrit (`g screen`, `module add`) : garder sa forme simple. ```ts import { defineManifest } from '@daroms/lib/manifest-schema' export default defineManifest({ name: 'Lecture', slug: 'lecture', bundleId: 'com.daroms.lecture', version: '1.0.0', variant: 'tracker', locales: ['fr', 'en'], defaultLocale: 'fr', screens: ['home', 'books'], tabs: [ { name: 'index', icon: 'House', titleKey: 'tabs.home' }, { name: 'books', icon: 'BookOpen', titleKey: 'books.title' }, ], modules: { onboarding: {}, settings: {} }, monetization: { model: 'free' }, store: { supportEmail: 'aide@exemple.fr', privacyUrl: 'https://exemple.fr/confidentialite' }, }) ``` ## Champs | Champ | Type | Défaut | Rôle | |---|---|---|---| | `name` | texte, 1 à 30 caractères | requis | nom affiché | | `slug` | `[a-z0-9-]+` | requis | identifiant Expo, dossier | | `bundleId` | `com.exemple.app` | requis | identifiant iOS et Android | | `version` | semver `1.2.3` | `1.0.0` | version de l'app et version runtime OTA | | `scheme` | texte | slug | schéma de liens profonds | | `variant` | `blank`, `list-detail`, `social`, `tracker`, `ai-chat`, `shop` | `blank` | variante de départ | | `locales` | liste de `fr`, `en`, `es`, `de`, `it`, `pt` | `['fr', 'en']` | langues ; un fichier `locales/.json` par langue | | `defaultLocale` | une des langues | `fr` | langue de repli | | `orientation` | `portrait`, `landscape`, `default` | `portrait` | orientation | | `screens` | liste de noms de features | `[]` | écrans attendus (vérifiés par `doctor`) | | `tabs` | `{ name, icon, titleKey }[]` | `[]` | onglets : route dans `app/(tabs)/`, icône Lucide, clé i18n du titre | | `modules` | `{ : config }` | `{}` | modules actifs et leur config (validée par chaque module) | | `monetization` | `{ model, entitlement, trialDays }` | `free`, `pro`, `0` | `free`, `freemium`, `subscription`, `paid` | | `supabase.projectRef` | texte | - | projet Supabase lié (`db push`, `db types`) | | `eas.projectId`, `eas.owner` | texte | - | projet EAS (écrit au premier build) | | `store` | `{ category, supportEmail, privacyUrl, termsUrl }` | - | fiche store ; `supportEmail` et `privacyUrl` requis pour soumettre | ## Ce que le manifest pilote - **Configuration Expo** : `app.config.ts` appelle `withDaroms(ctx, { manifest, brand })` : nom, identifiants, schéma, orientation, icônes, splash, plugins des modules actifs (`core/modules//plugin.ts`) et manifest injecté dans `extra`. - **Navigation** : `TabsLayout` construit les onglets depuis `tabs` ; les modules ajoutent leurs routes et leurs gates. - **Modules** : le registre `src/generated/modules.ts` n'importe que les modules de `modules`. - **Runtime** : `getManifest()` de `@daroms/lib/manifest` relit le manifest validé (tests : `setManifestForTests`). La marque est dans un fichier à part : [brand.ts](https://daromsartof.github.io/daroms-speedapp/reference/marque.md). --- Page HTML : https://daromsartof.github.io/daroms-speedapp/reference/manifest/ Index de la documentation : https://daromsartof.github.io/daroms-speedapp/llms.txt # Marque (brand.ts) > Les 5 valeurs de brand.ts et ce que @daroms/theme en calcule - palettes OKLCH, neutres, thèmes clair et sombre, sous-thèmes, rayons, espacements, graisses et ressorts selon le ton. `brand.ts` est la seule entrée du thème. `createBrandTheme(brand)` de `@daroms/theme` en dérive tous les tokens et thèmes Tamagui v3 ; changer d'identité = changer ces valeurs. L'éditeur de la [page d'accueil](https://daromsartof.github.io/daroms-speedapp/llms.txt) utilise exactement ce calcul. ```ts import { defineBrand } from '@daroms/theme/brand' export default defineBrand({ primary: '#0F766E', accent: '#F59E0B', font: 'inter', radius: 'lg', tone: 'minimal' }) ``` | Valeur | Type | Défaut | Effet | |---|---|---|---| | `primary` | hex `#RRGGBB` | requis | palette `brand-*`, sous-thèmes `brand` et `soft`, boutons, liens, éléments actifs, teinte des neutres | | `accent` | hex `#RRGGBB` | requis | palette `accent-*`, sous-thèmes `accent` et `highlight` | | `font` | `inter`, `system` | `inter` | Inter embarquée, ou San Francisco / Roboto | | `radius` | `none`, `sm`, `md`, `lg`, `xl`, `full` | `lg` | rayon de base : 0, 4, 8, 12, 16 px ; `full` = boutons et champs en pilule | | `tone` | `playful`, `pro`, `minimal` | `pro` | rayons, espacements, graisses, rebond des animations, teinte des neutres | ## Palettes - **11 nuances** `50` à `950` par couleur, calculées en OKLCH sur une échelle de luminosité proche de Tailwind v4. - **Ta couleur est gardée telle quelle** sur la nuance de luminosité la plus proche (souvent 500 ou 600) ; les voisines suivent son écart de luminosité, la chroma suit une courbe pleine au milieu et faible aux extrémités. - **Neutres** (`surface-*`) : gris teintés par la teinte de `primary`, avec la chroma du ton (0 en `minimal`, gris pur). ## Thèmes et sous-thèmes Thèmes `light` et `dark`, avec des sous-thèmes aux noms fixes (typés), utilisables partout avec `theme="..."` : | Sous-thème | Palette | Rendu | |---|---|---| | `brand` | brand, plein | fond de la couleur principale | | `soft` | brand, teinté | fond léger de la couleur principale | | `accent` | accent, teinté | mise en avant discrète | | `highlight` | accent, plein | mise en avant forte | | `success`, `warning`, `danger` | émeraude, ambre, rouge, teintés | états | | `destructive` | rouge, plein | action irréversible | | `level2`..`level4`, `inverse` | surface | profondeur, inversion clair/sombre | Les noms des sous-thèmes sont en minuscules d'un seul mot (une majuscule casse la résolution des niveaux relatifs de Tamagui v3). ## Ce que change le ton | | `playful` | `pro` | `minimal` | |---|---|---|---| | Chroma des neutres | 0.018 | 0.01 | 0 | | Multiplicateur de rayon | x 1.25 | x 1 | x 0.75 | | Rebond des ressorts | 0.35 | 0.12 | 0 | | Graisse des titres | 800 | 700 | 600 | | Interlettrage des titres | -0.4 | -0.3 | -0.2 | | `gutter` / `section` / `stack` | 20 / 28 / 14 | 16 / 24 / 12 | 24 / 32 / 16 | | `inline` / `tight` | 10 / 6 | 8 / 4 | 8 / 4 | ## Rayons sémantiques Rayon de base = rayon du preset x multiplicateur du ton. | Token | Valeur | |---|---| | `button`, `input` | base (pilule si `radius: 'full'`) | | `card` | base x 1.5 (24 si `full`) | | `sheet` | base x 1.5 + 8 (28 si `full`) | | `chip`, `badge` | pilule (0 si `radius: 'none'`) | | `avatar`, `pill` | pilule | ## Animations Durées partagées : `fast` 160 ms, `medium` 280 ms, `slow` 420 ms. Ressorts `fast`, `medium`, `slow`, `bouncy`, `lazy` réglés par le rebond du ton. Usage : `transition="fast"` sur un composant Tamagui. ## API `defineBrand`, `BrandSchema`, `createBrandTheme`, `createBrandPalettes`, `createPalette`, `createNeutral`, `anchorShade`, `contrastRatio`, `semanticRadius`, `semanticSpace`, `springs`, `TONES`, `DURATIONS`, `Z_INDEX`. Voir [Paquets](https://daromsartof.github.io/daroms-speedapp/reference/paquets.md#daromstheme). Mode d'emploi : [Changer la marque](https://daromsartof.github.io/daroms-speedapp/guides/changer-la-marque.md). --- Page HTML : https://daromsartof.github.io/daroms-speedapp/reference/marque/ Index de la documentation : https://daromsartof.github.io/daroms-speedapp/llms.txt # Composants @daroms/ui > Catalogue des composants @daroms/ui : props clés et exemple d'usage, tokens Tamagui v3 et icônes Lucide disponibles. Une ligne par composant, avec ses props clés et un exemple. Les états (chargement, désactivé, pressé), l’accessibilité et l’haptique sont intégrés. :::note[Page générée] Générée depuis `packages/ui/CATALOG.md` à chaque build du site : modifier la source, pas cette page. ::: :::tip[Pour un agent] Dans une app, lire `core/packages/ui/CATALOG.md` (même contenu) plutôt que les sources des composants : c’est le contrat, et il coûte une soixantaine de lignes. ::: # Catalogue @daroms/ui Import : `import { Button, Screen } from '@daroms/ui'`. Primitives de mise en page : `XStack`, `YStack`, `ScrollView`, `Separator`, `Spacer`, `Theme` (aussi exportées). ## Composants | Composant | Props clés | Exemple | |---|---|---| | Avatar | name (initiales de repli, a11y) · uri · size: sm\|md\|lg\|xl | `` | | Badge | tone: neutral\|brand\|accent\|success\|warning\|danger · children (texte court) | `{t('order.paid')}` | | BarChart | data[{ key, label, value }] · label (a11y, requis) · max (défaut : plus grande valeur) · tone: brand\|accent\|success · height (token, défaut 40) · highlight (key mise en avant) | ` e.date).map((d) => ({ key: d.day, label: formatWeekday(d.day, 'narrow'), value: d.value }))} label={t('stats.week')} />` | | Button | variant: primary\|secondary\|outline\|ghost\|danger · size: sm\|md\|lg (défaut lg) · loading · icon · iconAfter · fullWidth · haptic | `` | | Card | pressable (retour visuel) · flat (sans bordure) · padded (défaut true) · + props YStack | ` open(item.id)}>{item.title}` | | Chip | label · selected · icon · onPress (filtres, tags) | ` setFilter('today')} />` | | Dialog | open · onOpenChange · title · description · confirmLabel · cancelLabel · destructive · loading · onConfirm | `` | | EmptyState | icon · title · description · actionLabel + onAction (textes par défaut fournis) | `` | | ErrorState | error (AppError traduite automatiquement) · title · description · onRetry (bouton Réessayer) | ` query.refetch()} />` | | FormField | control · name · label · hint · + props Input (secureTextEntry, keyboardType...). Messages zod = clés i18n. testID e2e : field- | `` | | Header | title · subtitle · back (true \| fn) · right · large | `
} />` | | IconButton | icon (requis) · label (requis, a11y) · variant (défaut ghost) · size: sm\|md\|lg | `` | | Image | source (uri ou require) · alt (requis) · ratio · rounded (défaut card) · contentFit · placeholder (blurhash) | `` | | Input | value · onChangeText · placeholder · invalid · secureTextEntry · keyboardType · autoComplete | `` | | List | data · renderItem (item mémoïsé) · keyExtractor · loading · error · onRetry · empty (ou ListEmptyComponent) · onRefresh · refreshing · separators | `` | | ListItem | title · subtitle · icon · leading · value · trailing · chevron · destructive · onPress | `` | | OnboardingPager | pages[{ key, icon, title, description }] · onDone · doneLabel | `` | | Paywall | title · subtitle · features[] · plans[{id,title,price,period,badge,trialDays}] · selectedId · onSelect · ctaLabel · onPurchase · onRestore · onClose · purchasing · termsUrl · privacyUrl | ` (branché par le module payments)` | | ProgressBar | value (0 à 1) · tone: brand\|accent\|success | `` | | QueryView | query (useQuery) · children(data) · skeleton · empty (EmptyState) · isEmpty. Gère les 4 états d'un écran. | `{(items) => }` | | Screen | scroll · padded · header · footer · keyboard · onRefresh + refreshing · safeTop | `} onRefresh={q.refetch} refreshing={q.isRefetching}>...` | | SegmentedControl | options [{ value, label }] · value · onChange (2 à 4 choix exclusifs) | `` | | SettingsList | sections[{ key, title, items[{ key, label, icon, type: link\|toggle\|action, value, checked, selected, onToggle, onPress, destructive }] }] | ` (utilisé par le module settings)` | | Sheet | open · onOpenChange · title · snapPoints (défaut [60]) · scroll | ` setOpen(false)} />` | | Skeleton | w · h (tokens) · rounded. SkeletonList : rows, avatar | ` · ` | | Stat | value (nombre ou texte formaté) · label · tone: default\|brand\|accent\|success · icon. Carte qui s'étire (flex 1) : en XStack pour une rangée de chiffres clés | `` | | Tabs | tabs [{ value, label, count }] · value · onChange (onglets soulignés, défilants) | `` | | Title | tone: default\|muted\|brand\|accent\|danger\|success · align: start\|center\|end | `{t('home.title')}` | | Heading | tone · align · level: 1\|2\|3 (taille 7, 6, 5) | `{t('section.title')}` | | Body | tone · align · weight: regular\|medium\|bold · size: '3'..'5' | `{t('item.description')}` | | Caption | tone (défaut muted) · align · weight | `
` | | Toast | monté une fois à la racine (AppProvider le fait) ; notify.success\|error\|info(message) | `notify.success(t('todos.saved'))` | ## Tokens et thèmes (valeurs plates Tamagui v3, sans `$`) - Espaces : `gutter` (marge écran), `section`, `stack`, `inline`, `tight` + échelle 4 pt `0`..`96` (`4` = 16 px), demi-pas `0-5`, `1-5`. - Rayons : `button`, `input`, `card`, `sheet`, `chip`, `badge`, `avatar`, `pill`. - Couleurs de thème : `background`, `background-hover`, `background-press`, `color` (texte), `color-1`..`color-11` (1 = proche du fond), `border-color`, `placeholder-color`, `card`, `separator`, `overlay`, `skeleton` ; absolues : `brand-50`..`brand-950`, `accent-*`, `surface-*`, Tailwind (`emerald-500`...). - Sous-thèmes (`theme="..."`) : `brand` (plein), `soft`, `accent`, `highlight`, `success`, `warning`, `danger`, `destructive`, `level2`..`level4`, `inverse`. - Conditions dans la valeur : `bg="card press:background-press dark:surface-900"`, `opacity="1 enter:0 exit:0"`. - Texte : `size="1"`..`"10"` (11 à 40 px) ; icônes : `size="5"` (échelle de police) et `color="color-9"`. - Animations : `transition="fast" | "medium" | "slow" | "bouncy"` (ressorts réglés par le ton de marque). ## Icônes Lucide (102) : `import { Plus } from '@daroms/ui/icons'` Autre icône : `bun daroms icons add NomLucide` (jamais d'autre jeu). Activity, ArrowLeft, ArrowRight, Bell, BookOpen, Bookmark, Bot, Calendar, Camera, ChartColumn, Check, ChevronDown, ChevronLeft, ChevronRight, ChevronUp, CircleAlert, CircleCheck, CircleHelp, CirclePlus, Clock, Coffee, Compass, Copy, CreditCard, Crown, Download, Dumbbell, Ellipsis, ExternalLink, Eye, EyeOff, FileText, Filter, Flame, Gift, Globe, Heart, Home, House, Image, Inbox, Info, KeyRound, Languages, Layers, LayoutGrid, Leaf, Link, List, ListChecks, Lock, LogIn, LogOut, Mail, MapPin, Menu, MessageCircle, Mic, Minus, Moon, Music, Notebook, Package, Palette, Pause, Pencil, Phone, Play, Plus, RefreshCw, Search, Send, Settings, Share2, Shield, ShoppingBag, ShoppingCart, SlidersHorizontal, Smartphone, Smile, Sparkles, SquarePen, Star, Store, Sun, Tag, Target, ThumbsUp, Trash2, TrendingUp, TriangleAlert, Trophy, Upload, User, UserRound, Users, Vibrate, Wallet, Wifi, WifiOff, X, Zap --- Page HTML : https://daromsartof.github.io/daroms-speedapp/reference/composants/ Index de la documentation : https://daromsartof.github.io/daroms-speedapp/llms.txt # Tokens et thèmes > Les tokens Tamagui v3 utilisables dans une app daroms - espaces, rayons, couleurs de thème, sous-thèmes, conditions, typographie, icônes et animations, avec la syntaxe v3. Les styles d'une app daroms n'utilisent que des tokens, en **valeurs plates Tamagui v3 sans `$`**. Liste courte et à jour : la section Tokens du [catalogue](https://daromsartof.github.io/daroms-speedapp/reference/composants.md). ## Espaces | Token | Usage | |---|---| | `gutter` | marge horizontale d'écran | | `section` | entre deux sections | | `stack` | entre éléments empilés | | `inline` | entre éléments en ligne | | `tight` | entre un libellé et sa valeur | | `0`..`96`, `0-5`, `1-5` | échelle 4 pt (`4` = 16 px), demi-pas | Les valeurs sémantiques dépendent du ton ([Marque](https://daromsartof.github.io/daroms-speedapp/reference/marque.md#ce-que-change-le-ton)). ```tsx ... ``` ## Rayons `button`, `input`, `card`, `sheet`, `chip`, `badge`, `avatar`, `pill` : `rounded="card"`. ## Couleurs | Token | Rôle | |---|---| | `background`, `background-hover`, `background-press` | fond et états | | `color` | texte | | `color-1`..`color-11` | rampe du thème, `1` proche du fond, `11` proche du texte | | `border-color`, `placeholder-color`, `separator`, `overlay`, `skeleton` | rôles | | `card` | fond de carte | | `brand-50`..`brand-950`, `accent-*`, `surface-*` | palettes absolues de la marque | | `emerald-500`, `red-500`... | palettes Tailwind | Une couleur hex ou `rgb()` est refusée par le hook et par ESLint. Préférer les clés de thème (`color-9`, `card`) aux palettes absolues : elles suivent le mode sombre. ## Sous-thèmes ```tsx {t('tips.today')} ``` `brand`, `soft`, `accent`, `highlight`, `success`, `warning`, `danger`, `destructive`, `level2`..`level4`, `inverse`. Détail : [Marque](https://daromsartof.github.io/daroms-speedapp/reference/marque.md#thèmes-et-sous-thèmes). ## Conditions dans la valeur Tamagui v3 met les états et les points de rupture dans la valeur : ```tsx ``` `hoverStyle`, `pressStyle`, `$4`, `$color10` sont la syntaxe v2 : elle n'existe plus. Voir [Tamagui v3](https://daromsartof.github.io/daroms-speedapp/explications/tamagui-v3.md). ## Typographie Composants `Title`, `Heading level={1|2|3}`, `Body`, `Caption` avec `tone` (`default`, `muted`, `brand`, `accent`, `danger`, `success`) et `align`. Tailles `size="1"`..`"10"` (11 à 40 px), graisse des titres réglée par le ton. ## Icônes ```tsx import { Plus } from '@daroms/ui/icons' ``` 102 icônes Lucide incluses (liste dans le [catalogue](https://daromsartof.github.io/daroms-speedapp/reference/composants.md)). Autre icône : `bun daroms icons add NomLucide`. ## Animations `transition="fast" | "medium" | "slow" | "bouncy"` ; Reanimated 4 pour le geste et le continu. Jamais l'API Animated de React Native en app. --- Page HTML : https://daromsartof.github.io/daroms-speedapp/reference/tokens/ Index de la documentation : https://daromsartof.github.io/daroms-speedapp/llms.txt # Paquets @daroms/* > API publique des paquets du cœur - @daroms/ui, @daroms/theme, @daroms/data, @daroms/lib, @daroms/i18n, @daroms/app, @daroms/config - avec leurs points d'entrée et exemples. Les paquets sont des sources TypeScript liées par workspace Bun (`core/packages/*`), sans étape de build. Importer uniquement les points d'entrée listés ici. | Paquet | Rôle | |---|---| | [`@daroms/ui`](#daromsui) | composants, icônes Lucide, primitives de mise en page | | [`@daroms/theme`](#daromstheme) | thème piloté par `brand.ts` | | [`@daroms/data`](#daromsdata) | Supabase, requêtes, erreurs, tables locales | | [`@daroms/lib`](#daromslib) | formats, dates, zod partagé, stockage, haptique, manifest | | [`@daroms/i18n`](#daromsi18n) | i18next, `useT` typé | | [`@daroms/app`](#daromsapp) | provider racine, gates, modules, layouts | | [`@daroms/config`](#daromsconfig) | tsconfig, ESLint, Jest, Prettier | ## @daroms/ui ```ts import { Button, Screen, List, YStack } from '@daroms/ui' import { Plus } from '@daroms/ui/icons' ``` - Composants : voir le [catalogue](https://daromsartof.github.io/daroms-speedapp/reference/composants.md) (props et exemples). - Primitives Tamagui réexportées : `XStack`, `YStack`, `View`, `ScrollView`, `Separator`, `Spacer`, `Spinner`, `Switch`, `Theme`, `styled`, `useMedia`, `useTheme`. - Notifications : `notify.success | error | info(message)` (`` est monté par `AppProvider`). ## @daroms/theme ```ts import { defineBrand } from '@daroms/theme/brand' // dans brand.ts import { createBrandConfig } from '@daroms/theme' // dans tamagui.config.ts ``` | Export | Rôle | |---|---| | `defineBrand`, `BrandSchema` | valide et complète la marque | | `createBrandConfig` | configuration Tamagui complète (tokens, thèmes, polices, animations) | | `createBrandTheme`, `createBrandPalettes` | données pures : palettes, tokens couleur, thèmes | | `createPalette`, `createNeutral`, `anchorShade`, `paletteToTokens`, `SHADES` | palettes OKLCH | | `contrastRatio`, `hexToRgb`, `rgbToHex`, `rgbToOklch`, `oklchToHex` | couleurs | | `semanticRadius`, `semanticSpace`, `springs`, `TONES`, `DURATIONS`, `Z_INDEX` | ton, rayons, espaces, animations | | `createBrandFonts`, `FONT_SIZES`, `INTER_FILES` (`@daroms/theme/fonts`) | polices | Détail du calcul : [Marque](https://daromsartof.github.io/daroms-speedapp/reference/marque.md). ## @daroms/data Seul accès à Supabase, et seulement depuis `src/features//api.ts`. | Export | Rôle | |---|---| | `db()` | client Supabase typé par `src/types/db.ts` : `db().from('books').select()` | | `configureSupabase`, `getSupabase`, `getSupabaseEnv`, `isSupabaseConfigured` | configuration (faite par `createRootLayout`) | | `moduleDb()` | client non typé, réservé aux modules du cœur | | `parseOrThrow(schema, data)` | valide une réponse ; erreur `validation` sinon | | `unwrap(result)` | renvoie `data` ou lève l'erreur Supabase normalisée | | `queryKeys('books')` | clés `all`, `list(params)`, `detail(id)` | | `AppError`, `toAppError` | erreurs normalisées avec `code` et `messageKey` i18n | | `localTable(name, schema)` | table sur l'appareil, mêmes lignes que Supabase : `list`, `get`, `insert`, `update`, `remove`, `clear` | | `SessionProvider`, `useSession()`, `useUser()` | session Supabase | | `QueryProvider`, `createQueryClient` | TanStack Query | Codes d'`AppError` : `network`, `unauthorized`, `forbidden`, `notFound`, `conflict`, `validation`, `rateLimit`, `config`, `unknown`. `ErrorState` affiche le message traduit et le bouton Réessayer. ```ts export function useBooks() { return useQuery({ queryKey: booksKeys.list(), queryFn: async () => parseOrThrow(BookSchema.array(), unwrap(await db().from('books').select('*'))), }) } ``` ## @daroms/lib Fonctions pures, testées. Sous-chemins : `@daroms/lib/format`, `/dates`, `/zod`, `/storage`, `/haptics`, `/id`, `/manifest`, `/manifest-schema`. | Domaine | Exports | |---|---| | Formats | `formatPrice(montant, devise?)`, `formatNumber`, `formatPercent(ratio)`, `formatDate(valeur, 'short'\|'medium'\|'long'\|'time'\|'datetime')`, `formatRelative` (« il y a 3 min »), `initials`, `deviceLocale` | | Jours calendaires | `dayKey` (« AAAA-MM-JJ » local), `toDay`, `addDays`, `daysBetween`, `isToday`, `lastDays(items, date, { days, value })` (somme par jour), `streak(dates)` (série), `formatWeekday(jour, 'narrow'\|'short'\|'long')` | | zod | `zEmail`, `zPassword`, `zRequired`, `zUuid`, `zUrl`, `zIsoDate`, `zMoney`, `zPositiveInt`, `zRow`, `z` | | Stockage | `storage` (instance de l'app), `createStorage(id)` : `getString`, `setString`, `getBoolean`, `setBoolean`, `getJSON(cle, schema?)`, `setJSON`, `has`, `remove` ; `zustandStorage`, `supabaseAuthStorage`. MMKV, sinon SQLite kv-store (Expo Go), sinon mémoire. | | Haptique | `haptic('light'\|'medium'\|'heavy'\|'selection'\|'success'\|'warning'\|'error')`, `setHapticsEnabled`, `hapticsEnabled` | | Identifiants | `createId()` (UUID si disponible) | | Manifest | `getManifest()`, `defineManifest`, `ManifestSchema`, `setManifestForTests` | Les jours sont calculés par date civile, pas en millisecondes : un jour de 23 h ou 25 h (heure d'été) ne décale rien. ## @daroms/i18n | Export | Rôle | |---|---| | `useT()` | traductions de l'app, typées par les clés de `locales/` | | `useDaromsT()` | textes du cœur (namespace `daroms`) | | `useTranslateKey()` | traduit une clé préfixée (`daroms:validation.email`) | | `createNamespaceT(ns)` | hook typé pour un namespace (modules) | | `initI18n`, `detectLanguage`, `coreResources`, `i18next` | initialisation (faite par `createRootLayout`) | Mode d'emploi : [Textes et langues](https://daromsartof.github.io/daroms-speedapp/guides/textes-et-langues.md). ## @daroms/app L'assemblage d'une app ; une app n'y touche presque jamais directement. | Export | Rôle | |---|---| | `createRootLayout({ config, modules, locales, supabase, loadInter })` | layout racine : i18n, Supabase, modules, polices, splash, providers, pile | | `AppProvider` | gestes, safe area, Tamagui, thème de navigation, requêtes, session, gates, modules, toasts | | `TabsLayout` | onglets construits depuis `manifest.tabs` | | `defineModule`, `resolveModules` | définir et valider les modules | | `useModules`, `useModuleConfig(slug)`, `useModuleSettings` | modules au runtime | | `GateKeeper`, `useGate(slug, priorité, { ready, redirect })` | redirections au démarrage (onboarding 10, auth 20, paywall 30) ; le splash reste tant qu'une gate n'est pas prête | | `usePreferences`, `useAppColorScheme` | préférences : apparence système, clair ou sombre | | `withDaroms` (`@daroms/app/expo-config`) | configuration Expo dérivée du manifest et de la marque | | `@daroms/app/testing` | rendu de test avec providers | `defineModule({ slug, config, Provider, init, locales, useSettings, gate, modals })` : voir [Architecture](https://daromsartof.github.io/daroms-speedapp/explications/architecture.md#modules). ## @daroms/config `tsconfig.base.json` (strict), `eslint` (règles daroms : routes, styles, données, imports, `any`), `jest` (preset jest-expo, mocks), `prettier`. Les apps les étendent ; ne pas les copier. --- Page HTML : https://daromsartof.github.io/daroms-speedapp/reference/paquets/ Index de la documentation : https://daromsartof.github.io/daroms-speedapp/llms.txt # Chat IA (ai-chat) > Module ai-chat : Assistant Claude en streaming : edge function (clé API côté serveur), historique Supabase, onglet Chat. Assistant Claude en streaming : edge function (clé API côté serveur), historique Supabase, onglet Chat. :::note[Page générée] Générée depuis `modules/ai-chat/README.md`, `module.json`, `config.ts`, `index.ts` à chaque build du site : modifier la source, pas cette page. ::: ## Fiche technique | | | |---|---| | Paquet | `@daroms/module-ai-chat` | | Activer | `bun daroms module add ai-chat` | | Requiert | `auth` | | Dépendances natives | aucune | | Routes ajoutées | `app/(tabs)/chat.tsx` (`ChatScreen`) | | Onglets | `chat` (icône `Sparkles`) | | Migrations | `migrations/ai_chat.sql` | | Edge functions | `functions/ai-chat` | | Variables d'environnement | aucune | | Gate de navigation | non | | Section de réglages | non | ## Mise en place et usage Assistant conversationnel Claude en streaming. Requiert `auth`. - Activer : `bun daroms module add ai-chat` (onglet Chat, migration `ai_conversations` / `ai_messages`, edge function `ai-chat`). - Secrets serveur (jamais dans l'app) : `bunx supabase secrets set ANTHROPIC_API_KEY=...` - `AI_CHAT_MODEL` (défaut `claude-opus-5-5`), `AI_CHAT_SYSTEM` (prompt système), `AI_CHAT_DAILY_LIMIT` (défaut 50 messages / 24 h). - Déployer : `bunx supabase functions deploy ai-chat`. - Effort `low` (réponses rapides) ; repli automatique côté serveur si le modèle décline (`fallbacks: "default"`), sinon message « ne peut pas répondre ». - Config app : `suggestions` (clés i18n affichées quand la conversation est vide). ## Configuration Dans `app.manifest.ts > modules['ai-chat']`, validée par `modules/ai-chat/config.ts` au démarrage. | Clé | Schéma zod | Rôle | |---|---|---| | `suggestions` | `z.array(z.string()).default([])` | Clés i18n (namespace app) des suggestions affichées quand la conversation est vide. | Valeurs écrites par `module add` : ```json { "suggestions": [ "aiChat.suggestion1", "aiChat.suggestion2" ] } ``` ## Exports ```ts import { ChatScreen, streamReply } from '@daroms/module-ai-chat' ``` - Valeurs : `ChatScreen`, `streamReply` - Types : `ChatMessage` --- Page HTML : https://daromsartof.github.io/daroms-speedapp/reference/modules/ai-chat/ Index de la documentation : https://daromsartof.github.io/daroms-speedapp/llms.txt # Analytique (analytics) > Module analytics : Événements et écrans vus, envoyés par lots dans Supabase (table analytics_events, écriture seule). Événements et écrans vus, envoyés par lots dans Supabase (table analytics_events, écriture seule). :::note[Page générée] Générée depuis `modules/analytics/README.md`, `module.json`, `config.ts`, `index.ts` à chaque build du site : modifier la source, pas cette page. ::: ## Fiche technique | | | |---|---| | Paquet | `@daroms/module-analytics` | | Activer | `bun daroms module add analytics` | | Requiert | aucun module | | Dépendances natives | aucune | | Routes ajoutées | aucune | | Onglets | aucun | | Migrations | `migrations/analytics_events.sql` | | Edge functions | aucune | | Variables d'environnement | aucune | | Gate de navigation | non | | Section de réglages | non | ## Mise en place et usage Analytique légère et souveraine : événements envoyés par lots dans Supabase (`analytics_events`, écriture seule). - Activer : `bun daroms module add analytics`. - Code : `track('todo_created', { source: 'sheet' })` ; les écrans sont suivis automatiquement. - Lecture : requêtes SQL dans le tableau de bord Supabase (aucune lecture depuis l'app). - Config : `sink` (`supabase` | `console`), `flushIntervalMs`, `screens`. ## Configuration Dans `app.manifest.ts > modules['analytics']`, validée par `modules/analytics/config.ts` au démarrage. | Clé | Schéma zod | Rôle | |---|---|---| | `sink` | `z.enum(['supabase', 'console']).default('supabase')` | supabase : table analytics_events ; console : journal de dev. | | `flushIntervalMs` | `z.number().int().min(2000).default(15000)` | - | | `screens` | `z.boolean().default(true)` | Enregistre automatiquement chaque écran vu. | Valeurs écrites par `module add` : ```json { "sink": "supabase", "flushIntervalMs": 15000, "screens": true } ``` ## Exports ```ts import { flush, track, trackScreen } from '@daroms/module-analytics' ``` - Valeurs : `flush`, `track`, `trackScreen` --- Page HTML : https://daromsartof.github.io/daroms-speedapp/reference/modules/analytics/ Index de la documentation : https://daromsartof.github.io/daroms-speedapp/llms.txt # Authentification (auth) > Module auth : Connexion e-mail, Apple et Google (Supabase Auth), profil, suppression de compte. Connexion e-mail, Apple et Google (Supabase Auth), profil, suppression de compte. :::note[Page générée] Générée depuis `modules/auth/README.md`, `module.json`, `config.ts`, `index.ts` à chaque build du site : modifier la source, pas cette page. ::: ## Fiche technique | | | |---|---| | Paquet | `@daroms/module-auth` | | Activer | `bun daroms module add auth` | | Requiert | aucun module | | Dépendances natives | `expo-apple-authentication`, `expo-crypto` | | Routes ajoutées | `app/(auth)/_layout.tsx` (`AuthLayout`), `app/(auth)/sign-in.tsx` (`SignInScreen`), `app/(auth)/sign-up.tsx` (`SignUpScreen`), `app/(auth)/forgot-password.tsx` (`ForgotPasswordScreen`) | | Onglets | aucun | | Migrations | `migrations/profiles.sql` | | Edge functions | `functions/delete-account` | | Variables d'environnement | aucune | | Gate de navigation | oui (décide de la redirection au démarrage) | | Section de réglages | oui (ajoutée au module settings) | ## Mise en place et usage Connexion Supabase : e-mail + mot de passe, Apple (jeton natif), Google (OAuth PKCE). Gate de connexion, profil, suppression de compte. - Activer : `bun daroms module add auth` (ajoute routes `(auth)/*`, migration `profiles`, edge function `delete-account`). - Config (`manifest.modules.auth`) : `providers` (`email`, `apple`, `google`), `requireAuth`. - Supabase : activer les fournisseurs Apple et Google dans Auth > Providers ; ajouter `://**` aux URL de redirection. - Déployer la fonction : `bunx supabase functions deploy delete-account`. - Profil : `useProfile()` ; déconnexion : `signOut()`. ## Configuration Dans `app.manifest.ts > modules['auth']`, validée par `modules/auth/config.ts` au démarrage. | Clé | Schéma zod | Rôle | |---|---|---| | `providers` | `z.array(z.enum(['email', 'apple', 'google'])).min(1).default(['email', 'apple', 'google'])` | Méthodes proposées. Apple est obligatoire sur iOS dès qu'une connexion sociale existe. | | `requireAuth` | `z.boolean().default(true)` | true : toute l'app exige une session ; false : connexion à la demande. | Valeurs écrites par `module add` : ```json { "providers": [ "email", "apple", "google" ], "requireAuth": true } ``` ## Exports ```ts import { deleteAccount, signInWithApple, signInWithEmail } from '@daroms/module-auth' ``` - Valeurs : `deleteAccount`, `signInWithApple`, `signInWithEmail`, `signInWithGoogle`, `signOut`, `signUpWithEmail`, `useProfile`, `AuthLayout`, `ForgotPasswordScreen`, `SignInScreen`, `SignUpScreen` - Types : `Profile` --- Page HTML : https://daromsartof.github.io/daroms-speedapp/reference/modules/auth/ Index de la documentation : https://daromsartof.github.io/daroms-speedapp/llms.txt # Photos (camera) > Module camera : Photo prise ou choisie (expo-image-picker), envoyée dans Supabase Storage (dossier par utilisateur), composant PhotoPicker. Photo prise ou choisie (expo-image-picker), envoyée dans Supabase Storage (dossier par utilisateur), composant PhotoPicker. :::note[Page générée] Générée depuis `modules/camera/README.md`, `module.json`, `config.ts`, `index.ts` à chaque build du site : modifier la source, pas cette page. ::: ## Fiche technique | | | |---|---| | Paquet | `@daroms/module-camera` | | Activer | `bun daroms module add camera` | | Requiert | `auth` | | Dépendances natives | `expo-image-picker` | | Routes ajoutées | aucune | | Onglets | aucun | | Migrations | `migrations/storage_images.sql` | | Edge functions | aucune | | Variables d'environnement | aucune | | Gate de navigation | non | | Section de réglages | non | ## Mise en place et usage Photos prises ou choisies (expo-image-picker, fonctionne dans Expo Go), envoyées dans Supabase Storage. Requiert `auth`. - Activer : `bun daroms module add camera` (migration : bucket `images` public en lecture par URL, écriture et suppression limitées au dossier `/`). - Configuration (`modules.camera`) : `bucket` (défaut `images` ; autre nom = adapter la migration), `quality` (0.7), `aspect` (ex. `[1, 1]` pour un avatar), `cameraPermission` et `photosPermission` (textes iOS, défaut selon `defaultLocale`). - Écran : `` (feuille appareil photo, galerie, retirer). - Envoi dans `api.ts`, au moment d'enregistrer : ```ts import { uploadPhoto, type Photo } from '@daroms/module-camera' mutationFn: async ({ photo, ...input }: PostInput & { photo: Photo | null }) => { const image = photo ? await uploadPhoto(photo) : null return insertPost({ ...input, image_url: image?.url ?? null }) } ``` - `pickPhoto('camera' | 'library')` et `removePhoto(path)` pour un usage sans composant. - Colonne d'image : `image_url:text?` dans `g table`. ## Configuration Dans `app.manifest.ts > modules['camera']`, validée par `modules/camera/config.ts` au démarrage. | Clé | Schéma zod | Rôle | |---|---|---| | `bucket` | `z.string().default('images')` | Bucket Supabase Storage (créé par la migration du module, lecture publique). | | `quality` | `z.number().min(0).max(1).default(0.7)` | Compression JPEG, de 0 à 1. | | `aspect` | `z.tuple([z.number().positive(), z.number().positive()]).optional()` | Recadrage imposé par défaut, ex. [1, 1] pour un avatar (sinon photo entière). | | `cameraPermission` | `z.string().optional()` | Textes iOS des permissions (plugin) : défaut dans la langue par défaut de l'app. | | `photosPermission` | `z.string().optional()` | - | Valeurs écrites par `module add` : ```json { "bucket": "images", "quality": 0.7 } ``` ## Exports ```ts import { PhotoPicker, pickPhoto, removePhoto } from '@daroms/module-camera' ``` - Valeurs : `PhotoPicker`, `pickPhoto`, `removePhoto`, `uploadPhoto` - Types : `PhotoPickerProps`, `Photo`, `PhotoSource`, `UploadedPhoto` --- Page HTML : https://daromsartof.github.io/daroms-speedapp/reference/modules/camera/ Index de la documentation : https://daromsartof.github.io/daroms-speedapp/llms.txt # Cartes (maps) > Module maps : Carte avec marqueurs (react-native-maps, repli web), position de l'utilisateur (expo-location), ouverture dans Plans/Maps. Carte avec marqueurs (react-native-maps, repli web), position de l'utilisateur (expo-location), ouverture dans Plans/Maps. :::note[Page générée] Générée depuis `modules/maps/README.md`, `module.json`, `config.ts`, `index.ts` à chaque build du site : modifier la source, pas cette page. ::: ## Fiche technique | | | |---|---| | Paquet | `@daroms/module-maps` | | Activer | `bun daroms module add maps` | | Requiert | aucun module | | Dépendances natives | `react-native-maps`, `expo-location` | | Routes ajoutées | aucune | | Onglets | aucun | | Migrations | aucune | | Edge functions | aucune | | Variables d'environnement | `GOOGLE_MAPS_ANDROID_API_KEY` | | Gate de navigation | non | | Section de réglages | non | ## Mise en place et usage Carte avec marqueurs (react-native-maps : Apple Plans sur iOS, Google Maps sur Android ; fonctionne dans Expo Go), position de l'utilisateur (expo-location), distances. Sur le web (`daroms shot`), une zone de repli remplace la carte. - Activer : `bun daroms module add maps`. - Configuration (`modules.maps`) : `center` (repli, défaut Paris), `span` (zoom, 0.05), `locationPermission` (texte iOS, défaut selon `defaultLocale`). - Écran : ` ({ id: p.id, latitude: p.lat, longitude: p.lng, title: p.name }))} onMarkerPress={(m) => router.push(\`/place/${m.id}\`)} ratio={4 / 3} />` (`flex={1}` pour une carte plein écran). - Position : `const here = useCurrentLocation({ enabled: nearby })` (permission demandée à l'usage ; refus = `AppError forbidden` pour `ErrorState`) ; `sortByDistance(places, here.data)`, `formatDistance(distanceKm(a, b), locale)`. - Itinéraire : `openInMaps({ latitude, longitude }, nom)`. - Colonnes : `lat:float, lng:float` dans `g table`. - Build Android : clé Google Maps (API « Maps SDK for Android ») dans `GOOGLE_MAPS_ANDROID_API_KEY` (`.env` en local, `eas env:create` pour EAS), sinon la carte est vide hors Expo Go. iOS : rien à faire (Apple Plans). ## Configuration Dans `app.manifest.ts > modules['maps']`, validée par `modules/maps/config.ts` au démarrage. | Clé | Schéma zod | Rôle | |---|---|---| | `center` | `point.default({ latitude: 48.8566, longitude: 2.3522 })` | Centre par défaut quand il n'y a ni marqueur ni position (Paris). | | `span` | `z.number().positive().default(0.05)` | Zoom par défaut : degrés de latitude visibles (0.05 = un quartier, 0.5 = une ville). | | `locationPermission` | `z.string().optional()` | Texte iOS de la permission de localisation (plugin) : défaut selon la langue par défaut de l'app. | Valeurs écrites par `module add` : ```json { "span": 0.05 } ``` ## Exports ```ts import { distanceKm, formatDistance, regionFor } from '@daroms/module-maps' ``` - Valeurs : `distanceKm`, `formatDistance`, `regionFor`, `sortByDistance`, `getCurrentLocation`, `openInMaps`, `useCurrentLocation`, `Map` - Types : `LatLng`, `Region`, `MapMarker`, `MapProps` --- Page HTML : https://daromsartof.github.io/daroms-speedapp/reference/modules/maps/ Index de la documentation : https://daromsartof.github.io/daroms-speedapp/llms.txt # Onboarding (onboarding) > Module onboarding : Pages d'accueil au premier lancement (icône, titre, texte), affichées une fois. Pages d'accueil au premier lancement (icône, titre, texte), affichées une fois. :::note[Page générée] Générée depuis `modules/onboarding/README.md`, `module.json`, `config.ts`, `index.ts` à chaque build du site : modifier la source, pas cette page. ::: ## Fiche technique | | | |---|---| | Paquet | `@daroms/module-onboarding` | | Activer | `bun daroms module add onboarding` | | Requiert | aucun module | | Dépendances natives | aucune | | Routes ajoutées | `app/onboarding.tsx` (`OnboardingScreen`) | | Onglets | aucun | | Migrations | aucune | | Edge functions | aucune | | Variables d'environnement | aucune | | Gate de navigation | oui (décide de la redirection au démarrage) | | Section de réglages | non | ## Mise en place et usage Pages d'accueil au premier lancement, affichées une seule fois par `version`. - Activer : `bun daroms module add onboarding` (route `app/onboarding.tsx`). - Textes : ajouter les clés `onboarding.page1Title`... dans `locales/*.json` de l'app (namespace app). - Config : `pages` (icône Lucide + clés), `version` (incrémenter pour réafficher). ## Configuration Dans `app.manifest.ts > modules['onboarding']`, validée par `modules/onboarding/config.ts` au démarrage. | Clé | Schéma zod | Rôle | |---|---|---| | `version` | `z.number().int().positive().default(1)` | Incrémenter pour réafficher l'onboarding après une refonte. | | `pages` | `z.array(z.object({ icon: z.string(), titleKey: z.string(), descriptionKey: z.string() })).min(1).default([{ icon: 'Sparkles', titleKey: 'onboarding.page1Title', descriptionKey: 'onboarding.page1Description' }])` | Pages : icône Lucide + clés i18n du namespace app (locales/*.json). | Valeurs écrites par `module add` : ```json { "version": 1, "pages": [ { "icon": "Sparkles", "titleKey": "onboarding.page1Title", "descriptionKey": "onboarding.page1Description" }, { "icon": "Zap", "titleKey": "onboarding.page2Title", "descriptionKey": "onboarding.page2Description" }, { "icon": "Heart", "titleKey": "onboarding.page3Title", "descriptionKey": "onboarding.page3Description" } ] } ``` ## Exports ```ts import { OnboardingScreen, markOnboardingDone, useOnboardingDone } from '@daroms/module-onboarding' ``` - Valeurs : `OnboardingScreen`, `markOnboardingDone`, `useOnboardingDone` --- Page HTML : https://daromsartof.github.io/daroms-speedapp/reference/modules/onboarding/ Index de la documentation : https://daromsartof.github.io/daroms-speedapp/llms.txt # Paiements (payments) > Module payments : Abonnements RevenueCat : paywall, statut Pro, restauration (paywall souple ou bloquant). Abonnements RevenueCat : paywall, statut Pro, restauration (paywall souple ou bloquant). :::note[Page générée] Générée depuis `modules/payments/README.md`, `module.json`, `config.ts`, `index.ts` à chaque build du site : modifier la source, pas cette page. ::: ## Fiche technique | | | |---|---| | Paquet | `@daroms/module-payments` | | Activer | `bun daroms module add payments` | | Requiert | aucun module | | Dépendances natives | `react-native-purchases` | | Routes ajoutées | `app/paywall.tsx` (`PaywallScreen`) | | Onglets | aucun | | Migrations | aucune | | Edge functions | aucune | | Variables d'environnement | `EXPO_PUBLIC_REVENUECAT_IOS_KEY`, `EXPO_PUBLIC_REVENUECAT_ANDROID_KEY` | | Gate de navigation | oui (décide de la redirection au démarrage) | | Section de réglages | oui (ajoutée au module settings) | | Modales | `paywall` | ## Mise en place et usage Abonnements et achats avec RevenueCat (`react-native-purchases`). - Activer : `bun daroms module add payments` (route modale `app/paywall.tsx`). - `.env` : `EXPO_PUBLIC_REVENUECAT_IOS_KEY`, `EXPO_PUBLIC_REVENUECAT_ANDROID_KEY` (clés publiques du SDK). - RevenueCat : créer l'entitlement (`pro` par défaut), les produits et l'offering courante. - Config : `entitlement`, `mode` (`soft` : paywall à la demande ; `hard` : obligatoire), `offering`, `features` (clés i18n). - Code : `usePro()` (statut), `` (contenu réservé), `router.push('/paywall')`. - Expo Go : RevenueCat fonctionne en mode aperçu ; achats réels uniquement en build de dev ou de prod. ## Configuration Dans `app.manifest.ts > modules['payments']`, validée par `modules/payments/config.ts` au démarrage. | Clé | Schéma zod | Rôle | |---|---|---| | `entitlement` | `z.string().default('pro')` | Identifiant d'entitlement RevenueCat qui débloque la version Pro. | | `mode` | `z.enum(['soft', 'hard']).default('soft')` | soft : paywall à la demande ; hard : paywall obligatoire après connexion. | | `offering` | `z.string().optional()` | Offering RevenueCat (défaut : l'offering courante). | | `features` | `z.array(z.string()).default(['paywall.feature1', 'paywall.feature2', 'paywall.feature3'])` | Clés i18n (namespace app) des avantages listés sur le paywall. | Valeurs écrites par `module add` : ```json { "entitlement": "pro", "mode": "soft", "features": [ "paywall.feature1", "paywall.feature2", "paywall.feature3" ] } ``` ## Exports ```ts import { usePro, PaywallScreen, ProGate } from '@daroms/module-payments' ``` - Valeurs : `usePro`, `PaywallScreen`, `ProGate` --- Page HTML : https://daromsartof.github.io/daroms-speedapp/reference/modules/payments/ Index de la documentation : https://daromsartof.github.io/daroms-speedapp/llms.txt # Notifications push (push) > Module push : Notifications Expo : permission, jeton par utilisateur (Supabase), liens profonds, fonction d'envoi. Notifications Expo : permission, jeton par utilisateur (Supabase), liens profonds, fonction d'envoi. :::note[Page générée] Générée depuis `modules/push/README.md`, `module.json`, `config.ts`, `index.ts` à chaque build du site : modifier la source, pas cette page. ::: ## Fiche technique | | | |---|---| | Paquet | `@daroms/module-push` | | Activer | `bun daroms module add push` | | Requiert | `auth` | | Dépendances natives | `expo-notifications`, `expo-device` | | Routes ajoutées | aucune | | Onglets | aucun | | Migrations | `migrations/push_tokens.sql` | | Edge functions | `functions/send-push` | | Variables d'environnement | aucune | | Gate de navigation | non | | Section de réglages | oui (ajoutée au module settings) | ## Mise en place et usage Notifications push Expo. Requiert `auth` (jeton rattaché à l'utilisateur). - Activer : `bun daroms module add push` (migration `push_tokens`, edge function `send-push`). - Build de dev requis (pas d'Expo Go) ; projet EAS requis pour le jeton (`bun daroms build development`). - Envoyer : `POST /functions/v1/send-push` avec `Authorization: Bearer ` et `{ user_id, title, body, url }` (depuis le serveur uniquement). - `url` (ex. `/todos/42`) est ouverte au toucher de la notification. ## Configuration Dans `app.manifest.ts > modules['push']`, validée par `modules/push/config.ts` au démarrage. | Clé | Schéma zod | Rôle | |---|---|---| | `askOnLaunch` | `z.boolean().default(false)` | Demander la permission dès la connexion (sinon depuis les réglages ou un écran dédié). | Valeurs écrites par `module add` : ```json { "askOnLaunch": false } ``` ## Exports ```ts import { registerForPush, permissionGranted } from '@daroms/module-push' ``` - Valeurs : `registerForPush`, `permissionGranted` --- Page HTML : https://daromsartof.github.io/daroms-speedapp/reference/modules/push/ Index de la documentation : https://daromsartof.github.io/daroms-speedapp/llms.txt # Réglages (settings) > Module settings : Onglet Réglages : apparence, langue, haptique, compte, abonnement, notifications, à propos. Onglet Réglages : apparence, langue, haptique, compte, abonnement, notifications, à propos. :::note[Page générée] Générée depuis `modules/settings/README.md`, `module.json`, `config.ts`, `index.ts` à chaque build du site : modifier la source, pas cette page. ::: ## Fiche technique | | | |---|---| | Paquet | `@daroms/module-settings` | | Activer | `bun daroms module add settings` | | Requiert | aucun module | | Dépendances natives | aucune | | Routes ajoutées | `app/(tabs)/settings.tsx` (`SettingsScreen`) | | Onglets | `settings` (icône `Settings`) | | Migrations | aucune | | Edge functions | aucune | | Variables d'environnement | aucune | | Gate de navigation | non | | Section de réglages | non | ## Mise en place et usage Onglet Réglages : apparence (système, clair, sombre), langue, retour haptique, sections apportées par les autres modules (`useSettings` : compte, abonnement, notifications), à propos (version, liens `manifest.store`). - Activer : `bun daroms module add settings`. - Ajouter une section depuis un module : `useSettings: () => SettingsSection[]` dans `defineModule`. ## Configuration Dans `app.manifest.ts > modules['settings']`, validée par `modules/settings/config.ts` au démarrage. | Clé | Schéma zod | Rôle | |---|---|---| | `appearance` | `z.boolean().default(true)` | - | | `language` | `z.boolean().default(true)` | - | | `haptics` | `z.boolean().default(true)` | - | ## Exports ```ts import { SettingsScreen } from '@daroms/module-settings' ``` - Valeurs : `SettingsScreen` --- Page HTML : https://daromsartof.github.io/daroms-speedapp/reference/modules/settings/ Index de la documentation : https://daromsartof.github.io/daroms-speedapp/llms.txt # Variantes d'app > Les 6 variantes de départ de daroms new (blank, list-detail, tracker, ai-chat, social, shop) avec leurs modules, tables et écrans. Une variante est une recette appliquée à la création (`daroms new --variant `) : modules activés avec leur configuration, tables générées, écrans générés. Elle vit dans `template/variants//variant.json`. `/daroms-idea` choisit la variante dans `app.spec.md`. | Variante | Pour | Modules | Tables | Écrans (onglets) | |---|---|---|---|---| | `blank` | partir de zéro | aucun | aucune | Accueil | | `list-detail` | collection personnelle : notes, lieux, recettes | auth, settings | `items` (`title:text, notes:text?, favorite:bool=false`) | Éléments | | `tracker` | habitudes, progrès : entrées datées, rappels | onboarding, auth, settings, push | `entries` (`label:text, value:numeric=1, done_on:date`) | Aujourd'hui, Progression | | `ai-chat` | assistant IA avec abonnement | onboarding, auth, ai-chat, payments (`soft`), settings | aucune | Chat | | `social` | réseau léger : publications, profils, fil | auth, push, settings | `posts` (`body:text, image_url:text?`) | Fil | | `shop` | catalogue, panier, commandes | auth (`requireAuth: false`), settings, analytics | `products`, `orders` | Boutique, Commandes | Notes des variantes : - `social` : la lecture publique des posts demande une politique `select to authenticated using (true)` en plus de la RLS propriétaire, voir [Données et sécurité](https://daromsartof.github.io/daroms-speedapp/explications/donnees-et-securite.md). - `shop` : le catalogue est en lecture publique et les achats physiques passent par un paiement externe (Stripe) à ajouter en module ; les achats intégrés (`payments`) ne conviennent qu'aux biens numériques. ## Options utiles ```bash bun daroms new carnet --variant list-detail --local # tables sur l'appareil, sans Supabase bun daroms new coach --variant tracker --modules analytics # modules en plus ``` Après la création, tout se modifie : `module add|remove`, `g table`, `g screen`. Voir [CLI, new](https://daromsartof.github.io/daroms-speedapp/reference/cli.md#new). --- Page HTML : https://daromsartof.github.io/daroms-speedapp/reference/variantes/ Index de la documentation : https://daromsartof.github.io/daroms-speedapp/llms.txt # Règles du framework > Toutes les règles de code daroms : résumé, fichiers concernés, détail, et ce qui les vérifie (ESLint, hooks, générateurs, agents). Les règles vivent dans `_ai/rules/` (chargées selon les fichiers touchés, frontmatter `paths:`) et `_ai/rules-ondemand/` (lues quand le sujet arrive). Le noyau `_ai/CLAUDE.core.md` les résume et reste toujours chargé. :::note[Page générée] Générée depuis `_ai/rules/*.md`, `_ai/rules-ondemand/*.md` à chaque build du site : modifier la source, pas cette page. ::: ## Qui vérifie quoi | Règle | Vérifiée par | |---|---| | Routes minces (`app/**` = réexports) | ESLint, hook `daroms-check-rules` | | Couleurs : tokens seulement | ESLint, hook, typage Tamagui (`somewhat-strict`) | | Pas de nombre magique de style, `StyleSheet`, style inline | ESLint, hook | | Supabase seulement dans `features/x/api.ts` | ESLint, hook | | `any` interdit | ESLint, hook | | Textes via `t()`, sans emoji ni tiret long, clés FR = EN | ESLint, hook, `daroms doctor` | | Pas de librairie UI ni d'icônes tierces | ESLint, hook | | Imports absolus `@/`, `~/` | ESLint | | Fichiers <= 200 lignes | ESLint, hook | | RLS sur chaque table, pas de `service_role` dans l'app | générateur `g table`, agent `daroms-reviewer` | | 4 états par écran | générateur `g screen`, `QueryView` / `List`, agent `daroms-reviewer` | | `.env` jamais lu | permissions `deny`, hook `daroms-deny-env` | ## Index | Règle | Résumé | Chargée pour | |---|---|---| | [daroms-ai-files](#daroms-ai-files) | Les fichiers daroms-* de .claude/ sont écrasés à chaque synchro ; ce qui est propre à l'app porte le préfixe app-. | `.claude/**`, `CLAUDE.md`, `app.spec.md` | | [daroms-data](#daroms-data) | Supabase seulement dans features/x/api.ts via db() ; entrées et sorties validées zod ; hooks TanStack Query. | `src/features/**/api.ts`, `src/features/**/schema.ts`, `src/features/**/hooks.ts` | | [daroms-database](#daroms-database) | Structure uniquement par migration SQL ; RLS sur chaque table ; jamais de clé service_role côté app. | `supabase/**` | | [daroms-i18n](#daroms-i18n) | Tout texte affiché passe par t('cle') ; clés identiques dans chaque langue ; ni emoji, ni tiret long. | `locales/**`, `src/**/*.tsx` | | [daroms-naming](#daroms-naming) | PascalCase composants, useX hooks, kebab-case routes et dossiers, fichiers ≤ 200 lignes, imports absolus @/ et ~/. | `src/**`, `app/**` | | [daroms-performance](#daroms-performance) | FlashList pour toute liste, expo-image, items mémoïsés, animations Reanimated ou transition Tamagui. | `src/**/*.tsx` | | [daroms-routes](#daroms-routes) | app/** ne contient que des réexportations d'écrans ; aucune logique, aucun JSX. | `app/**/*.tsx` | | [daroms-screens](#daroms-screens) | Chaque écran gère 4 états (chargement, vide, erreur + réessayer, données) et reste sous 200 lignes. | `src/features/**/*Screen.tsx`, `src/features/**/components/**` | | [daroms-state](#daroms-state) | Serveur = TanStack Query ; local persistant = @daroms/lib/storage ; global UI = Zustand, un store par feature, rare. | `src/**/*.ts`, `src/**/*.tsx` | | [daroms-styles](#daroms-styles) | Composants @daroms/ui et tokens Tamagui v3 uniquement ; ni hex, ni nombre magique, ni StyleSheet, ni style inline. | `src/**/*.tsx`, `app/**/*.tsx` | | [daroms-tests](#daroms-tests) | Tests unitaires jest-expo à côté du code ; flows Maestro dans e2e/ ; pas de réseau dans les tests. | `**/*.test.ts`, `**/*.test.tsx`, `e2e/**` | | [daroms-animations](#daroms-animations) | transition Tamagui pour les états simples ; Reanimated 4 pour le geste et le continu. | à la demande | | [daroms-core-dev](#daroms-core-dev) | Travailler dans le dépôt daroms-speed-app (et non dans core/ d'une app), tester, publier une version. | à la demande | | [daroms-eas](#daroms-eas) | Profils development, preview, production ; canaux OTA ; version runtime = version de l'app. | à la demande | | [daroms-store-compliance](#daroms-store-compliance) | Points de refus fréquents à vérifier avant chaque soumission. | à la demande | | [daroms-supabase-advanced](#daroms-supabase-advanced) | Tables partagées, lecture publique, storage, realtime, fonctions SQL et edge functions. | à la demande | ## daroms-ai-files > Les fichiers daroms-* de .claude/ sont écrasés à chaque synchro ; ce qui est propre à l'app porte le préfixe app-. Chargement : quand Claude lit ou écrit `.claude/**`, `CLAUDE.md`, `app.spec.md`. - Règle propre à l'app : `.claude/rules/app-.md` (frontmatter `paths:`, une ligne `> résumé`). - Skill propre à l'app : `/daroms-skill-new` ; zones apprises : `.claude/context/app-zones.json` (`/daroms-learn`). - `CLAUDE.md` de l'app reste court : importe le noyau et le digest, ajoute seulement les spécificités. - `app.spec.md` est la source de vérité produit : le mettre à jour quand une décision change. ## daroms-data > Supabase seulement dans features/x/api.ts via db() ; entrées et sorties validées zod ; hooks TanStack Query. Chargement : quand Claude lit ou écrit `src/features/**/api.ts`, `src/features/**/schema.ts`, `src/features/**/hooks.ts`. - Générer : `bun daroms g table
{formatRelative(item.created_at)}
--fields "titre:text, fait:bool=false, echeance:timestamptz?"` -> migration RLS, `schema.ts`, `api.ts`, types. - Données sur l'appareil seulement : `--local` -> `localTable('t', Schema)` (`list`, `get`, `insert`, `update`, `remove`), mêmes hooks ; jamais de `storage.getJSON` à la main pour une collection. - Lecture : `useQuery({ queryKey: keys.list(), queryFn: async () => parseOrThrow(Schema.array(), unwrap(await db().from('t').select('*'))) })`. - Écriture : `useMutation` + `Input.parse(values)` + `invalidateQueries({ queryKey: keys.all })`. Optimiste seulement si l'UX l'exige. - Erreurs : `unwrap()` et `toAppError()` (`@daroms/data`) ; l'UI affiche `ErrorState error={q.error}` (message traduit). - Clés : `queryKeys('feature')` -> `all`, `list(params)`, `detail(id)`. - Utilisateur courant : `useUser()` ; ne jamais filtrer par user_id côté client à la place de la RLS. - `hooks.ts` combine requêtes et état local de l'écran (`useXScreen`) ; l'écran ne parle qu'à ce hook. - Types : `src/types/db.ts` (généré : `bun daroms db types`) ; ne pas l'éditer. ## daroms-database > Structure uniquement par migration SQL ; RLS sur chaque table ; jamais de clé service_role côté app. Chargement : quand Claude lit ou écrit `supabase/**`. - Nouvelle table : `bun daroms g table` (préféré) ou `bun daroms db new ` puis SQL à la main. - Chaque table : `enable row level security` + politiques `select/insert/update/delete` `to authenticated` avec `(select auth.uid()) = user_id`. - Index sur toute colonne de filtre (`user_id`, clés étrangères). `updated_at` via `public.daroms_set_updated_at()`. - Ne jamais modifier une migration déjà poussée : en créer une nouvelle. - Appliquer : `bun daroms db push` (puis types régénérés). Edge functions : `supabase/functions//index.ts` (Deno). - Secrets serveur : `bunx supabase secrets set NOM=...` ; jamais dans `.env` de l'app (seules les clés publiques `EXPO_PUBLIC_*`). - Détails avancés (tables partagées, storage, realtime) : `.claude/rules-ondemand/daroms-supabase-advanced.md`. ## daroms-i18n > Tout texte affiché passe par t('cle') ; clés identiques dans chaque langue ; ni emoji, ni tiret long. Chargement : quand Claude lit ou écrit `locales/**`, `src/**/*.tsx`. - `const t = useT()` (`@daroms/i18n`) ; clés typées depuis `locales/fr.json` (namespace app). - Organisation : `.` (`todos.emptyTitle`), onglets `tabs.`. Pluriels : `_one` / `_other`. - Ajouter chaque clé en FR et EN dans le même changement (`bun daroms doctor` vérifie la parité). - Textes courts, ton du cahier (`app.spec.md`). Interpolation : `t('x.count', { count })`. - Textes du cœur (Réessayer, Annuler...) : déjà fournis par les composants ; messages zod = clés `daroms:validation.*`. ## daroms-naming > PascalCase composants, useX hooks, kebab-case routes et dossiers, fichiers ≤ 200 lignes, imports absolus @/ et ~/. Chargement : quand Claude lit ou écrit `src/**`, `app/**`. - Feature : `src/features//` avec `Screen.tsx`, `hooks.ts`, `api.ts`, `schema.ts`, `components/`. - Hooks : `use` ; composants : `.tsx` exportant `` ; tests à côté : `.test.tsx`. - Imports : `@/` pour `src/`, `~/` pour la racine (`~/brand`, `~/locales/fr.json`) ; jamais `../`. - Fonctionnalité optionnelle réutilisable : module du cœur (`/daroms-module`), pas dans l'app. ## daroms-performance > FlashList pour toute liste, expo-image, items mémoïsés, animations Reanimated ou transition Tamagui. Chargement : quand Claude lit ou écrit `src/**/*.tsx`. - Liste : `List` (FlashList v2) ; `renderItem` stable (`useCallback`) ; item extrait et `memo`. Jamais `map` sur plus de ~20 éléments dans un ScrollView. - Images : `Image` de `@daroms/ui` (cache, placeholder blurhash, `ratio`). - Animations : prop `transition` + valeurs `enter:`/`exit:` ; cas avancés Reanimated (voir rules-ondemand/daroms-animations.md). - React Compiler actif : pas de `useMemo`/`useCallback` défensifs partout, seulement pour les props de listes. ## daroms-routes > app/** ne contient que des réexportations d'écrans ; aucune logique, aucun JSX. Chargement : quand Claude lit ou écrit `app/**/*.tsx`. - Une route = une ligne : `export { default } from '@/features//Screen'`. - Layouts : `export { default } from '@/features/root/RootLayout'` ; écrans de module : `export { XScreen as default } from '@daroms/module-x'`. - Créer une route : `bun daroms g screen [--tab --icon Nom]` (route + feature + i18n + test + manifest). - Onglets : déclarés dans `app.manifest.ts > tabs` (nom, icône Lucide, clé de titre), pas dans le layout. - Paramètres : `app/todo/[id].tsx` -> l'écran lit `useLocalSearchParams<{ id: string }>()`. - Navigation : `useRouter()` ; liens typés (`router.push('/todo/42')`). ## daroms-screens > Chaque écran gère 4 états (chargement, vide, erreur + réessayer, données) et reste sous 200 lignes. Chargement : quand Claude lit ou écrit `src/features/**/*Screen.tsx`, `src/features/**/components/**`. - Squelette : `} onRefresh refreshing>` puis contenu. - 4 états : `QueryView query={q} empty={{...}}` (ou `List data loading error onRetry empty`) ; jamais d'état oublié. - Formulaire : `useForm({ resolver: zodResolver(schema) })` + `FormField control name label` ; soumission `onPress={() => void form.handleSubmit(onValid)()}` ; bouton `loading={mutation.isPending}`. - Pied fixe (action principale) : `Screen footer={