← All changes
What shipped
Watch
- Five distinct layout states —
heartRate,durationCountdown,restCountdown,idle,joining— each composed against an explicit per-size profile (41 / 45 / 49mm) keyed off screen height. Fixes the text-overlap bug on the smaller cases. - The Stop button only renders during a duration countdown, and now
sends
.stopDurationTimer(protocol v4 → v5) instead of ending the workout. The wrist can no longer end the session at all — that authority lives on the phone, restoring invariant #3. - A
joiningstate appears whenever the phone says “running” but no state has arrived yet, with a 3-second fall-through to “iPhone session not found”.
Share
- Dark-theme image cards rendered at 1080×1920 (9:16) and 1080×1080
(1:1), with strength (hero volume) and rehab (pain readout, never hero)
variants. Avg HR omits cleanly when
nil— no ”—” placeholders. - A modal with format chips (image 9:16, image 1:1, markdown) and a coral
Share action. Entry from
SessionSummaryViewand from the History tab, so past sessions are covered too. - Markdown export shares a single rollup with the image card so the two always agree on volume, time-under-tension, and HR.
- New
attachPhotoToStravasetting (off by default, gated onuploadSource == .direct) auto-uploads the 9:16 card as a photo on the Strava activity right after the activity is created. Failures are non-fatal.
Why
The watch’s job hasn’t changed — HR + elapsed + a single allowed action — but the layout had grown crowded enough on the 41mm case to overlap. Rebuilding it as discrete states with size profiles means each surface gets exactly the room it needs.
Sharing a session image is the first surface that lets you hand a friend or a Strava feed something you can read at a glance, without a log-in or an export. Off by default, like everything social.