8.1 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 | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 04-polish-audio | 01 | execute | 1 |
|
true |
|
|
Purpose: Auditives Feedback macht das Tipperlebnis lebendig und belohnend -- per D-01 bis D-05 aus CONTEXT.md. Output: Zwei neue Module (sounds.ts, speech.ts), Mute-Button in index.html, speechEnabled Feld in Settings.
<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/04-polish-audio/04-CONTEXT.md@src/types.ts @src/storage/db.ts @src/main.ts @index.html @src/styles/main.css
Task 1: Audio module + Speech module + Settings extension src/audio/sounds.ts, src/audio/speech.ts, src/types.ts src/types.ts, src/storage/db.ts, .planning/phases/04-polish-audio/04-CONTEXT.md 1. In `src/types.ts`: Add `speechEnabled: boolean` to the `Settings` interface (per D-05). Keep existing fields unchanged.-
Create
src/audio/sounds.ts(per D-01, D-02):- Module-level
let audioCtx: AudioContext | null = null initAudioOnInteraction(): Creates/resumes AudioContext on first call. Intended to be called from a user-interaction event handler. Export this.- Helper
isAudioEnabled(): async, callsgetSettings(db)from db.ts (needs db reference -- use a module-levellet db: IDBDatabase | nullset via ansetAudioDB(d)function, or importgetDB()from app.ts). Checksettings?.audioEnabled !== false. Return boolean. playCorrectSound(): async. CheckisAudioEnabled(). If false, return. Ensure audioCtx exists (call initAudioOnInteraction if needed). Create OscillatorNode: type sine, frequency 800Hz, connect to gain (0.15), start, stop after 0.1s. Ramp gain to 0 over last 0.02s for click-free cutoff.playRewardSound(): async. Check enabled. Ascending tone sequence: play 3-4 notes (e.g. C5=523, E5=659, G5=784, C6=1047) each ~0.2s apart, each 0.15s duration, sine wave, gain 0.12. Total ~0.8s.playForestElementSound(): async. Check enabled. Create white noise buffer (AudioBuffer, fill with Math.random()*2-1), play through BiquadFilterNode (bandpass, frequency 3000Hz, Q 0.5), gain 0.1, duration 0.5s with gain ramp-down.- All functions: wrap in try/catch (never throw to caller).
- Module-level
-
Create
src/audio/speech.ts(per D-04, D-05):speakText(text: string): async. CheckspeechEnabledfrom Settings via getSettings. If not enabled, return. Checkwindow.speechSynthesisexists. CreateSpeechSynthesisUtterance(text), setlang='de-DE',rate=0.9,pitch=1.1. Try to find a German voice (getVoices().find(v => v.lang.startsWith('de'))), set if found. CallspeechSynthesis.speak(utterance). Wrap in try/catch, silent fallback.- Handle voice loading: voices may load async. Use
speechSynthesis.onvoiceschangedifgetVoices()returns empty initially. cd /home/dev/workspace/zauberwald && npx tsc --noEmit 2>&1 | head -20 <acceptance_criteria> - src/audio/sounds.ts exports playCorrectSound, playRewardSound, playForestElementSound, initAudioOnInteraction
- src/audio/speech.ts exports speakText
- Settings interface in types.ts includes speechEnabled: boolean
- All three sound functions check audioEnabled before playing
- speakText checks speechEnabled before speaking
- TypeScript compiles without errors </acceptance_criteria> Three sound functions and speakText function exist, all check Settings before executing, TypeScript compiles clean
-
In
src/styles/main.css: Add.mute-btnstyles:position: fixed; bottom: 16px; right: 16px; z-index: 100;width: 44px; height: 44px; border-radius: 50%;background: var(--bg-cream); border: 2px solid var(--accent-green);cursor: pointer; font-size: 20px; display: flex; align-items: center; justify-content: center;box-shadow: 0 2px 8px rgba(0,0,0,0.1);transition: transform 0.2s ease;.mute-btn:hover { transform: scale(1.1); }.mute-btn--muted { border-color: var(--text-light); opacity: 0.6; }
-
In
src/main.ts: AfterinitApp, wire up the mute button:- Import
getSettings,saveSettingsfrom db.ts,getDBfrom app.ts,initAudioOnInteractionfrom audio/sounds.ts - On DOMContentLoaded (after initApp): read settings, set initial icon state
muteBtn.onclick: toggleaudioEnabled, save to settings, update icon (U+1F50A vs U+1F507), toggle.mute-btn--mutedclass- Also call
initAudioOnInteraction()on first mute-btn click (satisfies autoplay policy per D-02) - Add a one-time click/keydown listener on document body that calls
initAudioOnInteraction()for autoplay policy compliance cd /home/dev/workspace/zauberwald && npx tsc --noEmit 2>&1 | head -20 <acceptance_criteria> - index.html contains element with id="mute-btn" outside all section.screen elements
- main.css contains .mute-btn with position: fixed
- main.ts imports from audio/sounds and wires mute button click handler
- Mute button toggles audioEnabled in Settings store
- initAudioOnInteraction called on first user interaction </acceptance_criteria> Mute button visible in fixed position, toggles audio state, persists across reload, AudioContext initialized on first interaction
- Import
<success_criteria> Audio system ready to be wired into lesson/reward/forest screens (Plan 04). Mute button functional and persisting state. </success_criteria>
After completion, create `.planning/phases/04-polish-audio/04-01-SUMMARY.md`