Typing lessons like duolingo & musicmouse cleanup

This commit is contained in:
2026-09-12 18:58:02 +02:00
parent 498243af46
commit a5210fead2
50 changed files with 3074 additions and 710 deletions

View File

@@ -6,7 +6,7 @@
* artwork agree - and so do the LED strips, which run the same primary colour.
*/
import type { Album, AlbumKind } from "../api/types";
import type { Album, AlbumKind, PlayerState } from "../api/types";
import { GROUP_HUE } from "./theme";
export const isBook = (album: Album): boolean => album.kind === "book";
@@ -14,8 +14,8 @@ export const isBook = (album: Album): boolean => album.kind === "book";
/** Shape is how you tell the two apart without reading anything: albums are square,
* audiobooks are taller than wide, everywhere they appear - grid, list, group preview,
* player bar, now-playing. Nothing else may set an aspect ratio on a cover. */
export const ALBUM_ASPECT = 1;
export const BOOK_ASPECT = 0.82;
const ALBUM_ASPECT = 1;
const BOOK_ASPECT = 0.82;
export const aspectOfKind = (kind: AlbumKind): number =>
kind === "book" ? BOOK_ASPECT : ALBUM_ASPECT;
@@ -29,7 +29,7 @@ const colours = (album: Album): [string, string, string] => [
];
/** Diagonal two-tone stripes, the mockup's stand-in for a music cover. */
export function stripes(album: Album, width: number): string {
function stripes(album: Album, width: number): string {
const [primary, secondary] = colours(album);
return (
`repeating-linear-gradient(135deg, ${primary} 0px, ${primary} ${width}px, ` +
@@ -82,3 +82,24 @@ export const albumLine = (album: Album): string => {
? `${prefix}${album.title} · ${album.artist}`
: `${prefix}${album.title}`;
};
export interface NowPlayingText {
title: string;
subtitle: string;
/** "Kapitel 3 von 8" or "Song 3 von 8" - only meaningful while an album is loaded. */
progress: string;
/** Changes whenever the track itself changes, so a progress bar can reset its drag
* state instead of animating across two unrelated tracks. */
resetKey: string;
}
/** The title/subtitle/progress text shared by the player bar and the full-screen
* now-playing view, so the two cannot drift apart on a copy change. */
export function nowPlayingText(state: PlayerState, album: Album | null): NowPlayingText {
return {
title: state.track_title ?? "Wähl ein Album!",
subtitle: album ? albumLine(album) : "Tippen oder klicken",
progress: `${album && isBook(album) ? "Kapitel" : "Song"} ${state.track_index + 1} von ${state.track_count}`,
resetKey: `${state.album_id ?? ""}:${state.track_index}`,
};
}

View File

@@ -1,10 +1,16 @@
/** The browse screen's shelf/row look, in one place: per-group colors and identity,
* and the feature toggles that have gone back and forth while this design was under
* review. Flip a toggle here rather than hunting through BrowseView.tsx/covers.ts.
/** Per-group colors and identity, and the feature toggles that have gone back and
* forth while this design was under review - flip one here rather than hunting
* through the components that read it. Most toggles are about the browse screen's
* shelf/row look, but some (`SHOW_GLASS_BLUR`, `SHOW_AMBIENCE`,
* `SHOW_DECORATIVE_ANIMATIONS`) reach further: the play view, the player bar, the
* room page and `App.tsx` all read this module too, wherever they share the same
* "cut this on weak hardware" knob.
*
* `SHOW_ROW_TITLES` and `SHOW_ROW_ICONS` both `true` reproduces the original shelf
* header - an icon badge plus label button above each row's tiles. */
import type { CSSProperties } from "react";
import type { Group } from "./search";
// ---------------------------------------------------------------- toggles --
@@ -70,7 +76,7 @@ export const GROUP_LABEL: Record<Group, string> = {
/** A shelf/row's frosted background and border, tinted with its group's hue - or,
* with `SHOW_ROW_TINT` off, `{}` so `.glass-panel`'s own neutral CSS shows through. */
export function glassTint(hue: number): React.CSSProperties {
export function glassTint(hue: number): CSSProperties {
if (!SHOW_ROW_TINT) return {};
return {
background: `linear-gradient(160deg, oklch(55% 0.1 ${hue} / .32), oklch(30% 0.06 ${hue} / .14))`,
@@ -81,7 +87,7 @@ export function glassTint(hue: number): React.CSSProperties {
/** One list row's background, tinted with its group's hue and brighter/more opaque
* while it's the currently-playing row - or, with `SHOW_ROW_TINT` off, the same
* neutral highlight the row used before tinting existed. */
export function rowTint(hue: number, highlighted: boolean): React.CSSProperties {
export function rowTint(hue: number, highlighted: boolean): CSSProperties {
if (!SHOW_ROW_TINT) {
return { background: `oklch(97% 0.01 210 / ${highlighted ? ".22" : ".10"})` };
}