BikeControl (SwiftControl)
使用 Zwift Click、Zwift Play、Shimano Di2,或任何藍牙控制器搭配 MyWhoosh、Rouvy、Zwift 等。可於 iOS、Android、Windows 與 macOS 免費試用。
AI 翻譯
尚未偵測到更新。GitHub 發布版本後會顯示在這裡。
個人 GitHub 帳號下公開儲存庫的擁有者可以登入並驗證所有權,自行管理介紹,無需管理員核准。
登入並產生驗證檔案,在 30 分鐘內將其提交至此儲存庫的預設分支。其他分支或分叉儲存庫中的檔案無效。然後在下方檢查檔案,即可無需管理員核准直接管理專案。請保留檔案以維持管理權限。
如需登入,將前往登入頁面。登入後請回到此處,再次點擊所選的驗證按鈕。
在專案資料夾中執行 /nomorevibe verify,即可驗證所有權並自行管理介紹。
一分鐘完成驗證正在載入完整 README。 以下為已儲存的摘錄。
A phone-first training app: pick a session, hit start, work the checklist. Thirty plans
across free weights, elastic bands, bodyweight, climbing-gym days, soccer, standing-only travel
sessions, band work for walks and bus stops, and warm-ups, with a session clock, superset
grouping, a rest timer, a hangboard interval timer, and form cues on every exercise.
Everything is stored in localStorage. No account, no database, no server calls.檢視完整 README ↗A phone-first training app: pick a session, hit start, work the checklist. Thirty-nine plans across free weights, elastic bands, bodyweight, climbing-gym days, soccer, golf, knee rehab, standing-only travel sessions, band work for walks and bus stops, and warm-ups, with a session clock, superset grouping, a rest timer, an interval timer, and form cues on every exercise.
Everything is stored in localStorage. No account, no database, no server calls. A backup in
Settings exports it to one JSON file and merges it back in on any device.
| Category | Plans | Length |
|---|---|---|
| Free Weights | Push Day, Pull Day, Legs & Core, Dumbbell Express | 40-75 min |
| Elastic Bands (bands + a door) | Upper Body, Legs & Glutes, Full-Body Express, Shoulder & Elbow Prehab | 15-60 min |
| Bodyweight (floor + one chair) | Push & Core, Pull & Posterior, Legs, Express Circuit | 25-55 min |
| Climbing Gym (wall, hangboard, weights) | Hangboard & Limit Boulders, Power Endurance 4x4s, Repeaters & Volume, Technique & Movement | 60-120 min |
| Soccer | Injury Prevention, Strength & Power, Speed & Agility, Ball Skills & Touch, Match Day Warm-Up | 20-65 min |
| Golf | First Tee Warm-Up, Range Warm-Up, Strength & Power, Clubhead Speed, Mobility & Back Care | 8-55 min |
| Knee Care | Phase 1: Calm & Activate, Phase 2: Build Strength, Phase 3: Load & Return, Knee Prep Before Sport | 8-45 min |
| Standing Only (nothing, one square metre) | Gate Area Reset, Long-Haul Recovery, Standing Climber Prehab | 8-20 min |
| On the Move (one band, no anchor) | Band Walk, Bus Stop Band | 6-25 min |
| Warm-Up & Recovery | Express Warm-Up, Full Warm-Up, Pre-Gym Activation, Post-Session Recovery | 10-30 min |
Elastic Bands exists because bands load the opposite way to free weights — hardest at the end of the range, easiest at the start — which makes them better than dumbbells for shoulder, rotator cuff and glute-medius work and worse for anything you want heavy. Rep ranges run higher to match.
Soccer leads with injury prevention because it is the only part of this app with a serious evidence base: meta-analyses of the Nordic hamstring exercise in football squads report roughly a halving of hamstring injury rates, trials of Copenhagen adduction report a large reduction in groin problems, and FIFA 11+ style warm-ups report around a third fewer injuries overall. Figures vary by study and by adherence, so the plan notes give them as orders of magnitude rather than precise numbers. Everything else in the category — speed, power, touch — is ordinary programming.
Golf splits into two warm-ups and three training sessions. The warm-ups contain no static stretching: in a trial of young competitive golfers, holding stretches before swinging reduced clubhead speed, distance and quality of contact, so the holds live in the mobility session instead. The training sessions put rotational and lower-body power first, because those track clubhead speed more closely than arm strength, and finish on the lead elbow and the low back, the two places amateur golfers most often get sore.
Knee Care is phased rehab rather than a menu: calm an irritable knee with isometrics and activation, build quad and hip strength (combined hip and knee work has the strongest evidence for kneecap pain), then load it heavy and slow and add landing before returning to sport. Every phase names the numbers that unlock the next one. Progress is paced by symptoms, not the calendar, using the pain-monitoring model physiotherapists use for tendon and kneecap pain: up to 3/10 during the work is acceptable if it has settled by the next morning. The morning check-in on the Knee filter records two 0-10 readings and turns them into a call: step back if either is over 3, hold if morning pain rose by 2 or more since the last check-in, progress after three clean check-ins in a row. The rule is a pure function with its own tests. None of this is a diagnosis, and every phase plan says when to see a clinician instead.
Standing Only has a hard constraint: nothing touches the floor, no chair, no kneeling, and it fits in about one square metre. Built for a gate area or a hotel corridor. A wall is used only for balance and every exercise that mentions one works without it.
On the Move is band work with no door: everything is self-anchored, held in the hands or stood on. The walk session is hand-held only, so you never stop walking; the bus-stop session is written in priority order in self-contained blocks, because the bus decides when it ends. A test keeps anchored movements out of the category and stood-on movements out of the walk.
Loads are left to the athlete. Plans prescribe reps, rest and intent only.
Works offline and installs to the home screen. A service worker precaches the shell and every exercise image, so the app runs with no signal — the normal condition in a basement gym.
Session clock with pause/resume, persisted across reloads and browser restarts.
Checklist per exercise per round. Multi-round single-exercise blocks (max hangs, limit boulder attempts) collapse into numbered set buttons.
Superset and circuit grouping, marked with a badge and a colored rail so paired work is obvious at a glance.
Exercise overlay with a large illustration, form cues and step-by-step instructions. Opens from a session, the library, or a plan you have not started yet, and never reflows the page behind it.
Back button works. Every view and every overlay is a history entry, so the Android back gesture walks back through the app instead of leaving it. Views are URLs, so a session survives a reload and a plan can be linked to directly.
Rest timer in a fixed bottom bar, with the current block's prescribed rest preselected. Audio countdown on the last three seconds.
Interval timer for hangboard protocols (7s on / 3s off x 6) and timed circuits (40/20), driven by the block's own spec.
Per-exercise timers on any prescription that is purely a duration — planks, wall sits, dead hangs, carries. One tap starts it in the bottom bar.
Session customization: skip a block or change its round count mid-session. Progress recalculates against what you are actually doing.
Added-weight log on hangboard blocks, off by default and enabled in settings. Prefilled with last session's number, in lb or kg.
Progression ladders for fifteen movement patterns — push-up, dip, overhead, pull-up, row, squat, hinge, two core ladders, fingers, jumping, adductor, rotation, knee strength and patellar tendon loading. Each rung carries a countable criterion for moving up, and collapsed ladders show a thumbnail strip so you can see the whole progression at a glance. A test rejects any rung whose criterion has no number in it.
Saved exercises. Star anything from the overlay or the library; the Saved view lists them newest first with per-item removal and a clear-all.
UI density — compact, normal or comfortable. Row height, card padding, gaps, type size and control size all scale from one set of CSS custom properties on the document root, so a session page runs roughly 1900px tall at compact and 2500px at comfortable.
Next.js 16 (App Router), React 19, TypeScript, hand-written CSS. No UI framework, no state library, no analytics, three runtime dependencies.
Session state is read through useSyncExternalStore over localStorage, so there is no
after-mount hydration flicker. Navigation is a fourth store of the same shape, backed by the
History API: lib/navigation.ts owns which view and which overlay are showing, and the URL is
the source of truth (?v=week, ?v=preview&p=fw-push, ?...&ex=bench-press). Real Next routes
would give back/forward for free, but each route would be another document the service worker
has to precache, and the overlay is a layer over a view rather than a view of its own — so one
document with explicit history entries handles both uniformly and keeps offline simple. Both timers expand their schedule into wall-clock deadlines and
derive the current phase from the clock, which means a backgrounded tab or a locked screen —
where browsers throttle timers to roughly once a minute — cannot desynchronize a hangboard set.
npm install
npm run dev # http://localhost:3000
npm run build # production build
npm run check # typecheck + lint + tests
npm test runs 44 checks with node --test and no test framework. Most guard the data:
unresolved exercise keys, duplicate ids, block minutes against the advertised session length,
interval specs matching block rounds, image frames present on disk, and week templates never
stacking two finger-intensive days. The rest cover duration parsing, the knee check-in rule,
session progress counting, Chalk's current-block and next-item logic, the merge rules behind
backup import, the pre-paint style script, and the light theme's contrast, read straight from
globals.css.
Editing lib/plans.ts by hand is the likely way this breaks, and a typo'd exercise key renders
nothing at all — which you would discover standing at the hangboard.
The repo is a stock Next.js app, so Vercel needs no configuration:
next build, no env vars.Or from the CLI:
npx vercel # preview deployment
npx vercel --prod # production
Plans are data, not components. To add or edit a session, edit lib/plans.ts; to add a movement,
add an entry to lib/exercises.ts and reference its key from a plan slot. lib/types.ts documents
both shapes. A block declaring an interval spec automatically renders an interval timer.
| Key | Contents |
|---|---|
ct.active.v2 | The in-progress session: plan id, elapsed time, checked slots, skipped blocks, round overrides |
ct.history.v2 | Last 60 finished sessions |
ct.loads.v1 | Added weight per hangboard block, last 8 entries each |
ct.settings.v1 | Preferences, merged over defaults so new settings are not undefined |
ct.favorites.v1 | Starred exercise keys, newest first, capped at 200 |
ct.knee.v1 | Knee check-ins (day, morning pain, session pain), newest first, capped at 90 |
Navigation is not stored: it lives in the URL and the browser's history stack, namespaced under a
ct key inside history.state so the App Router's own bookkeeping there survives a popstate.
Replacing that state wholesale makes Next resynchronize with a full page navigation, which silently
reloads the document and discards component state.
Clearing site data resets both. Every key but ct.active.v2 goes into a backup; without one, a
cleared browser or a new phone starts from nothing.
Illustrations come from free-exercise-db, released into the public domain under the Unlicense. Each entry provides a start and an end frame; the app crossfades them, which reads like a GIF at about 10 KB per frame — 1.7 MB for all 174 frames, small enough to precache entirely for offline use.
Two frames is the ceiling, not a shortcut. All 873 dataset entries have exactly two images — there are no exceptions in the data. wger is unreachable behind some network policies and its imagery is CC-BY-SA rather than public domain; everkinetic is an abandoned migration with no declared license. Generating intermediate frames from two stills would be a cross-dissolve, which is what the animation already does and adds no information. What the dataset does carry, and the app now uses, is a step-by-step instruction list per exercise — bundled for 85 of the 87 illustrated movements and shown in the overlay under the cues.
scripts/exercise-image-map.json maps exercise keys to dataset ids by hand. Automatic name
matching produces confident nonsense (it pairs "Barbell Row" with "Barbell Curl"), and an image
that teaches the wrong movement is worse than no image, so every pair was checked by eye.
The movements with no illustration are climbing-specific (hangboard grips, limit bouldering, footwork drills) or standing-only variants with no equipment-free match in the dataset — the dataset's calf raise is on a machine and its quad stretch needs a box, which contradicts the whole premise of the standing plans. Most golf and knee rehab movements are missing too: the dataset has no clamshell, Spanish squat, terminal knee extension or club drill, and its split squat is a jump. Leg swings are the one golf match. Those keep their text cues plus a plain web search link. That link deliberately does not point at youtube.com, because Android hands those URLs to the YouTube app.
To regenerate after changing the mapping:
npm i -D sharp && node scripts/fetch-exercise-images.mjs
sharp is only needed to re-run the pipeline, so it stays out of the dependency tree.
public/sw.js is hand-written rather than generated. The app is one static page plus images, so
a build plugin would add coupling without adding capability — and a plugin that breaks on a Next
upgrade would take offline support down with it.
public/image-manifest.json.Bump VERSION in public/sw.js when the shell changes in a way that must invalidate caches.
Two styles, Classic and Chalk, switched in Settings. Classic is the original. Chalk is a one-handed phone redesign:
An inline script in <head> applies the stored style and theme before first paint, so a light
theme does not flash dark on open. Every Chalk rule is scoped to data-style="chalk" on the
document root, so Classic renders pixel-for-pixel as it did.
Settings for style and theme, density, weight logging and unit, timer sounds, screen wake lock and figure animation.
Backup and restore. Export writes sessions, knee check-ins, the hangboard log, saved
exercises and settings to training-days-yyyyMMdd_HHmm.json. Import merges rather than
replaces: entries match by id, day or timestamp, the copy already on the device wins a clash,
and importing the same file twice changes nothing — so a phone and a tablet can trade files.
Settings come across only to a device that has none of its own, since density is a per-device
choice. The session in progress is never exported.
Filters that fit a phone. Plan categories, directory groups and week templates are a wrapped
grid with every option visible, not a sideways-scrolling chip row that hid most of its options
off-screen. On long lists a pill appears once the grid scrolls away and jumps back to it. The
chosen filter lives in the URL (?f=golf), so opening a plan and pressing back keeps it.
Week structure with six rotations and the spacing rules behind them, plus a recovery warning if you open a finger-intensive plan within 48 hours of the last one.
Exercise directory — all 200 movements in one scrollable list, grouped the way the plans use
them (warm-up, fingers, push, pull, legs, core, climbing, bands, standing, three soccer groups,
golf, knee).
Filter to a group or search across everything; search results are labelled with their group. The
grouping is generated from the section comments in lib/exercises.ts, so it cannot drift from
the file it describes, and a test asserts every exercise appears in exactly one group.
Screen wake lock while a session runs, so the phone does not sleep between sets.