15 KiB
15 KiB
phase, plan, type, wave, depends_on, files_modified, autonomous, requirements, must_haves
| phase | plan | type | wave | depends_on | files_modified | autonomous | requirements | must_haves | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 01-grundger-st-tippmechanik | 01 | execute | 1 |
|
true |
|
|
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.
<execution_context> @$HOME/.claude/get-shit-done/workflows/execute-plan.md @$HOME/.claude/get-shit-done/templates/summary.md </execution_context>
@.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: `<section id="screen-welcome" class="screen">`,
`<section id="screen-forest" class="screen">`,
`<section id="screen-lesson" class="screen">`,
`<section id="screen-reward" class="screen">`,
`<section id="screen-parent" class="screen">`
- Only screen-welcome gets class `screen--active` initially
- `<script type="module" src="/src/main.ts"></script>`
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').
```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<string, string>; // 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<IDBDatabase> {
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<Progress | null> { ... }
export async function saveProgress(db: IDBDatabase, progress: Progress): Promise<void> { ... }
export async function getSettings(db: IDBDatabase): Promise<Settings | null> { ... }
export async function saveSettings(db: IDBDatabase, settings: Settings): Promise<void> { ... }
```
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
<success_criteria>
- 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 </success_criteria>