fix(onboarding): calibration Tuner step no longer exposes the input-select overlay (#577)

Tester: "at the tune step, pressing the Tuner button starts a second wizard at
the input-select step."

Root cause is stacked full-screen overlays. During onboarding the input-setup
flow runs as #input-setup-overlay (z-210) on top of the onboarding modal
#v3-onboarding (z-200), and note_detect's Calibration Wizard (z-300) launches on
top of that. When the player opens the Tuner, that wizard minimizes itself to
transparent + pointer-events:none so the Tuner (z-1000) is usable — but the
input-setup overlay underneath, still showing its "select your input" card, then
shows through behind the floating tuner and reads as a second wizard.

Two targeted hides so only the active surface is visible:
- input_setup: hide #input-setup-overlay while launchCalibration runs; restore on
  its onDone/onCancel (one always fires on close), so the calibration wizard /
  tuner own the screen.
- onboarding runInputSetup: hide #v3-onboarding for the whole input-setup phase
  (its own overlay replaces it visually); restore in finally before advancing to
  the calibration-challenge step.

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
Byron Gamatos
2026-06-23 12:51:59 +02:00
committed by GitHub
co-authored by Claude Opus 4.8
parent 7399a2ac63
commit a43e7b13be
2 changed files with 49 additions and 18 deletions
+32 -16
View File
@@ -222,23 +222,39 @@
if (!instruments.length) return;
// Don't let a plugin-load race skip the mandatory input-setup step.
if (!(await waitForInputSetup(8000))) return;
const caps = window.feedBack && window.feedBack.capabilities;
if (!caps || typeof caps.command !== 'function') {
try { await window.feedBackInputSetup.launch(instruments); } catch (e) { /* proceed */ }
return;
// Hide this onboarding modal while the input-setup wizard (its own
// full-screen overlay) runs on top. Otherwise both stay stacked, and when
// the note-detect calibration wizard minimizes to expose the Tuner the
// onboarding modal shows through behind the tuner. Restored in `finally`
// before we advance to the calibration-challenge step.
const ob = document.getElementById('v3-onboarding');
const obPrevDisplay = ob ? ob.style.display : '';
if (ob) ob.style.display = 'none';
const restoreOnboarding = () => {
const o = document.getElementById('v3-onboarding');
if (o) o.style.display = obPrevDisplay;
};
try {
const caps = window.feedBack && window.feedBack.capabilities;
if (!caps || typeof caps.command !== 'function') {
try { await window.feedBackInputSetup.launch(instruments); } catch (e) { /* proceed */ }
return;
}
await new Promise((resolve) => {
let settled = false;
let unsub = null;
const done = () => { if (settled) return; settled = true; try { unsub && unsub(); } catch (e) { /* noop */ } resolve(); };
try { unsub = typeof caps.subscribe === 'function' ? caps.subscribe('input-calibration:calibration-done', done) : null; } catch (e) { unsub = null; }
// `run` is fire-and-launch; completion arrives via the event above.
// A non-handled outcome (no owner / plugin absent / error) means
// nothing was launched, so proceed immediately.
caps.command('input-calibration', 'run', { requester: 'onboarding', payload: { instruments } })
.then((r) => { if (!r || r.outcome !== 'handled') done(); })
.catch(() => done());
});
} finally {
restoreOnboarding();
}
await new Promise((resolve) => {
let settled = false;
let unsub = null;
const done = () => { if (settled) return; settled = true; try { unsub && unsub(); } catch (e) { /* noop */ } resolve(); };
try { unsub = typeof caps.subscribe === 'function' ? caps.subscribe('input-calibration:calibration-done', done) : null; } catch (e) { unsub = null; }
// `run` is fire-and-launch; completion arrives via the event above.
// A non-handled outcome (no owner / plugin absent / error) means
// nothing was launched, so proceed immediately.
caps.command('input-calibration', 'run', { requester: 'onboarding', payload: { instruments } })
.then((r) => { if (!r || r.outcome !== 'handled') done(); })
.catch(() => done());
});
}
function show(profile, opts) {