fix(v3): add "Show 'Up Next'" toggle so the player pill can be turned off (#612)

The v0.3.0 player chrome's persistent upcoming-section pill (#v3-upnext,
drawn by static/v3/player-chrome.js's updateUpNext) shipped with no off
switch: it always showed during playback whenever a section was upcoming,
overlapping the top-right FPS HUD and ignoring the 3D-highway "Show 'Up
Next' section card" checkbox (a different, in-canvas widget demoted to
default-off precisely because this pill is the canonical readout). Users
reading the pill as that same setting saw "disabled in settings but still
there."

Add a real core toggle, following the autoplayExit idiom:
- static/app.js: client-only `showUpNext` localStorage pref (absence =
  enabled), _showUpNextEnabled()/setShowUpNext(), loadSettings()
  hydration, and a read-only window.feedBack.showUpNext getter. Disabling
  mid-playback hides the pill immediately.
- static/v3/index.html: a "Show 'Up Next'" switch in the Gameplay tab.
- static/v3/player-chrome.js: gate updateUpNext() on the pref.
- static/v3/settings.js: add showUpNext to RESET_MAP.gameplay.local.

Default ON, so behaviour is unchanged for existing users. v3-only (the
pill is v3 core chrome); no Tailwind rebuild.


Claude-Session: https://claude.ai/code/session_01QbexxfTt8q2tAn436MqGWF

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
ChrisBeWithYou
2026-06-27 13:16:31 +02:00
committed by GitHub
co-authored by Claude Opus 4.8
parent 6dbcc5861b
commit d1f7f12293
5 changed files with 46 additions and 1 deletions
+1
View File
@@ -35,6 +35,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- **v3 library: exact artist/album filters + scroll/page-depth restore** (feedBack#857). The v3 Songs toolbar gains Artist and Album dropdowns (Album populates from the selected artist and stays disabled until one is chosen), backed by new exact, case-insensitive (`COLLATE NOCASE`) `artist` / `album` query params threaded through `MetadataDB._build_where``query_page` / `query_artists` / `query_stats` and the `/api/library`, `/api/library/artists`, `/api/library/stats` endpoints (the free-text `q` search stays fuzzy and composes with the exact filters). The artist/album catalog is fetched independently of the active artist/album selection so the dropdowns always list the full set for the current provider/search. The toolbar is now sticky so filter controls stay reachable when browsing deep libraries, and returning from the player restores the previous scroll position **and** the loaded infinite-scroll page depth via a `sessionStorage` snapshot keyed by a filter/sort/view state hash (invalidated whenever those change, so a filter change still resets to the top). Tests: `tests/test_library_filters.py` (backend artist/album filters), `tests/js/v3_songs_scroll.test.js` (state-hash + snapshot helpers).
### Fixed
- **The v3 "Up Next" pill can now be turned off — new "Show 'Up Next'" gameplay toggle (default ON).** The v0.3.0 player chrome's persistent upcoming-section pill (`#v3-upnext`, drawn by `static/v3/player-chrome.js`'s `updateUpNext()`) shipped with no off switch, so it always showed during playback whenever a section was upcoming — overlapping the top-right FPS HUD and ignoring the 3D-highway "Show 'Up Next' section card" checkbox (a *different*, in-canvas widget that was demoted to default-off precisely because this pill is the canonical readout). Users reading the pill as the same setting saw "disabled in settings but still there." Adds a real core toggle following the `autoplayExit` idiom: a client-only `showUpNext` `localStorage` pref (absence = enabled), a **Show "Up Next"** switch in the Gameplay settings tab (`static/v3/index.html`), reader/writer + `loadSettings()` hydration + a read-only `window.feedBack.showUpNext` getter in `static/app.js`, and a gate at the top of `updateUpNext()` that hides the pill when off. Disabling mid-playback hides it immediately; re-enabling re-shows it on the next chrome tick (~6 Hz). Added to `RESET_MAP.gameplay.local` in `static/v3/settings.js` so the Gameplay "Reset" restores the default-on state. Default ON = zero change for existing users. No Tailwind rebuild (plain markup + existing classes).
- **v3 list/tree view brought to parity with the grid: select mode, parts chips, and song actions — plus a stale-CSS Docker fix.** Re-lands a previously-reverted change. **Frontend (`static/v3/songs.js`):** entering select mode no longer collapses the tree — `loadTree()` now captures the expanded artist groups (`details[open]` keyed by `data-artist`) before the "Loading…" wipe and restores them on rebuild, so toggling select mode (which re-renders via `reload()`) keeps groups open and selection usable; tree rows gain a display-only checkbox + selection ring, the same fav / save-for-later / overflow-menu cluster as the grid card (always shown, all bound by `wireCards()`), and a capture-phase select guard mirroring the grid so clicking a row or arrangement chip in select mode selects instead of playing (`<summary>` headers sit outside `[data-fn]`, so native expand/collapse is untouched). **Docker fix (`static/tailwind.min.css`):** the committed Tailwind stylesheet was stale — `.sm\:flex` (and the other utilities behind #582's `hidden sm:flex` arrangement chips and the new action cluster) were never compiled in, so they rendered `display:none` on the Docker build (which serves the committed CSS as-is; Desktop rebuilds from source so it looked fine). Regenerated with the pinned `tailwindcss@3.4.19` via `scripts/build-tailwind.sh` so Docker matches Desktop and #582's chips render on every Docker deploy. Regression tests: `tests/browser/v3-tree-select.spec.ts`.
- **Space bar now plays/pauses on the player screen even when a sidebar nav link or rail button has focus.** When any `<button>` in the player rail (viz, audio, mixer, lyrics, plugins, advanced), a sidebar nav link, or a popover control held keyboard focus, pressing Space was swallowed by `_shortcutDispatchBlocked``_isInsideInteractiveControl` (which treats `BUTTON`/`A` as interactive), so the Space shortcut never reached the dispatcher and `togglePlay()` never ran. `_shortcutDispatchBlocked` (`static/app.js`) now extends the same carve-out already used for the Section Practice bar: while the player screen is active, Space is always routed through the shortcut system — the dispatcher calls `e.preventDefault()` before invoking the handler, so the focused element does not also activate. Text inputs (`_isTextInput`) remain exempted first, so typing space in a search/input field still works normally, and focus inside a true modal dialog (`role="dialog" aria-modal="true"` / `.feedBack-modal`) layered over the player is also exempted so Space reaches the modal's focused control (e.g. its Close button) instead of toggling playback behind it — non-modal player popovers/toasts (loop A/B, arrangement pin) stay covered. Regression tests in `tests/browser/keyboard-shortcuts.spec.ts` cover the focused-rail-button play/pause, the text-input exemption, and the modal-dialog exemption.
- **A song's accuracy badge now updates on its library card right after you play it — no restart needed.** The v3 library (`static/v3/songs.js`) loaded the best-accuracy map (`/api/stats/best`) once into `state.accuracy` at render time and only ever refreshed it on a full re-render; the play→return flow takes the screen-entry fast-path that restores the cached grid DOM without re-fetching, so a just-earned score stayed invisible until the next restart re-ran `render()`. The `stats-recorder` now emits a `stats:recorded` event (carrying `filename`/`arrangement`) once the scored `POST /api/stats` resolves on the server — the correct moment, since `song:stop` fires before the POST completes. `songs.js` listens: if the library is the active screen it re-fetches `/api/stats/best` and patches the affected card/row badge in place; otherwise it marks the filename dirty and `onV3SongsScreenEnter` applies it on return (a failed fetch keeps the entry dirty to retry). Badge markup was factored into a shared `accuracyBadge(filename, variant)` (grid pill + tree-row percentage, both tagged `.fb-acc-badge`) so the in-place `repaintAccuracy` can find and replace them without a full list re-render (scroll/pagination preserved). The old empty `song:stop` "refresh lazily next render" placeholder is replaced.