Skip to content

Commit 84a5ea6

Browse files
committed
feat(web): add local-first web preview and redesigned landing
1 parent 19947d7 commit 84a5ea6

117 files changed

Lines changed: 56541 additions & 1 deletion

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.github/workflows/web-pages.yml

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
name: IronLog Web
2+
3+
on:
4+
pull_request:
5+
paths: ['web/**', '.github/workflows/web-pages.yml']
6+
push:
7+
branches: [main]
8+
paths: ['web/**', '.github/workflows/web-pages.yml']
9+
workflow_dispatch:
10+
11+
permissions:
12+
contents: read
13+
14+
concurrency:
15+
group: ironlog-web-${{ github.ref }}
16+
cancel-in-progress: false
17+
18+
jobs:
19+
verify:
20+
runs-on: ubuntu-latest
21+
defaults:
22+
run:
23+
working-directory: web
24+
steps:
25+
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1
26+
- uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020
27+
with:
28+
node-version: '24'
29+
cache: npm
30+
cache-dependency-path: web/package-lock.json
31+
- run: npm ci
32+
- run: npm test
33+
- run: npm run build
34+
- run: npm run verify:output
35+
- run: npx playwright install --with-deps chromium webkit
36+
- name: Browser acceptance
37+
run: |
38+
npm run preview -- --port 4173 > /tmp/ironlog-preview.log 2>&1 &
39+
for i in {1..30}; do curl -fsS http://127.0.0.1:4173/Ironlog/ >/dev/null && break; sleep 1; done
40+
curl -fsS http://127.0.0.1:4173/Ironlog/ >/dev/null
41+
npx playwright test
42+
- name: Origin-unavailable workout acceptance
43+
run: npx playwright test --config=diagnostics/playwright.config.ts
44+
- uses: actions/upload-artifact@v4
45+
if: failure()
46+
with:
47+
name: browser-failures
48+
path: |
49+
web/playwright-report
50+
web/test-results
51+
- uses: actions/upload-pages-artifact@fc324d3547104276b827a68afc52ff2a11cc49c9
52+
if: github.ref == 'refs/heads/main' && github.event_name != 'pull_request'
53+
with:
54+
path: web/dist
55+
deploy:
56+
if: github.ref == 'refs/heads/main' && github.event_name != 'pull_request'
57+
needs: verify
58+
runs-on: ubuntu-latest
59+
permissions:
60+
pages: write
61+
id-token: write
62+
environment:
63+
name: github-pages
64+
url: ${{ steps.deployment.outputs.page_url }}
65+
steps:
66+
- uses: actions/configure-pages@45bfe0192ca1faeb007ade9deae92b16b8254a0d
67+
- uses: actions/deploy-pages@cd2ce8fcbc39b97be8ca5fce6e763baed58fa128
68+
id: deployment

README.md

Lines changed: 27 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,13 +10,39 @@
1010

1111
**A native Android strength-training companion for fast logging, recovery awareness, plans, and an honest record of the work.**
1212

13-
[Features](#built-for-the-work-between-sets) · [Screens](#the-current-build) · [Architecture](#how-it-is-built) · [Build](#build-it-locally) · [Contributing](CONTRIBUTING.md)
13+
[Website & web app](#ironlog-web-preview) · [Features](#built-for-the-work-between-sets) · [Screens](#the-current-build) · [Architecture](#how-it-is-built) · [Build](#build-it-locally) · [Contributing](CONTRIBUTING.md)
1414

1515
</div>
1616

1717
> [!NOTE]
1818
> IronLog is under active development and physical-device QA. The signed build is not distributed from this repository yet; build locally or follow a future tagged release.
1919
20+
## IronLog Web preview
21+
22+
The website now has a separate product landing page and a local-first browser app under [`web/`](web/README.md). The production addresses are [the website](https://yannam-builds.github.io/Ironlog/) and [the web app](https://yannam-builds.github.io/Ironlog/app/); GitHub Pages deploys the new output only after the website workflow passes.
23+
24+
The August 31 website update includes:
25+
26+
- The current transparent monochrome IronLog mark, lighter Lexend landing typography, Forge Fox artwork and twelve native themes shared between the page and app. Native app headings keep their heavier weight.
27+
- Home, Plans, Log, Stats and Settings; saved onboarding, editable plans, custom exercises, notes, explicit warmup queues, resumable workouts, set editing/deletion, rest timers and plate calculation.
28+
- Local history, recovery estimates, Ledger progression, body measurements, photo comparison and validated backup/restore. No account, phone synchronization or browser cloud-AI backend.
29+
- Opaque modal sheets with keyboard focus wrapping, Escape dismissal and blocked background taps; mobile safe-area layouts and accessible numeric entry.
30+
- A six-paper research bibliography with limitations, separate software/font notices, and optional Home Screen installation.
31+
32+
**Preview, not full native parity.** On Windows, 85 unit tests pass, the production build and scoped output scan pass, and 19 of 20 desktop Chromium/WebKit browser tests pass. The remaining WebKit simulated-offline reload fails even in an app-independent service-worker reproduction. Supplemental tests with the actual HTTP origin stopped pass in both engines. These results do not verify real iPhone Safari, Home Screen lifecycle or VoiceOver. See the [acceptance record](web/docs/acceptance.md), [native parity gaps](web/docs/domain-parity.md) and [offline investigation](web/diagnostics/OFFLINE-WEBKIT.md).
33+
34+
Workout instruction media from OpenGym is **not bundled**: its media notice and upstream dataset require separate reuse permission. Kotlin source, APKs and phone data are unchanged by this website update.
35+
36+
Run locally with Node 24:
37+
38+
```sh
39+
cd web
40+
npm ci
41+
npm run dev
42+
```
43+
44+
The landing page runs at `http://127.0.0.1:5173/Ironlog/`. See the [web README](web/README.md) for production-preview and test commands.
45+
2046
## Built for the work between sets
2147

2248
IronLog keeps the main training loop fast and keeps the primary record on your device.

web/.gitignore

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
node_modules/
2+
dist/
3+
test-results/
4+
playwright-report/
5+
*.tsbuildinfo
6+
output/

web/README.md

Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
# IronLog Web
2+
3+
Local-first React/TypeScript application and product landing page for GitHub Pages. This is a separate web project; it does not build or modify Android.
4+
5+
## Run
6+
7+
Use Node 24 and the checked-in lockfile:
8+
9+
```sh
10+
npm ci
11+
npm run dev
12+
```
13+
14+
Landing: `http://127.0.0.1:5173/Ironlog/`. App: `/Ironlog/app/#/home`.
15+
16+
For production/offline verification:
17+
18+
```sh
19+
npm test
20+
npm run build
21+
npm run verify:output
22+
npx playwright install chromium webkit
23+
npm run preview -- --port 4173
24+
# Separate terminal:
25+
npm run test:e2e
26+
```
27+
28+
Service workers run in the production preview, not the development server. Browser tests use port 4173 by default; `PLAYWRIGHT_BASE_URL` can override it. Do not rebuild while a browser acceptance run is in progress.
29+
30+
## Architecture
31+
32+
- `src/data/`: validated Dexie transactions, serial workout mutations, revision conflicts, local snapshot subscription.
33+
- `src/domain/`: calculations, date rules, plan/backup codecs and manual external-AI prompt construction.
34+
- `src/features/`: onboarding, the five native tabs, detail screens and editor flows.
35+
- `src/ui/`: native theme tokens, opaque dialogs, shared controls, body-map rendering and update prompt.
36+
- `src/landing.tsx`: landing page and explicitly synthetic product demonstration.
37+
- `src/research.tsx`: shared, six-paper registry with feature relevance and limitations.
38+
- `tests/`: synthetic regression fixtures and desktop Chromium/WebKit journeys.
39+
40+
See [domain fidelity](docs/domain-parity.md) and [acceptance status](docs/acceptance.md). Do not infer full native parity or physical-iPhone verification from a passing unit test suite.
41+
42+
## Native identity and provenance
43+
44+
Website work starts from public main `19947d7cad8a46fc9a74904ea6a85ef54f42ac93`. The newer native working tree was a read-only reference. `src/generated/native-provenance.json` records its revision and hashes of each selected source file; file hashes identify dirty native content precisely.
45+
46+
`scripts/extract-native.mjs --source <native-repository>` extracts all 12 palettes and 24 roles, 32 templates, the 1,731-exercise library, the body-map dataset, Lexend, and selected public artwork. Never point this extractor at a backup or personal-data directory.
47+
48+
The current monochrome logo comes directly from the native `res/drawable/ic_launcher_foreground.xml` path. `public/assets/ironlog-logo.svg` has a transparent background and is used inline and as the favicon. Inline marks are white on dark themes and inverted to black in Light. Only the generated Home Screen PNG icons retain an opaque black platform-icon background. The retired orange launcher image is not shipped. `build-assets.mjs` makes mobile-sized WebP copies of native illustrations without redesigning them.
49+
50+
Native tokens remain intact. The landing page uses lighter Lexend weights (350 hero, 450 section headings, 500 brand); app headings retain the native heavier treatment. Documented web accessibility adaptations include high-contrast button ink, visible input borders, bounded keyboard-accessible numeric entry, adaptive compact controls and opaque browser dialogs with forward/backward focus wrapping. Monet uses the native fallback, not wallpaper colors.
51+
52+
## Storage and portability
53+
54+
IndexedDB is the source of truth. There is no backend, account, analytics, automatic external-AI request or phone synchronization. Version 2 adds a single-row bundled catalog while preserving version-1 custom exercise overrides.
55+
56+
Complete browser ZIP backups contain photo bytes. Android-compatible JSON is a separate export; Android photo URIs do not transfer image files. Restore is validated before clearing and committed atomically. Restoring is blocked transactionally while any workout is active, including a session started in another tab after preview.
57+
58+
Browser storage is not a guaranteed backup. Safari and installed Home Screen contexts may have separate stores. Export before switching, clearing site data, or testing storage recovery. Never test reset against a real user's browser profile.
59+
60+
Warmups remain pending until explicitly logged. Rest timers persist deadlines rather than relying on background intervals. Screen wake lock is optional. Locked-screen alarms, native widgets, Health Connect and Android on-device AI are not available here.
61+
62+
## Updates and deployment
63+
64+
The PWA uses prompt updates. Pending writes and active workouts disable update controls. Worker activation, including activation from another tab, never automatically reloads the page. A user explicitly reloads when ready. Public artwork receives content revisions; overlapping precache entries are prohibited.
65+
66+
`../.github/workflows/web-pages.yml` tests/builds only `web/` and uploads only `web/dist`. It retains existing Android CI. Hash routing avoids GitHub Pages rewrite rules. Keep base `/Ironlog/`; follow the [Vite GitHub Pages instructions](https://vite.dev/guide/static-deploy.html).
67+
68+
**Preview publication requested by the owner on 31 August 2026.** Repository publication is separate from full product acceptance. The workflow must pass before Pages deployment; never skip a failed test to publish. Real iPhone testing and the documented parity/acceptance gaps remain open. Do not include unrelated Android edits, APKs, keystores or user backups.
69+
70+
On Windows, the latest run passed 85 unit tests and 19/20 main browser tests. The remaining desktop WebKit simulated-offline reload failure reproduces without IronLog. Run the supplemental origin-unavailable workout tests with `npx playwright test --config=diagnostics/playwright.config.ts` (both engines pass). See [the diagnostic evidence and limitations](diagnostics/OFFLINE-WEBKIT.md); this does not replace real device testing or suppress the original test.
71+
72+
OpenGym exercise pictures/GIFs are not included. Its [media notice](https://gitlab.com/DuarteSantos8/opengym/-/blob/main/NOTICE.md) and the [upstream dataset terms](https://github.com/hasaneyldrm/exercises-dataset#license--usage) require separate media permission; software licenses do not grant those image rights.
73+
74+
## Skill provenance
75+
76+
Installed with the skill-installer helper without replacing existing directories:
77+
78+
- Anthropic Frontend Design: `3b3fad96af16a10759d930941b4520ba0c40edae`.
79+
- Vercel React Best Practices and Composition Patterns: `063bee94c3f4df8453406c830b0a7df0f2860278`.
80+
81+
Existing Impeccable/redesign, accessibility, citation-verification, TDD, browser-testing and independent-review workflows guided implementation. Native IronLog identity takes precedence over generic styling advice.
82+
83+
## License
84+
85+
IronLog Personal Use License 1.0 applies; see the repository's `LICENSE`. Third-party software/font notices are distinct from research citations. Research informs design principles and does not validate IronLog's exact readiness formula.

web/app/index.html

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
<!doctype html><html lang="en"><head><meta charset="UTF-8"/><meta name="viewport" content="width=device-width, initial-scale=1, viewport-fit=cover"/><meta name="theme-color" content="#121212"/><meta name="apple-mobile-web-app-capable" content="yes"/><meta name="apple-mobile-web-app-status-bar-style" content="black-translucent"/><link rel="icon" type="image/svg+xml" href="/Ironlog/assets/ironlog-logo.svg"/><link rel="apple-touch-icon" href="/Ironlog/assets/icon-180.png"/><title>IronLog</title></head><body><div id="root"></div><script type="module" src="/src/main.tsx"></script></body></html>

web/diagnostics/OFFLINE-WEBKIT.md

Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
# Desktop WebKit offline-emulation failure — 2026-08-31
2+
3+
The original cached-offline startup acceptance test remains enabled and failing
4+
on this host's desktop Playwright WebKit. No application or PWA configuration
5+
change was made for this failure. No physical iPhone or Home Screen launch was
6+
tested.
7+
8+
## Environment and original failure
9+
10+
- Windows host, Playwright `1.62.1`.
11+
- WebKit `26.5`, bundled revision `2336`.
12+
- Chromium `151.0.7922.34`, bundled revision `1234`.
13+
- Both browser projects use Playwright's iPhone 13 device descriptor. This is
14+
desktop browser emulation, not a device test.
15+
- Existing production preview: `http://127.0.0.1:4173/Ironlog/`.
16+
17+
Reproduction command (does not rebuild the app):
18+
19+
```powershell
20+
npx playwright test tests/browser/resilience.spec.ts --project=webkit --grep 'cached offline startup' --output=output/playwright/webkit-offline-baseline --reporter=list
21+
```
22+
23+
Result: **1 failed**, at the first `page.reload()` after
24+
`context.setOffline(true)`, with `WebKit encountered an internal error`.
25+
The test already verified activated registration, a controlling worker, cached
26+
app HTML, and cached exercise data. Its full reload/fresh-tab/logging/finish
27+
assertions were not bypassed. Trace and screenshot are under
28+
`output/playwright/webkit-offline-baseline/`.
29+
30+
## Minimal reproduction isolates the emulation boundary
31+
32+
```powershell
33+
node diagnostics/service-worker-offline-repro.mjs
34+
```
35+
36+
This standalone diagnostic creates its own ephemeral loopback HTTP server and
37+
uses a small vanilla service worker. It contains no IronLog code, React, Vite,
38+
Workbox, navigation fallback, or app data. HTTP responses carry `no-store` so
39+
the service-worker Cache Storage is responsible for the cached document.
40+
41+
The worker precaches `/`, serves that cached document for `/`, and serves a
42+
synthetic `new Response('handled-by-worker')` for `/worker-probe`. Each case
43+
verifies an active controller, a populated cache, the synthetic response, and
44+
a successful online reload before changing one variable.
45+
46+
| Browser | Network change | Synthetic worker response | Offline reload / fresh tab |
47+
| -------- | -------------------------- | ------------------------- | ------------------------------------------------------- |
48+
| Chromium | `context.setOffline(true)` | Works | Both pass |
49+
| Chromium | Actual HTTP server stopped | Works | Both pass |
50+
| WebKit | `context.setOffline(true)` | `TypeError: Load failed` | Reload has internal engine error; fresh tab not reached |
51+
| WebKit | Actual HTTP server stopped | Works | Both pass |
52+
53+
All four cases verify that an uncached network fetch fails after the network
54+
change. Stopping the server closes its listening socket and existing connections.
55+
`navigator.onLine` stays true in that case; it is only a connectivity hint and
56+
is not used as proof of network availability. The matrix reproduced consistently
57+
in repeated runs. The diagnostic intentionally exits nonzero when a case fails;
58+
it does not invert or suppress the WebKit failure.
59+
60+
Inference: the local WebKit/Playwright offline-emulation path prevents even a
61+
synthetic service-worker response from being delivered. This reproduces without
62+
the application's PWA stack, so changing Workbox caching would not address the
63+
isolated failure. The evidence does not identify a precise upstream source-code
64+
defect or prove behavior in shipping Safari. Playwright's
65+
[service-worker documentation](https://playwright.dev/docs/service-workers)
66+
also warns that its service-worker support is Chromium-only; that warning is
67+
context, not a substitute for the reproduction above.
68+
69+
## Supplemental actual-app origin-unavailable acceptance
70+
71+
```powershell
72+
npx playwright test --config=diagnostics/playwright.config.ts
73+
```
74+
75+
Result against the existing production `dist`: **2 passed**, Chromium and WebKit
76+
(20.5 seconds total in the initial run). `app-server-stopped.spec.ts` serves the
77+
existing build on its own ephemeral port, marks HTTP responses `no-store`, then
78+
stops only its own server. It neither rebuilds nor interrupts preview port 4173.
79+
This is a supplemental origin-unavailable check, not a replacement for the
80+
original airplane-mode-like emulation test.
81+
82+
After proving worker control and app precaching, this test verifies:
83+
84+
1. Reload with the server stopped retains four pending warmups.
85+
2. Closing the page and opening a fresh page starts the cached app.
86+
3. An uncached request fails, proving the origin is unavailable.
87+
4. Resuming the workout retains the four warmups without counting them as sets.
88+
5. Explicitly logging one warmup and one working set creates two persisted sets.
89+
6. A further offline reload retains those sets and three pending warmups.
90+
7. Finishing/saving yields history with exactly the two logged sets, not pending
91+
warmups, and history survives another reload.
92+
8. No JavaScript page errors are raised during the flow.
93+
94+
Attachments are generated under `output/playwright/origin-stopped/`. The ordinary
95+
resilience suite remains unchanged and still exposes the WebKit emulation
96+
failure. The supplementary pass increases confidence in cached startup and
97+
workout persistence with an unavailable origin; it does **not** prove full
98+
device-level offline behavior, installed-PWA lifecycle, browser-process restart,
99+
cache eviction resilience, or physical-iPhone acceptance.

0 commit comments

Comments
 (0)