mirror of
https://github.com/got-feedBack/feedBack.git
synced 2026-09-11 02:54:30 +00:00
static/js/resume-session.js (157) — the snapshot taken when you leave a song and the
pill that offers it back. Bodies VERBATIM. app.js 7,727 → 7,601.
Fifth slice out of the strongly-connected core. ONE hook (playSong) + a
currentFilename getter.
S.pendingResume JOINS THE CONTAINER — on demand, exactly as intended. app.js WRITES
it (playSong({ resume }) arms it; the song:ready listener consumes it) while this
module reads it, so it cannot be a plain export: an imported binding is read-only.
Same reason isPlaying is there. The container grows one field per carve that needs
it, never speculatively.
THE CONTRACT TEST CAUGHT THE MISSING HOOK, again on a path nothing executes:
"playSong is read by a module but never wired by app.js — it would throw at runtime".
Second time it has caught a real wiring gap the moment it appeared.
A REAL TRAP, worth remembering: I first did the S.pendingResume rewrite by feeding
acorn's identifier RANGES from node into python, and it corrupted the file
(`_pS.pendingResume null;`). **Acorn's offsets are UTF-16 code units; Python's string
indices are code points.** static/app.js contains emoji, so every offset past one
drifts. Do an AST-driven rewrite in the SAME language that produced the offsets.
`node --check` caught it; a silent version of that bug is very easy to imagine.
VERIFIED. A/B against origin/main in two browsers, real song: the window API
(resumeLastSession / _snapshotResumeSession / _readResumeSession /
_clearResumeSession), snapshot, read-back, and clear — IDENTICAL, zero page errors.
HONEST LIMIT: my probe never got the snapshot to actually PERSIST (there is a guard
beyond the 3s minimum position that a scripted playSong does not satisfy), so that
path is verified only as identical-to-main, not as observed-working. The real
coverage is tests/browser/resume-session.spec.ts, which drives the flow properly.
Zero harnesses broke. pytest 2396, node 1040/1040, ESLint 0 (no-cycle clean),
tailwind clean, Codex 0.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
43 lines
2.1 KiB
JavaScript
43 lines
2.1 KiB
JavaScript
// Shared, MUTABLE player state.
|
|
//
|
|
// WHY A CONTAINER AND NOT PLAIN EXPORTS. An imported binding is read-only. Every
|
|
// slice carved out of app.js so far has only ever READ the state it shares
|
|
// (loopA/loopB, _audioSeekGen, currentFilename), so a getter hook was enough and no
|
|
// container was needed. That runs out here: count-in genuinely WRITES `isPlaying`
|
|
// (it starts and stops playback) and `lastAudioTime`. `import { isPlaying }` then
|
|
// `isPlaying = true` throws — the binding cannot be assigned to.
|
|
//
|
|
// So the state moves onto an object. `S.isPlaying = true` is a property write, which
|
|
// works from any module holding the same `S`. This is the same shape the stems,
|
|
// studio, and editor migrations converged on.
|
|
//
|
|
// It is deliberately SMALL. app.js has ~104 top-level `let` scalars; lifting all of
|
|
// them would be a ~977-site rewrite for no benefit, since most are private to one
|
|
// cluster and travel with it. Only the ones a carved module must WRITE belong here.
|
|
// Add to it when a carve actually needs it, not before.
|
|
//
|
|
// NB app.js's own 71 reference sites were rewritten mechanically — but from the AST,
|
|
// not by text substitution. Of 100 textual occurrences of these two names, only 71
|
|
// resolve to the module binding: 22 are member accesses (`someObj.isPlaying`), 4 are
|
|
// the local parameter of setPlayButtonState(isPlaying), one is an object key, and two
|
|
// are shorthand properties (`{ isPlaying }`) that must become `{ isPlaying: S.isPlaying }`.
|
|
// A blind find-and-replace corrupts all 29.
|
|
export const S = {
|
|
/** Is the transport running? Written by playback, count-in, and the JUCE shims. */
|
|
isPlaying: false,
|
|
|
|
/**
|
|
* The last audio position we saw, in seconds. Used to detect a seek that did not
|
|
* land where it was asked to (JUCE can clamp; HTML5 can round).
|
|
*/
|
|
lastAudioTime: 0,
|
|
|
|
/**
|
|
* A resume request armed by playSong({ resume }) and consumed on song:ready.
|
|
* Written by app.js (playSong, and the song:ready listener that consumes it) and
|
|
* read by the resume-session module — so, like the two above, it cannot be a plain
|
|
* export.
|
|
*/
|
|
pendingResume: null,
|
|
};
|