mirror of
https://github.com/got-feedBack/feedBack.git
synced 2026-08-11 03:09:57 +00:00
refactor(app): carve the player controls out of app.js (R3a) (#891)
static/js/player-controls.js (229) — the speed + mastery sliders and the four
playback-preference reads (autoplay-exit, up-next, countdown-before-song,
confirm-exit). Bodies VERBATIM. app.js 7,914 → 7,727.
The fourth slice out of the strongly-connected core, and by far the easiest: ONE
hook (handleSliderInput) and NO shared mutable state. The three groups are the same
surface — the controls under the highway — and the preference reads are the
one-line localStorage lookups half of app.js consults before deciding whether to
auto-start, show the Up Next pill, run a count-in, or confirm on exit. They travel
with the controls that set them.
Zero missed members on the first build (the no-undef pass was clean), which is the
first time that has happened in this phase.
TWO HARNESSES ARE SPLIT, and both taught something:
* speed_reset spans BOTH files — playSong (app.js) resets the speed controls
(module). Its presence GUARDS still read `src.includes('function setSpeed')`
against app.js, so once the code moved they silently evaluated FALSE and the
helpers were quietly dropped from the sandbox. A guard that disables itself is
worse than no guard. Repointed at the file the code actually lives in.
* Its `host.handleSliderInput` stub had to route at the sandbox's EXISTING spy,
not a fresh `() => {}`. The test asserts the slider was actually refreshed
(`deepEqual(__sliderInputs, ['speed-slider'])`); a fresh stub swallows the call
and the assertion passes VACUOUSLY. Same failure mode as a no-op host default —
the thing this whole seam design exists to prevent.
* autoplay_exit is split too: _autoplayExitEnabled moved, but the auto-exit
machinery around it (_clearAutoExit, holdAutoExit, _resolvePlayerOrigin) stayed.
VERIFIED. A/B against origin/main in two browsers, real song: setSpeed(0.75) ->
playbackRate 0.75; applySpeedPreset(100) -> 1; the speed slider; setMastery;
setAutoplayExit / setCountdownBeforeSong / setShowUpNext — IDENTICAL, zero page errors.
pytest 2396, node 1040/1040, ESLint 0 (no-cycle clean), tailwind clean.
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.8
parent
11f8c36b61
commit
dc429ecd16
@@ -5,6 +5,9 @@ const path = require('node:path');
|
||||
const vm = require('node:vm');
|
||||
|
||||
const APP_JS = path.join(__dirname, '..', '..', 'static', 'app.js');
|
||||
// The speed controls were carved out into static/js/player-controls.js (R3a); playSong,
|
||||
// which resets them on a new song, stayed in app.js. This test spans both.
|
||||
const CONTROLS_JS = path.join(__dirname, '..', '..', 'static', 'js', 'player-controls.js');
|
||||
|
||||
function extractFunction(src, signature) {
|
||||
const start = src.indexOf(signature);
|
||||
@@ -130,15 +133,17 @@ function extractConstLine(src, name) {
|
||||
|
||||
function loadPlaySong(sandbox) {
|
||||
const src = fs.readFileSync(APP_JS, 'utf8');
|
||||
const resetHelper = src.includes('function _resetPlaybackSpeedForNewSong')
|
||||
? extractFunction(src, 'function _resetPlaybackSpeedForNewSong')
|
||||
// the module is ESM; the vm sandbox evaluates plain script text
|
||||
const controls = fs.readFileSync(CONTROLS_JS, 'utf8').replace(/^export /gm, '');
|
||||
const resetHelper = controls.includes('function _resetPlaybackSpeedForNewSong')
|
||||
? extractFunction(controls, 'function _resetPlaybackSpeedForNewSong')
|
||||
: '';
|
||||
const speedPresetHelpers = src.includes('function _updateSpeedPresetButtons')
|
||||
const speedPresetHelpers = controls.includes('function _updateSpeedPresetButtons')
|
||||
? `
|
||||
${extractConstLine(src, 'SPEED_PRESET_PCTS')}
|
||||
${extractConstLine(src, 'SPEED_SNAP_THRESHOLD')}
|
||||
${extractFunction(src, 'function _speedPresetPctFromActive')}
|
||||
${extractFunction(src, 'function _updateSpeedPresetButtons')}
|
||||
${extractConstLine(controls, 'SPEED_PRESET_PCTS')}
|
||||
${extractConstLine(controls, 'SPEED_SNAP_THRESHOLD')}
|
||||
${extractFunction(controls, 'function _speedPresetPctFromActive')}
|
||||
${extractFunction(controls, 'function _updateSpeedPresetButtons')}
|
||||
`
|
||||
: '';
|
||||
const code = `
|
||||
@@ -147,6 +152,11 @@ function loadPlaySong(sandbox) {
|
||||
// WRITE it (an imported binding is read-only). NB window.feedBack.isPlaying — the
|
||||
// public mirror stubbed above — is a different thing and is unchanged.
|
||||
var S = { isPlaying: true, lastAudioTime: 0 };
|
||||
// The speed controls reach app.js through the host seam (static/js/host.js).
|
||||
// Route it at the sandbox's EXISTING handleSliderInput spy — a fresh stub would
|
||||
// swallow the call and the assertion below (which checks the slider was actually
|
||||
// refreshed) would pass vacuously.
|
||||
var host = { handleSliderInput: (el) => handleSliderInput(el) };
|
||||
var currentFilename = null;
|
||||
var _playerOriginScreen = null;
|
||||
var _pendingAutostart = false;
|
||||
@@ -166,7 +176,7 @@ function loadPlaySong(sandbox) {
|
||||
function _scheduleSectionPracticeRetries() {}
|
||||
function loadSavedLoops() {}
|
||||
function _songEventPayload() { return { time: 7, audioT: 7, chartT: 7, perfNow: 7 }; }
|
||||
${extractFunction(src, 'function setSpeed')}
|
||||
${extractFunction(controls, 'function setSpeed')}
|
||||
${speedPresetHelpers}
|
||||
${resetHelper}
|
||||
${extractFunction(src, 'async function playSong')}
|
||||
|
||||
Reference in New Issue
Block a user