The run screen now hides the navigation bar in both orientations and draws
its own header — back chevron top-left, exercise name (plus library name
when renamed) in large type where the toolbar was, previewing the next
exercise during a between-exercise rest. Work and rest counters are pinned
to the exact vertical center of the timer half via equal flexible bands,
with the adjust pill moved to an overlay so complications can't shift them.
Counter digits are now state-driven and roll through numericText
transitions (work count-up, rest countdown, and the Done button count).
Reopening an in-progress exercise now resumes its timer from the durable
timestamps instead of restarting at zero: no sets done anchors the
stopwatch to startedAt; mid-rest lands back on the rest page with the
countdown still running off its true window; past the window the next
set's stopwatch counts from the rest's computed end. The resume page is
computed at init because the paged TabView must initialize on it — a
post-layout backward jump wedges the pager, which then ignores the next
animated programmatic advance.
Claude-Session: https://claude.ai/code/session_01H8VxUX4ckjU3vRF5M4L5FV
A new iOS widget extension shows the active exercise, its phase, and the
work/rest countdown on the lock screen and in the Dynamic Island, driven
by the run flow's live frames so locking the phone mid-set keeps the
timer. The activity is seeded on open, refreshed on every page settle,
dismissed when the flow is left, and cleared on next launch if stranded.
Unifies the build-info stamping across all targets via a YAML anchor.
Claude-Session: https://claude.ai/code/session_01HJDQQDA9QdP8zByg43H5v3
The propped-up iPhone now runs the real ExerciseProgressView for a live
watch workout instead of a read-only mirror, and the live-run channel is
symmetric — either device can drive the flow and the other follows.
Each page transition is classified human / auto / remote: only human
transitions (swipe, Start, One More, swipe-back reset) are broadcast and
recorded by the actor; auto-advances (rest / timed-work countdown) record
locally but aren't sent, since both devices reach them independently off
the shared wall-clock anchors; an applied remote frame jumps the page
without re-recording or re-broadcasting. That rule is also what stops an
echo loop.
- PhoneConnectivityBridge gains sendLiveProgress/sendLiveEnded (the
missing phone->watch direction); WatchConnectivityBridge receives
frames into an observable liveIncoming via a new didReceiveMessage
route. Both share one increasing per-run version sequence so the
stale-frame guard works across the two devices' counters.
- Both ExerciseProgressViews gain an incomingFrame input + applyIncoming
(syncing setCount for a remote One More); the iPhone one gains the
liveSnapshot/broadcast machinery the watch already had.
- New LiveRunCoverView wraps the real driver for the cover (resolves the
workout, persists via SyncEngine, wires the live channel + close);
ContentView presents it; LiveProgressMirrorView is removed.
Claude-Session: https://claude.ai/code/session_01SCv7zvGFcKy47KSTnTLxRe