| .claude | ||
| .github/workflows | ||
| docs | ||
| public | ||
| scripts | ||
| src | ||
| tests | ||
| website | ||
| .gitignore | ||
| .prettierignore | ||
| .prettierrc | ||
| AGENTS.md | ||
| CLAUDE.md | ||
| eslint.config.js | ||
| index.html | ||
| jsconfig.json | ||
| LICENSE.md | ||
| notice.md | ||
| package-lock.json | ||
| package.json | ||
| README.md | ||
| vite.config.js | ||
TrainUs
A local-first PWA fitness training application. Plan, track, and execute your workouts entirely on your device — no server needed.
Warning
IA has been massively used.
I have the technical skills to carry out this kind of project, but not the time. The rise of AI tools changed the equation: what would have taken me months of evenings and weekends became achievable. AI didn’t replace expertise — it made possible what wasn’t, purely due to time constraints. It was also an opportunity to learn how to work with these tools, which is a benefit in itself: I got to test and compare quite a few models and assistants throughout the development.
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
- Technical Documentation -- Architecture, tech stack, data model, and feature details
- User Documentation -- How each feature works from the user's perspective
- Implementation Plan -- Step-by-step feature plan and progress tracking
- Decisions Log -- All design decisions with rationale
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/.