Repository navigation
Search this area over the map, place search near the view, and a speed pass - #3
Open
coinmandeer wants to merge 11 commits into
Open
coinmandeer wants to merge 11 commits into
coinmandeer wants to merge 11 commits into
Conversation
…er notices None of the three is imported anywhere since the shell redesign; PlacesPanel was the only reader of the old search-here pending flag. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01J3QFugXbaTxPuuP3q3rR16
A search for "Náměstí Míru" over Brno found Prague's square first, because the geocoder never learned where the reader was looking. Search now sends the centre of the view as `near` (two decimals, only from region zoom), and `/geocode` passes it on as a bias, never a filter: Mapy `preferNear` for geocode and suggest, a Nominatim `viewbox` with `bounded=0`, and distance as the tie-breaker in the offline gazetteer. Planning stop search sends the same bias. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01J3QFugXbaTxPuuP3q3rR16
After a long pan the pin layers deliberately wait to be asked, but the only button that asked lived inside the Map status popover, so the map simply seemed not to load. The button now floats over the map (above a phone's sheet, stacked with Discover's chip) whenever the view is waiting for it, and it means one thing: layers that wait for the button. Loading, partial and failed layers keep their own indicators; counting them, and every expensive layer during the debounce before an automatic load, made it flicker. - Pressing it brings only out-of-date layers up to date for the view (`LayerEngine.searchHere`); layers already loaded or loading for it cost nothing, fresh data is not refetched and tiles are not reattached. - With the assistant open, the last place search is offered again once the reader has moved the map (drag, wheel, keys — MapLibre's `originalEvent`), and pressing it asks that question for the new view. The answer's own camera fit does not count as a move. - Jumps that ask for data right after starting a flight (a search result, My location, a country) waited for nothing and loaded the view being left, so the destination then waited for the button. The request now waits for the camera to land. - Settings: the unused "Search this area" switch becomes "Load only on Search this area" (`manualRefresh`, off by default): the reader's own moves then load nothing until asked, while programmatic jumps still load. A move whose refresh waits for the basemap's idle keeps that flag. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01J3QFugXbaTxPuuP3q3rR16
`/v2/themes` ran a PostGIS join over every territory for each exact bbox, cached for 60 s, and the drawer asks on every pan: the live median was 3.8 s. Coverage is a property of the imports, so requests now share one computation per zoom band (the zooms at which the dataset choice changes) and per view snapped outward to that band's grid, for 15 minutes, with concurrent requests for one key waiting on a single query. The join reads only theme datasets. The list answer is marked cacheable for five minutes. The theme inventory (two queries per detail request) is cached for two minutes, and the Discover boundary manifest keeps its coverage for as long as the published revision is unchanged; only the cheap manifest lookup repeats each minute. The API computes both catalogues once after it starts listening, so the first visitor after a restart no longer waits ~8 s. A small `TtlCache` (bounded, LRU on hit, in-flight sharing, failures not cached) backs the new caches. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01J3QFugXbaTxPuuP3q3rR16
The image defaults (128 MB shared buffers, 4 MB work_mem, JIT on) and tables that were bulk-imported but never analysed made the heavy catalogue queries plan, compile and spill more than they read: JIT alone cost 1.8 s of a 4.2 s coverage query, and the planner guessed 139 rows for a 242 000-row join. - Compose starts PostgreSQL with JIT off, 512 MB shared buffers, 32 MB work_mem, 256 MB maintenance memory, SSD page costs and a 256 MB /dev/shm, each sized for the shared 8 GiB host and overridable per host. The API also sets JIT off and work_mem on its own connections, which helps before the server settings apply. - The deploy recreates PostgreSQL only when those settings change, inside the rollback boundary; a failed rollout brings the previous settings back. - Imports (statistics, boundaries, LAU correspondences, staged boundary publication) analyse the tables they rewrote; a failed ANALYZE never fails the import. After a healthy rollout the deploy analyses tables that were never analysed or changed by more than a tenth. - After a healthy rollout the deploy keeps the three newest releases and backups (never the live or previous release), six rollback image tags per service, and prunes dangling images and week-old build cache. None of it can fail the release. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01J3QFugXbaTxPuuP3q3rR16
Vector tiles left the API uncompressed: MVT is not on `@fastify/compress`'s list nor on the edge proxy's, although protobuf gzips to about a third. The API now compresses MVT and protobuf responses (event streams still never). The web image gzips its scripts, styles, HTML, SVG and JSON at build time and nginx serves those files with `gzip_static`, so the busy host no longer compresses the same bundle for every visitor. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01J3QFugXbaTxPuuP3q3rR16
The subset kept 6,186 glyphs (367 kB): passing the icon names as text makes the subsetter's layout closure follow every ligature those letters can spell, which is every icon in Material Symbols. The script now shapes each name with HarfBuzz to find its glyph and private-use codepoint, keeps exactly those codepoints plus the letters with layout closure off, and fails unless every name still turns into a single icon glyph in the result. 229 icons, FILL and weight axes kept: 64 kB. A render of all icons at both axis ends matches the previous font within anti-aliasing. `harfbuzzjs` and `wawoff2`, already installed with `subset-font`, are declared because the script now imports them. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01J3QFugXbaTxPuuP3q3rR16
- React Query was installed and wrapped around the app, but nothing queried through it. - `@mapos/layer-sdk` is side-effect free, so the table parser the browser never calls (and `fflate` with it) no longer ships. - SGP4 (`satellite.js`, ~100 kB) arrives with the satellites layer's first update instead of with the map. - MapLibre resolves to its CSP build: its worker already loads from its own file, so the default build's inlined copy was dead weight. - MapLibre, React and the UI kit get their own chunks, which stay cached across releases while the app chunk changes. - The cleanup of an earlier app's service workers and caches runs once per browser instead of on every start. Startup JavaScript: one 2.42 MB chunk (706 kB gzip) → 2.26 MB in four chunks (651 kB gzip). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01J3QFugXbaTxPuuP3q3rR16
- Tile watchers read the whole style (`getStyle()` serialises every basemap layer) on each `sourcedata` event, hundreds per pan and mostly for the basemap. They now filter by source name and keep the list of their sources until the style changes. - A map click read the style three times to find cluster, weather and theme layers; it now reads the layer order once. - The map renders at most at pixel ratio 2 (a 3× phone drew 2.25× the pixels for no visible gain), following density changes within that cap. - The store snapshot hooks passed React a new subscribe function every render, which unsubscribed and resubscribed every component each time; loading flags, empty layers staying empty and repeated source reports no longer notify at all. - A partial answer whose retry is already scheduled is not asked again for the same request. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01J3QFugXbaTxPuuP3q3rR16
The statistics store asked `/v2/themes` with the exact view after every pan. The coverage snapping (zoom band + outward grid) moves to `@mapos/adapter-sdk` as `statCoverageRequest`, used by the API's cache key and by the client, which now asks only when that key changes; a pan back is served by the browser cache the API now allows. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01J3QFugXbaTxPuuP3q3rR16
About 110 classes in the stylesheets had no element left in the app (the removed activity indicator and layer notices, the old mega menu, planner sections the redesigned planning panel no longer has, unused motion helpers). Rules that only targeted them are removed; selector lists keep their live members. Found by matching every class selector against the TypeScript sources, including class names built from a prefix. App CSS 225 → 194 kB. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01J3QFugXbaTxPuuP3q3rR16
Owner
Author
|
The It failed immediately, before touching the VPS: Neither secret is set on the repository (Settings → Secrets and variables → Actions). This isn't fixable from the PR — no code change addresses a missing secret. Once the owner adds Generated by Claude Code |
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.
Change
A floating Search this area after a map move, place search biased to the view, and one pass over what made the live site slow (
/v2/themesmedian 3.8 s, untuned PostgreSQL without statistics, a 2.4 MB startup bundle, a 367 kB icon font, cold Discover endpoints after each restart).Search this area
LayerEngine.searchHere); layers already loaded or loading for the view cost nothing, fresh data is not refetched and tiles are not reattached.originalEvent), and pressing it asks that question for the new view. The answer's own camera fit is not a move.manualRefresh, off): the reader's own moves then load nothing until asked; programmatic jumps still load.near(two decimals, from region zoom);/geocodepasses it on as a bias, never a filter (MapypreferNear, Nominatimviewbox+bounded=0, distance tie-break in the offline gazetteer).Backend
/v2/themes: coverage is shared per zoom band (the zooms where the dataset choice changes) and per view snapped outward to that band's grid, for 15 minutes, with concurrent requests on one query; the join reads only theme datasets; the answer is cacheable for five minutes. The client asks with the same key (statCoverageRequestin@mapos/adapter-sdk), so an ordinary pan asks nothing./dev/shm— sized for the shared 8 GiB host, overridable withPOSTGRES_*. The API also sets JIT off and work_mem per connection.gzip_static.Frontend
@mapos/layer-sdkmarked side-effect free (drops the table parser andfflate),satellite.jsloaded with its layer, MapLibre's CSP build (its worker already loads separately), and MapLibre, React and the UI kit in their own chunks that stay cached across releases.sourcedataevent (hundreds per pan); a click reads the layer order once instead of the style three times; pixel ratio capped at 2; store snapshot hooks keep one subscription; repeated loading flags, empty layers and source reports don't notify; a partial answer with a scheduled retry is not asked again.Measured on
vite build: icon font 375,564 → 65,536 bytes; startup JS 2,420,314 bytes in one chunk (706 kB gzip) → index 858 kB + maplibre 970 kB + ui-kit 243 kB + react 194 kB (651 kB gzip); app CSS 295 kB (incl. MapLibre CSS) → 194 kB + 70 kB MapLibre CSS.Evidence
npm test(layer-sdk 111, adapter-sdk 85, map-runtime 4, API 852, web 590; skips are the existing environment-gated ones),test:contracts,test:privacy(218),test:migrations(37),test:load,test:secrets,test:css-tokens. New unit tests: engine waiting/manual/searchHere, area-query slot,nearparsing and bias,TtlCache, coverage snapping, compressible types, tile watcher style reads, store no-op notifications.npm run typecheck,npm run lint,npm run format:check.MAPOS_LONG_PERFgate), 1 failed before the change dropped below (that spec now 18/18); profiles, run after it, offline 5/5, security 1/1 (production bundle under CSP, so MapLibre's CSP build and worker work), accessibility 8/8, visual 18/18, runtime-starter 2/2, performance 2/2. New e2e: the chip floats without opening anything; a long drag waits and one press loads the new view; a place picked in search loads at the destination without asking; manual mode loads nothing until asked; with the assistant open, a drag offers the last place search and pressing it re-asks it with the new bbox. Walk-through at 1440 px and iPhone 14 Pro size (long pan → chip → load, search "Brno", settings): no page errors.viewportRefresh.spec.ts:174failed; with the debounce back the spec passes 18/18.Layer or provider integration (if applicable)
searchHerereuses the engine's per-layer cancellation and query keys.nearis the view centre rounded to ~1 km, sent to our API and on to the geocoder only as a ranking bias; no device location is involved. Re-asking the assistant happens only on an explicit press.near; manual mode is a setting; caches are in-process and bounded.Release safety
command/shm_sizeblock or overridingPOSTGRES_*; the chip's manual mode is a user setting.@tanstack/react-query);harfbuzzjsandwawoff2declared as dev dependencies (already installed viasubset-font); bundle numbers above.docs/environment.md(PostgreSQL sizing),docs/operations/backup-restore.md(restart, ANALYZE, pruning, restored databases have no statistics),infra/.env.production.example.Not in this PR: lazy loading of the AJV manifest validator (it backs the synchronous
assertLayerManifestV2contract used by the layer registry — worth its own change), and the PWA service worker (separate PR as agreed).🤖 Generated with Claude Code
https://claude.ai/code/session_01J3QFugXbaTxPuuP3q3rR16
Generated by Claude Code