diff --git a/src/app.ts b/src/app.ts index ea38818..da70c98 100644 --- a/src/app.ts +++ b/src/app.ts @@ -1,11 +1,12 @@ -import type { ScreenName } from "./types"; -import { openDB, getProgress, saveProgress } from "./storage/db"; -import { initWelcomeScreen, initLessonScreen } from "./ui/screens"; -import { initForestScreen, renderForestScene } from "./forest/scene"; -import { startPreGeneration, initRewardScreen } from "./forest/reward"; -import { getGreeting } from "./companion/companion"; +import { speakText } from "./audio/speech"; import { companions } from "./companion/characters"; -import { showParentCodePrompt, initParentScreen } from "./ui/parent"; +import { getGreeting } from "./companion/companion"; +import { initRewardScreen, startPreGeneration } from "./forest/reward"; +import { initForestScreen, renderForestScene } from "./forest/scene"; +import { getProgress, openDB, saveProgress } from "./storage/db"; +import type { ScreenName } from "./types"; +import { initParentScreen, showParentCodePrompt } from "./ui/parent"; +import { initLessonScreen, initWelcomeScreen } from "./ui/screens"; let db: IDBDatabase; let lessonCleanup: (() => void) | null = null; @@ -215,6 +216,7 @@ export async function initApp(): Promise { // Fetch greeting async (shows immediately with avatar, text fills in) getGreeting(progress.selectedCharacter, db).then((text) => { textEl.textContent = text; + speakText(text); }); } } else { diff --git a/src/forest/reward.ts b/src/forest/reward.ts index 96200f8..7abbd12 100644 --- a/src/forest/reward.ts +++ b/src/forest/reward.ts @@ -20,6 +20,8 @@ import { import { getForestComment } from "../companion/companion"; import { getRandomFallbackForestComment } from "../companion/fallbacks"; import type { CompanionType, ForestElement } from "../types"; +import { playRewardSound } from "../audio/sounds"; +import { speakText } from "../audio/speech"; // --- Element types for random selection --- @@ -192,16 +194,18 @@ export async function initRewardScreen( }; const elementId = await saveForestElement(db, element); - // 6. Replace placeholder with actual image + // 6. Replace placeholder with actual image + play reward sound const objectUrl = URL.createObjectURL(result.blob); imageArea.innerHTML = ""; const img = document.createElement("img"); img.src = objectUrl; img.alt = result.description; imageArea.appendChild(img); + playRewardSound(); - // 7. Show companion comment + // 7. Show companion comment + speak it aloud companionTextEl.textContent = comment; + speakText(comment); // 8. Wire buttons continueBtn.onclick = () => { diff --git a/src/forest/scene.ts b/src/forest/scene.ts index d573284..b13f257 100644 --- a/src/forest/scene.ts +++ b/src/forest/scene.ts @@ -1,6 +1,7 @@ import type { Progress, LevelStatus } from "../types"; import { levels } from "../game/levels"; import { getProgress, getForestElements } from "../storage/db"; +import { playForestElementSound } from "../audio/sounds"; let activeObjectUrls: string[] = []; @@ -44,6 +45,7 @@ export async function renderForestScene( // Animate newly added element if (element.id === newElementId) { slot.classList.add("forest__element--new"); + playForestElementSound(); } slot.appendChild(img); diff --git a/src/ui/screens.ts b/src/ui/screens.ts index b580ffd..59b32af 100644 --- a/src/ui/screens.ts +++ b/src/ui/screens.ts @@ -19,6 +19,7 @@ import { renderKeyboard, highlightKey, pressKey } from "../game/keyboard"; import { getLevelByNumber } from "../game/levels"; import { getLetterIntro } from "../companion/companion"; import { getWordsForLevel } from "../game/words"; +import { playCorrectSound } from "../audio/sounds"; export function initWelcomeScreen( db: IDBDatabase, @@ -213,6 +214,7 @@ export function initLessonScreen( if (result.correct) { cumulativeCorrect++; + playCorrectSound(); const currentEl = letterArea.querySelector(".falling-letter"); if (currentEl) { currentEl.classList.add("falling-letter--dissolve"); @@ -288,6 +290,7 @@ export function initLessonScreen( if (result.correct) { cumulativeCorrect++; + playCorrectSound(); if (result.exerciseComplete) { document.removeEventListener("keydown", onKeyDown);