Add generic watch-tracked Cardio exercise and starter split
A single "Cardio" library entry (running-in-place figure, duration-logged) plus a matching Cardio starter split tagged .cardio (HealthKit .mixedCardio), so the Apple Watch records the real aerobic workout while the phone logs the time — no per-machine cardio entries or schema changes. Claude-Session: https://claude.ai/code/session_012qw2itfzKyEJ1HpsFt8Ex4
This commit is contained in:
@@ -1,5 +1,7 @@
|
||||
**July 2026**
|
||||
|
||||
A new Cardio exercise and matching starter split let you log aerobic sessions, tracked on your Apple Watch as a cardio workout.
|
||||
|
||||
Starting a workout with a split's Start This Split button now brings it up on your Apple Watch too, matching how starting one from the main list already worked.
|
||||
|
||||
A new Auto-Advance option lets a split flow hands-free from one exercise to the next, resting automatically between them.
|
||||
|
||||
@@ -74,8 +74,14 @@ entries) a user logging by that station's name will find.
|
||||
regression in their Progression sections, not separate picker entries.
|
||||
- **Dumbbell RDL, dumbbell lunge** — folded into Romanian Deadlift and Lunge
|
||||
("barbell or dumbbells" in their pages), not separate entries.
|
||||
- **Kettlebells, T-bar row, pullover machine, cardio equipment** — out; not
|
||||
strength stations found in most gyms (cardio is out of the app's scope).
|
||||
- **Kettlebells, T-bar row, pullover machine** — out; not strength stations
|
||||
found in most gyms.
|
||||
- **Specific cardio machines (treadmill, bike, elliptical, rower, …)** — out as
|
||||
individual picker entries. Cardio is covered instead by one generic **Cardio**
|
||||
entry: a single duration-logged block whose split is tagged `.cardio` (→
|
||||
HealthKit `.mixedCardio`), so the Apple Watch records the real workout (heart
|
||||
rate, calories, duration) and the phone just logs the time. It reuses a
|
||||
running-in-place figure rather than a per-machine rig.
|
||||
- **Morning wake-up mobility & stretching set** — landed. Dynamic warm-up
|
||||
drills (March in Place, Leg Swings, Neck Rolls, Arm Circles, Torso Twist,
|
||||
Standing Side Bend, Hip Circles) now fill the mobility row alongside Cat-Cow,
|
||||
|
||||
@@ -0,0 +1,41 @@
|
||||
# Cardio
|
||||
|
||||
A steady aerobic session — run in place, ride, row, or use the machine of your
|
||||
choice and just keep moving. It's logged as a single timed block; your Apple
|
||||
Watch records the real effort — heart rate, calories, and duration — as a
|
||||
cardio workout.
|
||||
|
||||
- **Category:** Cardio
|
||||
- **Type:** Aerobic / conditioning
|
||||
- **Targets:** Heart, lungs, endurance
|
||||
- **Prescription:** 15–30 minutes at a conversational-to-brisk pace
|
||||
- **Defaults:** 1 × 1200 s
|
||||
|
||||
## Setup
|
||||
|
||||
Pick your mode — treadmill, bike, rower, or simply running in place — and let
|
||||
your watch start the session so the effort is tracked on your wrist.
|
||||
|
||||
## Execution
|
||||
|
||||
1. Ease in for the first minute or two, letting your heart rate climb.
|
||||
2. Settle into a steady pace you can hold for the whole block.
|
||||
3. Keep your breathing rhythmic and your shoulders relaxed.
|
||||
4. Cool down with an easy minute before you stop.
|
||||
|
||||
## Cues
|
||||
|
||||
- Aim for a pace where you could speak in short sentences, not full paragraphs.
|
||||
- Stay tall and loose — don't grip the rails or hunch over the machine.
|
||||
- Let the watch handle the numbers; just hold your effort steady.
|
||||
|
||||
## Common Mistakes
|
||||
|
||||
- Starting too hard and fading long before the time is up.
|
||||
- Leaning on the handrails, which quietly drops the real workload.
|
||||
- Skipping the warm-up and cool-down minutes at either end.
|
||||
|
||||
## Progression
|
||||
|
||||
Add minutes, lift the pace, or fold in short faster intervals to raise the
|
||||
challenge over time.
|
||||
@@ -0,0 +1,52 @@
|
||||
{
|
||||
"name": "Cardio",
|
||||
"primary": 1,
|
||||
"camera": {"zoom": 0.7},
|
||||
"working": ["leg_r", "leg_l"],
|
||||
"frames": [
|
||||
{
|
||||
"hold": 0.1, "tween": 0.3,
|
||||
"root": {"pos": [160, 62], "pitch": 4},
|
||||
"spine": [0, 0],
|
||||
"neck": 2, "head": -6,
|
||||
"shoulder_r": -28, "elbow_r": 82,
|
||||
"shoulder_l": 34, "elbow_l": 78,
|
||||
"hip_r": 68, "knee_r": 88, "ankle_r": -8,
|
||||
"hip_l": 2, "knee_l": 4, "ankle_l": 0,
|
||||
"pins": {"foot_l": [162, 150]}
|
||||
},
|
||||
{
|
||||
"hold": 0.08, "tween": 0.26,
|
||||
"root": {"pos": [160, 64], "pitch": 4},
|
||||
"spine": [0, 0],
|
||||
"neck": 2, "head": -6,
|
||||
"shoulder_r": 3, "elbow_r": 64,
|
||||
"shoulder_l": 3, "elbow_l": 64,
|
||||
"hip_r": 4, "knee_r": 4, "ankle_r": 0,
|
||||
"hip_l": 4, "knee_l": 4, "ankle_l": 0,
|
||||
"pins": {"foot_r": [158, 148], "foot_l": [162, 150]}
|
||||
},
|
||||
{
|
||||
"hold": 0.1, "tween": 0.3,
|
||||
"root": {"pos": [160, 62], "pitch": 4},
|
||||
"spine": [0, 0],
|
||||
"neck": 2, "head": -6,
|
||||
"shoulder_r": 34, "elbow_r": 78,
|
||||
"shoulder_l": -28, "elbow_l": 82,
|
||||
"hip_r": 2, "knee_r": 4, "ankle_r": 0,
|
||||
"hip_l": 68, "knee_l": 88, "ankle_l": -8,
|
||||
"pins": {"foot_r": [158, 148]}
|
||||
},
|
||||
{
|
||||
"hold": 0.08, "tween": 0.26,
|
||||
"root": {"pos": [160, 64], "pitch": 4},
|
||||
"spine": [0, 0],
|
||||
"neck": 2, "head": -6,
|
||||
"shoulder_r": 3, "elbow_r": 64,
|
||||
"shoulder_l": 3, "elbow_l": 64,
|
||||
"hip_r": 4, "knee_r": 4, "ankle_r": 0,
|
||||
"hip_l": 4, "knee_l": 4, "ankle_l": 0,
|
||||
"pins": {"foot_r": [158, 148], "foot_l": [162, 150]}
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -15,7 +15,7 @@ your own iCloud Drive.
|
||||
**Bodyweight Core** circuit with its own warm-up) appear automatically on first
|
||||
launch; editing one makes it your own copy, and deleted starters can be restored
|
||||
from Settings.
|
||||
- **Exercise library** — a bundled library of 47 exercises to populate your
|
||||
- **Exercise library** — a bundled library of 65 exercises to populate your
|
||||
splits, authored in `Exercise Library/` with per-exercise setup, cues,
|
||||
mistakes, progressions, and an anatomically-rigged 3D stick-figure visual
|
||||
system — covering every common movement pattern and gym station (see the
|
||||
@@ -23,6 +23,10 @@ your own iCloud Drive.
|
||||
free-weight canon (deadlifts, squats, benches, rows, curls, raises), the
|
||||
cable staples, and the bodyweight staples (push-ups, pull-ups, dips,
|
||||
lunges, and the floor core set).
|
||||
- **Cardio** — a generic **Cardio** exercise and matching starter split for
|
||||
aerobic work. Tag a split's activity type Cardio and your Apple Watch records
|
||||
the real effort (heart rate, calories, duration) as a `.mixedCardio` workout,
|
||||
while the phone logs a simple timed block — no per-machine tracking needed.
|
||||
- **Run a workout** — start a session from a split, then tap an exercise to run it
|
||||
as a paged flow: a **Ready?** lead-in, count-up work phases, count-down rests, and
|
||||
a **Finish** page — mirroring the Apple Watch. Swipe a row to mark it complete, or
|
||||
|
||||
@@ -177,6 +177,12 @@ let seeds: [Sp] = [
|
||||
Ex(name: "Cobra Stretch", sets: 1, reps: 8, load: 0),
|
||||
Ex(name: "Child's Pose", sets: 1, reps: 0, load: 2, dur: 45),
|
||||
]),
|
||||
// A single generic, watch-tracked cardio block: one duration exercise in a
|
||||
// `.cardio` (→ HealthKit `.mixedCardio`) split. The phone logs the time; the
|
||||
// watch records the real workout (heart rate, calories, duration).
|
||||
Sp(name: "Cardio", color: "red", icon: "figure.run", activity: 4, ex: [
|
||||
Ex(name: "Cardio", sets: 1, reps: 0, load: 2, dur: 1200),
|
||||
]),
|
||||
]
|
||||
|
||||
// MARK: - Emit
|
||||
|
||||
@@ -0,0 +1,41 @@
|
||||
# Cardio
|
||||
|
||||
A steady aerobic session — run in place, ride, row, or use the machine of your
|
||||
choice and just keep moving. It's logged as a single timed block; your Apple
|
||||
Watch records the real effort — heart rate, calories, and duration — as a
|
||||
cardio workout.
|
||||
|
||||
- **Category:** Cardio
|
||||
- **Type:** Aerobic / conditioning
|
||||
- **Targets:** Heart, lungs, endurance
|
||||
- **Prescription:** 15–30 minutes at a conversational-to-brisk pace
|
||||
- **Defaults:** 1 × 1200 s
|
||||
|
||||
## Setup
|
||||
|
||||
Pick your mode — treadmill, bike, rower, or simply running in place — and let
|
||||
your watch start the session so the effort is tracked on your wrist.
|
||||
|
||||
## Execution
|
||||
|
||||
1. Ease in for the first minute or two, letting your heart rate climb.
|
||||
2. Settle into a steady pace you can hold for the whole block.
|
||||
3. Keep your breathing rhythmic and your shoulders relaxed.
|
||||
4. Cool down with an easy minute before you stop.
|
||||
|
||||
## Cues
|
||||
|
||||
- Aim for a pace where you could speak in short sentences, not full paragraphs.
|
||||
- Stay tall and loose — don't grip the rails or hunch over the machine.
|
||||
- Let the watch handle the numbers; just hold your effort steady.
|
||||
|
||||
## Common Mistakes
|
||||
|
||||
- Starting too hard and fading long before the time is up.
|
||||
- Leaning on the handrails, which quietly drops the real workload.
|
||||
- Skipping the warm-up and cool-down minutes at either end.
|
||||
|
||||
## Progression
|
||||
|
||||
Add minutes, lift the pace, or fold in short faster intervals to raise the
|
||||
challenge over time.
|
||||
@@ -0,0 +1,52 @@
|
||||
{
|
||||
"name": "Cardio",
|
||||
"primary": 1,
|
||||
"camera": {"zoom": 0.7},
|
||||
"working": ["leg_r", "leg_l"],
|
||||
"frames": [
|
||||
{
|
||||
"hold": 0.1, "tween": 0.3,
|
||||
"root": {"pos": [160, 62], "pitch": 4},
|
||||
"spine": [0, 0],
|
||||
"neck": 2, "head": -6,
|
||||
"shoulder_r": -28, "elbow_r": 82,
|
||||
"shoulder_l": 34, "elbow_l": 78,
|
||||
"hip_r": 68, "knee_r": 88, "ankle_r": -8,
|
||||
"hip_l": 2, "knee_l": 4, "ankle_l": 0,
|
||||
"pins": {"foot_l": [162, 150]}
|
||||
},
|
||||
{
|
||||
"hold": 0.08, "tween": 0.26,
|
||||
"root": {"pos": [160, 64], "pitch": 4},
|
||||
"spine": [0, 0],
|
||||
"neck": 2, "head": -6,
|
||||
"shoulder_r": 3, "elbow_r": 64,
|
||||
"shoulder_l": 3, "elbow_l": 64,
|
||||
"hip_r": 4, "knee_r": 4, "ankle_r": 0,
|
||||
"hip_l": 4, "knee_l": 4, "ankle_l": 0,
|
||||
"pins": {"foot_r": [158, 148], "foot_l": [162, 150]}
|
||||
},
|
||||
{
|
||||
"hold": 0.1, "tween": 0.3,
|
||||
"root": {"pos": [160, 62], "pitch": 4},
|
||||
"spine": [0, 0],
|
||||
"neck": 2, "head": -6,
|
||||
"shoulder_r": 34, "elbow_r": 78,
|
||||
"shoulder_l": -28, "elbow_l": 82,
|
||||
"hip_r": 2, "knee_r": 4, "ankle_r": 0,
|
||||
"hip_l": 68, "knee_l": 88, "ankle_l": -8,
|
||||
"pins": {"foot_r": [158, 148]}
|
||||
},
|
||||
{
|
||||
"hold": 0.08, "tween": 0.26,
|
||||
"root": {"pos": [160, 64], "pitch": 4},
|
||||
"spine": [0, 0],
|
||||
"neck": 2, "head": -6,
|
||||
"shoulder_r": 3, "elbow_r": 64,
|
||||
"shoulder_l": 3, "elbow_l": 64,
|
||||
"hip_r": 4, "knee_r": 4, "ankle_r": 0,
|
||||
"hip_l": 4, "knee_l": 4, "ankle_l": 0,
|
||||
"pins": {"foot_r": [158, 148], "foot_l": [162, 150]}
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
{
|
||||
"activityType" : 4,
|
||||
"color" : "red",
|
||||
"createdAt" : "2020-01-01T00:00:00Z",
|
||||
"exercises" : [
|
||||
{
|
||||
"durationSeconds" : 1200,
|
||||
"id" : "01DXF6DT0032GKBRB638SQX8M3",
|
||||
"loadType" : 2,
|
||||
"name" : "Cardio",
|
||||
"order" : 0,
|
||||
"reps" : 0,
|
||||
"sets" : 1,
|
||||
"weight" : 0
|
||||
}
|
||||
],
|
||||
"id" : "01DXF6DT00BXJ6N63ZBX9M9VNH",
|
||||
"name" : "Cardio",
|
||||
"order" : 12,
|
||||
"schemaVersion" : 3,
|
||||
"systemImage" : "figure.run",
|
||||
"updatedAt" : "2020-01-01T00:00:00Z"
|
||||
}
|
||||
@@ -11,16 +11,16 @@ import IndieSync
|
||||
/// both the raw document and a SwiftData cache round-trip.
|
||||
struct SeedLibraryTests {
|
||||
|
||||
@Test func catalogLoadsTwelveSeedsInOrder() {
|
||||
@Test func catalogLoadsThirteenSeedsInOrder() {
|
||||
let seeds = SeedLibrary.seeds
|
||||
#expect(seeds.count == 12)
|
||||
#expect(seeds.map(\.doc.order) == Array(0...11))
|
||||
#expect(seeds.count == 13)
|
||||
#expect(seeds.map(\.doc.order) == Array(0...12))
|
||||
#expect(seeds.map(\.doc.name) == [
|
||||
"Upper Body", "Core", "Lower Body", "Bodyweight Core",
|
||||
"Full Body Machines", "Free Weight Basics",
|
||||
"Upper Body Warm-Up", "Lower Body Warm-Up",
|
||||
"Morning Wake-Up", "Morning Mobility",
|
||||
"Full Body Stretch", "Evening Stretch",
|
||||
"Full Body Stretch", "Evening Stretch", "Cardio",
|
||||
])
|
||||
}
|
||||
|
||||
@@ -44,6 +44,7 @@ struct SeedLibraryTests {
|
||||
"01DXF6DT00QHJ9G4SDMEX64NZD", // Morning Mobility
|
||||
"01DXF6DT003PRJB3FGYSSGTQ5X", // Full Body Stretch
|
||||
"01DXF6DT00M5YRFPHJQ2FS2B9P", // Evening Stretch
|
||||
"01DXF6DT00BXJ6N63ZBX9M9VNH", // Cardio
|
||||
])
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user