# AGENTS.md Jeu d'entraînement aux tables de multiplication — Kotlin Multiplatform + Compose Multiplatform (Android, iOS, desktop JVM). UI, commits et docs sont en français. ## Commandes ```bash ./gradlew :sharedUI:jvmTest # tous les tests ./gradlew :sharedUI:jvmTest --tests "com.livrets.game.GameEngineTest" # une seule classe de test ./gradlew :desktopApp:run # app desktop (fenêtre 480×800) ./gradlew :androidApp:installDebug # appareil/émulateur connecté ./gradlew :androidApp:assembleDebug # APK debug ``` - Les tests vivent dans `sharedUI/src/commonTest` et ne s'exécutent que sur la cible JVM. - Aucun lint/formatter/CI n'est configuré : la vérification passe par `:sharedUI:jvmTest` + compilation des modules touchés. - iOS se construit via Xcode uniquement (`iosApp/iosApp.xcodeproj`, scheme `iosApp`) — pas de module Gradle pour l'app iOS ; `:sharedUI` produit le framework statique `SharedUI`. ## Architecture - `sharedUI/` — toute l'app : `game/` (logique pure : `GameEngine`, `QuestionGenerator`, `GameConfig`…), `data/` (persistance JSON via kotlinx.serialization + okio), `ui/` (écrans Compose), `theme/`, `games/` (abstraction Play Jeux), `App.kt` (navigation par sealed interface `Screen`). - `androidApp/` — hôte Android ; impl réelle Play Jeux dans `PlayGamesServices.kt`. - `desktopApp/` — hôte JVM (`com.livrets.desktop.MainKt`) ; données dans `~/.livrets/`. - `iosApp/` — hôte SwiftUI. Câblage à connaître : - IO fichiers : `expect val systemFileSystem` (okio) dans `sharedUI/src/commonMain/kotlin/com/livrets/data/SystemFileSystem.kt`, avec actuals android/jvm/ios ; les tests utilisent okio `FakeFileSystem`. - Play Jeux : interface `GamesServices` (commonMain), défaut `NoopGamesServices`, injectée par `androidApp` ; tout échec (hors connexion, pas de config console) est silencieux par design — le jeu reste jouable. - La logique testable est extraite de l'UI en commonMain/commonTest (ex. `ui/GameKeyMapping.kt`), plutôt que testée via l'UI. ## Conventions - Commits : conventional commits en français (`feat:`, `docs:`, `fix:`). - Nouvelle fonctionnalité : spec puis plan dans `docs/superpowers/specs/` et `docs/superpowers/plans/`, nommés `YYYY-MM-DD-[-design].md`. `docs/superpowers/` est committé ; `.superpowers/` est un répertoire de travail local (gitigné). - Dépendances : uniquement via le version catalog `gradle/libs.versions.toml`. Chaîne : Kotlin 2.4.x, AGP 9.x, Compose Multiplatform 1.12, JVM 17 partout. ## Pièges - Le build Android release échoue volontairement sans `androidApp/keystore.properties` (gitigné) — ne pas « corriger ». Publication et signature : voir README. - Compose Multiplatform 1.12 ne distingue pas l'auto-répétition (`KeyEventType` n'a pas de repeat) : une touche maintenue répète ses événements ; les gardes (`MAX_ENTRY_LENGTH`, états hors `Playing` ignorés) en dépendent. Les chiffres sont mappés positionnellement (AZERTY sans Shift). - Icônes launcher (Android/iOS/desktop) : régénérées depuis les SVG de `docs/branding/` via `docs/branding/generate-icons.sh` (prérequis : rsvg-convert, magick, python3). Ne pas éditer les PNG générés à la main. - À chaque release Android : incrémenter `versionCode` dans `androidApp/build.gradle.kts`.