Watch redesign + share your session

The watch becomes a five-state, layout-budgeted runner that can no longer end the session — and a new share sheet exports 9:16 / 1:1 image cards or markdown, with optional auto-attach to Strava.

← 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 joining state 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 SessionSummaryView and 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 attachPhotoToStrava setting (off by default, gated on uploadSource == .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.