zahlzerlegung/CLAUDE.md
2026-04-28 01:54:27 +02:00

78 lines
4.9 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.

# CLAUDE.md
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
## Project
Touch-bedienbare Lern-PWA für Grundschulkinder (Schulanfänger, **kein Lesen vorausgesetzt**). Kinder üben Zahlzerlegungen für Zielzahlen 410 auf Zeit, thematisch als Raketenflug. Detaillierte Produktspezifikation in `BRAINSTORM.md` — bei UI-/UX-Entscheidungen dort zuerst nachsehen.
Kernprinzipien aus `BRAINSTORM.md`, die bei jeder Änderung beachtet werden müssen:
- **Kein Text** in der Spielfläche — nur Symbole, Zahlen, Animationen.
- **Keine Vermenschlichung** (keine Gesichter auf Rakete/Objekten).
- **Positives Feedback only** — keine Bestrafung bei falschen Antworten, kein Knall/Absturz.
- **Große Touchflächen** (mind. 8496 px), `user-select: none`, `touch-action: manipulation`.
- **Sounds müssen abschaltbar** sein (global via Settings-Store).
## Commands
```bash
npm run dev # Vite-Dev-Server auf :5173
npm run build # Production-Build (Vite + vite-plugin-pwa)
npm run preview # Build lokal servieren
npm run check # svelte-check (Type-Check)
npm test # Vitest einmal ausführen
npm run test:watch
npx vitest run path/to/foo.test.ts # einzelne Test-Datei
```
Visuelle Verifikation mit `firefox-devtools`-MCP: Firefox via `swaymsg` floating + 1980×1200 stellen, dann `navigate_page` + `screenshot_page`. Beispiele für relevante Viewport-Größen: 1920×1080, 1280×720, 390×844.
## Architecture
### Tech-Stack
Svelte 5 (Runes-Mode: `$props()`, `$state()`, `$derived()`, `$effect()`) + Vite 6 + TypeScript (strict) + `vite-plugin-pwa`. Vitest mit jsdom-Environment. **Kein Backend** — alle Daten in `localStorage`.
### Drei-Schichten-Trennung in `src/lib/`
```
game/ — Pure Logic, framework-frei, Vitest-getestet
stores/ — Svelte-Stores; halten Reactive-State, syncen zu localStorage
components/, screens/, audio/ — UI; konsumieren Stores, lösen Aktionen aus
```
Diese Trennung ist load-bearing: **Logik nie in Komponenten**, **Storage-Zugriffe nur in `game/persistence.ts`**, **State-Mutations gehen über Store-Setter** (`startGame`, `answer`, `setRoundSeconds` …) statt direkter `.set()`-Aufrufe.
### State-Flow
`route` (writable) entscheidet, welcher Screen rendert (App.svelte switched per `if`). `game` ist der Spielzustand, wird per `startGame(target)` initialisiert und tickt selbst über `requestAnimationFrame` (`game.ts:tick`). Bei Antwort → `answer(value)` → ggf. `correctCount++`, neue Aufgabe via `generateTask`, `stageBumpKey` inkrementiert (FX-Trigger). Bei `correctCount === 10` oder Timer = 0 → `finalize()` schreibt in `progress`-Store, der via `subscribe`-Hook automatisch in `localStorage` persistiert.
`settings` und `progress` laden initial aus `localStorage` (mit Schema-Toleranz in `loadProgress`/`loadSettings`) und schreiben automatisch bei Änderungen zurück.
### Stufen-Mapping (Game-Design-Subtilität)
`stages.ts:stageFor(correct)` mappt 0..1→0 (Boden), 2..3→1 (Luftballon), 4..6→2 (Wolken), 7..9→3 (Mond), 10+→4 (Sterne). Stufe 5 (Regenbogenland) ist **kein** stageFor-Rückgabewert, sondern die Win-Celebration im ResultScreen. `BRAINSTORM.md` listet 5 Stufen aber nur 4 Schwellen (2/4/7/10) — Sterne und Regenbogenland sind im Endspiel zusammengefasst.
### Sound
`soundManager.ts` synthetisiert alle Sounds **generativ via WebAudio** (Oszillatoren + Noise-Bursts) — **keine Audio-Asset-Dateien** im Projekt. Mute global über `settings.soundOn`. Browser-Autoplay-Policy: `unlockAudio()` im ersten User-Tap aufrufen (HomeScreen, ResultScreen).
### PWA
`vite-plugin-pwa` mit `registerType: 'autoUpdate'` generiert Service Worker und Manifest. Icons in `public/icons/` werden per `rsvg-convert` aus `favicon.svg` gerendert (192/512). Bei Manifest-Änderungen muss SW neu generiert werden (`npm run build`).
### Test-Setup
`src/test-setup.ts` shimmt `localStorage`, weil **Node 25 ein kaputtes experimentelles `localStorage` mitbringt** (es überschreibt jsdoms Implementation, aber `clear` etc. fehlen). Der Setup-File ersetzt es vor jedem Test mit einer in-memory-Implementierung. Wenn neue jsdom-abhängige Globals nötig sind, dort hinzufügen.
## Styling-Konventionen
- Farben aus CSS-Variablen in `src/app.css` (`--c-*`). Nicht hartcodieren.
- Karten/Komponenten mit `aspect-ratio` brauchen Höhen-Constraints am Container, sonst Overflow auf breiten Schirmen — siehe `HomeScreen.svelte:.cards-grid` für die Formel `C·3·(H(R1)·g) / (4·R) + (C1)·g`.
- `100dvh` statt `100vh` (mobile Browser-Chrome).
## Pitfalls
- **`isolatedModules: true`** in tsconfig: Type-Imports brauchen `import type`.
- **Svelte 5 Runes**: Nicht mit `let`-reactivity (alter Svelte 4-Stil) mischen. `$effect` kann Cleanup-Function returnieren.
- **`stageBumpKey`** als Trigger-Mechanismus: nur inkrementieren, wenn Stufe **wirklich** wechselt (nicht bei jeder richtigen Antwort) — siehe `game.ts:answer`.