Skip to content

keyboard overlay fixes: step arcs, degree labels, and the 1-of-0 desync

jedmund requested to merge jedmund/montevideo-v7 into main

Created by: jedmund

Summary

Three bug fixes that build on the recent Note Identifier and keyboard work.

  • d97a2c8 note identifier: fix 1-of-0 desync on first exercise load. The exercise initializer wrote the prompt pool into an @State var and read promptPool.count back on the very next line. On the first render that read could return the stale empty value, so state.reset(totalPrompts: 0) was called; the first advance() then flipped isComplete immediately (any promptIndex >= 0 passed the check) and the completion card showed "1 of 0". Retry worked because by then the @State had materialized. Fix: compute the pool into a local first, write the @State off it, and pass the local's count to state.reset. Same shape applied in resetExercise. Belt-and-braces guard in ExerciseState.advance() so isComplete can't flip when totalPrompts <= 0.
  • 567d4f8 keyboard overlay: fix arc/label rendering and ForEach tuple-keypath crash. Step arcs and degree labels were drawing on top of the keys instead of above them. The overlayStack relied on .alignmentGuide(.top) { $0[.bottom] + offset } to push overlays outside the keyboard's top edge; that trick didn't take effect reliably and the content landed at the ZStack's natural top (inside the key area). Replaced with an explicit .offset(y: -(height + bottomOffset)). Separately, overlayStack and StepArcOverlay iterated tuple-typed arrays ([(item: …, bottomOffset: …)], [(arc: …, tier: …)]) with keypath ids. SwiftUI's runtime generic-witness lookup for ForEach over tuple-keypath-id is fragile and crashed with EXC_BAD_ACCESS in swift_getAssociatedTypeWitness the moment the overlay was toggled on. Introduced PositionedOverlay and TieredArc as proper Identifiable structs; ForEach now uses the native Identifiable overload.
  • c4ce5de playground degree labels: fix stack-overflow from self-referential keyboardConfig. Toggling 'Degree labels' in the Playground sidebar crashed with EXC_BAD_ACCESS: Thread stack size exceeded due to excessive recursion. When the toggle was true, keyboardConfig built an overlay item whose content was defaultDegreeLabels, and defaultDegreeLabels read keyboardConfig.pitchRange to bound its iteration — self-reference. Extracted pitchRange as its own computed property derived directly from params; both keyboardConfig and defaultDegreeLabels read from it now. Bonus: DegreeLabel is now Identifiable and DegreeLabelOverlay's ForEach drops its keypath-id, matching the step-arc fix.

Test plan

  • Open lesson 1-1 "Same Key, Two Names" fresh (no prior progress). Exercise starts at prompt 1, all 10 prompts present, scoring card reads "X of 10".
  • Lesson 1-2 "Half Steps", "Three Kinds of Half Step", "Whole Steps": arcs render above the keys, not overlapping them.
  • Lesson 1-3 "Perfect Intervals": P4 / P5 / P8 arcs tiered above the keyboard (taller stack, most likely to surface clipping).
  • Scale Builder: completed arcs stack above the keys as each step is answered.
  • Playground Keyboard tool: toggle "Step arcs" on — no crash, arcs render above keys. Toggle off/on repeatedly.
  • Playground Keyboard tool: toggle "Degree labels" on — no crash, circled degree badges above their keys. Toggle off/on repeatedly.
  • Playground with both toggles on: arcs + labels stacked above the keys without overlap.
  • Tap an arc — still plays the interval and shows the expanded name. Tap a degree badge — still shows the full degree name.

Merge request reports

Loading