BikeControl (SwiftControl)
Use Zwift Click, Zwift Play, Shimano Di2, or any Bluetooth controller with MyWhoosh, Rouvy, Zwift, and more. Free to try on iOS, Android, Windows, and macOS.
GlideComp는 행글라이딩·패러글라이딩 대회 트랙을 분석해 파일럿의 상승·활공·노선 선택을 평가하고 브라우저에서 점수를 매긴다
GlideComp analyses every track flown in a hang gliding or paragliding competition — how the fast pilots climbed, glided and chose their lines — and scores the comp in the browser, GAP or open distance
스포츠·피트니스TypeScript · HTMLGitHub @pokle온라인운영자 미확인
써 보셨어요? 한 번만 눌러 주세요. 로그인은 필요 없어요.
개인 계정의 공개 저장소 소유자는 GitHub로 로그인해 운영자를 확인하고 소개를 직접 관리할 수 있습니다. 관리자 승인은 필요하지 않습니다.
로그인한 뒤 확인 파일을 만들어 이 저장소의 기본 브랜치에 30분 안에 커밋해 주세요. 다른 브랜치나 포크의 파일은 인정하지 않습니다. 파일을 올린 뒤 아래에서 확인하면 관리자 승인 없이 관리할 수 있습니다. 자격을 유지하려면 파일을 남겨 두세요.
로그인이 필요하면 로그인 화면으로 이동합니다. 로그인 후 이곳으로 돌아와 원하는 확인 버튼을 다시 눌러 주세요.
프로젝트 폴더에서 /nomorevibe verify를 실행하면 소유권을 확인하고 소개를 직접 관리할 수 있습니다.
1분 만에 확인하기Hang Gliding and Paragliding competition task score and flight analysis
저장소 README에서 가져옴
README 전체를 불러오는 중입니다. 아래는 저장된 발췌입니다.
GlideComp scores hanggliding and paragliding competitions — and analyses the whole field's flights, so pilots can learn from the people they flew the day with.README 전문 보기 ↗GlideComp scores hanggliding and paragliding competitions — and analyses the whole field's flights, so pilots can learn from the people they flew the day with.
What it does:
How it's built:
@glidecomp/engine package handles geo calculations (WGS84 ellipsoid math, Vincenty formulas) and flight analysisKey design principles:
The app is live at glidecomp.com.
glidecomp.com and localhost:3000 are in the allowed URLs list.bun install
# Copy .env.example and add your MapBox token
cp .env.example .env
# Edit .env and set VITE_MAPBOX_TOKEN=your_token_here
Start the frontend and all API workers together:
bun run dev
This starts two processes:
wrangler dev session, reached through the dev-router Worker at
http://localhost:8790Every worker shares a single Miniflare instance, so the D1 database has one
writer — running them as separate processes on separate ports raced on the
shared SQLite file (issue #477). Only the router's port is exposed; it
dispatches /api/auth/*, /api/comp/*, /api/airscore/* and friends to the
right worker over service bindings, exactly as the Pages Functions do in
production. Vite proxies all of /api there, so the browser only ever talks to
:3000. To start the workers on their own: bun run dev:workers.
The auth worker requires a .dev.vars file in web/workers/auth-api/ — see docs/auth.md for setup.
To start only the frontend (without any workers):
bun run dev:frontend
AirScore features are served by the same router; nothing extra to start. If you'd rather use the production AirScore API instead of the local worker:
VITE_AIRSCORE_URL=https://glidecomp.com/api/airscore bun run dev
bun run preview builds the site and serves it through wrangler pages dev, so
you exercise the SSR Functions, the sitemap and the Astro pages exactly as
production does — no HMR, so a code change needs a re-run.
bun run preview # http://localhost:3000
bun run preview binds ports on your Mac (pages dev, the Workers' dev-router,
a workerd inspector) and keeps D1 + R2 in web/.wrangler/state, so a second copy
collides with the first. To run one that can't, put it in a container — the
stock ports stay inside the VM and only one is published:
bun run preview:container # http://localhost:3200
PORT=3201 bun run preview:container # a second, wholly independent instance
Requires Apple's container (macOS 26+,
Apple silicon): brew install container. Each PORT gets its own named volume
for D1 + R2, so instances never see each other's data — or your host checkout's.
Your checkout is bind-mounted read-only and staged into the container on
start, so a source edit needs a restart (Ctrl-C, re-run), not an image rebuild.
There's no HMR — preview serves a production build, same as on the host. Use
bun run dev for a fast edit loop.
The image holds only the dependency tree, and the script rebuilds it for you
when it needs to: it hashes the files that layer is built from (bun.lock, the
workspace manifests, patches/) and compares against a stamp beside the image,
printing the reason when it rebuilds. Editing source never triggers one. Even a
missed rebuild is only ever slower, not wrong — the entrypoint runs
bun install --frozen-lockfile after staging, which reconciles node_modules
to whatever lockfile the mount brought in.
| variable | default | when you need it |
|---|---|---|
PORT | 3200 | run more than one instance |
SKIP_SEED | 0 | keep an instance's existing data instead of reseeding |
REBUILD | 0 | force an image rebuild — normally detected for you |
CPUS / MEMORY | 6 / 8G | vite build is OOM-killed below ~4G |
The default port is 3200 because 3000 (vite), 3100 (SSR e2e), 4321 (astro),
8790 (the Workers' dev-router) and 9232 (its inspector) are all already spoken
for — and
because a native bun run dev binds Vite to [::1]:3000 while a published
container port binds 127.0.0.1:3000. macOS treats those as different sockets,
so both bind happily and localhost quietly serves you whichever one the
resolver picked. The script refuses a port anything already holds, on either
stack, rather than let that happen.
The browser only ever talks to the published port. The one thing that doesn't
work in the container is the analysis page's AirScore import — it calls
same-origin /api/airscore, and there's no Pages Function proxying that, so it
404s here exactly as it does in production.
bun run test # bun test sweep (root, engine, airscore-api, dev-router, scripts) + type check
bun run test:all # the above, then the three vitest suites concurrently
bun run test:comp # Competition API tests only
bun run test:auth # Auth API tests only
bun run test:frontend # Frontend tests only
bun run test:e2e # Playwright end-to-end tests
bun run test:e2e:ssr # Playwright suite against the server-rendered build
bun run db:migrate # Apply D1 migrations to the local state
bun run typecheck # Type check the root project
bun run typecheck:all # Type check everything (frontend + engine + all four workers)
The e2e suite writes to the persistent local D1 state in web/.wrangler/state.
Specs clean up after themselves and each run sweeps anything a killed run left
behind, so this should stay tidy on its own — but if local e2e results ever look
inexplicable, bun run kill-state resets the database to empty (then
bun run seed to put the sample comps back).
Read https://developer.chrome.com/blog/chrome-devtools-mcp-debug-your-browser-session
master → deploys to productionbun run deploy # Manual deploy to Cloudflare Pages
bun run deploy:worker # Manual deploy AirScore API Worker
bun run deploy:comp # Manual deploy Competition API Worker
bun run deploy:auth # Manual deploy Auth API Worker
bun run deploy:all # Deploy Pages + AirScore & Competition Workers (auth-api is separate: deploy:auth)
URLs:
detect-events - Detect flight events from an IGC file, outputting CSV:
bun run detect-events -- <flight.igc> [task.xctsk]
# Example with sample data
bun run detect-events -- \
web/frontend/public/data/tracks/2025-01-05-Tushar-Corryong.igc \
web/frontend/public/data/tasks/buje.xctsk
get-xcontest-task - Download a task from XContest by code:
bun run get-xcontest-task -- face
bun run get-xcontest-task -- --file task.json
score-task - Score multiple pilots against a task using CIVL GAP (FAI Sporting Code Section 7F):
bun run score-task -- <task.xctsk> <igc-file-or-folder>... [options]
# Scores identically to the web app. --wing (HG or PG) is REQUIRED for GAP
# scoring — the CLI has no comp record and won't guess. Given it, the run starts
# from the official per-category FAI/S7F defaults and each flag overrides one
# parameter. Flag names are the kebab-case of the gap_params keys the UI saves;
# units are the engine's (metres / seconds / 0-1 ratios). Grouped by function:
#
# Wing (required for GAP):
# --wing <HG|PG> Competition wing (`scoring`)
# Task mode:
# --open-distance Score as open distance (GAP options ignored)
# --comp <slug-or-dir> Score a whole bundled comp (every task per class,
# plus a per-class cross-task aggregate); wing comes
# from the comp.json manifest. Implies --analysis.
# Task analysis:
# --analysis After the scores, print the behavioural task
# analysis — per-pilot metrics (climbing, gliding,
# decisions, gaggle, race craft, day profile/wind)
# led by the metric-separation ranking (Spearman ρ
# vs GAP rank). See docs/2026-07-18-field-analysis-plan.md.
# Nominal parameters:
# --nominal-distance <m> `nominalDistance` (default: 70% of task distance)
# --nominal-distance-pct <%> That percentage, when you'd rather not do the sum
# --nominal-goal <ratio> `nominalGoal` 0-1 (default: 0.3)
# --nominal-time <s> `nominalTime` in seconds (default: 5400)
# --minimum-distance <m> `minimumDistance` in metres (default: 5000)
# Scoring terms (per-wing defaults; each has a --use-… form to turn it back on):
# --no-use-leading Disable leading (departure) points (`useLeading`)
# --no-use-arrival Disable arrival points (`useArrival`)
# --no-use-distance-difficulty Disable difficulty points (`useDistanceDifficulty`)
# Stopped tasks (S7F §13.4):
# --stop-time <iso-datetime> Announce a stop, e.g. 2026-01-15T03:45:00Z
# --score-back-time <s> PG score-back window (default: 300)
# Formula & advanced:
# --leading-formula <weighted|classic> `leadingFormula` (default: classic HG / weighted PG)
# --leading-weight-formula <gap2020|s7f2024> PG leading weight (default: gap2020)
# --time-points-exponent <5/6|2/3> `timePointsExponent` (default: 5/6)
# --ess-not-goal-factor <ratio> `essNotGoalFactor` (default: 0.8 HG / 0 PG)
# (see --help for the full list, incl. nominal-launch, distance-origin,
# jump-the-gun-factor, jump-the-gun-max-seconds, leading-time-ratio)
# Output:
# --json Output as JSON
# Example: score Corryong Cup 2026 Open Task 1 as HG with the official defaults
bun run score-task \
web/samples/comps/corryong-cup-2026-open-t1/task.xctsk \
web/samples/comps/corryong-cup-2026-open-t1/ \
--wing HG
# Example: score the whole bundled comp, with task analysis per task and a
# per-class cross-task aggregate
bun run score-task -- --comp corryong-cup-2026
Example output:
=== Task Scoring Results (CIVL GAP) ===
Scoring config:
Sport: HG
Leading: on (classic)
Time exponent: 5/6
Arrival: on
Difficulty: on
Nominal: dist 55.2 km / time 90 min / goal 30% / launch 96%
Task distance: 78.8 km
Pilots: 32 flying / 32 present
In goal: 12 (37.5%)
Best time: 1:37:55
Task Validity: 100.0%
Available Points: 1000 (dist: 486, time: 360, lead: 90, arr: 64)
# Pilot Dist SS Time Dist Pts Diff Pts Time Pts Lead Pts Arr Pts Total
-----------------------------------------------------------------------------------------------------------------------
1 Rohan Holtkamp 78.8 km 1:47:55 485.6 242.8 294.1 90.0 64.3 934
2 Jon Durand 78.8 km 1:37:55 485.6 242.8 360.1 21.2 44.2 911
3 Peter Burkitt 78.8 km 1:50:13 485.6 242.8 281.7 89.4 53.4 910
...
13 Rich Reinauer 76.9 km LO 479.7 242.8 0.0 0.0 0.0 480
The full HG table also shows a leading-coefficient (
LC) column; it's elided here for width.
seed - Seed the bundled sample competitions into local D1 + R2 (idempotent; --remote targets production):
bun run seed # every bundled comp
bun run seed big-chip kosci-loop # just these slugs
civl-rankings - Import the FAI/CIVL monthly world pilot rankings into D1 (all ten disciplines; idempotent, and a no-op on a day CIVL hasn't published anything new). Runs daily in CI — see docs/civl-rankings.md:
bun run civl-rankings
build-3dvis - Pack a task's tracks into a 3D replay asset for offline inspection (the replay itself loads it from the Worker):
bun run build-3dvis
bench-task / bench-analysis - Benchmark the scoring and analysis paths (see the benchmark-engine skill).
functions/ - Cloudflare Pages Functions (SSR for the public /comp pages,
/api/* proxies to the Workers, sitemap, middleware)
web/
frontend/ - Cloudflare Pages frontend (Vite + TypeScript)
static/ - Astro app: the prerendered content pages (home, about,
legal, the scoring guides)
engine/ - Shared analysis library (IGC parsing, event detection, GAP scoring,
task analysis, track quality, weather)
cli/ - CLI utilities (detect-events, get-xcontest-task, score-task, build-3dvis, benchmarks)
workers/
auth-api/ - Authentication API (Cloudflare Worker + D1)
competition-api/ - Competition management API (Cloudflare Worker + D1 + R2)
airscore-api/ - AirScore caching proxy (Cloudflare Worker)
dev-router/ - Dev-only router: owns :8790 and dispatches to the three above
db/
migrations/ - D1 schema migrations (shared by auth-api and competition-api)
samples/ - Bundled sample competitions, CIVL ranking fixtures, reference data
scripts/ - Operational scripts (comp seeding/download/generation, dev helpers, secrets)
e2e/ - Playwright end-to-end tests
docs/ - Feature and architecture specifications
patches/ - Patched dependencies (applied by bun)