Body Refactoring is a personal, gamified progressive web app (PWA) designed to treat fitness, strength training, and habit building like a software project.
It runs primarily in the browser using LocalStorage for data persistence, but utilizes a lightweight PHP backend to serve dynamic, evolvable training schedules without breaking historical data.
- Changelog - Complete version history and release notes
- Roadmap - Planned features and future development
- v13.0.0 Refactoring - Complete architectural refactoring documentation (6 phases, 13 modules)
- Schedule Editor Guide - Complete guide for using the visual schedule editor
- Schedule Validation Guide - Complete guide for creating and validating training schedules
- AI Schedule Creation Guide - Comprehensive guide for AI assistants to generate new schedule JSON files
A web-based visual editor for creating and managing training schedule JSON files.
Access: https://your-domain.com/schedule-editor.php
Quick Overview:
- 📝 Visual day-by-day editor with tabbed interface
- 📁 Load existing schedules from server
- 📤 Import from JSON files or clipboard
- 📥 Export schedules for Git deployment
- ➕ Add, edit, remove, and reorder exercises
- ✅ Built-in validation with error reporting
📖 Complete Schedule Editor Documentation - Features, workflows, tips, and troubleshooting
Quick Start:
- Navigate to
schedule-editor.php - Create new or load existing schedule
- Edit days and exercises visually
- Validate and export
- Deploy via Git workflow
- Automatic Vocal Counting: Full-screen modal with hands-free rep guidance for strength exercises
- Visual Breathing Animation: Numbers animate from 100% → 75% → 50% to represent rep motion (up/down)
- Color-Coded Progress: Blue for regular reps, green for last 3 reps
- 5-Second Countdown: "5, 4, 3, 2, 1, Los!" with breathing animation
- Rest Timer Management: Automatic 60s rest between sets with vocal countdown
- Quick Rest Option: Tap rest timer to skip to 5 seconds for faster pace
- Auto-Completion: Exercise automatically marked complete upon successful finish
- Configurable Timing: Millisecond-precision delay per exercise (e.g., 3000ms, 4000ms)
- Enhanced Audio: Natural German voice with proper iOS Safari and Chrome support
How to use:
- Tap the purple rep counter chip (🔁 3 x 12) on any configured exercise
- Follow the countdown and automated rep counting
- Rest periods happen automatically
- Click "Abbrechen" to cancel anytime
Configuration example:
{
"id": "ex_benchpress",
"type": "main",
"title": "Bench Press",
"desc": "3 x 12 Reps",
"weight": "60",
"defaultUnit": "KG",
"repCounter": {
"sets": 3,
"reps": 12,
"restSeconds": 60,
"delayMilliseconds": 3000,
"bilateral": false
}
}Bilateral exercises (e.g., single-leg exercises): Set "bilateral": true to show "Links" / "Rechts" indicator per set.
- Evolvable Plans: Workout routines are stored as JSON files (schedule-YYYY-MM-DD.json) in a trainings/ directory.
- Smart History: The app automatically loads the correct schedule for any given date. You can change your plan next week without breaking the logs of the past.
- Time Travel: Seamlessly navigate through past and future training weeks.
- Travel & Vacation Schedules: Create customized schedules before trips that utilize local possibilities (hotel gym, Apple Fitness+ videos, outdoor walking/jogging, bodyweight exercises). See AI Schedule Creation Guide for details.
- Visual Feedback: Confetti showers for every completed set and a massive fireworks display when a day is fully completed.
- Streaks: Tracks your consistency with visual flame counters.
- Streak Insurance: Earn up to 3 shields (🛡️) by completing 7 consecutive training days. Use shields when severely ill to maintain your streak.
- NoSleep Mode: Prevents iOS Safari from locking the screen during workouts using a background video hack.
- Sound & Speech: Integrated Text-to-Speech announces timer starts and completion naturally (e.g., "5 Minutes Rowing started").
- Recovery Mode: When feeling under the weather but not fully sick, activate recovery mode with 3 light activities (breathing exercises, light stretching, hydration). Completing all 3 maintains your streak.
- Sick Mode with Shield: Use a streak insurance shield when severely ill. Only requires hydration tracking and preserves your streak.
- Sick Mode without Shield: Document longer illnesses even without shields. Breaks the streak but maintains honest tracking.
- Back to Normal: Cancel recovery or sick mode anytime during the day if you feel better. Shields are refunded automatically.
- Visual Integration: Original exercises are shown as disabled/greyed out, recovery or sick activities are displayed inline with clear visual distinction.
- Intelligent Weight Tracking: If you increase the weight for an exercise today, this new standard is automatically applied to all future workouts.
- Historical Integrity: Past logs remain unchanged.
- Day-by-Day Lookback: The system finds your last used weight for specific exercises, even if the schedule changes.
- Local First: All user data (ticks, weights, notes) resides in localStorage.
- JSON Backup: Full export/import functionality to move data between devices.
- Privacy: No external tracking. No cookies. No consent banners needed (private use).
- Debug Mode: Access with
#debugin URL to remove day editing restrictions (indicated by 🐛 DEBUG badge) - Composer Integration: Professional dependency management with centralized version control
- Version Management: Single source of truth in
composer.json, auto-loaded viatools.php - Cache Busting: Automatic cache invalidation using file modification timestamps
- Pull Request Templates: Standardized contribution workflow with
.github/pull_request_template.md
- Optimized as a PWA for the iOS Home Screen.
- Haptic Feedback: Vibrations on timer completion.
- Input Optimization: Custom numeric input handling for easier weight logging on mobile.
Version 13.0+ introduces a modular architecture with improved code quality, maintainability, and testability. See Architecture Documentation for full details.
- SOLID Principles: Single responsibility, dependency injection, clear interfaces
- Clean Code: Small functions (<20 lines), minimal nesting, self-documenting
- Testability: Pure functions, dependency injection, separation of concerns
- State Machines: Explicit state management for timers, modals, and app lifecycle
bodyrefactoring/
├── index.php # Main application entry point (PHP for cache busting)
├── schedule-editor.php # Visual schedule editor tool
├── composer.json # Project metadata and version management
├── tools.php # Shared utilities (version function, APP_VERSION constant)
├── deploy.php # GitHub webhook handler
├── .env # Environment configuration (not in repo)
├── .htaccess # Apache configuration
├── .cursorrules # AI assistant guidelines
├── .github/
│ └── pull_request_template.md # PR template for standardized contributions
├── assets/
│ ├── cachebuster.php # Cache busting helper function
│ ├── css/
│ │ └── styles.css # All application styles and animations
│ └── js/
│ ├── app.js # Main application (being refactored)
│ └── modules/ # 🆕 Modular architecture (v13.0+)
│ ├── constants.js # Constants, enums, configuration
│ ├── state-machine.js # Generic state machine implementation
│ ├── storage-service.js # localStorage abstraction layer
│ ├── state-manager.js # Centralized application state
│ ├── utils.js # Common utility functions
│ └── speech-service.js # Text-to-speech service
├── trainings/
│ ├── index.php # API endpoint for available schedules
│ ├── validate-schedule.php # Schedule validator (CLI only)
│ ├── schema-schedule-v1.json # JSON Schema v1 (basic features)
│ ├── schema-schedule-v2.json # JSON Schema v2 (extended features)
│ ├── template-schedule.json # Template for new schedules
│ └── schedule-*.json # Training schedule files
└── docs/
├── architecture.md # 🆕 Architecture documentation
├── schedule-editor.md # Complete schedule editor guide
├── schedule-validation.md # Validation system documentation
└── ai-schedule-creation.md # Guide for AI-assisted schedule creation
Phase 1 Complete - Core foundation modules:
- constants.js: Single source of truth for all constants and configuration
- state-machine.js: Generic state machine with transition validation
- storage-service.js: localStorage abstraction with type-safe methods
- state-manager.js: Centralized reactive state management
- utils.js: Common utilities (dates, formatting, notifications)
- speech-service.js: Text-to-speech with voice selection
Upcoming Phases:
- Phase 2: State machine integration (app, timer, modal state machines)
- Phase 3: Feature module extraction (rendering, streak calculation, etc.)
- Phase 4: Integration and optimization
See Architecture Documentation for migration strategy and module details.
The app uses file modification timestamps for automatic cache invalidation:
- CSS and JS files are loaded with
?v=<timestamp>query parameter - Ensures users always get the latest version after updates
- Implemented via PHP's
filemtime()function
- HTML (index.php): Structure and layout only
- CSS (assets/css/styles.css): All styling and animations
- JavaScript (assets/js/): Application logic (modular from v13.0+)
- PHP Backend: Dynamic schedule loading and cache busting
Unlike the initial version, v8.0+ requires a web server (Apache/Nginx/PHP) to list the schedule files.
Note: Netlify PR previews were used until January 2026 but have been removed. As the app evolved to rely more heavily on PHP backend features (dynamic schedule API, authentication, etc.), static previews became less useful. The free tier bandwidth limitations also became a concern.
This repository uses a semi-automated release pipeline with GitHub Actions and webhooks.
Release Flow:
- Create PR with version bump in
composer.json+ CHANGELOG entry - Merge PR → GitHub Action creates a draft release automatically
- Review and publish the draft release in GitHub
- Publishing creates the git tag → webhook deploys to production
📖 Complete Release Process Documentation - Detailed workflow with flowchart
Deployment Trigger: Git tags (created when publishing a release)
How it works: When you publish a GitHub Release, it creates the tag (e.g., v14.1.0), which triggers the webhook to deploy that version to production.
- Clone repository:
cd /var/www/vhosts/your-domain.com
git clone git@github.com:apermo/bodyrefactoring.git httpdocs
cd httpdocs- Create .env file:
cp .env.example .envGenerate a secure secret:
openssl rand -hex 32Edit .env and add the secret:
nano .envExample:
DEPLOY_SECRET=a1b2c3d4e5f6...
REPO_PATH=/var/www/vhosts/your-domain.com/httpdocs
- Set permissions:
# Make .env readable only by owner (security)
chmod 600 .env
# Ensure git can write to the directory
# Note: On Plesk, file permissions are usually already correct
# If you get permission errors, contact your hosting provider- Git configuration:
git config user.email "deploy@your-domain.com"
git config user.name "Plesk Deploy"- SSH key for GitHub:
# Generate SSH key if not already exists
ssh-keygen -t ed25519 -C "deploy@your-domain.com"
# Display public key to add to GitHub
cat ~/.ssh/id_ed25519.pubAdd the public key as a Deploy Key:
👉 https://github.com/apermo/bodyrefactoring/settings/keys
- Configure GitHub Webhook:
- URL:
https://your-domain.com/deploy.php - Content-Type:
application/json - Secret: Your
DEPLOY_SECRETfrom.env - Events: Select "Let me select individual events" → Check only "Releases" or "Tags" (not "Pushes")
- Active: ✅ Enabled
Recommended (Semi-Automated):
- Create PR with version bump and CHANGELOG entry
- Merge PR to main
- Go to GitHub Releases → find the draft release
- Click "Edit" → review notes → click "Publish release"
- Production automatically deploys
Manual Fallback:
# Create and push a tag directly (e.g., for v14.1.0)
git tag -a v14.1.0 -m "Release v14.1.0"
git push origin v14.1.0
# The webhook automatically deploys this tag to production# Test deployment with a tag
git tag test-deploy
git push origin test-deploy
# Monitor deployment
tail -f deploy.log
# Clean up test tag
git tag -d test-deploy
git push origin :refs/tags/test-deploy- A web server with PHP support (e.g., XAMPP, Docker, or any standard shared hosting).
- (Optional) GitHub Pages is no longer sufficient for the dynamic JSON loading (unless you hardcode the file list).
- Upload the contents of this repository to your web server.
- Ensure the folder structure is correct:
/ (root)
├── index.html \# Main App
├── gymlogo.png \# Icon
├── background.jpg \# Wallpaper
└── trainings/ \# Config Directory
├── index.php \# JSON Loader API
└── schedule-2025-12-22.json
- Open the URL in your browser.
- Open the URL in Safari.
- Tap the Share Button (square with an upward arrow).
- Select "Add to Home Screen".
- Complete exercises by tapping the checkboxes
- Adjust weights inline - they automatically carry forward to future workouts
- Use timers for cardio and timed exercises
- Use rep counter for strength exercises with configured rep counting (tap 🔁 chip)
- Add notes in the logbook section
Starting a rep counter workout:
- Find an exercise with the purple rep counter chip (🔁 3 x 12)
- Tap the chip to start
- Watch the 5-second countdown with breathing animation
- Follow the automated rep counting - voice will count each rep
- Rest automatically between sets (60s by default)
- Tap rest timer to skip to 5 seconds if you're ready earlier
- Click "Abbrechen" to cancel anytime
Timing Calibration (optional): A purple "Set bereits fertig" button appears during sets. Tap it to log the set's timing to your notes for schedule calibration. The log entry shows:
- Exercise name and set number
- Actual elapsed time
- Suggested milliseconds per rep (for updating your schedule JSON)
Visual & Audio Feedback:
- Countdown: Yellow numbers breathing (5-4-3-2-1)
- Regular reps: Blue numbers with breathing animation, voice counts rep number
- Last 3 reps: Green numbers with enhanced glow, voice says "Noch 3", "Noch 2", "Der letzte"
- Rest timer: Yellow countdown with breathing animation
- Completion: Green checkmark + confetti
Access the app with #debug in the URL to remove day editing restrictions:
- Edit any day regardless of date (past, present, future)
- Useful for testing, corrections, or historical data updates
- Indicated by 🐛 DEBUG badge in header
- Example:
https://your-domain.com/#debug
When to use:
- Recovery Mode: Minor illness, not feeling 100%, or preventive rest day
- Sick Mode: Moderate to severe illness, unable to complete normal training
How to use:
-
Access Sick Mode:
- Click the menu button (☰) in the top right
- Select "Krank / Recovery"
-
Choose Your Option:
🌱 Recovery Mode (No shield required)
- Light activities to maintain streak
- Complete 3 simple tasks:
- 5 Min breathing exercises
- Light stretching
- Hydration (2L water/tea)
- Streak continues ✅
- Original exercises shown as disabled
🛡️ Sick Mode with Shield (Requires 1 shield)
- Only requires hydration tracking
- Streak continues ✅
- Shield is consumed
- Best for severe illness
🛡️ Sick Mode without Shield
- Available when no shields left
- Only requires hydration tracking
- Streak breaks
⚠️ - Allows proper documentation of longer illness
-
Back to Normal:
- If you feel better during the day, click "Zurück zu Normal" button
- Restores normal exercises
- Automatically refunds shield if one was used
Earning Shields:
- Complete 7 consecutive training days → earn 1 shield 🛡️
- Maximum 3 shields can be stored
- Shields shown in header next to streak counter
- Recovery days don't count toward earning shields (only full training days)
You define your workouts in the trainings/ folder. The file name must follow the pattern schedule-YYYY-MM-DD.json. The app always selects the schedule that is closest to (but not after) the current date being viewed.
All schedules should be validated before deployment. The validator runs from the command line and checks for proper JSON structure, required fields, and data integrity.
📖 Complete Validation Guide - Commands, workflow, error reference, and troubleshooting
Quick Start:
cd trainings/
php validate-schedule.php # Validates all schedules{
"version": 2,
"days": [
{
"id": "mon",
"dayIndex": 1,
"name": "MONDAY",
"theme": "Push Day",
"details": [
{
"id": "warmup_row",
"type": "warmup",
"title": "Rowing",
"desc": "Warmup",
"timers": [
{
"l": "5 Min",
"s": 300
},
{
"l": "10 Min",
"s": 600
}
]
},
{
"id": "ex_benchpress",
"type": "main",
"title": "Bench Press",
"desc": "3 x 12 Reps",
"weight": "40",
"defaultUnit": "KG"
},
{
"id": "alt_cardio",
"type": "alternatives",
"alternatives": [
{
"title": "Outdoor Run",
"desc": "Good weather",
"timers": [
{
"l": "20 Min",
"s": 1200
}
]
},
{
"title": "Treadmill",
"desc": "Rainy day",
"timers": [
{
"l": "20 Min",
"s": 1200
}
]
}
]
}
]
}
]
}A schedule is a JSON object with a version number and an array of days. Each day contains exercises that can be of three types: warmup, main (weighted), cool, or alternatives (multiple options).
Key Requirements:
- Root level:
version(integer, 1 or 2) anddays(array) - Filename:
schedule-YYYY-MM-DD.json - Unique IDs for days and exercises (lowercase, underscores only)
- dayIndex: 0 or 7 = Sunday, 1 = Monday, ..., 6 = Saturday
- Required day fields: id, dayIndex, name, theme, details
Optional Exercise Features:
timers: Array of timer options with label and secondsweight+defaultUnit: Weight tracking with unit selection (KG, LBS, STUFE)repCounter: Automatic rep counting configuration (sets, reps, restSeconds, delayMilliseconds)
📖 Complete Field Reference - Detailed field documentation and validation rules
JSON Schemas are available for IDE integration (VS Code, PhpStorm, etc.):
trainings/schema-schedule-v1.json- Basic featurestrainings/schema-schedule-v2.json- Extended features (optional, hideOn, custom type, bilateral)
This project uses Git hooks to enforce Conventional Commits format. Set up hooks to get instant feedback before commits:
bash .githooks/setup.shWhat this enables:
- ✅ Validates commit message format (type, scope, subject)
- ✅ Enforces 50/72 character rules (subject/body)
- ✅ Detects BREAKING CHANGE markers
- ✅ Provides helpful error messages with examples
- ✅ Runs locally (instant feedback, no CI wait)
Commit Format:
<type>(<scope>): <subject>
<body>
<footer>
Examples:
feat(intro): add welcome modal for first-time users
fix(timer): resolve voice-over stuck after app switch
docs: update roadmap with v14-v16 features
refactor(storage)!: migrate to domain storage serviceValid Types: feat, fix, docs, style, refactor, test, chore, perf
Rules:
- Scope: Optional but encouraged (e.g.,
feat(auth):) - Subject: Max 72 characters (50 recommended)
- Body lines: Max 72 characters
- Use imperative mood: "add" not "added"
Bypass (not recommended):
git commit --no-verify📖 Complete Commit Guidelines - Full conventional commits documentation
The repository includes automated checks that run on every PR and push to main:
- Conventional Commits validation
- Version bump verification
- CHANGELOG entry validation
- Schedule JSON validation
- Renovate: Automated dependency update PRs (weekly, grouped by ecosystem)
- Stale bot: Auto-labels and closes inactive issues/PRs (30 days stale, 14 days to close)
These run automatically - no setup required.
Local Testing (Optional):
If you have act installed, you can test GitHub Actions workflows locally before pushing:
# Helper script (checks for act, lists workflows, offers to test)
bash .github/test-workflows.sh
# Or use act directly:
act -l # List all workflows
act pull_request --dryrun # Test PR workflows (dry run)
act push --dryrun # Test push workflows (dry run)Install act (optional):
brew install act # macOS
# Other platforms: https://github.com/nektos/act#installationNote: act is completely optional. If not installed, workflows are automatically tested when you push to GitHub.
This project follows Conventional Commits with strict formatting rules.
Format: <type>(<scope>): <subject>
Character limits (enforced by git hooks):
- Subject line: 50 characters recommended, 72 maximum (hard limit)
- Body lines: 72 characters maximum per line
Important for AI-Generated Commits: When using "Generate Commit Message" features (Copilot, etc.), always verify the character count. If the generated message exceeds 50 characters, manually shorten it before committing.
Quick Reference:
# Good examples (under 50 chars)
feat(timer): add pause functionality
fix(auth): resolve login timeout
docs: update installation guide
refactor(api): simplify error handling
# Bad examples (too long)
feat(schedule-editor): add comprehensive validation and error handling support
fix(rep-counter-modal): resolve animation timing issue during cooldownTools:
.gitmessage- Git commit message template (shows character guides)tools/validate-commit-msg.sh- Manual validation scripttools/README.md- Complete commit message guidelines and tips
Commit types:
feat- New featurefix- Bug fixdocs- Documentation changesstyle- Code formatting (no logic change)refactor- Code refactoringtest- Adding testschore- Maintenance tasksperf- Performance improvements
Full documentation: See tools/README.md for detailed guidelines, examples, and troubleshooting.
- No Medical Advice: This software is for informational purposes only. Consult a physician before starting any training program.
- Private Use: This app is designed for personal use. If you host it publicly, you are responsible for GDPR compliance regarding server logs (IP addresses).
This project was built using Vibe Coding — a fluid, iterative collaboration between human creativity and AI capability.
- Concept & Vision: apermo
- AI Co-Pilots:
- Google Gemini (Initial code generation, logic implementation, UI design)
- GitHub Copilot using Claude Sonnet 4.5 (Refactoring, architecture design, code quality improvements, continuous development)
- Claude Code using Claude Sonnet & Opus 4.5 (since v14.2.0+: Testing infrastructure, workflow automation, documentation, continuous development)
A heartfelt thank you to Alexikon whose educational and entertaining Instagram Reels sparked a profound shift in my relationship with food and drink. His content was the catalyst that led me to rethink my habits and ultimately inspired the creation of this project.
Sometimes the smallest nudge creates the biggest change. 🙏
This project is licensed under the GPL-3.0 License.