Files
ZabavnaMatematika/docs/DEVELOPMENT.md
T
klevze 6e85917f73 Initial commit: Zabavna Matematika web and Android app.
Kids math practice (SL/EN) in a Vite React app, wrapped with Capacitor for Android (net.bit76.matematika). Includes session refactor, i18n, and text-free difficulty icons with translated overlays.
2026-08-15 10:59:25 +02:00

96 lines
3.3 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Development
The playable app lives in `web/`. Stack: React 18, Vite 5, Tailwind 3. Android is the same app inside a Capacitor WebView (`web/android/`). No TypeScript, no backend.
## Screens
`App.jsx` switches three states:
1. **Loading** — preload images / audio / the settings video, then **Nadaljuj**
2. **Settings** — name, operations, difficulty, audio, start modal
3. **Game** — one session, then game-over overlay
Audio prefs persist as `matematika_music`. Game settings persist as `matematika_settings`. Language persists as `matematika_lang` (`sl` | `en`).
UI copy lives in `web/src/i18n/sl.json` and `en.json`. Screens call `t('key')` from `useI18n()`. Difficulty is stored as `easy` / `medium` / `hard`, never as a translated label. To add a language, add a JSON file with the same keys, register it in `i18n/core.js`, and add a button in Settings.
## Session loop
`Game.jsx` only runs a session. It does not invent operands.
1. `buildSession(config, level)` in `questionGenerator.js` builds the full list.
2. `showQuestion(i)` shows item `i` from that list.
3. `resolveAnswer(given)` scores a tap or keypad OK.
4. After the last item, `GameOver` offers restart (rebuilds the pool) or back to settings.
Question index is a ref, so restart cannot immediately re-trigger game-over from a stale counter.
Each question looks like `{ op, a, b, result, text }`.
`op` is one of `add` | `sub` | `mul` | `div` (`src/config.js`).
## Difficulty vs settings
- **Settings** choose operations, question count, add/sub range (`max_number`), multiply factors, divide divisors.
- **Difficulty** (`src/levels.js`) chooses input UI and some caps:
| Key | Input | Choices | Multiply other-factor cap |
|---|---|---|---|
| `easy` | choices | 4 | 110 |
| `medium` | choices | 6 | 112 |
| `hard` | keypad | — | 120 |
Negatives are off (`allowNegative: false`). The keypad hides `±` unless that flag is flipped.
Full product wording: [LEVELS.md](../LEVELS.md).
## Source map
```
web/src/
App.jsx
main.jsx boot + service worker register
config.js defaultConfig, OPS, mergeConfig
levels.js easy / medium / hard
questionGenerator.js + × ÷ factory + distractors
loadAssets.js
hooks/useAnimatedBackgrounds.js
components/
Loading.jsx
Settings.jsx
Game.jsx session only
AnswerChoices.jsx
Keypad.jsx
GameQuestionCard.jsx
GameToolbar.jsx
GameOver.jsx
ExitConfirm.jsx
BackgroundMusic.jsx
ToastContainer.jsx
UpdateBanner.jsx
NumberPills.jsx
```
`public/sw.js` caches the shell using paths relative to its own scope (safe with Vite `base: './'`).
## Adding an operation later
Keep generation out of `Game.jsx`:
1. Add a named op in `config.js` if needed.
2. Teach `buildSession` how to emit `{ op, a, b, result, text }`.
3. Add a settings toggle only when the UI exists.
4. Cover it in `questionGenerator.test.js`.
Rounding, dice, calculator, equations, and text problems were removed from the live path on purpose.
## Local checks
```bash
npm test
npm run build
```
Android store build: [ANDROID.md](ANDROID.md).
Optional Playwright smoke (`scripts/smoke.mjs`) walks loading → easy → restart → medium (wrong then right) → hard keypad (empty OK disabled, no `±`) → a 390×844 viewport.