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
+28
View File
@@ -3359,6 +3359,8 @@ async function loadSettings() {
if (leftyEl) leftyEl.checked = highway.getLefty();
const autoplayExitEl = document.getElementById('setting-autoplay-exit');
if (autoplayExitEl) autoplayExitEl.checked = _autoplayExitEnabled();
const showUpNextEl = document.getElementById('setting-show-upnext');
if (showUpNextEl) showUpNextEl.checked = _showUpNextEnabled();
// Restore master-difficulty slider from persisted value (defaults
// to 100 when the key is absent — no behaviour change for users
// who've never touched the slider).
@@ -5851,6 +5853,32 @@ Object.defineProperty(window.feedBack, 'autoplayExit', {
get: _autoplayExitEnabled, configurable: true,
});
// ── "Up Next" pill (global option, default ON) ────────────────────────
// Gates the v3 player chrome's persistent upcoming-section pill
// (#v3-upnext, driven by player-chrome.js's updateUpNext). Client-only
// localStorage pref (`showUpNext`); absence of the key means enabled.
// player-chrome.js reads window.feedBack.showUpNext each tick and hides
// the pill when off.
function _showUpNextEnabled() {
try { return localStorage.getItem('showUpNext') !== '0'; } catch (_) { return true; }
}
// Settings checkbox setter (onchange="setShowUpNext(this.checked)").
window.setShowUpNext = function (on) {
try { localStorage.setItem('showUpNext', on ? '1' : '0'); } catch (_) { /* private mode */ }
const el = document.getElementById('setting-show-upnext');
if (el && el.checked !== !!on) el.checked = !!on;
// Reflect immediately when disabling mid-playback; the chrome's rAF
// loop (~6 Hz) re-shows it when re-enabled and a section is upcoming.
if (!on) {
const pill = document.getElementById('v3-upnext');
if (pill) pill.classList.add('hidden');
}
};
// Read-only view for the player chrome (and any plugin) to gate the pill.
Object.defineProperty(window.feedBack, 'showUpNext', {
get: _showUpNextEnabled, configurable: true,
});
// "Countdown before song" (Gameplay tab). Mirrored to localStorage by
// loadSettings so the song-start path can read it synchronously here — no
// async /api/settings fetch on the play hot path. Defaults off.
+14
View File
@@ -505,6 +505,20 @@
</label>
</div>
</div>
<!-- "Up Next" pill -->
<div class="fb-srow">
<span class="fb-srow-icon"><svg fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M13 5l7 7-7 7M5 5l7 7-7 7"/></svg></span>
<div class="fb-srow-main">
<div class="fb-srow-title">Show &ldquo;Up Next&rdquo;</div>
<div class="fb-srow-desc">Display the upcoming-section pill in the top-right of the player during playback.</div>
</div>
<div class="fb-srow-control">
<label class="fb-switch">
<input type="checkbox" id="setting-show-upnext" checked onchange="setShowUpNext(this.checked)">
<span class="fb-switch-track"></span>
</label>
</div>
</div>
</div>
</div>
+2
View File
@@ -172,6 +172,8 @@
function updateUpNext() {
const pill = $('v3-upnext');
if (!pill) return;
// Gated by the core "Show 'Up Next'" pref (Gameplay tab, default ON).
if (window.feedBack && window.feedBack.showUpNext === false) { pill.classList.add('hidden'); return; }
const hw = window.highway;
const secs = (hw && typeof hw.getSections === 'function') ? hw.getSections() : null;
const t = (hw && typeof hw.getTime === 'function') ? hw.getTime() : null;
+1 -1
View File
@@ -29,7 +29,7 @@
gameplay: {
server: ['master_difficulty', 'av_offset_ms', 'miss_penalty',
'fail_behavior', 'countdown_before_song', 'default_arrangement'],
local: ['lefty', 'autoplayExit', 'arrangementNamingMode', 'countdownBeforeSong'],
local: ['lefty', 'autoplayExit', 'showUpNext', 'arrangementNamingMode', 'countdownBeforeSong'],
after: function () {
// Left-handed is held on the highway object, not re-derived
// from localStorage on load — flip it back to the default.