diff --git a/Workouts/Views/Exercises/ExerciseLibraryView.swift b/Workouts/Views/Exercises/ExerciseLibraryView.swift index 449e1b4..ef8a775 100644 --- a/Workouts/Views/Exercises/ExerciseLibraryView.swift +++ b/Workouts/Views/Exercises/ExerciseLibraryView.swift @@ -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 { diff --git a/Workouts/Views/Settings/ExerciseFigureReviewView.swift b/Workouts/Views/Settings/ExerciseFigureReviewView.swift new file mode 100644 index 0000000..a2b0e4a --- /dev/null +++ b/Workouts/Views/Settings/ExerciseFigureReviewView.swift @@ -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 diff --git a/Workouts/Views/Settings/SettingsView.swift b/Workouts/Views/Settings/SettingsView.swift index 2a6df5f..d6cd2fa 100644 --- a/Workouts/Views/Settings/SettingsView.swift +++ b/Workouts/Views/Settings/SettingsView.swift @@ -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") }