migrate scale builder to scored exercise
Created by: jedmund
Rebuilds Scale Builder on top of the scored-exercise architecture (ExerciseState + ScoringOverlay), matching the Interval exercises. It now records per-lesson attempts, restores prior state, and presents its UI inside a glass card.
On top of the migration, this PR parameterizes the exercise the same way the Interval exercises are configurable, fixes a KeyboardContainer overlay anchoring issue, and polishes the in-card feedback.
Changes
Migration
- Move Scale Builder onto
ExerciseState/ScoringOverlay/PromptResult, with per-prompt review on completion. - New glass card with step-arc overlay docked above the keyboard.
Keyboard overlays (sidebar + arcs)
-
KeyboardContainernow anchors docked overlays via an alignment guide so step arcs and degree labels sit above the keys without callers compensating with a negative offset. The playground's-overlayHeightshim is gone. - Playground configurator sidebar is constrained above the docked keyboard using a
DockedKeyboardHeightpreference key.
Parameterization
- Configurator gains a Scale Builder section (replacing the keyboard-tool controls that leaked in during migration): root multi-select (chromatic), mode multi-select (all 7 classical modes), total questions stepper, progressive-difficulty toggle, show-pattern-hint toggle.
- Progressive mode unlocks roots along the circle of fifths and modes in difficulty order (one new entry per correct answer).
- Exercise samples a
(root, mode)pair each question and prefers unseen pairs; card title readsBuild a scale from <Root> <Mode>. -
ScaleBuilderConfig.Configgains optionaltotalQuestions,progressiveRoots,progressionThreshold,showPatternHint,modes, andprogressiveModes— lesson JSON (e.g.2-ex4.json) still loads unchanged. - Converts unicode ♭/♯ to ASCII
b/#before callingPitch(ascii:), which silently partial-parses unicode and produced the wrong pitch (e.g.A♭4→A4).
UX polish
- Before the first note the card shows "To start, play Note below", crossfading to the pattern pills once the root is played.
- Unsolved pattern pills use
primary/10%(matching the progress dots) instead of near-black. - Wrong key presses trigger a short horizontal shake on the pattern hint.
- Root key is drawn with the accent (orange) treatment; the most recent correct press gets the highlight (blue) treatment so both read at a glance.
Test plan
-
Standalone: home → Scale Builder card, build the scale, retry, inspect step arc placement -
Lesson: Music Theory lesson 1 exercise 2-ex4 → context markdown renders, card uses interactiveBackground, attempt is recorded and restores on re-open -
Playground sidebar: Scale Builder section shows only exercise parameters (no keyboard size / octave / overlays / highlights) -
Fixed mode: all 12 roots + Major, 8 questions, sampling avoids immediate repeats and seen pairs -
Progressive mode: starts with 3 roots unlocked; each correct answer unlocks a new root and mode until pool is exhausted -
Pattern hint: "To start, play X below" shows first, fades to pills after root; wrong key shakes the pills -
Root vs. last-correct: root key is orange, last correct key is blue, both persist during the scale -
Accidentals: pick a root like B♭ or F♯ — keyboard highlights the correct key and the expected sequence starts on that pitch -
WH pattern: pick a config using the WH step token, confirm the sequence still validates -
Free play keyboard: enable Step Arcs + Degree Labels, confirm they sit above the keys