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.
This commit is contained in:
@@ -0,0 +1,95 @@
|
||||
# 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 | 1–10 |
|
||||
| `medium` | choices | 6 | 1–12 |
|
||||
| `hard` | keypad | — | 1–20 |
|
||||
|
||||
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.
|
||||
Reference in New Issue
Block a user