Adaptive pull-up trainer. Local-first (everything is saved on the device), with optional Google sign-in + cloud sync via Firebase.
public/ ← everything that gets hosted
index.html ← the whole app (React via CDN, no build step)
sw.js ← service worker: full offline support after first load
manifest.webmanifest
icon.png
firebase.json ← Firebase Hosting + Firestore config
firestore.rules ← each user can only touch their own data
.firebaserc ← put your Firebase project id here
python3 -m http.server 8123 -d public
Then open http://localhost:8123. Sync is disabled until you add a Firebase config (the app runs fine without it — data stays in the browser).
One-time setup, ~10 minutes. Everything fits in Firebase’s free Spark plan.
the-bar-pullups). No need for Google Analytics.</>) →
register. Copy the firebaseConfig values it shows.public/index.html — find FIREBASE_CONFIG near the top
of the script and replace the PASTE_... placeholders (apiKey, authDomain,
projectId, appId)..firebaserc (replace the-bar-pullups if you named it
differently).npm install -g firebase-tools
firebase login
firebase deploy
The app goes live at https://<project-id>.web.app and https://<project-id>.firebaseapp.com.
iPhone tip: open the
.firebaseapp.comURL in Safari and use Share → Add to Home Screen. Using that domain keeps Google sign-in working in home-screen (standalone) mode, where popups are restricted.
localStorage doesn’t transfer between domains. On the old version of the app: History → Export, send the JSON file to yourself, then on the new URL: History → Import. Sessions merge by id, so importing twice is safe. Once you sign in, the same merge keeps every device consistent.
The timer is anchored to the clock, not to a running counter — lock the phone mid-rest and it will still be correct when you come back. While resting, the app plays a silent audio loop, which lets iOS keep it alive in the background so the chime can ring the moment rest is over (this also works with the silent switch on, since it plays through the media channel). If iOS still suspends the app, the timer self-corrects the instant you wake the screen.