66 lines
3.0 KiB
Markdown
66 lines
3.0 KiB
Markdown
# Classroom Timer Integration
|
|
|
|
## Purpose
|
|
|
|
A visual countdown timer with sensory backgrounds and a sound library. Sounds
|
|
come from three places, unified in one picker: hardcoded **built-in** synthesized
|
|
sounds, **generated** sounds (`recipe` rows), and **uploaded** audio
|
|
(`file`/`url` rows) from the `audio_files` library.
|
|
|
|
## Current Behavior
|
|
|
|
- `frontend/src/components/frameworks/ClassroomTimer.tsx` is a thin wrapper that
|
|
takes `userRole` (from the shell) and calls `useClassroomTimer(userRole)`.
|
|
- Timer state, fullscreen, custom-time parsing, progress/urgency, particles, and
|
|
Web Audio orchestration live in `frontend/src/business/classroom-timer/`.
|
|
`shared/constants/classroomTimer.ts` keeps timing/particle config plus the
|
|
sound-group labels and the generated/uploaded fallback icons.
|
|
- View pieces are under `frontend/src/components/classroom-timer/`. The sound
|
|
picker (`TimerSettingsPanel`) groups sounds by origin — **Built-in /
|
|
Generated / Uploaded** — for clear structure.
|
|
- Backgrounds/presets/tips and the built-in sound metadata are backend-owned
|
|
`content_catalog` records loaded via the shared content-catalog API. The
|
|
built-in **sounds themselves** are synthesized in-browser
|
|
(`business/classroom-timer/audio.ts`, `playBuiltInSound`).
|
|
- Missing content-catalog data renders an explicit backend error (no frontend
|
|
seed fallback).
|
|
|
|
## Audio Library
|
|
|
|
The picker merges the built-ins with the `audio_files` library
|
|
(`business/audio-files/`):
|
|
|
|
- **API/types**: `shared/api/audioFiles.ts`, `shared/types/audioFiles.ts` (incl.
|
|
the `SoundRecipe` shape).
|
|
- **Hooks**: `useAudioFiles` (list; `retry: false` so a caller without
|
|
`READ_AUDIO_FILES` silently falls back to the built-ins), `useGenerateAudioFile`,
|
|
`useDeleteAudioFile`. `canManageAudioFiles` gates the manage affordances
|
|
(director/office_manager/teacher).
|
|
- **Playback branches by kind**: `builtin` → `playBuiltInSound(id)`, `recipe` →
|
|
`playRecipe(recipe)` (`business/classroom-timer/audio-recipe.ts`, pure Web
|
|
Audio from JSON params), `file`/`url` → `new Audio(url)`.
|
|
- **Generate**: managers type a name and click *Generate*; this creates a
|
|
`recipe` row whose synthesis parameters come from a **local stub**
|
|
(`business/audio-files/generate.ts`). When an AI key is wired, only that
|
|
function changes — persistence, playback and the library list are unchanged.
|
|
An empty name falls back to a generated name.
|
|
- **Delete**: managers can remove their own (non-default) library rows.
|
|
|
|
## Tests
|
|
|
|
- `business/classroom-timer/selectors.test.ts` (timer math)
|
|
- `business/audio-files/selectors.test.ts` (`canManageAudioFiles`),
|
|
`business/audio-files/generate.test.ts` (local recipe stub shape)
|
|
|
|
## Verification
|
|
|
|
- `npm run typecheck`, `npm run lint`, `npm run test` pass.
|
|
|
|
## Remaining Work
|
|
|
|
- Swap the local `generateSoundRecipe` stub for a real model call once an AI key
|
|
exists.
|
|
- Binary `file` upload UI — needs a typed upload client and the file-download
|
|
ownership fix (see `backend/docs/audio-files.md`); `recipe`/`url` rows are
|
|
unaffected.
|