--- phase: 01-grundger-st-tippmechanik plan: 01 type: execute wave: 1 depends_on: [] files_modified: - package.json - tsconfig.json - vite.config.ts - biome.json - index.html - .gitignore - src/main.ts - src/types.ts - src/storage/db.ts - src/styles/main.css - public/config.example.json autonomous: true requirements: - FNDN-01 - FNDN-02 - FNDN-03 - FNDN-04 - PLSH-06 must_haves: truths: - "npm run dev startet Vite auf 0.0.0.0:5173 ohne Fehler" - "Alle Verzeichnisse aus Spec Abschnitt 13 existieren" - "TypeScript-Interfaces sind exportiert und typisiert" - "IndexedDB oeffnet mit 5 Stores und schliesst nicht bei reload" - "CSS Custom Properties fuer alle Farben und Fonts sind definiert" artifacts: - path: "package.json" provides: "Project dependencies and scripts" contains: "vite" - path: "src/types.ts" provides: "All shared TypeScript interfaces" exports: ["Progress", "ForestElement", "CompanionAssets", "Level", "Settings", "StyleReference"] - path: "src/storage/db.ts" provides: "IndexedDB wrapper with 5 stores" exports: ["openDB", "getProgress", "saveProgress", "getSettings", "saveSettings"] - path: "src/styles/main.css" provides: "CSS palette and typography" contains: "--bg-cream" key_links: - from: "src/storage/db.ts" to: "src/types.ts" via: "imports Progress, Settings, ForestElement etc." pattern: "import.*from.*types" - from: "src/main.ts" to: "src/storage/db.ts" via: "initializes DB on app start" pattern: "openDB" --- Scaffold the complete Vite project, define all TypeScript interfaces, create the IndexedDB wrapper, and establish the CSS design system. Purpose: Every subsequent plan depends on the project existing, types being defined, storage being available, and styles being set. Output: A runnable Vite dev server with types, IndexedDB, and CSS custom properties — no visible UI yet beyond a blank styled page. @$HOME/.claude/get-shit-done/workflows/execute-plan.md @$HOME/.claude/get-shit-done/templates/summary.md @.planning/PROJECT.md @.planning/ROADMAP.md @.planning/STATE.md @.planning/phases/01-grundger-st-tippmechanik/01-CONTEXT.md @SPEC.md (sections 6.2, 6.3, 7.2, 13) Task 1: Vite project scaffold + project structure + CSS design system package.json, tsconfig.json, vite.config.ts, biome.json, index.html, .gitignore, src/main.ts, src/styles/main.css, public/config.example.json SPEC.md (section 13 for project structure, section 6.2 for tech stack, section 7.2 for CSS palette) .planning/phases/01-grundger-st-tippmechanik/01-CONTEXT.md 1. Initialize Vite project with vanilla-ts template: `npm create vite@latest . -- --template vanilla-ts` (or manually create files if directory not empty). 2. Install dependencies: - `npm install` (vite, typescript) - `npm install -D vitest @biomejs/biome` 3. Configure vite.config.ts (per D-01, spec section 0): ```typescript import { defineConfig } from 'vite' export default defineConfig({ server: { host: '0.0.0.0', port: 5173 }, preview: { host: '0.0.0.0', port: 4173 } }) ``` 4. Configure tsconfig.json: `"strict": true`, target ES2020, module ESNext, moduleResolution bundler. 5. Configure biome.json: enable formatter and linter, indent with tabs, line width 100. 6. Configure package.json scripts: ```json { "dev": "vite --host", "build": "tsc && vite build", "preview": "vite preview --host --port 4173", "test": "vitest run", "test:watch": "vitest", "lint": "biome check .", "lint:fix": "biome check --write ." } ``` 7. Create .gitignore with: node_modules, dist, public/config.json, *.local 8. Create ALL directories from spec section 13: - src/game/, src/forest/, src/companion/, src/api/, src/storage/, src/ui/, src/styles/ - src/assets/companions/fee-lila/, src/assets/companions/einhorn-stella/, src/assets/companions/fuchs-finn/, src/assets/companions/eule-elsa/ - src/assets/style-reference/, src/assets/fallback-images/ - scripts/ - public/ Place empty .gitkeep in asset directories that have no files yet. 9. Create public/config.example.json: ```json { "geminiApiKey": "YOUR_API_KEY_HERE", "geminiModel": "gemini-2.5-flash", "imageModel": "gemini-3.1-flash-image-preview" } ``` 10. Create index.html with: - DOCTYPE html, lang="de" - Google Fonts link: Quicksand (400, 600, 700) + Nunito (400, 600) - Link to src/styles/main.css (via Vite) - 5 screen sections per D-01: `
`, `
`, `
`, `
`, `
` - Only screen-welcome gets class `screen--active` initially - `` 11. Create src/styles/main.css with ALL CSS custom properties from spec section 7.2: ```css :root { --bg-cream: #FFF8F0; --bg-forest: #E8F5E4; --text-dark: #3D3225; --text-warm: #5C4A3A; --text-light: #8B7D6B; --accent-green: #6DB87D; --accent-gold: #E8B84B; --accent-pink: #E88BAE; --accent-purple: #9B7DC4; --accent-blue: #7DB8D4; --finger-l-pinky: #E8B4C8; --finger-l-ring: #C4A8D4; --finger-l-mid: #A8C4E0; --finger-l-index: #A8D8B8; --finger-r-index: #D4D8A8; --finger-r-mid: #E0C4A8; --finger-r-ring: #D4A8A8; --finger-r-pinky: #C8B4E8; --finger-thumb: #D4CFC8; } ``` Body: font-family 'Nunito', sans-serif; background var(--bg-cream); color var(--text-dark); min font-size 16px; margin 0; padding 0. Headings (h1–h3): font-family 'Quicksand', sans-serif. Screen base: `.screen { display: none; opacity: 0; transition: opacity 0.3s ease; }` and `.screen--active { display: flex; flex-direction: column; align-items: center; opacity: 1; min-height: 100vh; }`. 12. Create src/main.ts as minimal entry: import './styles/main.css', console.log('Zauberwald loaded'). cd /home/dev/workspace/zauberwald && npm run dev -- --strictPort 2>&1 & sleep 3 && curl -s http://localhost:5173 | grep -q "Zauberwald" || curl -s http://localhost:5173 | grep -q "screen-welcome" && kill %1 && echo "PASS" - package.json contains "vite" in devDependencies and scripts "dev", "build", "preview", "test", "lint" - vite.config.ts contains `host: '0.0.0.0'` and `port: 5173` - tsconfig.json contains `"strict": true` - biome.json exists and contains `"formatter"` and `"linter"` - .gitignore contains `public/config.json` and `node_modules` - index.html contains 5 ` Vite project runs, all directories exist per spec section 13, CSS custom properties match spec section 7.2, index.html has 5 screen sections Task 2: TypeScript interfaces + IndexedDB wrapper src/types.ts, src/storage/db.ts, src/storage/db.test.ts SPEC.md (section 6.3 for IndexedDB stores, section 4.1 for level structure, section 5.2 for companion types) src/types.ts (after Task 1 creates it — may be empty placeholder) - Test: openDB() resolves to an IDBDatabase with 5 object store names - Test: saveProgress({ currentLevel: 1, ... }) then getProgress() returns same data - Test: saveSettings({ audioEnabled: true, ... }) then getSettings() returns same data - Test: getProgress() on empty DB returns null (not crash) - Test: getSettings() on empty DB returns null (not crash) 1. Create src/types.ts with ALL interfaces per spec section 6.3 (per FNDN-03): ```typescript // Companion types export type CompanionType = 'fee' | 'einhorn' | 'fuchs' | 'eule'; export type KeyboardLayout = 'de' | 'ch'; // Level status for the forest map export type LevelStatus = 'locked' | 'current' | 'completed'; // Progress store export interface Progress { id: 1; // singleton row currentLevel: number; completedLevels: number[]; totalSessions: number; sessionDates: string[]; // ISO date strings (days only) selectedCharacter: CompanionType; selectedLayout: KeyboardLayout; } // Companion assets store export interface CompanionAssets { characterType: CompanionType; characterSheet: Blob; avatarImage: Blob; generatedAt: string; // ISO date } // Style reference store export interface StyleReference { id: 1; // singleton imageBlob: Blob; generatedAt: string; } // Forest element store export interface ForestElement { id?: number; // auto-increment levelCompleted: number; imageBlob: Blob; imagePrompt: string; description: string; companionText: string; position: { x: number; y: number }; createdAt: string; } // Settings store export interface Settings { id: 1; // singleton audioEnabled: boolean; apiKey: string; } // Level definition (used in game/levels.ts) export interface Level { level: number; newKeys: string[]; allKeys: string[]; // cumulative keys up to this level fingerMap: Record; // key -> CSS variable name for finger color } // Typing exercise state export interface TypingExerciseState { currentLetterIndex: number; letters: string[]; intervalMs: number; correctStreak: number; totalCorrect: number; totalErrors: number; } // Screen names export type ScreenName = 'welcome' | 'forest' | 'lesson' | 'reward' | 'parent'; ``` 2. Create src/storage/db.ts — IndexedDB wrapper (per D-02, all 5 stores created, only progress + settings fully used): ```typescript import type { Progress, Settings, ForestElement, CompanionAssets, StyleReference } from '../types'; const DB_NAME = 'zauberwald'; const DB_VERSION = 1; export function openDB(): Promise { return new Promise((resolve, reject) => { const request = indexedDB.open(DB_NAME, DB_VERSION); request.onupgradeneeded = (event) => { const db = (event.target as IDBOpenDBRequest).result; if (!db.objectStoreNames.contains('progress')) { db.createObjectStore('progress', { keyPath: 'id' }); } if (!db.objectStoreNames.contains('companionAssets')) { db.createObjectStore('companionAssets', { keyPath: 'characterType' }); } if (!db.objectStoreNames.contains('styleReference')) { db.createObjectStore('styleReference', { keyPath: 'id' }); } if (!db.objectStoreNames.contains('forestElements')) { db.createObjectStore('forestElements', { keyPath: 'id', autoIncrement: true }); } if (!db.objectStoreNames.contains('settings')) { db.createObjectStore('settings', { keyPath: 'id' }); } }; request.onsuccess = () => resolve(request.result); request.onerror = () => reject(request.error); }); } export async function getProgress(db: IDBDatabase): Promise { ... } export async function saveProgress(db: IDBDatabase, progress: Progress): Promise { ... } export async function getSettings(db: IDBDatabase): Promise { ... } export async function saveSettings(db: IDBDatabase, settings: Settings): Promise { ... } ``` Each get/save function wraps IDB transaction in a Promise. Use 'readonly' for gets, 'readwrite' for saves. Store name matches interface. 3. Create src/storage/db.test.ts using vitest with fake-indexeddb: - `npm install -D fake-indexeddb` - Import `import 'fake-indexeddb/auto'` at top of test - Test all behaviors listed above cd /home/dev/workspace/zauberwald && npx vitest run src/storage/db.test.ts - src/types.ts exports: Progress, ForestElement, CompanionAssets, Settings, StyleReference, Level, CompanionType, KeyboardLayout, LevelStatus, ScreenName, TypingExerciseState - src/types.ts contains `type CompanionType = 'fee' | 'einhorn' | 'fuchs' | 'eule'` - src/types.ts contains `type KeyboardLayout = 'de' | 'ch'` - src/storage/db.ts exports: openDB, getProgress, saveProgress, getSettings, saveSettings - src/storage/db.ts contains `const DB_NAME = 'zauberwald'` - src/storage/db.ts contains all 5 store names: 'progress', 'companionAssets', 'styleReference', 'forestElements', 'settings' - src/storage/db.test.ts exists and `npx vitest run src/storage/db.test.ts` exits 0 - package.json contains `fake-indexeddb` in devDependencies All TypeScript interfaces exported, IndexedDB wrapper creates 5 stores, read/write for progress and settings works, all tests pass - `npm run dev` starts without errors on port 5173 - `npx vitest run` passes all DB tests - `npx biome check .` shows no errors (or only warnings) - All directories from spec section 13 exist - index.html has 5 screen sections - CSS custom properties match spec section 7.2 exactly - Vite dev server accessible at 0.0.0.0:5173 - TypeScript compiles without errors in strict mode - IndexedDB wrapper tested and working with 5 stores - CSS design system complete with all palette colors and typography - Project structure matches spec section 13 After completion, create `.planning/phases/01-grundger-st-tippmechanik/01-01-SUMMARY.md`