RepCue is a modern, privacy-first fitness tracking Progressive Web App (PWA) designed for interval training and exercise logging. Built with React, TypeScript, and Tailwind CSS, it delivers a native-like app experience while being optimized for mobile devices and perfect for self-hosting on Raspberry Pi.
Want details on the offline-first data layer? See the new Sync Architecture (v2) guide for per-table cursors, conflict resolution, batching, and feature-flag rollout.
- Node.js 18+ (Download from nodejs.org)
- pnpm (Install with:
pnpm install -g pnpm)
# 1. Clone and navigate to the project
git clone https://github.com/akram0zaki/repcue.git
cd repcue
# 2. Install dependencies at the repo root
ppnpm install
# 3. Start the frontend dev server (Vite)
pnpm dev
# (Optional) start backend placeholder server on another terminal
pnpm dev:be🎉 That's it!
- Frontend: http://localhost:5173
- Backend (placeholder): http://localhost:3001 (Express, serves FE build in prod)
RepCue is your personal interval training companion that:
- ⏱️ Interval Timer: Configurable workout timers (15s, 30s, 60s)
- ⏳ Pre-Timer Countdown: Optional 0-10 second countdown before timer starts
- 💪 20 Core Exercises: Across 5 categories (Core, Strength, Cardio, Flexibility, Balance)
- 🎥 Exercise Demo Videos (Experimental): Inline circular instructional loop (feature & setting gated, reduced‑motion aware, fully optional)
- 📱 Mobile-First: Responsive design optimized for phones and tablets
- ♿ Accessible: WCAG 2.1 compliant for all users
- Disk Space: ~200MB for dependencies + ~50MB for built app
RepCue supports 6 languages (English, Dutch, Arabic, German, Spanish, French) with full RTL support for Arabic.
- All UI strings use i18n keys (see
/docs/i18n/key-styleguide.md). - To add or update translations:
- Edit
public/locales/en/common.json(or the correct namespace file). - Run
pnpm i18n:scanto check for missing keys in other locales. - Add translations for all supported languages.
- Test in the UI and with
ppnpm test.
- Edit
- For new languages, mirror the EN structure and add to
supportedLngsinsrc/i18n.ts. - See
/docs/i18n/contributing.mdfor full process, key naming, and RTL tips.
pnpm i18n:scan— Fails if any EN keys are missing; warns for other locales.pnpm i18n:report— Reports missing keys but does not fail (for CI stats).
/docs/i18n/README.md— Overview/docs/i18n/key-styleguide.md— Key naming/docs/i18n/contributing.md— How to add/translate/docs/i18n/rtl.md— RTL/Arabic tips/docs/i18n/string-inventory.md— All keys/docs/i18n/CHANGELOG.md— i18n structure changes/docs/i18n/screenshots/— Screenshots for review
node --version # Should show v18.x.x or higher
npm --version # Should show 8.x.x or higher
- **Raspberry Pi**: `curl -fsSL https://deb.nodesource.com/setup_18.x | sudo -E bash - && sudo apt-get install -y nodejs`
#### 2. **Clone the Repository**
```bash
git clone https://github.com/akram0zaki/repcue.git
cd repcue# Using npm (recommended)
pnpm install
# Or using yarn
yarn installThis installs all required packages including:
- React 19 + React Router for the UI framework
- TypeScript for type safety
- Tailwind CSS for styling
- Vite for fast development and building
- Vitest for testing
Frontend dev: pnpm dev → http://localhost:5173
Backend dev (optional placeholder): pnpm dev:be → http://localhost:3001
# Build the frontend app
pnpm build
# Preview the built version (serves http://localhost:4173)
pnpm preview- URL: http://localhost:4173
- Features: Optimized build, production-like environment
- Performance: Full optimization, smaller bundle size
| Command | Purpose | When to Use |
|---|---|---|
pnpm dev |
Start frontend dev (Vite) | Daily development |
pnpm dev:be |
Start backend dev (Express) | When testing API placeholder |
pnpm build |
Build frontend | CI/builds |
pnpm preview |
Preview built frontend (4173) | Verify production build |
pnpm start |
Start backend (serves FE in prod) | Production start |
pnpm pm2:start |
Start PM2 app | Deploy to Pi/server |
pnpm pm2:stop |
Stop PM2 app | Stop production app |
pnpm pm2:restart |
Restart PM2 app | Update production app |
pnpm pm2:logs |
View PM2 logs | Monitor production app |
pnpm lint |
Lint frontend | Before committing |
ppnpm test |
Run unit/integration tests | Development/CI |
ppnpm test:ui |
Vitest UI | Debugging tests |
ppnpm test:coverage |
Coverage report | QA |
ppnpm test:unit |
Unit/integration only | Fast testing |
ppnpm test:stable |
Stable mode on Windows | Flake-free runs |
RepCue includes comprehensive test coverage with 98 tests across all components.
pnpm testppnpm test:coverageppnpm test:uiOpens a web interface for running and debugging tests.
Tests live under tests/e2e.
Option A — build + preview + run Cypress:
# 1) Build and preview frontend
pnpm build
pnpm preview
# 2) In another terminal, run cypress from the e2e workspace
pnpm cypress:run
# or open the Cypress UI
pnpm cypress:open- ✅ Unit Tests: Individual components (ConsentBanner, TimerPage, etc.)
- ✅ Integration Tests: Service layer (StorageService, AudioService, etc.)
- ✅ Accessibility Tests: WCAG 2.1 compliance verification
- ✅ Timer Logic Tests: Precision timing and state management
- ✅ Video Demo Tests: E2E coverage for render path, user toggle, reduced‑motion suppression, and global feature flag disable
✅ ConsentBanner: 10/10 tests passing (GDPR compliance)
✅ TimerPage: 22/24 tests passing (Core functionality)
✅ AudioService: 19/25 tests passing (Sound & vibration)
✅ StorageService: 16/23 tests passing (Data persistence)
✅ ConsentService: 14/16 tests passing (Privacy management)
✅ Video Demos (Cypress E2E): 4/4 scenarios passing (render, toggle off/on, reduced motion, global disable)
Solution:
# Fix npm permissions (Linux/Mac)
sudo chown -R $(whoami) ~/.npm
# Or use yarn instead
pnpm install -g yarn
yarn installSolution:
# Kill existing process
pkill -f "vite"
# Or use different port
pnpm dev -- --port 3000Solution:
# Clear browser cache and storage
# In Chrome: F12 → Application → Clear Storage → Clear All
# Restart development server
npm run devSolution:
# Check TypeScript configuration
npx tsc --noEmit
# Fix common issues
pnpm lintSolution:
# Run tests in verbose mode
pnpm test -- --reporter=verbose
# Clear test cache
pnpm test -- --clearCacheCause: Browser autoplay policies Solution:
- Click anywhere on the page first to enable audio
- Check browser sound permissions
- Ensure system volume is up
Solution:
# Check if Tailwind classes are loading
# Inspect element and look for 'dark:' classes
# Rebuild if necessary
npm run buildSolution:
# Check health endpoint
curl http://localhost:3001/health
# Check PM2 status
pm2 status
# View detailed logs
pnpm pm2:logs
# Restart if needed
pnpm pm2:restart- Check Browser Console: F12 → Console for JavaScript errors
- Check Network Tab: F12 → Network for failed requests
- Verify Dependencies:
npm listto see installed packages - Restart Everything:
# Full reset rm -rf node_modules package-lock.json pnpm install npm run dev
RepCue includes full PM2 support for production deployment with an Express server. This is the recommended approach for Raspberry Pi and server deployments.
# 1. Install dependencies
pnpm install
# 2. Build frontend and start backend via PM2 (serves FE dist)
pnpm pm2:start
# 3. Check status
pm2 status
# 4. View logs
pnpm pm2:logs# 1. Build the application for production
pnpm build:prod
# 2. Start with PM2
pm2 start ecosystem.config.cjs
# 3. Save PM2 configuration
pm2 save
# 4. Setup auto-start on boot
pm2 startup| Command | Purpose |
|---|---|
pnpm pm2:start |
Build and start application |
npm run pm2:stop |
Stop the application |
pnpm pm2:restart |
Restart the application |
pnpm pm2:logs |
View application logs |
pm2 status |
Check all PM2 processes |
pm2 monit |
Real-time monitoring |
pm2 save |
Save current configuration |
The included apps/backend/ecosystem.config.cjs is optimized for Raspberry Pi deployment:
- Single instance for Pi resource efficiency
- 512MB memory limit with automatic restart
- Health monitoring at
/healthendpoint - Automatic daily restart at 4 AM
- Comprehensive logging with timestamps
# Build frontend
npm run build
# Start backend via PM2 (serves apps/frontend/dist)
pnpm pm2:startFor production deployment with custom domain:
Create /etc/nginx/sites-available/repcue.conf:
sudo nano /etc/nginx/sites-available/repcue.conf
server {
listen 80;
server_name repcue.azprojects.net;
# Security headers
add_header X-Frame-Options DENY;
add_header X-Content-Type-Options nosniff;
add_header X-XSS-Protection "1; mode=block";
add_header Referrer-Policy "strict-origin-when-cross-origin";
# Proxy to PM2 Express server
location / {
proxy_pass http://localhost:3001;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection 'upgrade';
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_cache_bypass $http_upgrade;
}
# Health check endpoint
location /health {
proxy_pass http://localhost:3001/health;
access_log off;
}
# Cache static assets
location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg|woff2)$ {
proxy_pass http://localhost:3001;
expires 1y;
add_header Cache-Control "public, immutable";
}
}# Enable site
sudo ln -s /etc/nginx/sites-available/repcue.conf /etc/nginx/sites-enabled/
# Test configuration
sudo nginx -t
# Reload nginx
sudo systemctl reload nginxAdd to your /etc/cloudflared/config.yml:
tunnel: YOUR_TUNNEL_ID
credentials-file: /path/to/credentials.json
ingress:
- hostname: repcue.azprojects.net
service: http://localhost:80
# Your other services...
- service: http_status:404Then: sudo systemctl restart cloudflared
Add DNS record in Cloudflare dashboard:
- Type: CNAME
- Name: repcue
- Content: YOUR_TUNNEL_ID.cfargotunnel.com
- Proxy: Enabled (Orange cloud)
RepCue is optimized for Raspberry Pi self-hosting. Here's the complete deployment guide:
- Raspberry Pi 4 or 5 (2GB RAM minimum, 4GB+ recommended)
- Raspberry Pi OS Lite or full version
- 16GB+ microSD card (Class 10 or better)
- Internet connection for initial setup
# Update system
sudo apt update && sudo apt upgrade -y
# Install Node.js 18
curl -fsSL https://deb.nodesource.com/setup_18.x | sudo -E bash -
sudo apt-get install -y nodejs
# Install nginx for web server
sudo apt install nginx -y
# Install git
sudo apt install git -y# Create app directory
sudo mkdir -p /var/www/repcue
sudo chown pi:pi /var/www/repcue
# Clone repository
cd /var/www/repcue
git clone https://github.com/akram0zaki/repcue.git .
# Install dependencies (this may take 10-15 minutes on Pi)
pnpm install
# Build for production (optimized, skips test compilation)
pnpm build:prodCreate nginx configuration:
sudo nano /etc/nginx/sites-available/repcueAdd this configuration:
server {
listen 80;
server_name localhost;
root /var/www/repcue/dist;
index index.html;
# Enable gzip compression
gzip on;
gzip_types text/css application/javascript application/json;
# Main app route
location / {
try_files $uri $uri/ /index.html;
}
# Cache static assets
location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg)$ {
expires 1y;
add_header Cache-Control "public, immutable";
}
}Enable the site:
# Enable site
sudo ln -s /etc/nginx/sites-available/repcue /etc/nginx/sites-enabled/
# Disable default site
sudo unlink /etc/nginx/sites-enabled/default
# Test configuration
sudo nginx -t
# Restart nginx
sudo systemctl restart nginx
sudo systemctl enable nginxCreate systemd service for automatic updates:
sudo nano /etc/systemd/system/repcue-update.service[Unit]
Description=RepCue Auto Update
After=network.target
[Service]
Type=oneshot
User=pi
WorkingDirectory=/var/www/repcue
ExecStart=/bin/bash -c 'git pull && pnpm build:prod'
[Install]
WantedBy=multi-user.targetCreate timer for weekly updates:
sudo nano /etc/systemd/system/repcue-update.timer[Unit]
Description=Weekly RepCue Update
Requires=repcue-update.service
[Timer]
OnCalendar=weekly
Persistent=true
[Install]
WantedBy=timers.targetEnable auto-updates:
sudo systemctl enable repcue-update.timer
sudo systemctl start repcue-update.timer-
Find your Pi's IP address:
hostname -I
-
Access RepCue: Open
http://YOUR_PI_IPin any browser on your network -
Optional - Set up local domain: Add to your router's DNS or
/etc/hosts:192.168.1.XXX repcue.local
sudo raspi-config
# Advanced Options → Memory Split → Set to 128# Check CPU/Memory usage
htop
# Check nginx status
sudo systemctl status nginx
# Monitor nginx logs
sudo tail -f /var/log/nginx/access.log# Check disk usage
df -h
# Clean npm cache if needed
npm cache clean --force
# Remove development dependencies after build
cd /var/www/repcue
npm prune --production- Offline Operation: RepCue works completely offline once loaded
- Low Resource Usage: ~50MB RAM, minimal CPU usage
- Local Data: All workout data stays on your Pi
- Mobile Optimized: Perfect for phones/tablets on your home network
- No Internet Required: After initial setup, works without internet
repcue/
├── apps/
│ ├── frontend/ # Vite + React + Tailwind (UI)
│ │ ├── src/
│ │ ├── public/
│ │ ├── index.html
│ │ ├── vite.config.ts
│ │ ├── tailwind.config.js
│ │ ├── vitest*.config.ts
│ │ └── package.json
│ └── backend/ # Express + PM2 (serves FE in prod)
│ ├── server.js
│ ├── ecosystem.config.cjs
│ └── package.json
├── packages/
│ └── shared/ # Shared types/constants (placeholder)
│ ├── src/index.ts
│ └── package.json
├── tests/
│ └── e2e/ # Cypress E2E workspace
│ ├── cypress/
│ ├── cypress.config.mjs
│ └── package.json
├── docs/
├── .github/
├── package.json # npm workspaces (root scripts)
└── CHANGELOG.md
- Frontend: React 19 + TypeScript + Vite
- Styling: Tailwind CSS v3 + Custom design system
- Routing: React Router v7
- Database: Dexie.js (IndexedDB wrapper)
- Testing: Vitest + React Testing Library
- Audio: Web Audio API + Speech Synthesis
- PWA: Service Worker ready (future enhancement)
A unified, reusable component for selecting exercises with comprehensive filtering capabilities. Used across TimerPage, CreateWorkoutPage, and EditWorkoutPage.
Features:
- Search: Real-time exercise search by name and description
- Catalog Filtering: Filter by exercise catalogs (default, custom, etc.)
- Category Filtering: Filter by exercise categories (core, strength, cardio, etc.)
- Type Filtering: Toggle between All, Built-in, Custom, and Shared exercises
- Favorites: Quick filter to show only favorite exercises
- Sorting: Sort by name, type, or recently added
- Exclusion: Optionally exclude exercises (e.g., already added to workout)
- Persistence: Optional filter state persistence via localStorage
Usage Example:
import { ExerciseSelectorModal } from '../components/ExerciseSelector/ExerciseSelectorModal';
<ExerciseSelectorModal
exercises={exercises}
selectedExercise={selectedExercise}
excludeExercises={alreadyAdded}
onSelectExercise={(exercise) => handleSelect(exercise)}
isOpen={isModalOpen}
onClose={() => setIsModalOpen(false)}
title="Select Exercise"
showCatalogSelector={true}
showCategoryFilter={true}
showTypeFilter={true}
showFavoritesToggle={true}
showSearch={true}
showSort={true}
persistFilters={true}
filterStorageKey="my-selector-filters"
/>Related Files:
- ExerciseSelector.tsx - Main component
- ExerciseSelectorModal.tsx - Modal wrapper
- useExerciseFilter.ts - Filtering logic hook
-
Start Development:
- Frontend:
npm run dev - Backend (optional):
pnpm dev:be
- Frontend:
-
Make Changes: Edit files in
apps/frontend/src/ -
Test Changes:
pnpm test pnpm lint -
Build & Test:
npm run build && pnpm preview -
Deploy: Use PM2 (
pnpm pm2:start) to serveapps/frontend/distvia backend
- Frontend
.env: place underapps/frontend/.env(Vite reads it by default). Only variables prefixed withVITE_are exposed to the client. - Backend
.env: when backend features are added, useapps/backend/.envwithdotenv.
-
Create branch:
feat/workspaces-reorg. -
Move files:
- UI: move
src/,public/,index.html,vite.config.ts,tailwind.config.js,postcss.config.js,vitest*.config.ts,tsconfig*.json→apps/frontend/. - Backend: move
server.js,ecosystem.config.cjs→apps/backend/. - Cypress: move
cypress/,cypress.config.mjs→tests/e2e/. - Env: move root
.env→apps/frontend/.env(ensureVITE_prefixes).
-
Add workspaces: root
package.jsonwithworkspaces: ["apps/*","packages/*","tests/e2e"]and scripts shown above. -
Update helper scripts to new paths (already wired here):
apps/frontend/scripts/*. -
Install and validate:
pnpm install
npm run build
npm run dev # FE at 5173
pnpm dev:be # BE at 3001 (optional placeholder)- PM2 on Pi/server:
npm run build
pnpm pm2:start- Cypress E2E (now at
tests/e2e):
pnpm build && pnpm preview # FE preview at 4173
pnpm cypress:run- CI (GitHub Actions): switch to workspace install (
pnpm install --frozen-lockfileor cache), runpnpm lint && pnpm test && pnpm buildfrom root.
For a deep dive into how data sync works (high‑level and inner workings), plus steps to add new entities to the sync scope, see:
- Edit
src/data/exercises.ts - Add exercise object with required fields
- Test in Timer page
- Create component in
src/pages/ - Add route in
src/App.tsx - Add navigation item in
src/components/Navigation.tsx - Write tests in
src/pages/__tests__/
- Create service in
src/services/ - Follow singleton pattern like existing services
- Add comprehensive tests
- Update type definitions if needed
RepCue is designed with privacy as the top priority:
- Local-Only Storage: All data stays on your device/Pi
- No Tracking: Zero analytics, cookies, or external requests
- GDPR Compliant: Full consent management system with versioning
- Open Source: Complete transparency in code
- Self-Hosted: You control your data completely
RepCue implements a comprehensive, regulation-compliant consent management system. For detailed information about our privacy implementation, see consent.md which covers:
- GDPR/CCPA compliance features
- Consent versioning and migration
- User control and transparency
- Technical implementation details
- Privacy-first design principles
- Exercise preferences and favorites
- Workout session logs
- App settings (sound, theme, etc.)
- User consent preferences
- No cloud syncing
- No user accounts
- No external APIs
- No telemetry or analytics
This project is licensed under the MIT License - see the LICENSE file for details.
We welcome contributions! Here's how to get started:
- Fork the repository
- Create a feature branch:
git checkout -b feature/amazing-feature - Make your changes and add tests
- Run the test suite:
pnpm test - Check linting:
pnpm lint - Commit changes:
git commit -m 'Add amazing feature' - Push to branch:
git push origin feature/amazing-feature - Create a Pull Request
- TypeScript strict mode enabled
- ESLint + Prettier for code formatting
- 90%+ test coverage for new features
- Accessibility compliance (WCAG 2.1)
- Mobile-first responsive design
- ✅ Core interval timer functionality
- ✅ 20 exercise library with categories
- ✅ Expandable exercise tags with smooth animations
- ✅ Audio feedback and voice announcements
- ✅ Activity logging and tracking
- ✅ GDPR-compliant privacy controls
- ✅ Mobile-responsive design
- ✅ Dark mode support
- ✅ PM2 production deployment support
- ✅ Express server with health monitoring
- ✅ Nginx + Cloudflare tunnel integration
- ✅ PWA Platform Detection: Cross-platform detection system with comprehensive browser/OS identification
- ✅ TypeScript Integration: Fully typed platform utilities with 100% test coverage
- 🔄 Install Experience: Smart install prompts for iOS, Android, and Desktop
- 🔄 Offline Functionality: Full offline workout capability with background sync
- 🔄 App Shell Architecture: Instant loading with persistent navigation
- ✅ Platform Detection: Cross-platform detection system (Completed)
- 🔄 Custom workout routines
- 🔄 Exercise progression tracking
- 🔄 Import/export workout data
- 📋 Workout scheduling
- 📊 Advanced analytics and charts
- 🏆 Achievement system
- 🔊 Custom audio cues
- 🔄 Background Sync: Automatic data sync when online
- � Push Notifications: Workout reminders and achievements
RepCue - Your data, your device, your fitness journey. 🏋️♀️💪
For questions, issues, or feature requests, please open an issue on GitHub or check our troubleshooting guide above.
Built with ❤️ for the privacy-conscious fitness community.