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:
@@ -261,8 +261,9 @@ private struct MachineSettingsCard: View {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Renders a parsed `ExerciseInfo`: summary, target chips, then each authored
|
/// Renders a parsed `ExerciseInfo`: summary, target chips, then each authored
|
||||||
/// section with its numbered steps or bullets.
|
/// section with its numbered steps or bullets. Internal so the debug figure-review
|
||||||
private struct ExerciseInfoContent: View {
|
/// screen can reuse it.
|
||||||
|
struct ExerciseInfoContent: View {
|
||||||
let info: ExerciseInfo
|
let info: ExerciseInfo
|
||||||
|
|
||||||
var body: some View {
|
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: {
|
||||||
Label("Clean Up Duplicates", systemImage: "wrench.and.screwdriver")
|
Label("Clean Up Duplicates", systemImage: "wrench.and.screwdriver")
|
||||||
}
|
}
|
||||||
|
NavigationLink {
|
||||||
|
ExerciseFigureReviewView()
|
||||||
|
} label: {
|
||||||
|
Label("Review Exercise Figures", systemImage: "figure.strengthtraining.traditional")
|
||||||
|
}
|
||||||
} header: {
|
} header: {
|
||||||
Text("Developer")
|
Text("Developer")
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user