Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 20 additions & 0 deletions docs/ios-implementation-plan.md
Original file line number Diff line number Diff line change
Expand Up @@ -145,6 +145,26 @@ recipes/pantry/food-log via the Forge MCP tools; this phase adds the app surface
the read-only meals/macros strip on the Train week view. Light, photography-led, New
York serif — deliberately not a Train reskin.

**N4.1 — the phone surface `[done]`**, built ahead of N2/N3 because its boards were already
decided (`cook-browse.png`, `cook-mode.png`) and the server API needed nothing new:

- `ForgeTheme.cookLight` (Paper × Ember) + `ForgeFont.serif*` (New York) — the module's own
voice, scoped so volt and DIN never reach a Cook screen. `RootView` tints the shell from
the selected tab, so the bar changes accent with the module.
- Browse root, recipe detail, cook mode (per-step timers, keep-awake, background-step strip,
plate-and-log), full library with search/kind filter, week planner with per-slot tick and
a plan sheet that works from either end (pick a day / pick a recipe).
- `CookCue` — derives the done-when clause from step prose for cook mode's one accent. It
works because the import rule already mandates done-when voice.
- `ForgeAsyncImage` — authenticated image loading (recipe media is behind the session
cookie), cookie scoped to our own origin. `PlateImage` gives bundled fallback photography
since the seeded library ships no images.

**N4.2 — still open**: iPad `NavigationSplitView`; cook timers as Live Activities (needs the
widget extension target, [C1b] in `ios/XCODE-HANDOVER.md`); an authored `cue_done` per step so
the next-step CTA can read like the board ("Skin's released — next") instead of the step title;
real per-recipe imagery.

### N5 — Shop (design-doc phase D): shopping list

Server first, app second — the list is useful from the PWA and the coach on day one,
Expand Down
4 changes: 2 additions & 2 deletions docs/mockups/36-progress-options.html
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@ <h2>Progress</h2>
</div>

<div class="opt">
<div class="lab"><b>Option 2 — Sectioned story</b> · Strength / Engine / Body sections,<br>one chart each + stat chips, weekly ring</div>
<div class="lab"><b>Option 2 — Sectioned story</b> · Strength / Cardio / Body sections,<br>one chart each + stat chips, weekly ring</div>
<div class="phone num">
<div class="kick">Trends · 12 weeks</div>
<h2>Progress</h2>
Expand Down Expand Up @@ -105,7 +105,7 @@ <h2>Progress</h2>
</div>
</div>

<div class="sect">Engine</div>
<div class="sect">Cardio</div>
<div class="card">
<div class="row"><b>VO₂max</b><span class="sub">raw + smoothed</span></div>
<svg id="s2" viewBox="0 0 300 70"></svg>
Expand Down
2 changes: 1 addition & 1 deletion docs/mockups/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ in CI/web sessions).
| `history.png` | History with the All / ★ Starred filter and a row swiped open to delete |
| `logging.png` | Active-set logging: plate math, stepper, RPE, draining rest ring |
| `learn.png` | Exercise form guide: media, cues, muscle map |
| `progress.png` | Sectioned Progress: this-week ring, strength e1RM, engine, body |
| `progress.png` | Sectioned Progress: this-week ring, strength e1RM, cardio, body |
| `coach.png` | Coach chat thread with the pinned week-proposal bar |
| `run-detail.png` | Rich run detail (E5.4): route on a token-styled street basemap, HR vs prescribed band, five-zone breakdown |
| `appearance.png` | Settings → Appearance: Dark / Light / Auto and the five accent palettes |
Expand Down
4 changes: 2 additions & 2 deletions docs/mockups/generate.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -326,7 +326,7 @@ add('learn', { tab: 'today', screenH: 760 }, shell('today', `
<div class="mrow"><span>Glutes</span><span style="color:var(--volt)">●●○</span></div>
<div class="mrow"><span>Lower back</span><span class="num" style="color:var(--dim)">●○○</span></div></div>`));

// 8) PROGRESS — sectioned trends (this week ring, strength e1RM, engine, body)
// 8) PROGRESS — sectioned trends (this week ring, strength e1RM, cardio, body)
add('progress', { tab: 'progress', screenH: 806 }, shell('progress', `
<div><span class="kick">Trends</span><h2 class="title">Progress</h2></div>
<div class="card" style="display:flex;gap:12px;align-items:center">
Expand All @@ -344,7 +344,7 @@ add('progress', { tab: 'progress', screenH: 806 }, shell('progress', `
<span class="disp num" style="font-size:20px;color:var(--volt)">99.5<small style="font-size:11.5px;color:var(--mut);font-weight:400"> kg</small></span></div>
${line([0.15,0.28,0.24,0.4,0.52,0.49,0.66,0.72,0.85,1])}</div>
<button class="lrow press num"><b>Records</b><span class="rsub">all-time bests per lift</span><span class="chev">›</span></button>
<div class="sect">Engine</div>
<div class="sect">Cardio</div>
<div class="card"><div class="row"><span style="font-size:13.5px;font-weight:600">VO₂max · raw + trend</span>
<span style="font-size:11.5px;color:var(--mut)">ml/kg/min</span></div>
${line([0.4,0.55,0.48,0.6,0.58,0.7,0.66,0.78,0.82,0.88])}
Expand Down
2 changes: 1 addition & 1 deletion docs/mockups/native/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ Needs a Chromium/Chrome binary (`CHROME_BIN` honoured). Render on macOS so

| Board | Shows |
|---|---|
| `train-today` | Train tab: Void × Volt native — session hero, Cook's meals + macros as a shared-store strip, engine tiles |
| `train-today` | Train tab: Void × Volt native — session hero, Cook's meals + macros as a shared-store strip, cardio tiles |
| `train-logging` | Active set: big numerals, plate math, RPE, session-so-far tiles; rest timer in the Dynamic Island |
| `watch` | ForgeWatch: set logging with live HR, rest ring |
| `cook-browse` | Cook tab: paper-light, serif, photography-led; tonight hero, week strip, library. Accent: ember |
Expand Down
2 changes: 1 addition & 1 deletion docs/mockups/native/build-reviews.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -170,7 +170,7 @@ writeFileSync(resolve(HERE, 'review-redesign.html'), page('Forge Train redesign
<p class="intro">You were right — Cook felt richer because it leads with imagery, and the
workout side didn't. Here are three directions for Train, each shown in <b>dark and
light mode side by side</b> (the whole app will respect both). The screen content is
identical in all three — today's session, meals from Cook, the engine numbers, the week —
identical in all three — today's session, meals from Cook, the cardio numbers, the week —
only the personality changes. Photos are placeholders for real training imagery.</p>
${R2.map(([t, img, note], i) => `
<section><h2><span class="n">${String.fromCharCode(65 + i)}</span>${t}</h2>
Expand Down
4 changes: 2 additions & 2 deletions docs/mockups/native/redesign-a-progress.html
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@
</span></div>
<div class="screen">
<div class="nav"><h1>Progress</h1></div>
<div class="segs"><span class="seg on">Strength</span><span class="seg">Engine</span><span class="seg">Body</span></div>
<div class="segs"><span class="seg on">Strength</span><span class="seg">Cardio</span><span class="seg">Body</span></div>

<div class="week">
<div class="ring"><div class="in"><span class="v">3/4</span><span class="u">SESSIONS</span></div></div>
Expand Down Expand Up @@ -136,7 +136,7 @@
</span></div>
<div class="screen">
<div class="nav"><h1>Progress</h1></div>
<div class="segs"><span class="seg on">Strength</span><span class="seg">Engine</span><span class="seg">Body</span></div>
<div class="segs"><span class="seg on">Strength</span><span class="seg">Cardio</span><span class="seg">Body</span></div>

<div class="week">
<div class="ring"><div class="in"><span class="v">3/4</span><span class="u">SESSIONS</span></div></div>
Expand Down
Binary file modified docs/mockups/native/redesign-a-progress.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
4 changes: 2 additions & 2 deletions docs/mockups/native/redesign-b-progress.html
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@
</span></div>
<div class="screen">
<div class="nav"><h1>Progress</h1></div>
<div class="segs"><span class="seg on">Strength</span><span class="seg">Engine</span><span class="seg">Body</span></div>
<div class="segs"><span class="seg on">Strength</span><span class="seg">Cardio</span><span class="seg">Body</span></div>

<div class="week">
<div class="ring"><div class="in"><span class="v">3/4</span><span class="u">SESSIONS</span></div></div>
Expand Down Expand Up @@ -138,7 +138,7 @@
</span></div>
<div class="screen">
<div class="nav"><h1>Progress</h1></div>
<div class="segs"><span class="seg on">Strength</span><span class="seg">Engine</span><span class="seg">Body</span></div>
<div class="segs"><span class="seg on">Strength</span><span class="seg">Cardio</span><span class="seg">Body</span></div>

<div class="week">
<div class="ring"><div class="in"><span class="v">3/4</span><span class="u">SESSIONS</span></div></div>
Expand Down
Binary file modified docs/mockups/native/redesign-b-progress.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
4 changes: 2 additions & 2 deletions docs/mockups/native/redesign-d-progress.html
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@
</span></div>
<div class="screen">
<div class="nav"><h1>Progress</h1></div>
<div class="segs"><span class="seg on">Strength</span><span class="seg">Engine</span><span class="seg">Body</span></div>
<div class="segs"><span class="seg on">Strength</span><span class="seg">Cardio</span><span class="seg">Body</span></div>

<div class="week">
<div class="ring"><div class="in"><span class="v">3/4</span><span class="u">SESSIONS</span></div></div>
Expand Down Expand Up @@ -138,7 +138,7 @@
</span></div>
<div class="screen">
<div class="nav"><h1>Progress</h1></div>
<div class="segs"><span class="seg on">Strength</span><span class="seg">Engine</span><span class="seg">Body</span></div>
<div class="segs"><span class="seg on">Strength</span><span class="seg">Cardio</span><span class="seg">Body</span></div>

<div class="week">
<div class="ring"><div class="in"><span class="v">3/4</span><span class="u">SESSIONS</span></div></div>
Expand Down
Binary file modified docs/mockups/native/redesign-d-progress.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
2 changes: 1 addition & 1 deletion docs/mockups/native/review-redesign.html
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ <h1>Making Train as good as Cook</h1>
<p class="intro">You were right — Cook felt richer because it leads with imagery, and the
workout side didn't. Here are three directions for Train, each shown in <b>dark and
light mode side by side</b> (the whole app will respect both). The screen content is
identical in all three — today's session, meals from Cook, the engine numbers, the week —
identical in all three — today's session, meals from Cook, the cardio numbers, the week —
only the personality changes. Photos are placeholders for real training imagery.</p>

<section><h2><span class="n">A</span>Option A — Volt, but media-led</h2>
Expand Down
2 changes: 1 addition & 1 deletion docs/mockups/native/review.html

Large diffs are not rendered by default.

6 changes: 3 additions & 3 deletions docs/mockups/native/train-decision.html

Large diffs are not rendered by default.

4 changes: 2 additions & 2 deletions docs/mockups/native/train-today.html
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@
</style>
</head>
<body>
<div class="caption"><b>Train · Today</b> — Void × Volt goes native: SF large title, session hero, the Cook plan's meals + macros as a shared-store strip, engine tiles. Volt stays Train's only accent.</div>
<div class="caption"><b>Train · Today</b> — Void × Volt goes native: SF large title, session hero, the Cook plan's meals + macros as a shared-store strip, cardio tiles. Volt stays Train's only accent.</div>

<div class="device num">
<div class="island"></div>
Expand Down Expand Up @@ -96,7 +96,7 @@
</div>
<div class="macro"><span class="k">Protein</span><div class="mtrack"><div class="mfill" style="width:68%"></div></div><span class="v">109 / 160 g</span></div>

<div class="sec"><span class="t">Engine</span><span class="more">Progress ›</span></div>
<div class="sec"><span class="t">Cardio</span><span class="more">Progress ›</span></div>
<div class="tiles">
<div class="tile"><div class="k">ZONE 2</div><div class="v">86<small> /150 min</small></div><div class="s">on pace</div></div>
<div class="tile"><div class="k">SLEEP</div><div class="v">7:42</div><div class="s">▲ 22 min</div></div>
Expand Down
Binary file modified docs/mockups/native/train-today.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified docs/mockups/progress.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
40 changes: 36 additions & 4 deletions ios/CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,14 @@ and `docs/ios-implementation-plan.md` (setup + phases N0–N5) before starting w
carry the decisions; this file carries the day-to-day rules. Root `CLAUDE.md` still applies:
it describes the server and PWA this app talks to.

**Current phase: N1 slice 2 in progress.** N0's skeleton is built and verified in the Simulator
**Current phase: N1 done, N4 slice 1 in.** N0's skeleton is built and verified in the Simulator
(auth, `/api/week`, design system) but is **not signed off** — that needs an install on both
phones, which is James's step. N1.1 (Today/Plan read paths) and N1.2 (session logging with the
offline queue) are in. Next: history/records/trends, then coach, then settings.
phones, which is James's step. N1 is complete: Today/Plan, session logging with the offline
queue, history/records/trends, coach. **Cook** (N4.1) landed next, out of phase order because
its boards were already decided: browse, recipe detail, cook mode, library, meal planner, plus
the read-only meals strip on Train's Today. Still open in N4: iPad `NavigationSplitView`, cook
timers as Live Activities (needs [C1b] in the handover). N2/N3 (HealthKit, Watch, widgets) are
untouched.

> **Anything requiring the Xcode UI or the Apple Developer portal lives in
> [`XCODE-HANDOVER.md`](XCODE-HANDOVER.md), and is James's job — not yours.** That file is the
Expand Down Expand Up @@ -84,7 +88,35 @@ automatically — there is no Xcode step.
- **Design**: Void × Volt tokens for Train (dark and light both first-class; light = Paper × Moss),
DIN Condensed for display type and large numerals, SF for body. One accent *per module*, scoped
to that module's theme environment — never two accents on one screen. Warm `#e8a360` is warnings
only. Destructive actions always confirm.
only. Destructive actions always confirm. **Volt means positive status**, so a number only earns
it when it moved the way that metric should (`MetricDelta.Better`, `LiftTrend.isPositive`) —
everything else renders neutral. Most metrics have no universal good direction; don't invent one.
And a change is only worth an arrow if the formatter can print it: a fixed significance threshold
under a whole-number format rendered "▼ 0" on resting HR.
- **Cook is not a Train reskin** and never borrows from it: Paper × Ember (`ForgeTheme.cookLight`),
New York serif for display (`ForgeFont.serif*`), full-bleed photography, soft warm shadows
instead of hairlines. Volt and DIN must never appear on a Cook screen; ember and serif must
never appear on a Train one. **Cook is light-only** — `cookTheme()` pins `colorScheme` to
`.light` so system chrome matches the paper; both boards were drawn light and there is no
approved dark Cook. `RootView` tints the tab bar from the *selected* tab for the same reason —
a volt "Cook" label under a paper screen is two accents on one screen.
- **Cook mode's ember is the done-when cue and nothing else.** `CookCue` derives it from the step
prose, which works because Forge's import rule already requires done-when voice. Don't accent
anything else in the step text, and don't invent a cue for steps that have none.
- **Cook timers hold an absolute end date**, never a countdown — cook mode gets backgrounded
constantly and a decrementing counter drifts every time the view stops ticking.
- **Recipe images need the session cookie** (`/api/food/media/{id}` is authed), so `AsyncImage`
can't fetch them — use `ForgeAsyncImage`/`RecipePhoto`, which go through the client. The loader
attaches the cookie **only for our own origin**: imports keep remote URLs on failure, and a
recipe site's CDN must never be sent a Forge session token.
- **The seeded recipe library has no imagery.** `RecipePhoto` falls back to bundled plate
photography keyed by `platefig` (`PlateImage`) — the same split as Train's `HeroImage` vs
`ExerciseMedia`. Never render an empty frame where a photo belongs; in a photography-led module
that reads as breakage.
- **The meal plan is shared, the meal log is yours.** One household food week (`meal_revisions`
with `user_id IS NULL`) but per-user `meal_logs`. Any UI that plans says it changes the week for
both of you; any UI that ticks says it logs to your plate only. Never merge the two ideas into
one control.
- **Auth**: `ASWebAuthenticationSession` → the server's existing Google OAuth → same session
cookie the PWA uses, held in the Keychain. No new auth surface on the server — the only
addition is `GET /auth/login?native=1`, which makes `/auth/callback` finish on
Expand Down
9 changes: 9 additions & 0 deletions ios/Forge/Forge/Assets.xcassets/Hero/Contents.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
{
"info": {
"author": "xcode",
"version": 1
},
"properties": {
"provides-namespace": false
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
{
"images": [
{
"filename": "lift-dark.jpg",
"idiom": "universal",
"scale": "1x"
},
{
"idiom": "universal",
"scale": "2x"
},
{
"idiom": "universal",
"scale": "3x"
}
],
"info": {
"author": "xcode",
"version": 1
}
}
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
{
"images": [
{
"filename": "mat-warm.jpg",
"idiom": "universal",
"scale": "1x"
},
{
"idiom": "universal",
"scale": "2x"
},
{
"idiom": "universal",
"scale": "3x"
}
],
"info": {
"author": "xcode",
"version": 1
}
}
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
{
"images": [
{
"filename": "press-light.jpg",
"idiom": "universal",
"scale": "1x"
},
{
"idiom": "universal",
"scale": "2x"
},
{
"idiom": "universal",
"scale": "3x"
}
],
"info": {
"author": "xcode",
"version": 1
}
}
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
{
"images": [
{
"filename": "pullup-bw.jpg",
"idiom": "universal",
"scale": "1x"
},
{
"idiom": "universal",
"scale": "2x"
},
{
"idiom": "universal",
"scale": "3x"
}
],
"info": {
"author": "xcode",
"version": 1
}
}
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
{
"images": [
{
"filename": "runners-dawn.jpg",
"idiom": "universal",
"scale": "1x"
},
{
"idiom": "universal",
"scale": "2x"
},
{
"idiom": "universal",
"scale": "3x"
}
],
"info": {
"author": "xcode",
"version": 1
}
}
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
{
"images": [
{
"filename": "track-start.jpg",
"idiom": "universal",
"scale": "1x"
},
{
"idiom": "universal",
"scale": "2x"
},
{
"idiom": "universal",
"scale": "3x"
}
],
"info": {
"author": "xcode",
"version": 1
}
}
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
6 changes: 6 additions & 0 deletions ios/Forge/Forge/Assets.xcassets/Plate/Contents.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"info" : {
"author" : "xcode",
"version" : 1
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
{
"images" : [
{
"filename" : "bowl-tofu.jpg",
"idiom" : "universal",
"scale" : "1x"
},
{
"idiom" : "universal",
"scale" : "2x"
},
{
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Loading