Repository navigation
Rewrite as a Next.js + TypeScript app on Untitled UI - #4
Merged
Merged
Conversation
The label layer pointed at tile.stamen.com, which shut down in 2023, so the map showed no roads or labels. Replace it with Esri's keyless World_Transportation and World_Boundaries_and_Places overlays, which also restores the roads the checkbox always promised. Swap Mapbox.js 3.1.1 for plain Leaflet 1.9.4 (the Mapbox token is kept for place search only), skip failed elevation tiles instead of stalling the redraw, and position tiles relative to the map container. Rebuild the page chrome: top bar with search, left tool rail, sliding Style and Download panels, a phone layout, keyboard focus states and reduced-motion support. Styles move to css/contours.css. Fix the Axis Maps link and logo, update the blog link, and drop the dead Universal Analytics tag. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Replaces the static page (index.html, contours.js, jscolor, leaflet-hash) with a Next.js 16 App Router app, TypeScript strict, Tailwind v4 and Untitled UI React components installed with its CLI. The contouring pipeline is unchanged in behaviour but split into typed modules under src/lib/contours: Terrarium decoding, threshold selection with the junk-band filter, d3-contour, canvas and SVG rendering, GeoJSON, PNG and SVG exports, the URL hash and Mapbox geocoding. A hidden Leaflet GridLayer still fetches the elevation tiles, and use-contour-map owns the map, the canvases and the debounced redraw. The GeoJSON and SVG exports no longer carry the 5px canvas-buffer offset the old code had. The interface is rebuilt on Untitled UI's own patterns: a header with the place search, a slim icon rail with tooltips, slideout-style panels with label-above fields, React Aria Select, radio groups with hints, toggle, button group for units, and the Untitled UI color picker in an input group for every color. Brand ramp is the contour brown; grays use the warm stone scale. Reduced motion and keyboard focus are respected. The Mapbox token moves to NEXT_PUBLIC_MAPBOX_TOKEN (see .env.example). Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Set output: "export" (with images.unoptimized) in next.config.mjs so `next build` writes a self-contained out/ folder that the existing static host for contours.axismaps.com can serve; the same config still runs unchanged on Vercel. Verified the served export end to end with a plain file server: #zoom/lat/lng hash routing, icon.svg, the dynamically imported Leaflet chunk, contour drawing and the Mapbox place search. Point `npm start` at `npx serve out`, since `next start` refuses a static export. Add .github/workflows/build.yml, which runs npm ci and npm run build on pushes and pull requests with NEXT_PUBLIC_MAPBOX_TOKEN from a repository secret and uploads out/ as an artifact. No deploy step: the hosting target isn't decided. Document the export, the host requirements (serve from the domain root, basePath for sub-paths, caching, 404 page) and Vercel in the README. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Tests (npm test, Vitest, node environment) cover the pure modules in src/lib/contours: Terrarium decoding and unit rounding, threshold selection and the junk-band filter, hex normalisation and alpha, hash parsing and formatting, style resolution and fills (hypsometric domain shift, bathymetry only below sea level), SVG serialisation and the GeoJSON builder, all on small synthetic grids. Lint (npm run lint) uses eslint-config-next core-web-vitals + typescript with eslint-config-prettier on top. Fixes for what it reported: - color-field, number-field: adopt a changed prop during render instead of setState in an effect (react-hooks/set-state-in-effect). - place-search: clear results in the input handler, not the effect. - use-contour-map: mirror settings into the ref from an effect instead of during render (react-hooks/refs). - input.tsx: empty interface -> type alias. - avatar img tags: explicit no-img-element disables with a reason. - postcss.config.mjs: named export object. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Decoding, threshold selection and d3-contour now run in src/lib/contours/contour.worker.ts, a thin wrapper around the unchanged pure computeContours. The hook rasterizes the tiles on the main thread, transfers the pixel buffer to the worker and draws the result when it arrives; the worker keeps the last grid so an interval or unit change only sends the new parameters. A generation counter drops results that belong to a view the map has already left, and the "Drawing contours" card stays up from the moment work is scheduled until the result is drawn. The double requestAnimationFrame is gone since nothing blocks. Pixels with alpha 0 (a tile that failed to load or is outside the data) decoded to -32768 m, which stretched the threshold range over tens of thousands of steps and froze the tab. They are now NaN, which d3-contour v4 treats as missing, and computeThresholds skips them when picking the range and counting bands. Threshold counts above MAX_THRESHOLDS (2000) compute nothing and come back as a ContourResult failure that the hook exposes as `problem`, shown as a notice in the map's status card area. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…reat-goldwasser-880c74
…great-goldwasser-880c74
…great-goldwasser-880c74
The threshold and elevation tests were written before contouring moved to a Web Worker: computeThresholds now returns a status result instead of null, and decodeElevationGrid returns a Float32Array with NaN as no-data. Rewrite the assertions for those shapes and add cases for no-data values and the threshold cap. Silence the one lint warning on the generation counter bumped in the map effect cleanup, which is not a DOM ref. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Turbopack copies the contour worker's source into out/_next/static/media as an asset, so the second build on any machine, and the first one on Vercel, failed when tsc swept up out/. Exclude out, .next and .vercel. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Ground-up rewrite of the Contours app as a Next.js 16 (App Router) + TypeScript application using Untitled UI React components, replacing the static
index.html/contours.jspage. Branched fromclaude/map-tiles-ui-modernize-517f09(#3) so it carries the Esri reference tiles and Leaflet 1.9 fixes.What changed
src/lib/contours(Terrarium decoding, junk-band threshold selection, d3-contour, canvas + SVG rendering, GeoJSON/PNG/SVG export, URL hash, Mapbox geocoding).src/hooks/use-contour-map.tsowns the map, canvases and the debounced redraw, with work levels so a style tweak can't cancel a pending re-rasterization.Select, radio groups with hints, toggle, button group for units, and the Untitled UI color picker in an input group for every color. Brand ramp is the contour brown; grays use the warm stone scale. Reduced motion and visible keyboard focus respected.NEXT_PUBLIC_MAPBOX_TOKEN(.env.exampleprovided). README rewritten with run/build instructions.Behaviour changes
Test plan
npm run buildpasses with no type errors#zoom/lat/lnghash read on load and kept in sync; hashchange navigatesFollow-ups merged into this PR
next buildwrites a self-containedout/folder that any file server can host;npm startserves it. A GitHub Actions workflow builds on every push and PR. README has a Deploying section.npm run typecheck,npm run lint,npm testandnpm run buildall pass.🤖 Generated with Claude Code