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:
@@ -25,8 +25,8 @@ export interface UiState {
|
||||
* that row", the way it means "which flat position" everywhere else. */
|
||||
shelfRow: number;
|
||||
/** Which top-level page is showing. Orthogonal to `view`/`search`/`group`/… below,
|
||||
* so toggling to the room and back leaves the music side exactly as it was. */
|
||||
page: "music" | "room";
|
||||
* so switching pages and back leaves the music side exactly as it was. */
|
||||
page: "music" | "room" | "typing";
|
||||
view: "browse" | "play";
|
||||
openAlbumId: string | null;
|
||||
/** Which track is highlighted in the open album's track list. Only meaningful while
|
||||
@@ -232,6 +232,12 @@ export function handleKey(
|
||||
): Action[] {
|
||||
const { key } = event;
|
||||
|
||||
// The typing tab is a second app mounted alongside this one (TippenApp.tsx) with its
|
||||
// own complete keyboard handling, including its own Escape/F1 bindings. It must own
|
||||
// every key while active - even Shift+media and Space below, which would otherwise
|
||||
// hijack a key a lesson happens to be drilling (see TippenApp.tsx's own doc comment).
|
||||
if (state.page === "typing") return [];
|
||||
|
||||
if (event.shiftKey && !event.ctrlKey && !event.metaKey) {
|
||||
const media = SHIFT_MEDIA[key.toUpperCase()];
|
||||
if (media) return media;
|
||||
|
||||
Reference in New Issue
Block a user