Skip to content

migrate scale builder to scored exercise

jedmund requested to merge jedmund/scale-builder-migration into main

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)

  • KeyboardContainer now 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 -overlayHeight shim is gone.
  • Playground configurator sidebar is constrained above the docked keyboard using a DockedKeyboardHeight preference 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 reads Build a scale from <Root> <Mode>.
  • ScaleBuilderConfig.Config gains optional totalQuestions, progressiveRoots, progressionThreshold, showPatternHint, modes, and progressiveModes — lesson JSON (e.g. 2-ex4.json) still loads unchanged.
  • Converts unicode ♭/♯ to ASCII b/# before calling Pitch(ascii:), which silently partial-parses unicode and produced the wrong pitch (e.g. A♭4A4).

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

Merge request reports

Loading