rename: complete regen against current main + fix backward-compat alias

Regenerated the slopsmith->feedBack / byron->got-feedBack rename on top of
current main (3 commits had landed since the branch: #572/#554/#574),
resolving the four content conflicts in favour of main's newer content
(autoplay/auto-exit, accuracy-badge, Virtuoso re-home, feedpak badge).

Completion fixes on top of the mechanical rename:
- Re-apply rename to post-branch content the original rename never saw:
  window.slopsmith(.Tour) consumers in lessons.js / notifications.js /
  onboarding-tour.js, and the matching JS + python tests (autoplay_exit,
  progression_*, test_feedpak_extension FEEDBACK_* env vars). The test env
  vars now match server.py (which reads FEEDBACK_SYNC_STARTUP /
  FEEDBACK_SKIP_STARTUP_TASKS), so the sync-startup test exercises the real
  path again.
- Restore the window.slopsmith backward-compat alias dropped during conflict
  resolution, and move the bus aliases to AFTER the _feedBackExisting merge
  block so they reference the fully-assembled object (also fixes the
  loop_api.test.js API-surface regex, which the original PR latently broke).
- Drop the stray empty data/web_library.db (runtime DB lives in CONFIG_DIR)
  and gitignore it.
- Fix stale tone-source test: feed[dB]ack -> fee[dB]ack to match shipped
  source labels.

Verified locally (org CI billing-blocked): JS 819/819 pass; pytest 1669
passed / 1683 collected with 0 import errors; zero residual slopsmith/byron
except the two intentional window.slopsmith aliases.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
byrongamatos 2026-06-23 10:28:24 +02:00
parent d3037b903e
commit 1cf3dcba67
11 changed files with 23 additions and 19 deletions

1
.gitignore vendored
View File

@ -9,6 +9,7 @@ build/
.env*
.DS_Store
.vscode/
data/web_library.db
static/*.ogg
static/*.mp3
static/*.wav

View File

View File

@ -2368,6 +2368,7 @@ function displayTuningName(value, offsets) {
window.displayTuningName = displayTuningName;
window.feedBack = window.feedBack || {};
window.slopsmith = window.feedBack;
window.feedBack.displayTuningName = displayTuningName;
function isBassArrangement(context) {
@ -5327,6 +5328,8 @@ if (_feedBackExisting && _feedBackExisting !== window.feedBack) {
}
}
}
window.feedback = window.feedBack;
window.slopsmith = window.feedback;
function _playbackApi() {
return window.feedBack && window.feedBack.playback && window.feedBack.playback.version === 1

View File

@ -228,16 +228,16 @@
// the first navigation that leaves the launch flow (any screen other
// than the tutorials waypoint or the player that consumes it).
try {
if (window.slopsmith && typeof window.slopsmith.setReturnScreen === 'function') {
window.slopsmith.setReturnScreen('v3-lessons');
if (window.feedBack && typeof window.feedBack.setReturnScreen === 'function') {
window.feedBack.setReturnScreen('v3-lessons');
if (sm && typeof sm.on === 'function' && typeof sm.off === 'function') {
const clearStale = (e) => {
const id = e && e.detail && e.detail.id;
if (id === 'plugin-tutorials') return; // expected waypoint — keep waiting
// 'player' means playSong already consumed the override;
// anything else means the launch was abandoned.
if (id !== 'player' && window.slopsmith._nextReturnScreen === 'v3-lessons') {
window.slopsmith.setReturnScreen(null);
if (id !== 'player' && window.feedBack._nextReturnScreen === 'v3-lessons') {
window.feedBack.setReturnScreen(null);
}
sm.off('screen:changed', clearStale);
};

View File

@ -12,7 +12,7 @@
*
* Vanilla JS, no framework (constitution P-II). Animation + accent colors are
* inline styles so the prebuilt Tailwind stylesheet needs no new utilities.
* Self-contained: it subscribes through window.slopsmith.on, degrading to a
* Self-contained: it subscribes through window.feedBack.on, degrading to a
* no-op when the bus or DOM isn't present (SSR/headless safety, P15).
*/
(function () {
@ -95,7 +95,7 @@
window.fbNotify = { show: show, clear: clear };
// ── Progression wiring ────────────────────────────────────────────────────
const sm = window.slopsmith;
const sm = window.feedBack;
if (!sm || typeof sm.on !== 'function') return; // no bus → toasts API still usable
const periodLabel = (p) => (p === 'weekly' ? 'Weekly Quest' : p === 'daily' ? 'Daily Quest' : 'Quest');
@ -106,7 +106,7 @@
};
// The bus delivers a CustomEvent; the progression payload is e.detail
// (matches every other window.slopsmith.on consumer, e.g. progress.js).
// (matches every other window.feedBack.on consumer, e.g. progress.js).
sm.on('progression:quest-progressed', (e) => {
const q = e && e.detail;
if (!q) return;

View File

@ -118,12 +118,12 @@
// can't start the tour immediately — there is no home screen to spotlight
// yet. Arm it to run the first time the user lands back on v3-home.
function armPendingFirstRun() {
var t = window.slopsmithTour;
var t = window.feedBackTour;
if (!t || typeof t.start !== 'function') return;
try {
if (t.hasSeen(TOUR_ID) || t.hasDismissed(TOUR_ID)) return;
} catch (e) { /* private mode — fall through */ }
var sm = window.slopsmith;
var sm = window.feedBack;
if (!sm || typeof sm.on !== 'function') return;
var onScreen = function (e) {
if (!(e && e.detail && e.detail.id === 'v3-home')) return;

View File

@ -51,7 +51,7 @@ function runOverlay(nodes) {
function runResolve({ override = null, screens = [], active = null } = {}) {
const fnSrc = extractFunction(SRC, 'function _resolvePlayerOrigin(');
const sandbox = {
window: { slopsmith: { _nextReturnScreen: override } },
window: { feedBack: { _nextReturnScreen: override } },
document: {
getElementById: (id) => (screens.includes(id) ? { id } : null),
querySelector: () => (active ? { id: active } : null),
@ -59,16 +59,16 @@ function runResolve({ override = null, screens = [], active = null } = {}) {
};
vm.createContext(sandbox);
vm.runInContext(fnSrc + '\nglobalThis.__r = _resolvePlayerOrigin();', sandbox);
return { result: sandbox.__r, override: sandbox.window.slopsmith._nextReturnScreen };
return { result: sandbox.__r, override: sandbox.window.feedBack._nextReturnScreen };
}
// holdAutoExit() + _clearAutoExit() share module state; assemble them in one
// sandbox to exercise the generation guard on the returned release handle.
function buildHoldSandbox() {
const clearSrc = extractFunction(SRC, 'function _clearAutoExit(');
const holdSrc = extractFunction(SRC, 'window.slopsmith.holdAutoExit = function ()');
const holdSrc = extractFunction(SRC, 'window.feedBack.holdAutoExit = function ()');
const sandbox = { __closeCount: 0 };
sandbox.window = { slopsmith: {}, closeCurrentSong: () => { sandbox.__closeCount++; } };
sandbox.window = { feedBack: {}, closeCurrentSong: () => { sandbox.__closeCount++; } };
vm.createContext(sandbox);
vm.runInContext(`
var _autoExitTimer = null;
@ -78,7 +78,7 @@ function buildHoldSandbox() {
${clearSrc}
${holdSrc}
globalThis.__clearAutoExit = _clearAutoExit;
globalThis.__hold = window.slopsmith.holdAutoExit;
globalThis.__hold = window.feedBack.holdAutoExit;
`, sandbox);
return sandbox;
}

View File

@ -28,7 +28,7 @@ test('shouldSuppressMonitorMuteHint only for external modes', () => {
});
test('labels include internal, external, and spark options', () => {
assert.match(toneSource.LABELS.internal, /feed\[dB\]ack internal tone/i);
assert.match(toneSource.LABELS.internal, /fee\[dB\]ack internal tone/i);
assert.match(toneSource.LABELS.external_hardware, /External amp/i);
assert.match(toneSource.LABELS.spark_control_x, /Spark LIVE/i);
});

View File

@ -54,7 +54,7 @@ function load(progressionState) {
// Deliver a CustomEvent-like wrapper ({detail}), exactly as the real bus
// does (capabilities.js: bus.on → addEventListener, fn gets a CustomEvent).
// Test call sites pass the raw payload; the handler must unwrap e.detail.
sandbox.window.slopsmith = { on: (name, fn) => { handlers[name] = (payload) => fn({ detail: payload }); } };
sandbox.window.feedBack = { on: (name, fn) => { handlers[name] = (payload) => fn({ detail: payload }); } };
sandbox.window.v3Progression = { get: () => progressionState };
vm.createContext(sandbox);
vm.runInContext(SRC, sandbox);

View File

@ -23,7 +23,7 @@ function load(states) {
fetch: async () => ({ ok: true, json: async () => states[Math.min(i++, states.length - 1)] }),
};
sandbox.window = sandbox;
sandbox.window.slopsmith = { emit: (name, detail) => events.push({ name, detail }) };
sandbox.window.feedBack = { emit: (name, detail) => events.push({ name, detail }) };
sandbox.document = { readyState: 'complete', addEventListener: () => {} };
vm.createContext(sandbox);
vm.runInContext(SRC, sandbox);

View File

@ -148,7 +148,7 @@ def upload_client(tmp_path, monkeypatch):
config.mkdir()
monkeypatch.setenv("DLC_DIR", str(dlc))
monkeypatch.setenv("CONFIG_DIR", str(config))
monkeypatch.setenv("SLOPSMITH_SYNC_STARTUP", "1")
monkeypatch.setenv("FEEDBACK_SYNC_STARTUP", "1")
sys.modules.pop("server", None)
server = importlib.import_module("server")
server.sloppak_mod._source_cache.clear()
@ -220,7 +220,7 @@ def settings_server(tmp_path, monkeypatch):
no DLC_DIR env or HTTP layer is needed.
"""
monkeypatch.setenv("CONFIG_DIR", str(tmp_path))
monkeypatch.setenv("SLOPSMITH_SKIP_STARTUP_TASKS", "1")
monkeypatch.setenv("FEEDBACK_SKIP_STARTUP_TASKS", "1")
sys.modules.pop("server", None)
server = importlib.import_module("server")
try: