mirror of
https://github.com/got-feedBack/feedBack.git
synced 2026-08-12 11:49:28 +00:00
fix(onboarding): midi-input multi-provider discovery + home-tour lifecycle (#568)
Addresses Codex review of #526/#528: - midi-input discover(): one provider's enumerate() rejection no longer aborts the whole discovery — other providers (e.g. a native/desktop MIDI provider) are still queried; denial is only reported when NO provider enumerates. - Home tour now waits for a 'v3:dashboard-rendered' event (dashboard.js emits it after the #v3-home innerHTML swap) before attaching Shepherd, instead of a single animation frame that could latch onto pre-render nodes the async dashboard render then replaces. - "Play it now" onboarding now arms the tour (armPendingFirstRun) to run the first time the user returns to v3-home, instead of silently never showing it. 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
820a18648a
commit
5d0229fc82
@@ -240,6 +240,10 @@
|
||||
if (window.v3AudioRouting && typeof window.v3AudioRouting.render === 'function') {
|
||||
try { window.v3AudioRouting.render(document.getElementById('v3-audio-routing')); } catch (e) { /* */ }
|
||||
}
|
||||
// Signal that #v3-home has been (re)built, so the first-run onboarding
|
||||
// tour can wait for the real cards instead of attaching to nodes from a
|
||||
// prior render that this innerHTML swap just replaced.
|
||||
try { document.dispatchEvent(new CustomEvent('v3:dashboard-rendered')); } catch (e) { /* older runtimes */ }
|
||||
}
|
||||
|
||||
function statCard(value, unit, unitColor) {
|
||||
|
||||
@@ -93,18 +93,47 @@
|
||||
try {
|
||||
if (t.hasSeen(TOUR_ID) || t.hasDismissed(TOUR_ID)) return;
|
||||
} catch (e) { /* private mode — fall through and attempt once */ }
|
||||
// Make sure the home screen is in view so the spotlight targets exist;
|
||||
// the per-step waitFor handles the async dashboard render.
|
||||
// Make sure the home screen is in view so the spotlight targets exist.
|
||||
if (typeof window.showScreen === 'function') {
|
||||
try { window.showScreen('v3-home'); } catch (e) { /* best-effort */ }
|
||||
}
|
||||
// Defer a frame so the 'v3:profile-updated' dashboard re-render has a
|
||||
// chance to begin before Shepherd starts polling for the first target.
|
||||
var raf = window.requestAnimationFrame || function (fn) { return setTimeout(fn, 16); };
|
||||
raf(function () { try { t.start(TOUR_ID); } catch (e) { /* degrade */ } });
|
||||
// Wait for the dashboard to finish (re)rendering #v3-home before
|
||||
// attaching Shepherd. Starting after a single animation frame can latch
|
||||
// onto the pre-render #v3-hero / [data-tour] nodes that the async render
|
||||
// then replaces, breaking the tour. Listen for the render-complete
|
||||
// event; fall back on a timeout in case the render already finished
|
||||
// (no event coming) or never fires.
|
||||
var started = false;
|
||||
var go = function () {
|
||||
if (started) return;
|
||||
started = true;
|
||||
try { document.removeEventListener('v3:dashboard-rendered', go); } catch (e) { /* */ }
|
||||
try { t.start(TOUR_ID); } catch (e) { /* degrade */ }
|
||||
};
|
||||
try { document.addEventListener('v3:dashboard-rendered', go, { once: true }); } catch (e) { /* */ }
|
||||
setTimeout(go, 1200);
|
||||
}
|
||||
|
||||
window.v3OnboardingTour = { startFirstRun: startFirstRun };
|
||||
// Onboarding paths that navigate straight to the player ("Play it now")
|
||||
// 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;
|
||||
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;
|
||||
if (!sm || typeof sm.on !== 'function') return;
|
||||
var onScreen = function (e) {
|
||||
if (!(e && e.detail && e.detail.id === 'v3-home')) return;
|
||||
try { if (typeof sm.off === 'function') sm.off('screen:changed', onScreen); } catch (_) { /* */ }
|
||||
startFirstRun();
|
||||
};
|
||||
sm.on('screen:changed', onScreen);
|
||||
}
|
||||
|
||||
window.v3OnboardingTour = { startFirstRun: startFirstRun, armPendingFirstRun: armPendingFirstRun };
|
||||
|
||||
// tour-engine.js assigns window.slopsmithTour at script-eval time, so if it
|
||||
// is loaded before us register() succeeds immediately; otherwise retry once
|
||||
|
||||
@@ -520,6 +520,12 @@
|
||||
if (!editing && !finishOpts.launchingSong &&
|
||||
window.v3OnboardingTour && typeof window.v3OnboardingTour.startFirstRun === 'function') {
|
||||
try { window.v3OnboardingTour.startFirstRun(); } catch (e) { /* never block onboarding */ }
|
||||
} else if (!editing && finishOpts.launchingSong &&
|
||||
window.v3OnboardingTour && typeof window.v3OnboardingTour.armPendingFirstRun === 'function') {
|
||||
// "Play it now" navigates straight to the player, so the home
|
||||
// tour can't run now — arm it to fire the first time the user
|
||||
// returns to home, instead of silently never showing.
|
||||
try { window.v3OnboardingTour.armPendingFirstRun(); } catch (e) { /* never block onboarding */ }
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user