---
phase: 05-multi-wald-system
plan: 02
type: execute
wave: 2
depends_on:
- 05-01
files_modified:
- src/forest/scene.ts
- src/forest/reward.ts
- src/app.ts
autonomous: false
requirements:
- MWLD-05
- MWLD-06
- MWLD-07
must_haves:
truths:
- "Kind kann mit Pfeiltasten zwischen Waldseiten navigieren"
- "Aktuelle Waldseite wird in Progress gespeichert und nach Reload wiederhergestellt"
- "Nach Lesson-Abschluss zeigt der Wald automatisch die Seite mit dem neuen Element"
- "Seitenwechsel hat sanfte Fade-Animation"
artifacts:
- path: "src/forest/scene.ts"
provides: "Navigation event handlers, page persistence"
contains: "forest-nav-prev"
- path: "src/forest/reward.ts"
provides: "Correct slot index for paginated forest"
contains: "SLOTS_PER_PAGE"
- path: "src/app.ts"
provides: "Forest init with correct page, post-reward navigation"
contains: "currentForestPage"
key_links:
- from: "src/forest/scene.ts"
to: "src/storage/db.ts"
via: "saveProgress for page persistence"
pattern: "saveProgress"
- from: "src/forest/reward.ts"
to: "src/forest/scene.ts"
via: "SLOTS_PER_PAGE import for slot calculation"
pattern: "SLOTS_PER_PAGE"
- from: "src/app.ts"
to: "src/forest/scene.ts"
via: "renderForestScene with page parameter"
pattern: "renderForestScene.*db.*page"
---
Wire navigation interactions, persist current page, and fix reward flow for paginated forest.
Purpose: Plan 01 added the data model and rendering. This plan makes it interactive: clicking arrows changes pages, the page persists across sessions, and completing a lesson navigates to the correct page.
Output: Working multi-forest navigation, persisted page state, correct reward-to-forest flow.
@$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/05-multi-wald-system-wald-pagination-und-navigation-zwischen-w-ldern/05-CONTEXT.md
@.planning/phases/05-multi-wald-system-wald-pagination-und-navigation-zwischen-w-ldern/05-01-SUMMARY.md
From src/types.ts (after Plan 01):
```typescript
export interface Progress {
// ... existing fields ...
currentForestPage: number; // 0-indexed, default 0
}
```
From src/forest/scene.ts (after Plan 01):
```typescript
export const SLOTS_PER_PAGE = 12;
export async function renderForestScene(db: IDBDatabase, page: number, newElementId?: number): Promise;
export async function initForestScreen(db: IDBDatabase, onStartLesson: (level: number) => void): Promise;
```
From src/storage/db.ts (unchanged):
```typescript
export function getProgress(db: IDBDatabase): Promise
Task 1: Wire navigation handlers and page persistence in initForestScreen
src/forest/scene.ts
- src/forest/scene.ts (current initForestScreen — must add nav button handlers)
- src/storage/db.ts (saveProgress pattern)
- src/types.ts (Progress with currentForestPage)
**src/forest/scene.ts** — Modify `initForestScreen` to:
1. Import `saveProgress` from `../storage/db` (add to existing import).
2. Read `progress.currentForestPage` (with fallback to 0) and use it as the initial page for rendering:
```typescript
const page = progress.currentForestPage ?? 0;
await renderForestScene(db, page);
```
3. After the `renderLevelMap` call, wire up navigation buttons per D-05:
```typescript
const prevBtn = document.getElementById('forest-nav-prev') as HTMLButtonElement;
const nextBtn = document.getElementById('forest-nav-next') as HTMLButtonElement;
let currentPage = progress.currentForestPage ?? 0;
const navigateToPage = async (newPage: number) => {
currentPage = newPage;
await renderForestScene(db, currentPage);
// Persist page in progress per D-05
const freshProgress = await getProgress(db);
if (freshProgress) {
freshProgress.currentForestPage = currentPage;
await saveProgress(db, freshProgress);
}
};
if (prevBtn) {
prevBtn.onclick = () => {
if (currentPage > 0) navigateToPage(currentPage - 1);
};
}
if (nextBtn) {
nextBtn.onclick = async () => {
const elements = await getForestElements(db);
const totalPages = Math.max(1, Math.ceil(elements.length / SLOTS_PER_PAGE));
if (currentPage < totalPages - 1) navigateToPage(currentPage + 1);
};
}
```
4. Update all existing calls to `renderForestScene` within this file to pass the page parameter. The `initForestScreen` call becomes `renderForestScene(db, page)` (already done above).
5. Add a CSS transition for the grid on page change. In `renderForestScene`, before clearing `grid.innerHTML`, add:
```typescript
grid.style.opacity = '0';
grid.style.transition = 'opacity 0.3s ease';
```
And after the rendering loop completes:
```typescript
// Trigger fade-in after a frame
requestAnimationFrame(() => {
grid.style.opacity = '1';
});
```
cd /home/dev/workspace/zauberwald && npx tsc --noEmit 2>&1 | head -20
- src/forest/scene.ts contains `saveProgress` in import statement
- src/forest/scene.ts contains `currentForestPage`
- src/forest/scene.ts contains `prevBtn.onclick`
- src/forest/scene.ts contains `nextBtn.onclick`
- src/forest/scene.ts contains `navigateToPage`
- src/forest/scene.ts contains `grid.style.opacity`
- TypeScript compiles without errors
Arrow buttons navigate between forest pages, current page saved to Progress on every navigation, fade animation on page transitions
Task 2: Fix reward flow and all renderForestScene call sites for pagination
src/forest/reward.ts, src/app.ts
- src/forest/reward.ts (slotIndex calculation at line 179 — must account for pagination)
- src/app.ts (all renderForestScene calls — must pass page parameter; handleLessonComplete "Zurueck zum Wald" callback)
- src/forest/scene.ts (new renderForestScene signature with page parameter)
**src/forest/reward.ts** — Per D-01, fix the slotIndex calculation to work with pagination:
1. Import `SLOTS_PER_PAGE` from `./scene`:
```typescript
import { SLOTS_PER_PAGE } from "./scene";
```
2. Replace the slotIndex calculation (currently `const slotIndex = existing.length % 12`):
```typescript
const slotIndex = existing.length % SLOTS_PER_PAGE;
```
This ensures the element is placed in the correct grid position on its page.
**src/app.ts** — Update ALL `renderForestScene` and `initForestScreen` call sites per D-06:
1. In `handleLessonComplete`, the "Zurueck zum Wald" callback (the `onBackToForest` lambda around line 122-125):
```typescript
async () => {
// Per D-06: navigate to page containing the new element
const elements = await getForestElements(db);
const newPage = Math.max(0, Math.ceil(elements.length / SLOTS_PER_PAGE) - 1);
// Update progress with new page
const latestProgress = await getProgress(db);
if (latestProgress) {
latestProgress.currentForestPage = newPage;
await saveProgress(db, latestProgress);
}
await initForestScreen(db, startLessonFromForest);
await renderForestScene(db, newPage, elementId);
showScreen("forest");
}
```
2. Import `SLOTS_PER_PAGE` from `./forest/scene` and `getForestElements` from `./storage/db` (add to existing imports if not already there).
3. In the "Weiter ueben" callback from reward (the `onContinue` lambda), no forest rendering happens so no change needed.
4. In `handleLessonCancel` (line 129-132), `initForestScreen` already reads `currentForestPage` from progress internally, so no change needed here.
5. In `initApp` returning user flow (line 208), `initForestScreen` already handles page from progress, but the subsequent `renderForestScene` call does not exist separately — `initForestScreen` calls it. No change needed.
6. In the "Zurueck zum Wald" from parent area (line 182-183), same — `initForestScreen` handles it.
7. BUT: There is an explicit `renderForestScene(db, elementId)` call on line 123. This must become `renderForestScene(db, newPage, elementId)` as described in step 1 above.
cd /home/dev/workspace/zauberwald && npx tsc --noEmit 2>&1 | head -20 && grep -n "SLOTS_PER_PAGE" src/forest/reward.ts src/app.ts
- src/forest/reward.ts contains `import { SLOTS_PER_PAGE } from "./scene"`
- src/forest/reward.ts contains `existing.length % SLOTS_PER_PAGE`
- src/app.ts contains `SLOTS_PER_PAGE` import
- src/app.ts contains `currentForestPage = newPage`
- src/app.ts does NOT contain `renderForestScene(db, elementId)` (old 2-arg call with elementId)
- TypeScript compiles without errors
Reward flow correctly calculates slot position for paginated grid, "Zurueck zum Wald" navigates to the page containing the new element, all renderForestScene calls pass page parameter
Task 3: Verify multi-forest navigation works end-to-end
n/a
Human verification checkpoint for the multi-forest pagination system.
What was built: Arrow navigation between forest pages, page persistence in Progress, correct page navigation after lesson completion, fade animation on page transitions.
How to verify:
1. Open http://localhost:5173 in browser
2. If fresh install: complete onboarding, play through at least 1 lesson to get forest elements
3. On the forest screen: if you have fewer than 13 elements, the navigation arrows should be HIDDEN
4. To test pagination with many elements: Use browser DevTools console to add fake elements to IndexedDB, OR play through enough lessons to fill 12+ slots
5. With 13+ elements: navigation arrows ("Wald 1 von 2") should appear between the forest scene and level map
6. Click right arrow — should fade to page 2 showing element 13+
7. Click left arrow — should fade back to page 1
8. Left arrow should be disabled on page 1, right arrow disabled on last page
9. Reload browser — same page should be shown (persistence)
10. Complete a lesson — after clicking "Zurueck zum Wald", should show the page with the new element (last page)
Resume signal: Type "approved" or describe issues.
User confirms visual and functional correctness of multi-forest navigation
User approves that forest pagination, navigation arrows, page persistence, and post-lesson page selection all work correctly
- `npx tsc --noEmit` passes
- `npm run dev` starts without errors
- Navigation arrows hidden when <= 12 elements
- Navigation arrows visible when > 12 elements
- Page persists in IndexedDB across reloads
- New element after lesson appears on correct page
- Clicking prev/next arrows changes forest page with fade animation
- "Wald X von Y" text updates correctly
- Arrows disabled at boundaries (prev on page 1, next on last page)
- currentForestPage persisted in Progress and restored on reload
- After lesson completion, forest shows the page containing the new element
- No console errors during normal navigation