Public: anyone can read this; the crew can change it.
A GPS track (.gpx) in a repository renders as its route drawn as an outline, with distance and climb #10382
Check · Review · Land · Live done — Landed and live in production.
A `.gpx` file on a repository's file page now renders as an inline SVG outline of its tracks and routes, with total distance, elevation gain and point count beside it. The new tests, the kinds and subtitles tests, `tsc` and `scripts/gate.ts` pass. I did not run `scripts/design-lint.ts` or open a rendered file page in the running app. I also did not write a `docs/features` page or run the rest of t…
shipped production is serving it deploy f21b126
A .gpx file on a repository's file page now renders as an inline SVG outline of its tracks and routes, with total distance, elevation gain and point count beside it. The new tests, the kinds and subtitles tests, tsc and scripts/gate.ts pass. I did not run scripts/design-lint.ts or open a rendered file page in the running app. I also did not write a docs/features page or run the rest of the suite.
- Parser:
src/web/render/gpx.tsis a small tag scanner with no dependency. It reads only the five predefined entities and numeric references in names. A DOCTYPE with<!ENTITY>definitions is skipped, so&b;stays the literal text&b;and nothing expands. - Fallback to text: a file whose tags don't nest, has no
<gpx>root, or holds no valid point falls back to the source view. - Drawing: longitude is scaled by the cosine of the middle latitude and fitted to a 640×480 box. Distance is the haversine sum and climb is the sum of rises. Names are escaped.
- Thinning: distance, climb and point count use every point. Only the drawing is thinned, evenly across the whole track and keeping both ends, once a file has more than 20,000 points.
- Registration:
gpxis a new file kind inkinds.tswith a 20 MB render cap and an extension-based mapping, andrenderers.tsmaps it to the new renderer. I added agpxsample totest/render-kinds.test.ts, which walks every extension. - Tests:
test/render-gpx.test.tscovers a known two-point track within 1% of 111.2 km, a malformed file, an entity-expansion attempt, escaped names, even thinning, and a single-point route.
I did not de-duplicate points or filter GPS noise out of the climb. Climb is the plain sum of rises. Waypoints (<wpt>) are not drawn, because the issue asks for tracks and routes only.
The push hook demanded a Try: commit line, so there is an empty commit with one. To try it, open any .gpx file in a repository.
🤖 Generated with Claude Code
The preview deploy failure doesn't come from this pull's code, so I changed nothing and pushed nothing.
- The log: it shows no error from the app. The last line is
forge draining … why=SIGTERM, so the preview host was restarted during the deploy. "No tests declared, none run" is just what the preview deploy prints for this app. - The preview now:
/healthzon https://lab--10382--forge-trevato-apps.open-platform.sh returns 200. - Checks I ran locally:
- bun scripts/gate.ts passes.
- bun x tsc --noEmit is clean.
- test/render-gpx.test.ts and test/render-kinds.test.ts pass (15 tests).
- The
Try:rule: CLAUDE.md requires aTry:line in a commit message whensrc/webchanges. The subject of d7f3c66bc already satisfies it.
Pull description
A .gpx file in a repository now renders as its route outline, with distance, climb and point count. The renderer is src/web/render/gpx.ts, registered in kinds.ts and renderers.ts. Tests are in test/render-gpx.test.ts, with one added line in test/render-kinds.test.ts. There are no new dependencies. To try it, open a .gpx file in a repository on the preview.
🤖 Generated with Claude Code
A GPS track (.gpx) in a repository now renders as a track view, and the branch is merged with current main and pushed. The merge had no conflicts and gate.ts and tsc --noEmit came back clean. I did not run the full suite. fast.ts named 1,424 test files (an estimated two hours), so I ran the GPX tests instead.
- What changed:
src/web/render/gpx.tsis new. It is registered insrc/web/render/kinds.tsandsrc/web/render/renderers.ts. Tests are intest/render-gpx.test.ts, plus one added line intest/render-kinds.test.ts. - What I ran:
test/render-gpx.test.ts,test/render-kinds.test.tsandtest/security-floor-routes.test.ts. 18 of 19 tests pass. - The one failure: the "two passes' request loops together take under 5 s" timing check in
test/security-floor-routes.test.tstook 7.1 s. That file came in withmain, and I did not touch it. Requests there fail withposix_spawn 'git'ENOENT because my sandbox can't spawngit, which probably explains the slowness. I expect the forge's check to run it normally. - What I chose not to do: I did not touch anything outside the GPX renderer, its registration and its tests.
- How to try it: open a
.gpxfile in a repository on the preview at https://lab--10382--forge-trevato-apps.open-platform.sh.
I merged main into the branch and pushed. A GPS track (.gpx) in a repository still renders as a track view, now alongside the new calendar (.ics) renderer from main.
- Conflicts:
src/web/render/kinds.ts,src/web/render/renderers.tsandtest/render-kinds.test.ts. Each was two registrations added at the same spot, so I kept both. That means thegpxandcalendarkinds, their size caps, the extension mappings, the renderer entries, and one sample per kind in the registry test. - Checks:
- bun x tsc --noEmit is clean.
- test/render-gpx.test.ts, test/render-kinds.test.ts and test/render-ics.test.ts pass.
- bun scripts/gate.ts printed nothing, which I took as clean.
- Not run: the full suite. It is too broad to run in the sandbox, so the forge's check will run it on this push.
- Left alone: the timing test in
test/security-floor-routes.test.tsthat failed earlier in this sandbox. I did not re-run it this time. - To try it: open a
.gpxfile in a repository on the preview at https://lab--10382--forge-trevato-apps.open-platform.sh.
The parser, thinning and tests are solid, but the change is not finished on three counts.
- Phone: no CSS exists for
gpx-view, and the SVG is emitted with fixed width/height (up to 640px), so on a 360px phone it overflows sideways. Fix first: size it with CSS (max-width:100%; height:auto) and drop the fixed width/height attributes (keep viewBox), adding the rule in the right style module. - Both doors: the terminal workspace has no GPX view. The sibling .ics pull touched src/tui/screens/workspace.ts and test/tui-workspace.test.ts; this one must too (distance, climb, points as text).
- No docs/features/gpx.md how-to (ics added docs/features/ics.md).
Also: tests do not cover the rendered page route, and design-lint was not run; run bun scripts/design-lint.ts. Ticks: box 2 and 3 are met; box 1 is not until the phone and terminal gaps close.
A .gpx file in a repository now renders as a route outline with distance, climb and points, on the web and in the terminal workspace. I haven't opened it on a 360px screen. The checks that ran are green: bun x tsc --noEmit, bun scripts/gate.ts, bun scripts/design-lint.ts, and the GPX render, blob-page and TUI workspace tests. The full suite was not run here; the forge runs it on this push.
Changes since the review
- Phone: I dropped the fixed
widthandheightfrom the SVG and keptviewBox. A new.gpx-viewrule insrc/web/style/content.tssetsmax-width:100%; height:auto, so the route scales to the column. The stroke iscurrentColorand there is no accent. - Facts: I replaced the bespoke
figcaptionwith the existing.status-rows/.status-rowrows for Track, Distance, Climb and Points. A newgpxFacts()insrc/web/render/gpx.tssupplies them to both doors. - Terminal:
src/tui/screens/workspace.tsopens a.gpxas a Fact / Value table using the same parser;sflips to the source and the status line reads4 facts. - Docs:
docs/features/gpx.mdis the new how-to.docs/design/areas/render.mdnames.gpx-view, which is what design-lint asked for. - Tests:
- test/blob-gpx-page.test.ts fetches the real blob page route and checks the SVG, the fact rows and escaping.
- test/render-gpx.test.ts checks that the SVG has no width or height and that the facts are status rows.
- test/tui-workspace.test.ts opens a .gpx in the terminal workspace.
Not done: there are no map tiles and no elevation profile, as before. The terminal shows the facts as text only, with no outline drawing.
Fixed the failing check and pushed. A .gpx file in a repository renders as a route outline with its facts, and the type error is gone.
origin/main had made download a required field on RenderInput, and test/render-gpx.test.ts built its input without it. I merged current main into the branch and added download: "/d" to the test's input, the same way the CSV tests do. Only that test line changed.
After the merge, bun scripts/gate.ts and bun x tsc --noEmit ran clean. The 8 tests in test/render-gpx.test.ts pass. I did not run the rest of the suite, since the forge runs the full check on the push.
To try it, open any .gpx file in a repository on the preview at https://lab--10382--forge-trevato-apps.open-platform.sh.
This pull's GPX change is unchanged. I merged current main and made one edit to get the check green: I listed src/kernel/why_now.ts in scripts/unreached-allow.json.
The failing test was "every module under src/ is imported by another, or allowed". It failed because of main, not because of the GPX change. Commit 54dacba00 (retiring the gate's "why now" direction) left src/kernel/why_now.ts imported by nothing under src/. Only test/why-now.test.ts imports it.
After the merge and the allowlist edit, test/unreached.test.ts and tsc --noEmit pass here. I ran only those two; the forge's check will run the full suite on this push.
Decision for you: I did not delete the module or wire it into the app, because that is main's decision. If main fixes it first, this allowlist entry becomes stale and the "allowed module that has become reached" test will fail until the entry is removed.
Earlier asks are met. The SVG has no fixed width or height and scales to a 360px column. Facts are status-rows, not a figcaption. The terminal opens a .gpx as a Fact/Value table. docs/features/gpx.md exists and README is untouched. Blob-page, TUI and renderer tests cover the behaviour. Output is escaped and the DOCTYPE is never expanded. One accent rule holds: stroke is currentColor.
Two things I would do next, both non-blocking:
- Non-blocking: in src/web/style/content.ts the new gpx rules were inserted between the font specimen comment and .font-view, so that comment now sits above the wrong rules. Move the gpx block above it.
- Non-blocking: the src/kernel/why_now.ts entry in scripts/unreached-allow.json is unrelated to GPX. It goes stale once main fixes it, so drop it when main does. I did not load the preview with a .gpx file, so the rendered page is judged from the blob-page test and the served CSS source.
A
.gpxfile on a repository's file page now renders as an inline SVG outline of its tracks and routes, with total distance, elevation gain and point count beside it. The new tests, the kinds and subtitles tests,tscandscripts/gate.tspass. I did not runscripts/design-lint.tsor open a rendered file page in the running app. I also did not write adocs/featurespage or run the rest of the suite.src/web/render/gpx.tsis a small tag scanner with no dependency. It reads only the five predefined entities and numeric references in names. A DOCTYPE with<!ENTITY>definitions is skipped, so&b;stays the literal text&b;and nothing expands.<gpx>root, or holds no valid point falls back to the source view.gpxis a new file kind inkinds.tswith a 20 MB render cap and an extension-based mapping, andrenderers.tsmaps it to the new renderer. I added agpxsample totest/render-kinds.test.ts, which walks every extension.test/render-gpx.test.tscovers a known two-point track within 1% of 111.2 km, a malformed file, an entity-expansion attempt, escaped names, even thinning, and a single-point route.I did not de-duplicate points or filter GPS noise out of the climb. Climb is the plain sum of rises. Waypoints (
<wpt>) are not drawn, because the issue asks for tracks and routes only.The push hook demanded a
Try:commit line, so there is an empty commit with one. To try it, open any.gpxfile in a repository.🤖 Generated with Claude Code
Closes #10329