mirror of
https://github.com/got-feedBack/feedBack.git
synced 2026-08-12 19:59:35 +00:00
* style(tour): align tour engine + Shepherd bubbles to the v3 fb-* palette The tour/help engine shipped its own indigo/blue dark palette (#181830 / #4080e0) that predates the v3 fee[dB]ack tokens, and the spotlight bubbles themselves used the vendored Shepherd LIGHT default (white card, black text) — both clashed with the navy/sky v3 UI behind them. - Recolor the "?" menu button, popover and first-visit toast to the fb-* tokens (card #1e293b, primary #0ea5e9, border #334155, text #f8fafc/#94a3b8, gold #e8c040 unchanged). - Add a dark .shepherd-* override block (loads after the vendored shepherd.css, which is left pristine for upgrades): dark bubble + arrow, fb-primary Next/Done button, slate secondary button, fb text scale, and bump the modal dim to 0.6 to match the onboarding overlay. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * feat(tour-engine): let client/core tours register into the consolidated menu The tour engine only listed server-discovered plugins (those with a tour.json, populated from /api/plugins) in the "?" menu, and always prompted unseen relevant tours via the toast + button pulse. Generalize register() so a core/client-owned tour can participate: - `name` registers the tour into the menu catalog (_tourPlugins) so it shows in the "?" menu even without a server plugin; never clobbers a real plugin entry. - `autoPrompt:false` opts the tour OUT of the unseen toast + pulse (for tours driven programmatically by their owner), while still listing + running on demand. _unseenRelevant honours it. Both options are additive and default to the prior behaviour. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * chore(v3): add stable tour anchors to home cards + instrument badge Give the first-run home tour stable spotlight targets: #v3-hero on the hero panel and data-tour="continue" on the three continue/pick/browse card variants (dashboard.js), and #v3-instrument-wrap on the topbar instrument selector (badges.js, mirroring the existing #v3-tuner-wrap). The other targets (audio routing, tuner, profile, sidebar nav) already had stable ids. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * feat(onboarding): first-run home tour (spotlight coach marks) After a genuine onboarding completion, dim the home page and spotlight one card at a time with an explanatory bubble + Next, reusing the shared tour engine (Shepherd). 7 stops: Hero/Start Playing → Continue/Pick → Instrument selector → Tuner → Audio Routing → Profile → Sidebar nav. Auto-runs once; replayable forever from the "?" tour menu as "Welcome tour". - New static/v3/onboarding-tour.js: registers the spotlight tour (screens: ['v3-home'], name "Welcome tour", autoPrompt:false) and exposes startFirstRun(), gated on the engine's seen/dismissed state so it never repeats; loaded after tour-engine.js + dashboard.js. - profile.js finish(): trigger startFirstRun() only on a real onboarding completion (!editing) — a later profile edit must not relaunch it. Verified headlessly (native core + Playwright): all 7 anchors resolve, the spotlight advances one bubble at a time in the v3 dark theme, completion marks seen, startFirstRun is once-only, and the "?" menu lists "Welcome tour". Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(capabilities): clear the handler timeout timer once the race settles Codex round-6: _withTimeout raced the handler promise against a bare setTimeout but never cleared it, so a handler that resolves first leaves the timer alive until it fires. Harmless at 250ms, but the new 15s MIDI permission-command overrides (discover/open-source) kept the event loop alive ~15s after every successful call (and the test process hung that long) and could accumulate delayed callbacks across repeated scans. Capture the timer and clearTimeout it in a .finally on the race. (Domain/capabilities tests now finish in ~0.1s, not 15s.) Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(midi-input): give the built-in Web-MIDI provider a distinct participant id Codex round-7: the built-in Web-MIDI provider registered with participantId 'core.midi-input' — the same id as the domain owner. unregisterProvider() unregisters the provider's participant, so a provider swap/hot-reload would tear down the domain OWNER too, leaving midi-input with no owner for later commands. Register the provider as 'core.midi-input.web-midi'. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(onboarding): don't start the home tour when launching the diagnostic Codex round-7: on the final onboarding step, "Play it now" calls finish() (which started the home tour) and THEN playSong(target). startFirstRun() navigated to v3-home and scheduled the tour, then playSong switched to the player — so the tour spotlighted hidden home elements / stole focus from the diagnostic. Gate the tour on a launchingSong flag (passed by the "Play it now" path); the Skip path stays on home, so the tour still runs there. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
116 lines
5.8 KiB
JavaScript
116 lines
5.8 KiB
JavaScript
/*
|
||
* fee[dB]ack v0.3.0 — first-run home tour.
|
||
*
|
||
* Registers a spotlight tour over the home-page cards with the shared tour
|
||
* engine (window.slopsmithTour / Shepherd) and auto-runs it once, the first
|
||
* time the user lands on the home page after completing onboarding. It stays
|
||
* replayable forever from the per-screen "?" tour menu (registered with
|
||
* screens: ['v3-home']).
|
||
*
|
||
* Anchors (all stable, on-screen while #v3-home is active):
|
||
* #v3-hero hero / Start Playing (dashboard.js)
|
||
* [data-tour="continue"] continue / pick a song (dashboard.js, 3 variants)
|
||
* #v3-instrument-wrap instrument selector badge (badges.js, topbar)
|
||
* #v3-tuner-wrap tuner badge (badges.js, topbar)
|
||
* #v3-audio-routing audio routing card (dashboard.js)
|
||
* [data-v3-open-profile] profile badge (profile.js, topbar)
|
||
* #v3-nav left sidebar navigation (index.html)
|
||
*/
|
||
(function () {
|
||
'use strict';
|
||
|
||
var TOUR_ID = 'home-onboarding';
|
||
|
||
// Each step dims the page and spotlights one element (shape: 'spotlight').
|
||
// waitFor blocks the step until its target exists, so the async dashboard
|
||
// re-render kicked off by 'v3:profile-updated' can't race the first step.
|
||
function buildSteps() {
|
||
return [
|
||
{
|
||
id: 'hero', shape: 'spotlight', position: 'bottom',
|
||
selector: '#v3-hero', waitFor: '#v3-hero',
|
||
title: 'Welcome to fee[dB]ack',
|
||
content: 'This is your home base. Hit Start Playing to drop straight into a song from your library.',
|
||
},
|
||
{
|
||
id: 'continue', shape: 'spotlight', position: 'left',
|
||
selector: '[data-tour="continue"]', waitFor: '[data-tour="continue"]',
|
||
title: 'Pick up where you left off',
|
||
content: 'Your last song resumes right here in one click. Before you’ve played anything, it’s a quick random pick to get you going.',
|
||
},
|
||
{
|
||
id: 'instrument', shape: 'spotlight', position: 'bottom',
|
||
selector: '#v3-instrument-wrap', waitFor: '#v3-instrument-wrap',
|
||
title: 'Choose your instrument',
|
||
content: 'Set your instrument, string count and tuning here. The highway, tuner and scoring all adapt to this selection.',
|
||
},
|
||
{
|
||
id: 'tuner', shape: 'spotlight', position: 'bottom',
|
||
selector: '#v3-tuner-wrap', waitFor: '#v3-tuner-wrap',
|
||
title: 'Tune up first',
|
||
content: 'Open the tuner and match each string until the meter centers — accurate tuning means accurate scoring.',
|
||
},
|
||
{
|
||
id: 'audio', shape: 'spotlight', position: 'top',
|
||
selector: '#v3-audio-routing', waitFor: '#v3-audio-routing',
|
||
title: 'Your signal path',
|
||
content: 'Input → amp / NAM / IR → output, at a glance. Set up and monitor your gear from this card.',
|
||
},
|
||
{
|
||
id: 'profile', shape: 'spotlight', position: 'bottom',
|
||
selector: '[data-v3-open-profile]', waitFor: '[data-v3-open-profile]',
|
||
title: 'Track your progress',
|
||
content: 'Your profile, avatar and rank live here. Watch your accuracy climb and level up as you play.',
|
||
},
|
||
{
|
||
id: 'nav', shape: 'spotlight', position: 'right',
|
||
selector: '#v3-nav', waitFor: '#v3-nav',
|
||
title: 'Find everything here',
|
||
content: 'Browse your full library, lessons and plugins anytime. You can replay this tour from the ? button in the corner.',
|
||
},
|
||
];
|
||
}
|
||
|
||
function register() {
|
||
var t = window.slopsmithTour;
|
||
if (!t || typeof t.register !== 'function') return false;
|
||
t.register(TOUR_ID, {
|
||
name: 'Welcome tour', // label in the "?" tour menu
|
||
screens: ['v3-home'], // relevant only on the home screen
|
||
autoPrompt: false, // we auto-run it from onboarding; no toast nag
|
||
buildSteps: buildSteps,
|
||
});
|
||
return true;
|
||
}
|
||
|
||
// Auto-run once after a genuine onboarding completion. profile.js gates the
|
||
// call on !editing (a profile edit must not relaunch it); we additionally
|
||
// honour the engine's own seen/dismissed state so it never repeats and a
|
||
// dismissal isn't nagged. Stays replayable from the "?" menu either way.
|
||
function startFirstRun() {
|
||
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 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.
|
||
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 */ } });
|
||
}
|
||
|
||
window.v3OnboardingTour = { startFirstRun: startFirstRun };
|
||
|
||
// tour-engine.js assigns window.slopsmithTour at script-eval time, so if it
|
||
// is loaded before us register() succeeds immediately; otherwise retry once
|
||
// the DOM (and the engine) are ready.
|
||
if (!register()) {
|
||
document.addEventListener('DOMContentLoaded', register, { once: true });
|
||
}
|
||
})();
|