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

3.3 KiB
Raw Blame History

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.

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

npm test
npm run build

Android store build: 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.