Add the exercise reference library, animated exercise figures, and exercise categories

Exercise Library/ holds per-exercise reference docs (setup, cues,
mistakes, progressions) with SVG visuals and a Python-rendered motion
pipeline; Workouts/ExerciseFigure renders the bundled *.motion.json
rigs as animated stick figures on the exercise screen. Exercises gain
a warm-up/main-circuit category, timed exercises display hold time via
planSummary, and a completed exercise reopens to a check screen instead
of its timers.
This commit is contained in:
2026-07-06 01:15:52 -04:00
parent ddd5631ef2
commit 7274f155e9
79 changed files with 2521 additions and 11 deletions
@@ -33,6 +33,7 @@ struct ExerciseAddEditView: View {
@State private var sets: Int
@State private var weightReminderWeeks: Int
@State private var weightLastUpdated: Date?
@State private var category: ExerciseCategory
@AppStorage("weightUnit") private var weightUnit: WeightUnit = .lb
init(exercise: Exercise, split: Split) {
@@ -51,6 +52,7 @@ struct ExerciseAddEditView: View {
_sets = State(initialValue: exercise.sets)
_weightReminderWeeks = State(initialValue: exercise.weightReminderTimeIntervalWeeks)
_weightLastUpdated = State(initialValue: exercise.weightLastUpdated)
_category = State(initialValue: exercise.categoryEnum)
}
var body: some View {
@@ -98,6 +100,19 @@ struct ExerciseAddEditView: View {
}
}
Section(
header: Text("Category"),
footer: Text("The split screen groups its exercises by category, with the warm-up listed first.")
) {
Picker("", selection: $category) {
ForEach(ExerciseCategory.displayOrder, id: \.self) { cat in
Text(cat.displayName)
.tag(cat)
}
}
.pickerStyle(.segmented)
}
Section(
header: Text("Load Type"),
footer: Text("For bodyweight exercises choose None. For resistance or weight training select Weight. For exercises that are time oriented (like plank or meditation) select Time.")
@@ -204,6 +219,7 @@ struct ExerciseAddEditView: View {
doc.exercises[idx].durationSeconds = durationSecs
doc.exercises[idx].weightLastUpdated = updatedWeightDate
doc.exercises[idx].weightReminderWeeks = weightReminderWeeks
doc.exercises[idx].category = category.rawValue
}
doc.updatedAt = Date()
Task { await sync.save(split: doc) }