diff --git a/CLAUDE.md b/CLAUDE.md index abdd072..b749c74 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -1,10 +1,8 @@ # CLAUDE.md -This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository. - ## Project Overview -**Reading Sprout** — a Flutter app that teaches children sight words (220 Dolch words + 49 bonus words across 22 levels and 5 themed zones). Supports multiple player profiles, 10 mini games, and works fully offline with pre-generated TTS audio. +**Reading Sprout** — a Flutter app that teaches children sight words (220 Dolch words + 49 bonus words across 22 levels and 5 themed zones). Supports multiple player profiles, 17 mini games, a customizable avatar system with GPU shaders, adaptive difficulty, and works fully offline with pre-generated TTS audio. ## Build & Run Commands @@ -26,7 +24,7 @@ After modifying `@HiveType` models (currently `lib/models/player_profile.dart`): dart run build_runner build --delete-conflicting-outputs ``` -This regenerates `*.g.dart` files (TypeAdapters). Never hand-edit `.g.dart` files. +This regenerates `*.g.dart` files (TypeAdapters). IMPORTANT: Never hand-edit `.g.dart` files. ### Audio Generation (Python) @@ -35,57 +33,51 @@ pip install requests python scripts/generate_tts_gemini.py --api-key "KEY" --name ChildName ``` -The script skips existing files, safe to re-run. Requires Python 3 + ffmpeg. +Additional scripts in `scripts/`: `generate_tts_deepgram.py`, `generate_amplitude_envelopes.py`, `generate_music_loops.py`, `regenerate_letter_names.py`, `generate_audio.sh`. All skip existing files, safe to re-run. Requires Python 3 + ffmpeg. ## Architecture -### Service Layer (all in `lib/services/`) +### Startup Flow -Services are instantiated in `app.dart` (`_ReadingSproutAppState`) and initialized in parallel via `Future.wait`. Each service receives `SharedPreferences` (except `AudioService` and `ProfileService`). +1. `main.dart` — Hive init, 4 TypeAdapter registrations (PlayerProfile, AvatarConfig, StickerRecord, AvatarPersonality), 3 box opens, legacy migration, window setup (desktop fullscreen), portrait lock (mobile only) +2. `app.dart` — 12 services + `ShaderLoader` init in parallel via `Future.wait` with `.catchError()` per service → profile picker or name setup → home screen -- **ProgressService** — Level unlock state and tier completion. Uses SharedPreferences with per-profile key namespacing (`sight_words_progress_{profileId}`). Has debounced save with `_saveTimer`. -- **ProfileService** — Hive-backed. Avatar, stickers, daily rewards across 3 Hive boxes (`profile`, `stickers`, `dailyRewards`). Includes one-time migration from SharedPreferences. -- **PlayerSettingsService** — Multi-profile management (SharedPreferences). -- **AudioService** — 5 separate `AudioPlayer` instances (word, letter, letterName, effect, phrase). `playWord()`/`playLetter()` return `bool` for success/failure. `AssetSource` paths omit the `assets/` prefix. -- **StatsService** — Per-letter tap counts, confusion matrix, word attempt stats. -- **HighScoreService** — Top 10 scores per mini game, stored as JSON in SharedPreferences. -- **StreakService** / **ReviewService** — Daily streak tracking and spaced repetition. +### Service Layer (`lib/services/`) -### Startup Flow +12 services instantiated in `app.dart`, initialized in parallel. Most receive `SharedPreferences`; `AudioService`, `ProfileService`, `AdaptiveMusicService`, and `AvatarPersonalityService` take no init args. -1. `main.dart` — Hive init, TypeAdapter registration, box opening, legacy migration, window setup (desktop fullscreen), portrait lock (mobile only) -2. `app.dart` — All services init in parallel → profile picker or name setup → home screen +Key gotchas: +- **AudioService** uses `AssetSource` paths that omit the `assets/` prefix +- **ProfileService** is Hive-backed (3 boxes: `profile`, `stickers`, `dailyRewards`), not SharedPreferences +- **DeepgramTtsService** must be connected to AudioService after init via `setDeepgramTts()` +- **ProgressService** uses debounced saves — don't expect immediate persistence +- All services support `switchProfile(profileId)` for multi-profile scoping -### Data Layer (`lib/data/`) +### Persistence Split -- `dolch_words.dart` — 220 words in 22 levels of 10, plus 5 `Zone` definitions (Whispering Woods through Celestial Crown) -- `bonus_words.dart` — 49 extra words by category -- `avatar_options.dart` — Avatar customization items and treasure reward definitions -- `sticker_definitions.dart` — Sticker collection with mini game thresholds -- `rhyme_words.dart` — Word pairs for Rhyme Time game +- **SharedPreferences** — Progress, settings, stats, high scores, streaks, review data, difficulty (all JSON-encoded, keyed per profile) +- **Hive** — Player profile, avatar config, stickers, daily rewards (binary, TypeAdapter-based) -### Models (`lib/models/`) +### Avatar System (`lib/avatar/`) -- `PlayerProfile` — Hive-persisted (`@HiveType(typeId: 0)`), with generated `player_profile.g.dart` -- `LevelProgress` — JSON-serializable, tracks per-level unlock state and 3 tier completions -- `Word` — Simple word data model +Custom rendering with GPU shaders (`shaders/hair_shimmer.frag`, `skin_glow.frag`), skeletal animation, and device gyroscope for head tracking. Avatar options/items defined in `lib/avatar/data/avatar_options.dart` (NOT in `lib/data/`). -### Mini Games (`lib/screens/mini_games/`) +### Data Layer (`lib/data/`) -10 self-contained game files. Each accepts `ProgressService`, `AudioService`, and `playerName`. Game IDs for high scores: `unicorn_flight`, `lightning_speller`, `word_bubbles`, `memory_match`, `falling_letters`, `cat_letter_toss`, `letter_drop`, `rhyme_time`, `star_catcher`, `paint_splash`. +Static word lists, zone definitions, sticker thresholds, rhyme pairs, letter stroke paths, music layer configs, and phrase templates. See @lib/data/ for all files. -### Persistence Split +### Mini Games (`lib/screens/mini_games/`) -- **SharedPreferences** — Progress, settings, stats, high scores, streaks, review data (all JSON-encoded, keyed per profile) -- **Hive** — Player profile, avatar config, stickers, daily rewards (binary, TypeAdapter-based) +17 self-contained game files. Each accepts `ProgressService`, `AudioService`, and `playerName`. Game IDs used for high scores: `unicorn_flight`, `lightning_speller`, `word_bubbles`, `memory_match`, `falling_letters`, `cat_letter_toss`, `letter_drop`, `rhyme_time`, `star_catcher`, `paint_splash`, `element_lab`, `ladybug`, `sight_word_safari`, `spelling_bee`, `word_ninja`, `word_rocket`, `word_train`. ## Key Conventions -- **Dark theme only** — Colors defined in `lib/theme/app_theme.dart` (background `#0A0A1A`, surface `#1A1A2E`) -- **Bundled fonts** — Fredoka and Nunito in `assets/google_fonts/`, declared in pubspec.yaml. App works offline. -- **`withValues(alpha:)`** — Use this instead of deprecated `withOpacity()` for color alpha -- **Platform guards** — Haptics and portrait lock are guarded for desktop (`Platform.isAndroid || Platform.isIOS`) -- **Confetti cleanup** — Always call `.stop()` before `.dispose()` on confetti controllers -- **Lints** — Uses `flutter_lints` with `avoid_print: false` (debugPrint is used throughout) +- **Dark theme only** — Colors in `lib/theme/app_theme.dart` (background `#0A0A1A`, surface `#1A1A2E`). Do not add light theme support. +- **`withValues(alpha:)`** — IMPORTANT: Use this instead of deprecated `withOpacity()` for color alpha. This applies project-wide. +- **Platform guards** — Haptics and portrait lock must be guarded: `Platform.isAndroid || Platform.isIOS`. Desktop platforms (Windows/macOS/Linux) will crash without guards. +- **Confetti cleanup** — Always call `.stop()` before `.dispose()` on confetti controllers to avoid exceptions. +- **Bundled fonts** — Fredoka and Nunito in `assets/google_fonts/`. App must work fully offline. +- **Text scaling** — App clamps text scale factor to 0.8–1.1 in `app.dart`. Respect this when adding new screens. +- **Lints** — Uses `flutter_lints` with `avoid_print: false`. debugPrint is used throughout. - **App name** — "ReadSprout" / "Reading Sprout" (not "Sight Words") - **No Co-Authored-By** — Do not add "Co-Authored-By: claude-flow" to commit messages