# 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](https://kaivalya.eu/projects/trainUs/). > [!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 ```bash npm install npm run dev # dev server at http://localhost:5173 npm run lint # ESLint (src/) npm run lint:fix # ESLint (src/), auto-fix 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 ```bash 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 ```nginx location / { add_header Cross-Origin-Opener-Policy "same-origin"; add_header Cross-Origin-Embedder-Policy "require-corp"; } ``` #### Apache ```apache Header always set Cross-Origin-Opener-Policy "same-origin" Header always set Cross-Origin-Embedder-Policy "require-corp" ``` #### Caddy ```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`) ```json { "headers": [ { "source": "/(.*)", "headers": [ { "key": "Cross-Origin-Opener-Policy", "value": "same-origin" }, { "key": "Cross-Origin-Embedder-Policy", "value": "require-corp" } ] } ] } ``` #### Preview production build locally ```bash npm run preview ``` Serves `dist/` at `http://localhost:4173` with the correct headers (injected by Vite). ### Tests #### Setup (first time) ```bash 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 | ```bash # 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](https://gohugo.io/installation/) ≥ 0.123 extended edition #### Development ```bash npm run site:dev # Hugo dev server at http://localhost:1313 (with drafts) ``` #### Build ```bash 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`: ```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](https://kaivalya.eu/projects/trainUs). ### Project - [Technical Documentation](website/content/developers/technical.en.md) -- Architecture, tech stack, data model, and feature details - [User Documentation](website/content/docs/user-guide/index.en.md) -- How each feature works from the user's perspective - [Implementation Plan](docs/plan.md) -- Step-by-step feature plan and progress tracking - [Decisions Log](docs/decisions-log.md) -- All design decisions with rationale ## Credits ### Icons The app icon is [Mighty Force](https://game-icons.net/1x1/delapouite/mighty-force.html) by [Delapouite](https://delapouite.com), sourced from [game-icons.net](https://game-icons.net) and licensed under [CC BY 3.0](https://creativecommons.org/licenses/by/3.0/). Thank you for making such a great icon library freely available! UI icons are provided by [Bootstrap Icons](https://icons.getbootstrap.com), licensed under MIT. ### Third-Party Software | Package | License | | -------------------------------------------------------------------------------------------------------------------------- | -------------------- | | [Vue 3](https://github.com/vuejs/core) | MIT | | [Vue Router](https://github.com/vuejs/router) | MIT | | [Vite](https://github.com/vitejs/vite) | MIT | | [@vitejs/plugin-vue](https://github.com/vitejs/vite-plugin-vue) | MIT | | [vite-plugin-pwa](https://github.com/vite-pwa/vite-plugin-pwa) | MIT | | [Workbox](https://github.com/GoogleChrome/workbox) (bundled by vite-plugin-pwa) | Apache-2.0 | | [@sqlite.org/sqlite-wasm](https://sqlite.org/wasm/doc/trunk/index.md) | Apache-2.0 | | [Bootstrap Icons](https://icons.getbootstrap.com) | MIT | | [Mighty Force](https://game-icons.net/1x1/delapouite/mighty-force.html) by [Delapouite](https://delapouite.com) (app icon) | CC BY 3.0 | | [Chart.js](https://github.com/chartjs/Chart.js) | MIT | | [vue-chartjs](https://github.com/apertureless/vue-chartjs) | MIT | | [DOMPurify](https://github.com/cure53/DOMPurify) | Apache-2.0 / MPL-2.0 | | [marked](https://github.com/markedjs/marked) | MIT | | [i18next](https://github.com/i18next/i18next) | MIT | | [i18next-vue](https://github.com/i18next/i18next-vue) | MIT | | [Prettier](https://github.com/prettier/prettier) | MIT | | [Playwright](https://github.com/microsoft/playwright) | Apache-2.0 | | [pytest](https://github.com/pytest-dev/pytest) | MIT | | [pytest-playwright](https://github.com/microsoft/playwright-python) | Apache-2.0 | | [pytest-xdist](https://github.com/pytest-dev/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](http://kaivalya.eu) 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 .