Add a debug figure-review pager under Settings > Developer

Swipe through every bundled exercise rig, toggling between the looping
figure and its authored reference page, to QA the library on device.

Claude-Session: https://claude.ai/code/session_01PKptrgbx74peTwHGRxBojv
This commit is contained in:
2026-07-12 00:37:05 -04:00
parent 530021c2de
commit 400601283e
3 changed files with 87 additions and 2 deletions
@@ -261,8 +261,9 @@ private struct MachineSettingsCard: View {
}
/// Renders a parsed `ExerciseInfo`: summary, target chips, then each authored
/// section with its numbered steps or bullets.
private struct ExerciseInfoContent: View {
/// section with its numbered steps or bullets. Internal so the debug figure-review
/// screen can reuse it.
struct ExerciseInfoContent: View {
let info: ExerciseInfo
var body: some View {
@@ -0,0 +1,79 @@
//
// ExerciseFigureReviewView.swift
// Workouts
//
// Copyright 2026 Rouslan Zenetl. All Rights Reserved.
//
#if DEBUG
import SwiftUI
/// Debug-only QA screen (Settings Developer): swipe through every bundled
/// exercise to eyeball its stick-figure rig for glitches, flipping between the
/// looping figure and the authored reference copy with a segmented picker. The
/// picker state is shared across pages so a figure-only sweep stays in figure mode.
struct ExerciseFigureReviewView: View {
private enum Pane: String, CaseIterable, Identifiable {
case figure = "Figure"
case info = "Info"
var id: String { rawValue }
}
private let names = ExerciseMotionLibrary.exerciseNames
@State private var index = 0
@State private var pane: Pane = .figure
var body: some View {
VStack(spacing: 0) {
Picker("Pane", selection: $pane) {
ForEach(Pane.allCases) { pane in
Text(pane.rawValue).tag(pane)
}
}
.pickerStyle(.segmented)
.padding(.horizontal)
.padding(.bottom, 8)
TabView(selection: $index) {
ForEach(Array(names.enumerated()), id: \.offset) { i, name in
page(for: name)
.tag(i)
}
}
.tabViewStyle(.page(indexDisplayMode: .never))
}
.navigationTitle(names.indices.contains(index) ? names[index] : "Exercises")
.navigationBarTitleDisplayMode(.inline)
.toolbar {
ToolbarItem(placement: .primaryAction) {
Text("\(index + 1) / \(names.count)")
.font(.subheadline)
.monospacedDigit()
.foregroundStyle(.secondary)
}
}
}
@ViewBuilder
private func page(for name: String) -> some View {
switch pane {
case .figure:
ExerciseFigureSlot(exerciseName: name)
case .info:
ScrollView {
Group {
if let info = ExerciseInfoLibrary.info(for: name) {
ExerciseInfoContent(info: info)
} else {
Text("No reference copy for this exercise.")
.foregroundStyle(.secondary)
}
}
.padding()
.frame(maxWidth: .infinity, alignment: .leading)
}
}
}
}
#endif
@@ -181,6 +181,11 @@ struct SettingsView: View {
} label: {
Label("Clean Up Duplicates", systemImage: "wrench.and.screwdriver")
}
NavigationLink {
ExerciseFigureReviewView()
} label: {
Label("Review Exercise Figures", systemImage: "figure.strengthtraining.traditional")
}
} header: {
Text("Developer")
}