Merge the typing game into the music player as a tab, with lock/unlock UI

Moves tippen from a standalone app into web/ as a third tab (audio player /
smarthome / typing), replacing the old single room-toggle corner button with a
vertical icon tab rail. Curriculum and progress now come from the backend
(musicmouse/tippen/*) instead of a build-time YAML import and localStorage.

Adds reward-driven lock rendering: Cover/BrowseView/AlbumModal show a question
mark for locked albums/tracks with a hint on what unlocks them, and
ResultSheet gets a new unlock-animation block alongside the existing
lesson-unlock and aquarium-creature celebrations.

CSS from the two apps is merged carefully: identical rules (bubble/card/
key-cap/view-enter/backdrop-enter and their keyframes) are shared as-is,
while rules that bake in each app's own hue are kept separate under a
`tp-` prefix and scoped to the typing tab's own .tp-stage wrapper, so
neither app's look bleeds into the other's.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
2026-09-12 21:25:42 +02:00
parent f7a5d24d8d
commit 7f5e2733c2
84 changed files with 1577 additions and 4642 deletions

View File

@@ -0,0 +1,39 @@
/** Feature toggles and the one hue this app is built on.
*
* Same role as ../../web/src/lib/theme.ts: flip a switch here rather than hunting
* through components. The hue lives in styles/app.css because CSS is where it is used;
* it is repeated here only for the canvas, which cannot read a custom property. */
/** The turquoise lagoon. Music is 210, Hörbücher 55, "Mein Zimmer" 300. */
export const HUE = 175;
/** Frost the glass panels with a real backdrop blur. Expensive on weak GPUs. */
export const SHOW_GLASS_BLUR = true;
/** The decorative rising bubbles behind everything. */
export const SHOW_BUBBLES = true;
/** The earned pets swimming behind every screen. The reward that is always in view - off
* only to rule it out when chasing a performance problem. */
export const SHOW_AQUARIUM_CREATURES = true;
/** Fade screens in on entry. */
export const ANIMATE_VIEW_TRANSITIONS = true;
/** Show the on-screen keyboard with the finger colours. "auto" fades it out key by key
* as each one is mastered - the scaffold that removes itself, which is the whole point
* of teaching touch typing rather than hunt-and-peck. */
export const KEYBOARD_HINT_DEFAULT: "auto" | "on" | "off" = "auto";
/** Read the target letters and words aloud. She is six and still learning to read; a
* missing reading skill must never block the typing skill. */
export const SPEECH_DEFAULT = true;
export const SOUND_DEFAULT = true;
/** How many letters one bubbles or jellyfish round sends up - matched to the dive
* mode's length so a mode swap is not also a difficulty swap. Dive-mode line length
* lives on the lesson itself (`lengthFor` in lib/curriculum.ts). */
export function bubbleCountFor(world: number): number {
return world === 1 ? 50 : 100;
}