From c3174871ff166bc787e454f9fa31faa04c8d6602 Mon Sep 17 00:00:00 2001 From: Julian Cuni Date: Sat, 2 May 2026 21:16:14 +0200 Subject: [PATCH] docs: backfill task 2.2 done section + ROADMAP status --- .../02-tile-source-switcher.md | 23 ++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) diff --git a/.planning/phase-2-live-map/02-tile-source-switcher.md b/.planning/phase-2-live-map/02-tile-source-switcher.md index 9dc4b6a..f254b60 100644 --- a/.planning/phase-2-live-map/02-tile-source-switcher.md +++ b/.planning/phase-2-live-map/02-tile-source-switcher.md @@ -205,4 +205,25 @@ On first visit, default to `esri-satellite` — the most universally useful for ## Done -(Filled in when the task lands.) +- **`src/map/core/styles.ts`** — replaces 2.1's stub with the full `BasemapDescriptor` model. Each entry has `id`, `label`, `available(cfg)`, `buildStyle(cfg)`. Four entries: Esri World Imagery (satellite), OpenTopoMap (topo), OSM (street), Google Satellite (gated on `cfg.googleMapsKey`). `styleCustom()` helper synthesises a single-raster-source MapLibre style with a `glyphs` URL so future symbol layers render text. `defaultStyle` retained as the bootstrap. `ensureGoogleProtocol()` lazy-loads `maplibre-google-maps` on first use of the Google variant; `_googleRegistered` guards re-registration. +- **`src/map/core/map-pref-store.ts`** — Zustand store with `persist` middleware (`name: 'trm-map-prefs'`, `version: 1`). Holds `basemapId` + `setBasemap`. Survives reloads via localStorage. +- **`src/map/core/basemap-switcher.tsx`** — `` floating control top-right. Reads available basemaps (filtered by `available(cfg)`), highlights the active one, applies on click via `getMap().setStyle(...)`. Mount-time bootstrap effect applies the persisted choice once at first paint; user-driven `onClick` keeps a `switching` state for visual feedback. +- **`src/routes/_authed/monitor.tsx`** — renders `` as a child of ``. +- **`src/vite-env.d.ts`** — `declare module 'maplibre-google-maps'` ambient declaration; the package ships JS only, no `.d.ts`. + +**Deps installed:** `maplibre-google-maps@1.1.0` (runtime; lazy-imported only when the Google basemap is selected). + +**Deviations from spec:** + +1. Spec sketched `applyBasemap` as a unified function used by both the mount-bootstrap effect and the user-driven `onClick`. React 19's new `react-hooks/set-state-in-effect` rule flagged the `setSwitching` call inside the bootstrap effect as a cascading-render risk. Split into two paths: bootstrap calls `setStyle` directly without UI state (no need — the map is already showing OSM, the swap is silent); `onClick` keeps `setSwitching` for the per-click visual feedback. Cleaner anyway. +2. Spec sketched `BasemapDescriptor.style` as `StyleSpecification | string`. Implemented as `buildStyle: (cfg) => StyleSpecification` — a function. Reason: the Google variant's `style` depends on `cfg.googleMapsKey`, so it has to be computed at switch time. Uniform function shape across all entries keeps the type clean. + +**Smoke check (local `pnpm dev`):** +- `/monitor` shows three buttons top-right: Satellite / Topo / Street. Clicking each switches the basemap. +- Selected button highlights via `bg-accent`. +- Reloading the page restores the previously selected basemap. +- With `googleMapsKey` empty in `public/config.json`, the Google button is hidden and `maplibre-google-maps` doesn't appear in the network tab. + +**Bundle:** MapLibre is now its own chunk (`maplibre-gl-*.js`, 1MB / 273KB gzipped). Main bundle 371KB / 114KB gzipped. + +Landed in `3a9e07b`.