diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml new file mode 100644 index 0000000..398c7f1 --- /dev/null +++ b/.github/FUNDING.yml @@ -0,0 +1,7 @@ +# Shows a "Sponsor" button on the repo. Fill in the handles you actually use; +# delete the lines you don't. GitHub validates these, so a wrong handle just +# hides that entry rather than breaking the page. +github: [Alvaro5] # requires GitHub Sponsors enabled on your account +# ko_fi: your-handle +# buy_me_a_coffee: your-handle +# custom: ["https://gradepace.vercel.app"] diff --git a/.github/ISSUE_TEMPLATE/bug_report.yml b/.github/ISSUE_TEMPLATE/bug_report.yml new file mode 100644 index 0000000..8f67cb6 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug_report.yml @@ -0,0 +1,42 @@ +name: Bug report +description: Something in the pacing plan or UI is wrong or broken. +labels: [bug] +body: + - type: markdown + attributes: + value: | + Thanks for the report. GradePace runs entirely in your browser, so the + more you can tell me about the GPX and your setup, the faster this gets fixed. + - type: textarea + id: what-happened + attributes: + label: What happened? + description: What did you see, and what did you expect instead? + placeholder: "The projected finish was 3:12 but the course is 70 km, so that can't be right..." + validations: + required: true + - type: textarea + id: gpx + attributes: + label: The GPX file + description: > + If you can share the course (a link, or drag the file into the comment + box after creating the issue), that makes it reproducible. Note if it's a + recorded track (with times) or a route export. + validations: + required: false + - type: input + id: settings + attributes: + label: Settings + description: Flat pace, VAM, hike gate, terrain factor, units, language, if relevant. + placeholder: "6:00/km, VAM 750, gate 18%, terrain x1.04, metric, EN" + validations: + required: false + - type: input + id: env + attributes: + label: Browser / device + placeholder: "Chrome 141 on macOS, or Safari on iPhone" + validations: + required: false diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml new file mode 100644 index 0000000..6962fef --- /dev/null +++ b/.github/ISSUE_TEMPLATE/config.yml @@ -0,0 +1,5 @@ +blank_issues_enabled: true +contact_links: + - name: Try GradePace + url: https://gradepace.vercel.app + about: The live app. Load an example course to see the full output before filing. diff --git a/.github/ISSUE_TEMPLATE/feature_request.yml b/.github/ISSUE_TEMPLATE/feature_request.yml new file mode 100644 index 0000000..13345a0 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/feature_request.yml @@ -0,0 +1,25 @@ +name: Feature request +description: Suggest something that would make the pacing plan more honest or the app more useful. +labels: [enhancement] +body: + - type: textarea + id: problem + attributes: + label: What are you trying to do? + description: Describe the racing/planning problem, not just the feature. What can't you do today? + validations: + required: true + - type: textarea + id: idea + attributes: + label: Your idea + description: How would you like it to work? Rough is fine. + validations: + required: false + - type: checkboxes + id: thesis + attributes: + label: Fit check + description: GradePace is deliberately about honest self-calibration, not false precision. (Optional.) + options: + - label: This is about a more honest or personal prediction, not just more knobs to tune. diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md new file mode 100644 index 0000000..b820fa4 --- /dev/null +++ b/.github/pull_request_template.md @@ -0,0 +1,12 @@ + + +## What and why + + + +## Checklist + +- [ ] `npm run lint && npm test && npm run build` all pass +- [ ] Added or updated tests for any engine/behavior change +- [ ] If this moves the projected finish time, I've called that out explicitly +- [ ] STATUS.md / README updated if the change affects them diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..fbb2613 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,42 @@ +name: CI + +on: + push: + branches: [main] + pull_request: + branches: [main] + +# Cancel superseded runs on the same ref (e.g. rapid pushes to a PR). +concurrency: + group: ci-${{ github.ref }} + cancel-in-progress: true + +jobs: + build-and-test: + name: Lint, test, build + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + + - name: Set up Node + uses: actions/setup-node@v4 + with: + node-version: 22 + cache: npm + + # `npm install`, not `npm ci`: sharp and the tailwind-oxide / rolldown + # wasm fallbacks pull nested, platform-specific optional deps that a + # macOS-generated lockfile can't fully describe for a Linux runner, so + # `npm ci`'s strict lockfile check fails. `npm install` reconciles the + # tree for whatever platform the runner is. + - name: Install dependencies + run: npm install --no-audit --no-fund + + - name: Lint + run: npm run lint + + - name: Test (Vitest) + run: npm test + + - name: Build + run: npm run build diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 0000000..deea2ba --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,46 @@ +# Contributing to GradePace + +Thanks for taking a look. GradePace is a small, opinionated project with a real +race behind it, so contributions are welcome but the bar is "does this make the +pacing plan more honest or the app more useful," not "more features." + +## Ground rules + +- **The engine is the asset.** `src/lib/pacing.ts` is pure TypeScript: no React, + no DOM, fully unit-tested. Domain logic lives there and stays testable. UI + glue lives in `src/App.tsx` and the component files. +- **Verify against reality, not vibes.** Distances should match known race + lengths, D+ should match published figures, and the Minetti cost model must + match the 2002 paper's measured anchors. There are tests that lock these. +- **Keep the tests green.** `npm test` must pass. A coefficient typo should fail + loudly, so please add a test when you add behavior. +- **Honest uncertainty over false precision.** The product presents a finish + *range* on purpose. Changes that pretend to more accuracy than a GPX-only + prediction can deliver will be pushed back on. + +## Getting set up + +```sh +npm install +npm run dev # local dev server +npm run test # engine + app tests (Vitest) +npm run lint +npm run build # production build (also what CI runs) +``` + +## Proposing a change + +1. Open an issue first for anything non-trivial, so we can agree on the shape + before you write code. Bug reports with the GPX file (or a link) and what you + expected vs. saw are the most useful. +2. Keep pull requests small and focused. One idea per PR. +3. Don't bundle a model/behavior change (anything that moves the projected + finish time) into an unrelated PR. Call it out so it can be reviewed on its + own merits. +4. Run `npm run lint && npm test && npm run build` before pushing. + +## Where things are + +The current technical state, decisions, and roadmap live in +[STATUS.md](./STATUS.md). Read it first. The high-level architecture is in the +[README](./README.md#project-structure). diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..2ea6f99 --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2026 Alvaro Serero + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/README.md b/README.md index c02f8d8..cc12824 100644 --- a/README.md +++ b/README.md @@ -1,57 +1,99 @@ # GradePace -Grade-adjusted pacing plans for trail races — live at -[gradepace.vercel.app](https://gradepace.vercel.app/). +**Grade-adjusted pacing plans for trail races.** Upload a course GPX, get a +per-kilometer plan that knows steep climbs are power-hikes, not runs, and an +honest finish *range* instead of a false-precision single number. Everything +runs in your browser. Your GPX never leaves your device. + +[](https://github.com/Alvaro5/grade-pace/actions/workflows/ci.yml) +[](./LICENSE) +[](https://gradepace.vercel.app) +[](#how-it-works) + +### **[▸ Open the live app](https://gradepace.vercel.app)** · no signup, no upload, load an example and go + +
+
+
+
+
![]() |
+ ![]() |
+
| An honest finish range, not a single fake-precise number, plus distance, D+, and how much of the course you'll walk. | +A grade-colored course map (terrain / satellite / hybrid basemaps) with aid stations and opt-in water/toilet/viewpoint POIs. | +
![]() |
+ ![]() |
+
| A nutrition plan that puts carbs, fluids, and sodium on each segment between aid stations, sized by projected time, not distance. | +Per-km (or per-mile) splits: target pace, climb, hike share, and elapsed time, all consistent with the projected finish. | +