A local-first PWA fitness training application. Plan, track, and execute your workouts entirely on your device — no server needed.
Find a file
David efeeb164b8
Some checks are pending
CI / Lint & Format (push) Waiting to run
CI / Build (push) Waiting to run
CI / Tests (${{ matrix.browser }}) (chromium) (push) Waiting to run
CI / Tests (${{ matrix.browser }}) (firefox) (push) Waiting to run
F10-F12: Documentation sweep + Prettier pass
- plan.md: overview now lists English, French & Danish; status header
  tracks the remediation work; date-formatting note updated to Intl
- AGENTS.md: Build & Run section now lists lint, lint:fix, the test/
  test:* scripts and the site scripts
- technical docs (EN/FR): date formatting section rewritten for the
  Intl-based formatter (incl. Danish column), streak definition notes
  only completed logs count
- user guide (EN/FR): delete sections mention the new usage counts in
  the confirmation dialogs
- decision log entries for F1/F3/F4 landed with their fixes
- npm run format over the touched markdown/code

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-09 15:50:28 +02:00
.claude Step 26 to announce exercise 2026-07-06 15:33:33 +02:00
.github/workflows Initial commit 2026-06-18 13:06:57 +02:00
docs F10-F12: Documentation sweep + Prettier pass 2026-07-09 15:50:28 +02:00
public Initial commit 2026-06-18 13:06:57 +02:00
scripts Add some stats + plan after code review 2026-07-07 16:20:16 +02:00
src F10-F12: Documentation sweep + Prettier pass 2026-07-09 15:50:28 +02:00
tests F9: Small polish batch 2026-07-09 15:45:39 +02:00
website F10-F12: Documentation sweep + Prettier pass 2026-07-09 15:50:28 +02:00
.gitignore Step 26 to announce exercise 2026-07-06 15:33:33 +02:00
.prettierignore Initial commit 2026-06-18 13:06:57 +02:00
.prettierrc Initial commit 2026-06-18 13:06:57 +02:00
AGENTS.md F10-F12: Documentation sweep + Prettier pass 2026-07-09 15:50:28 +02:00
CLAUDE.md Initial commit 2026-06-18 13:06:57 +02:00
eslint.config.js Add udpate modal (step 24) and control by audio cask (step 25) 2026-07-05 21:26:21 +02:00
index.html Initial commit 2026-06-18 13:06:57 +02:00
jsconfig.json Initial commit 2026-06-18 13:06:57 +02:00
LICENSE.md Initial commit 2026-06-18 13:06:57 +02:00
notice.md Initial commit 2026-06-18 13:06:57 +02:00
package-lock.json Initial commit 2026-06-18 13:06:57 +02:00
package.json Initial commit 2026-06-18 13:06:57 +02:00
README.md Fix the IA warning 2026-06-18 13:15:26 +02:00
vite.config.js Add udpate modal (step 24) and control by audio cask (step 25) 2026-07-05 21:26:21 +02:00

TrainUs

A local-first PWA fitness training application. Plan, track, and execute your workouts entirely on your device — no server needed. The dedicated website is here.

Warning

AI authorship. The vast majority of the source code in this project was written by AI assistants (primarily Claude). The human author directed the architecture, reviewed every change, guided the implementation, and made all design and product decisions — but in terms of who typed the lines, that credit belongs mostly to the AI. This is stated here for transparency, not as a caveat: the code was reviewed and validated at every step. Why using it ? Mostly time (I have the skills but not the time) and learning (how to use it).

Tech Stack

  • Vue 3 + Vite — reactive UI framework with fast build tooling
  • SQLite WASM with OPFS — client-side database persistence
  • i18next — internationalization (English, French & Danish)
  • Chart.js — workout statistics
  • vite-plugin-pwa + Workbox — offline support and install prompt
  • Playwright (Python) — end-to-end testing

Getting Started

Prerequisites

  • Node.js ≥ 18
  • Python ≥ 3.10 (for tests)

Development

npm install
npm run dev        # dev server at http://localhost:5173
npm run lint       # ESLint (src/)
npm run format     # Prettier (auto-fix)
npm run format:check  # Prettier (check only)

The dev server runs at http://localhost:5173 with COOP/COEP headers enabled (precautionary; the SAH-pool VFS used by this app does not strictly require them). The Playwright test suite runs its own instance on port 5199 instead (see tests/README.md).

Note: OPFS persistence requires a modern browser (Firefox 111+, Chrome 102+, Safari 16.4+) and does not work in private browsing mode. If OPFS is unavailable, the app displays a compatibility error.

Build

npm run build

Produces a static dist/ folder ready for deployment.

Deploy

TrainUs uses a single deployment: copy the dist/ folder to the site root, replacing the previous build (no per-version folders). User data is preserved across updates — it lives at a stable OPFS location, and a startup migration step applies any schema changes.

TrainUs is a static site — it works on any web server or CDN. COOP/COEP headers are not required for the SAH-pool VFS used here, but are recommended for defense-in-depth and may be required if the VFS is ever changed:

Nginx

location / {
    add_header Cross-Origin-Opener-Policy "same-origin";
    add_header Cross-Origin-Embedder-Policy "require-corp";
}

Apache

<IfModule mod_headers.c>
    Header always set Cross-Origin-Opener-Policy "same-origin"
    Header always set Cross-Origin-Embedder-Policy "require-corp"
</IfModule>

Caddy

header {
    Cross-Origin-Opener-Policy "same-origin"
    Cross-Origin-Embedder-Policy "require-corp"
}

Netlify (public/_headers)

/*
  Cross-Origin-Opener-Policy: same-origin
  Cross-Origin-Embedder-Policy: require-corp

Vercel (vercel.json)

{
  "headers": [
    {
      "source": "/(.*)",
      "headers": [
        { "key": "Cross-Origin-Opener-Policy", "value": "same-origin" },
        { "key": "Cross-Origin-Embedder-Policy", "value": "require-corp" }
      ]
    }
  ]
}

Preview production build locally

npm run preview

Serves dist/ at http://localhost:4173 with the correct headers (injected by Vite).

Tests

Setup (first time)

cd tests
python -m venv .venv
.venv/bin/pip install -r requirements.txt
.venv/bin/playwright install firefox chromium

Running tests

Command Browsers Mode
npm run test firefox sequential
npm run test:chromium chromium sequential
npm run test:parallel firefox + chromium parallel
npm run test:parallel:firefox firefox parallel
npm run test:parallel:chromium chromium parallel
npm run test:parallel:n firefox + chromium parallel, fixed worker count
# Sequential — Firefox only (simple)
npm run test
# or: cd tests && .venv/bin/python -m pytest --browser firefox -v

# Parallel — both browsers, all CPU cores (fastest)
npm run test:parallel

# Parallel — Firefox only, all CPU cores
npm run test:parallel:firefox

# Parallel — both browsers, limited to N workers (default 4)
WORKERS=2 npm run test:parallel:n

# Via shell script — both browsers, auto workers
./tests/run_parallel.sh

# Via shell script — Firefox only, 4 workers
PYTEST_WORKERS=4 PYTEST_BROWSERS="firefox" ./tests/run_parallel.sh

# Single test file, Firefox only
cd tests && .venv/bin/python -m pytest test_step2_settings.py --browser firefox -v

# Single test file in parallel via shell script
./tests/run_parallel.sh test_step3_exercises.py

Website

The project site (docs + blog) is a Hugo static site under website/.

Prerequisites

  • Hugo ≥ 0.123 extended edition

Development

npm run site:dev   # Hugo dev server at http://localhost:1313 (with drafts)

Build

npm run site:build   # Minified output in website/public/

Deploy

The site is a static folder — copy website/public/ to any web host. Before building for production, set two values in website/hugo.toml:

baseURL = "https://yourdomain.example/trainus/"   # full URL to the site root

[params]
  appUrl = "https://yourdomain.example/app/"      # leave empty to show "Coming soon"

No special server headers are required for the website itself (COOP/COEP are only needed for the app).

Documentation

You can access the website here.

Project

Credits

Icons

The app icon is Mighty Force by Delapouite, sourced from game-icons.net and licensed under CC BY 3.0. Thank you for making such a great icon library freely available!

UI icons are provided by Bootstrap Icons, licensed under MIT.

Third-Party Software

Package License
Vue 3 MIT
Vue Router MIT
Vite MIT
@vitejs/plugin-vue MIT
vite-plugin-pwa MIT
Workbox (bundled by vite-plugin-pwa) Apache-2.0
@sqlite.org/sqlite-wasm Apache-2.0
Bootstrap Icons MIT
Mighty Force by Delapouite (app icon) CC BY 3.0
Chart.js MIT
vue-chartjs MIT
DOMPurify Apache-2.0 / MPL-2.0
marked MIT
i18next MIT
i18next-vue MIT
Prettier MIT
Playwright Apache-2.0
pytest MIT
pytest-playwright Apache-2.0
pytest-xdist MIT

License

TrainUs — an open, local-first, offline-capable fitness tracking application where you can create and share your own training sessions. Copyright (C) 2026 David Florance

This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.

This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details.

You should have received a copy of the GNU Affero General Public License along with this program. If not, see http://www.gnu.org/licenses/.