Two separate costs, both measured on musicdolphin (Pi 4, 1920x1080 kiosk), where the
app was burning ~70% of a core with nothing happening on screen.
Per-frame work. The ambient canvas repaints a full-screen gradient plus a particle
field every frame, and `usePlaybackClock` pushes a React setState per animation frame
into both PlayView and PlayerBar for the whole length of a track. `?pi=1` (lib/
lowPower.ts) makes those cheaper rather than switching them off: the canvas paints a
quarter of the pixels at 30fps with a bubble cap, and the clock renders ten times a
second - a progress bar advances one pixel every few hundred ms and its label has
one-second resolution, so nothing on screen can tell. Only the effects with no cheap
version actually go: the backdrop-filter glass blur and the decorative CSS loops.
Also drops a redundant full-canvas clearRect that the opaque gradient always covered.
Search. normalize() runs a Unicode NFD decomposition, and albumMatches/songMatches
called it on every album title and every track title on every keystroke - 4969 of them
for a track search, whose answer cannot change until the library does. buildSearchIndex
does it once per library payload; a keystroke is now String.includes over strings that
already exist. On the real library that is 33ms -> 3.4ms for an eight-letter track
query on a laptop, and this runs on a Pi. The same index partitions albums by shelf and
pre-sorts each shelf's categories, which App and BrowseView were deriving separately
from the same data.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The three tab buttons now share one frosted glass pill instead of each
floating separately, and BrowseView/RoomView reserve TAB_RAIL_CLEARANCE
of right-side padding so a full-width row or grid never renders under
it - verified against the real library, where dense rows (e.g. Conni's
82 audiobooks) previously butted right up against the rail.
Also gitignore the real (non-.example) tippen-curriculum.yml and
tippen-progress.json, matching config.yml's own privacy treatment,
since the curriculum now contains real reward paths into a personal
library.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Verified the whole reward-lock feature against a real library with an
actual browser (Playwright + headless Chromium): the album-grid cards
already showed the question-mark placeholder for a locked album, but
its 2x2 preview thumbnail in the category-tile view (the screen before
drilling into an artist/category) still rendered the real cover art,
since that Cover call never received the locked prop.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
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>
- Replace the "Mein Zimmer"/"Musik" text pills and the help button with
two fixed circular corner buttons: a music/room toggle (top-right)
and a single "back one level" button (top-left) used everywhere -
search, categories, and the play view.
- Split `view` (browse/play) from a new `page` (music/room) in UiState
so toggling to the room and back leaves the music side - search,
selection, even the full-screen play view - exactly as it was.
- Back peels one step at a time (search, then track-search mode, then
group+category together, since a root shelf tile sets both at once
and undoing that jump should be one step too).
- Album cards: click the cover to open the track list, click the title
to start playing immediately (matching what Enter already did from
the keyboard). Audiobook cards drop the artist line and clamp the
title to a fixed height so covers stay aligned across a row.
- Root shelf group headings ("Musik", "Hörbücher", "Podcasts") are now
the link into that group's full category grid, replacing the
separate search-icon button.
- "Taste zuweisen" is now an icon-only round button, moved to the
bottom-right so it doesn't sit under the new toggle.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>