93 Commits
Author SHA1 Message Date
Qiusheng Wu 1c3bec9869 fix(map): load Mapbox GL from its CDN instead of bundling it (#3294)
* fix(map): load Mapbox GL from its CDN instead of bundling it

Mapbox GL JS v2+ is proprietary, but the web, desktop and store builds
and the Python wheel have shipped it as a chunk since v3.1.0. Load the
script and stylesheet from Mapbox's CDN when a Mapbox pane mounts, pinned
by version and SRI hash to the installed package. The build now fails if
mapbox-gl reaches a chunk.

Also ship the gdal3.js LGPL license and a source link with every build,
and link third-party licenses from the About dialog.

* fix(deps): upgrade node-forge to 1.4.2 for new advisories

Five high node-forge advisories published on 2026-10-30 fail the audit
gate on every PR. All are patched in 1.4.1, which also clears the only
allowlist entry, so drop it.

* Address review feedback

- Inline the Mapbox stylesheet through fetch. A <link> was blocked by
  style-src in both CSPs, so every Mapbox pane failed outside vite preview.
- Refuse the CDN load in GEOLIBRE_NO_EXTERNAL_CDN builds and document it.
- Make the test's fake remove() idempotent, as a real element's is.
- Use new Map() for the empty audit allowlist.

* Fix stylesheet URL assertion in the Mapbox loader test

SVG data URIs contain url(#clip) references of their own, so strip whole
data URIs before asserting that no other url() remains.

* fix(map): stop MapLibre's class registry breaking Mapbox tiles

MapLibre 6 permanently sets _classRegistryKey on the global Set and Error.
Mapbox's serializer reads the same property, fails looking the key up in
its own registry, and no tile loads ("reading 'omit'"). Rename MapLibre's
private copy at build time, including in the dev dependency optimizer.

Also theme Mapbox's native and shared map controls in dark mode, and
retry only the failed half of the CDN load.

* fix(map): keep Mapbox control hover readable in dark mode

mapbox-gl paints #eee behind any hovered button in a control, so the
layer control's unclassed toggle lost its light icon against the
background. Use the theme's accent color in dark mode.

* Address review feedback

- Keep Mapbox's active geolocate icon colored in dark mode, as on MapLibre.
- Reload a script that ran without defining mapboxgl, since it still fires
  load.
- Cover a stylesheet-only retry, and reset the loader between tests.
- Wait for the style in the retry test instead of relying on microtask
  order.
- Fail the registry contract test if the plugin stops matching MapLibre.

* Address review feedback

- Reject any Mapbox GL module at load time, so its stylesheet is caught as
  well as its script.
- Keep the MapLibre registry rename the same length, so source maps stay
  correct.
- Leave the inverted attribution button out of the dark hover color, which
  the invert would turn light behind its icon.

* Address review feedback

- Describe the dark hover rule's specificity correctly in its comment.
2026-10-31 01:13:02 -04:00
Qiusheng Wu ef1c4a6317 feat(notebook): add a raster and vector example notebook (#3286)
* feat(notebook): add a raster and vector example notebook

Add Explore_GeoLibre.ipynb beside Welcome.ipynb. It covers raster
visualization and terrain analysis, vector styling and GeoPandas
analysis, and, on desktop, layer management and processing tools.

The kernel client had no way to add rasters, so add add_tile_layer,
add_cog, add_raster and run_whitebox_tool, with the matching scripting
commands. Seed the new notebook in both the web and desktop builds.

* fix(notebook): address review comments on the raster commands

Accept only known tile options, since the store spreads them over the
validated URL. Keep the COG engine and insertion options, and report
invalid base64 by command name.

In the example notebook, wait only for layers added by the current
command, and clean up by what Part 3 added, so a rerun cannot pick up an
old result and a user's own layers are safe.

* fix(notebook): point layer warnings at the caller's cell

Warnings from add_geojson and the raster methods pointed inside the
client after they moved to _add_layer. Also reject non-bytes input to
add_raster up front.

* Address CodeRabbit review feedback

- Pass add_raster's colormap as layer state, so a paletted GeoTIFF on the
  ArcGIS or Cesium renderer honors it, as the COG command does.
- Keep beforeLayerId and metadata in the tile options; the app API
  validates metadata.
- Mark the Whitebox docs example as desktop-only, since add_raster
  returns None on the web.

* Address review feedback

- Gate Part 3 of the notebook on every layer id it uses, since a relay
  timeout can leave any of them None, and reword the skip message.
- Raise a clear error when rescale is not a (min, max) pair.
- Forward zoomTo and beforeLayerId in addRasterBytes.
- Scope the docs note on layer ids to desktop JupyterLab kernels and
  guard the Whitebox example.
2026-10-30 11:18:31 -04:00
114bd7debf feat(plugins): add India GIS open geospatial and state geoportal catalog plugin (#3274)
* feat(plugins): add India GIS open geospatial and state geoportal catalog plugin

* feat(desktop): register India GIS plugin in built-in plugins list, UI profiles, and i18n locales

* style: auto-format (ruff + oxfmt) [pre-commit.ci]

* feat(plugins): provide authoritative Indian government geospatial datasets and documentation for India GIS

* style: auto-format (ruff + oxfmt) [pre-commit.ci]

* Address review feedback

- Replace the geoBoundaries raw.githubusercontent.com URLs, which serve
  Git LFS pointer text, with media.githubusercontent.com URLs that serve
  the GeoJSON with CORS.
- Load only a state's districts on each state district card: items take a
  featureFilter, and the cards keep the GADM NAME_1 values of their states.
- Name each dataset's real publisher (DataMeet, GADM, geoBoundaries) and
  drop the "authoritative/official" wording; regroup the national boundaries
  under their publishers and drop the Bhuvan entry that repeated a file.
- Credit DataMeet and link the source file on every Municipal Spatial Data
  card, as its license asks.
- Drop the Kanpur wards (Web Mercator metres) and four files that are not
  valid GeoJSON (Kochi wards and boundary, Ahmedabad zonal and ward offices).
- Correct feature counts and extents from the files themselves.
- Make check:gis-portals download every India file and check that it is a
  FeatureCollection in longitude/latitude.
- Remove the unused orgId placeholder and the searchQuery and itemFilter
  plumbing, which no India catalog used; this also restores the guard that
  never searches ArcGIS Online without an org or group scope.
- Pick a map service's first "Feature Layer" rather than its first non-group
  layer, and type subLayerIds as nullable.
- Translate the India panel through usePluginLabelSync and indiaGis.* keys.

* Address CodeRabbit review feedback

- Check every position of every retained feature for longitude/latitude
  in check:gis-portals, not only the first feature's first position.

---------

Co-authored-by: Prince Suthar <sutharprince098@gmail.com>
Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
Co-authored-by: Qiusheng Wu <giswqs@gmail.com>
2026-10-28 23:57:50 -04:00
Qiusheng Wu 7f6ac2c36f fix(i18n): use sentence case for English menu labels and headings (#3243)
* fix(i18n): use sentence case for English menu labels and headings

Menu items, buttons, dialog titles and headings in en.json mixed Title
Case and sentence case. Switch the Title Case labels to sentence case,
keeping proper nouns (products, services, standards, places), acronyms,
and names of other UI surfaces ("the Layers panel") capitalized.

Processing tool names are left alone: they come from the tool registries
through the generated processing.toolMeta keys, and the matching
toolbar.*Tool menus mirror them. Projection and CRS preset names are
proper names and stay as they are.

Also:
- update the English fallbacks next to catalog keys, three hard-coded
  Add Data panel titles, and the plugin reference menu paths
- regenerate docs/user-guide/plugins.md
- update e2e selectors and unit-test expectations
- lower the same strings where another locale copied the English verbatim
  (German is left alone: it capitalizes nouns)
- document the casing and ellipsis conventions in docs/i18n.md

Fixes #3231

* fix(i18n): match the Layers header collapse label to the rail's

The left rail reads "Collapse Layers" (Collapse {{title}} with the panel
name), so the Layers header button now says the same instead of
"Collapse layers".

* Address Claude review feedback

- Match the vantor.openPanel aria-label and title fallbacks in the Vantor
  panel to the sentence-case catalog value ("Open Vantor STAC explorer").

* Address Claude review feedback

- Update English strings that quote a renamed label or menu path to the
  new sentence-case names ("Download offline area", "Add comment",
  Settings → Environment variables, Project → Export → Export layers,
  Add data → LiDAR layer, and others), including the matching code
  fallbacks and hard-coded hints.
- Keep "Open in Processing…": Processing is the toolbar menu's name, so it
  keeps its capital under the new style rule.

* Address Claude review feedback

- Match the share.role and share.expiry fallbacks in the Share dialog to
  en.json ("Access role", "Link expiry"). A sweep of every key/fallback
  pair found no other case-only mismatch.
2026-10-26 21:51:49 +00:00
0c2c3df530 perf(vector): optimize vector pipeline with binary GeoArrow layout and zero-copy worker tiling (#3191)
* perf(vector): optimize vector pipeline with binary GeoArrow layout and zero-copy worker tiling

- Define GeoArrowBatch format with contiguous Float64Array coordinates and flat integer offsets

- Add buildTileIndexFromBinary and single fast binary worker messaging in geojson-vt-index

- Support GeoArrowBatch in shouldUseTiledRendering, layer-sync, and SqlWorkspacePanel

- Preserve dual representation (layer.geoarrow + layer.geojson) for complete backward compatibility

- Add comprehensive test suites: core unit, tiling integration, desktop pipeline, adversarial fault injection, and golden regression tests

- Benchmarks demonstrate 2.24x-2.78x total render prep speedup and up to 8.02x faster worker transfer

* style: auto-format (ruff + oxfmt) [pre-commit.ci]

* fix(vector): address GeoArrow review findings and memory retention

* style: auto-format (ruff + oxfmt) [pre-commit.ci]

* fix(vector): address join staleness, conversion fallback, undo stability, and decode deduplication

- SqlWorkspacePanel: simplify layer addition by delegating GeoArrow batching to addGeoJsonLayer

- geoarrow-pipeline: deduplicate WKB decoding and reuse decoded features in fallback path

- layers-slice: add try-catch fallback to GeoJSON layer creation on conversion error

- joins: strip stale runtime geoarrow batch when updating layer geojson with joined properties

- undo-history: keep layers reference in partializeHistory to preserve shallow equality and immutable geoarrow across undo/redo

- tests: add regression tests for all verified findings

* style: auto-format (ruff + oxfmt) [pre-commit.ci]

* Address review feedback

- Chunk binary properties and feature IDs with event-loop yields so worker serialization does not freeze the main thread.
- Exercise the thrown conversion fallback and assert that GeoJSON survives without a batch.
- Bail out early for unsupported multi geometries, inspect all vertices for Z, avoid invented XY ordinates, and validate batch offsets.
- Clear stale batches during project hydration and excluded-field updates, and document the extra memory retained by hybrid layers.
- Reuse decoded non-Point WKB features, preserve measured point dimensions, and remove unsupported pipeline performance claims.

---------

Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
Co-authored-by: Qiusheng Wu <giswqs@gmail.com>
2026-10-24 23:07:19 -04:00
Qiusheng Wu 4e480b46c0 fix(deps): bump body-parser to 2.4.0 for GHSA-27r3-jxmh-p828 (#3189)
The audit gate on main failed on a new high advisory in body-parser
(reached via @strands-agents/sdk -> express 5). 2.4.0 is the patched
release. Also drop the braces GHSA-vfj7-8cjw-p6xm allowlist entry,
which npm audit no longer reports.
2026-10-23 21:45:09 -04:00
Daniel Hofbauer f5255f9380 feat(mcp): add read-only map previews with MCP Apps (#3097) 2026-10-10 21:31:21 +02:00
Qiusheng Wu b606f13441 feat(plugins): NASA Earthaccess plugin for CMR search and Earthdata download (#3085)
* feat(plugins): add NASA Earthaccess plugin for CMR search and download

Add a Web Services plugin that searches NASA's Common Metadata
Repository for datasets and granules over the map view and a date
range, draws granule footprints as a Layers-panel entry, and downloads
granules with an Earthdata Login token. ICESat-2 (ATL06, ATL08) and GEDI
(L2A, L2B, L4A) granules open straight into Add Data -> ICESat-2 / GEDI.

The DAACs send no CORS headers and refuse a preflighted Authorization
header, so downloads need a non-browser hop:

- Desktop: a new native download_remote_file command streams the file
  to disk (no 64 MiB cap, progress over a channel, cancellable), either
  to a path picked in a native save dialog or to the app cache, read
  back once by take_cached_download. JavaScript never names a write
  path. Exposed to plugins as app.downloadRemoteFile.
- Web: a new /earthdata/download route on the tiles Worker, origin
  gated, that accepts only NASA Earthdata data hosts, sends the bearer
  token to the first hop only, follows the presigned CloudFront/S3
  redirect, and streams the file back with CORS and no-store.

Desktop users can also sign in with a user name and password (via
nativeFetch, since the EDL token endpoint has no CORS); only the
returned token is kept, in the keychain through app.credentials.

Part of #3078 (Phase 2: granule search and authenticated download).

* fix(earthaccess): Earthdata Search details link, CI checks

CMR serves granule records only as metadata formats, so Details now
opens the granule in Earthdata Search. Also add the plugin to the
generated plugin reference, list downloadRemoteFile as a desktop-only
app API member, and typecheck the Worker proxy test with the Workers
types.

* Address Claude review feedback

- earthdata.ts: catch a malformed redirect Location and answer 502 with
  CORS instead of an uncaught 500, and narrow the redirect allowlist from
  all of amazonaws.com to S3 endpoints.
- remote_download.rs: request Accept-Encoding: identity (and keep callers
  from overriding it) so the Content-Length check compares like with like.
- remote_download.rs: reject a reused request ID before the save dialog
  opens, and never let a new cache entry orphan an earlier one's file.

* feat(earthaccess): multi-file download and NASA COG map layers

- Multi-file granules (an HLS scene has 15 files) get a collapsible file
  list with a Download per file, and Download all: the desktop app asks
  for a folder once (new pick_download_folder command; JavaScript holds
  only the folder's id, never a path), the browser saves each file.
- GeoTIFFs in the list get Add, which puts them on the map as COG
  layers. The COG readers fetch with the global fetch and take no
  headers, and the DAACs send no CORS headers, so a layer reads through
  the tiles Worker relay. Its saved URL carries no token; a fetch
  wrapper installed at boot adds the Earthdata Login token to requests
  for that exact route, so reopened projects keep working.
- The Worker caches presigned redirect targets (keyed by a hash of the
  token and URL, in memory and in the edge cache, 50 minutes), cutting a
  cached range request from about 1.9 s to about 0.1 s.
- File names in the list drop the shared granule prefix (B04.tif), and
  toggling the list no longer closes it by re-rendering the card.

* fix(earthaccess): refresh Add/Added when a COG layer is removed

The panel computed a file's Added state only when it re-rendered, so
removing the layer from the Layers panel left the button disabled. The
panel now re-renders when the set of Earthdata COG layers in the store
changes, and ignores unrelated layer edits.

* Address review feedback

- earthdata.ts: restrict the content-disposition file name to
  [A-Za-z0-9._-] so an odd URL path cannot make Headers.set throw, and
  request identity encoding upstream so Content-Length matches the body.
- spaceborne-lidar-handoff.ts: isolate listener failures so one bad
  listener cannot stop the others or reach the plugin.
- maplibre-earthaccess.ts: fill MultiPolygon footprints too (CMR reports
  several polygons or boxes for some granules).
- maplibre-earthaccess.ts: freeze the search's date range so Load more
  pages the same query after the date inputs are edited.
- maplibre-earthaccess.ts: give the token link an href so keyboard users
  can reach it.
- remote_download.rs: a folder download never replaces an existing file
  (adds " (n)"), and folder ids come from a counter.

* Address Claude review feedback

- maplibre-earthaccess.ts / earthdata-relay.ts: send the Earthdata
  token only to HTTPS NASA Earthdata data hosts (isEarthdataDataUrl).
  CMR data links are chosen by each provider, so a link on another host
  gets no token on desktop, has no Add, and opens in a new tab on the web
  (the relay serves Earthdata hosts only).
- remote_download.rs: accept only Authorization, Cookie and Accept
  headers (the ones reqwest strips on a cross-host redirect, plus
  Accept), and refuse to send them over plain HTTP; the module docs now
  say exactly which headers stay on the first host.
- earthdata-fetch-auth.ts: install the fetch wrapper through an explicit
  installEarthdataFetchAuth() call in main.tsx, placed right before
  installDiagnosticsCapture(), instead of an import side effect.

* Address review feedback

- remote_download.rs: requests that carry credentials use a client whose
  redirects must stay on HTTPS, since reqwest strips credentials only
  when the host changes (a same-host https -> http hop would resend them).
- remote_download.rs: a folder download reserves its destination with
  create_new before transferring (name, then "name (n)"), streams into a
  request-specific .part file, and removes the placeholder if it fails,
  so concurrent downloads of one name never collide or replace a file.
- types.ts / remote-download.ts: GeoLibreRemoteDownloadOptions is a
  discriminated union that requires folderId for "folder", and the host
  rejects a missing id at runtime.
- maplibre-earthaccess.ts: the browser's Download all reports that the
  downloads started and mentions the multiple-downloads prompt, instead
  of claiming every file was saved.
- earthdata.ts: edge-cache hits go through rememberPresigned so the
  in-memory cap applies.

* Address review feedback

- earthdata.ts: cache the first redirect only when it is a presigned
  target, not another Earthdata host that would fail the tokenless
  replay and be evicted on every request.
- remote_download.rs: report a panicked transfer task as a failure, not
  as "cancelled".
- remote_download.rs: create the folder placeholder's cleanup guard right
  after the reservation and move it into the task, so an abort before
  the task first runs still removes the empty file.
2026-10-09 23:58:30 -04:00
Qiusheng Wu ab1d314ac7 feat(ftw): switch Fields of the World to the 2nd Edition data (#3057)
* feat(ftw): switch Fields of the World to the 2nd Edition data

FTW published a 2nd Edition (2017-2025) with a new layout: one PMTiles archive
per year holding field polygons and A5 density cells, and per-UTM-zone
GeoParquet files instead of the 1° download grid. The plugin now reads these,
filters by the new 0-100 score, and fetches only the row groups overlapping
the search area.

* Address review feedback

- Lower the GeoJSON export to 250,000 fields / 512 MiB read and the whole-file
  GeoParquet save to 256 MiB: the old caps bounded compressed bytes, not the
  decoded polygons or buffered blob, and could exhaust a tab's heap.
- Clear the result rows and footprints when a search fails, so they never
  describe a different area than the error message.
- Pin the score and density legends to LTR so their labels stay under the
  right swatches in right-to-left locales.

* Address Claude review feedback

- Skip clearing search results when the search was aborted by a newer task,
  which may still be using them.
- Word FtwTooManyFieldsError by the limit rather than the count kept so far.
2026-10-08 14:18:51 -04:00
Qiusheng Wu 63d4a583c3 feat(dynamical): draw the virtual and time-series datasets (#3050)
* feat(dynamical): draw the virtual and time-series datasets

The Dynamical panel greyed out 21 of dynamical.org's 26 datasets. Both
blockers are lifted:

- Virtual repositories point each chunk at one message in NOAA's or
  ECMWF's GRIB2 files and name the `gribberish` codec, which has no
  JavaScript build. Add a GRIB2 decoder (grid templates 3.0 and 3.30;
  packing 5.0, 5.2/5.3 and 5.42 CCSDS with a port of libaec's decoder)
  and register it as a zarrita codec, which zarr-layer reads through
  too. It matches ecCodes value for value on real GFS, GEFS, HRRR and
  AIFS messages.
- Analyses and ensembles pack hundreds to thousands of steps into each
  chunk. They now draw a region at a time: a minimum zoom is worked out
  from the chunk size and the map size so a view decodes at most
  256 MB, the map zooms in to it on add, and the layer's zoom range
  keeps a zoomed-out view (or a re-slice while zoomed out) from
  fetching anything.

Part of #3048.

* Address Claude review feedback

- grib2: a zero subdivision count falls back to microdegrees instead of
  dividing by zero, and a missing (all-ones) longitude increment leaves
  the step unset so the longitude roll is skipped.
- grib2: a truncated data section now throws instead of decoding the
  missing tail as zeros, and the encoded-value and group counts are
  checked against the grid before they size any allocation.
- grib2: orientGrib2Field defaults northUp to off, matching the codec.
- grib2-codec: document the zarrita contract the registry cast relies
  on, and test a read through zarrita end to end.
- Dynamical panel: branch on the dataset bbox explicitly and treat a
  bbox crossing 180 degrees correctly (bboxContains, bboxCenter).
- Dynamical panel: recompute regional layers' minimum zoom when the map
  resizes, since a larger map at the same zoom holds more chunks.

* Address review feedback

- grib2: validate template 5.3's differencing header (order 1 or 2, at
  least one extra octet, a data section long enough for it) instead of
  decoding NaN initial values (Claude).
- grib2: throw when the group lengths cover fewer values than the
  message declares, rather than scaling the untouched zeros (CodeRabbit).
- grib2: cap decoded grids at 16 million points, eight times the
  largest virtual grid (HRRR), so a corrupt message cannot make a
  decode allocate gigabytes (CodeRabbit).
- Dynamical panel: put the camera back if a regional add fails after
  the map jumped in to its minimum zoom (Claude).
- Dynamical panel: track regional layers even when their first minimum
  zoom is 0, so a later resize can raise it; clamp the zoom range at 0
  (CodeRabbit).
- Add an upstream contract test for zarr-layer's private minZoom and a
  docs/maintenance.md section on it (Claude).
- Drop chunkBytes' unused dataset parameter (Claude).

* test(grib2): drop the fixture files

Build the simple-packed messages inside the test with a small GRIB2
writer (grids, orientation, bitmap, truncation and count checks), and
keep the complex (5.3) and CCSDS (5.42) decoders covered by two tiny
ecCodes-encoded messages inlined as base64, checked against ecCodes'
sum, missing points and sampled values. This removes
tests/fixtures/grib2/ (13 files, including the generator).

* Address review feedback

- grib2-codec: clear the cached codec registration when the zarrita
  import fails, so a transient chunk-load error does not stick until
  reload.
- grib2: refuse a section whose length runs past the buffer with the
  descriptive "Corrupt GRIB section" error instead of truncating it or
  hitting a raw DataView RangeError; the truncation test now expects it.
- grib2: refuse a group whose length would push the decoded index past
  the declared count, rather than truncating it and accepting the layout.
- dynamical: move a regional layer's view into the dataset bbox when the
  map is already at the minimum zoom but centred outside it, keeping the
  current zoom.
- zarr: add a minZoom option that sets the renderer's own zoom gate in
  the synchronous layeradd handler, before its metadata (and first
  fetch) loads; the dynamical panel passes its regional threshold, so a
  zoom-out during the pending add cannot start a whole-globe read.
2026-10-08 12:46:36 -04:00
Qiusheng Wu 34aff3d626 feat(plugins): add a Dynamical weather data plugin under Web Services (#3023)
* feat(plugins): add a Dynamical weather data plugin under Web Services

Browse dynamical.org's STAC catalog of cloud-optimized weather forecasts
and add a variable as a Zarr raster layer read straight from its
Icechunk repository. The panel lists every dataset, enables the five
whose chunk layout suits a map (NOAA GFS forecast, NOAA HRRR 48-hour,
ECMWF AIFS Single, DWD ICON-EU, ECCC HRDPS), and explains why the rest
(time-series analyses and ensembles, GRIB-backed virtual datasets) are
unavailable.

Forecast run and lead time sliders start on the newest run and
re-slice the added layer in place. Color ranges are fixed per variable
where the physics give one and otherwise sampled from one chunk.
Projected grids are placed from the archive's CF spatial_ref: HRRR's
Lambert WKT as is, HRDPS's rotated pole rewritten as ob_tran because
proj4js ignores GDAL's GRIB pole-rotation WKT.

The STAC browser now recognizes application/x-icechunk assets and
carries a collection's datacube fields onto its collection-asset item,
so dynamical.org's collections are addable there too.

* Address review feedback

- Keep Add disabled until the chosen variable's slice axes have loaded,
  so an add that finishes mid-reload cannot re-enable it for a click
  that would add an empty selector (Claude review).
- Snapshot the axes and slider positions when Add is clicked, and write
  a sampled range back only if the same dataset and variable are still
  chosen (CodeRabbit).
- Run live re-slices one after another and rename against the current
  live record, so an older re-slice cannot land last or skip a rename
  (CodeRabbit).
- Clear the slice-axes cache on deactivate (Claude review).
- Document that Dynamical layers are not restored with a saved project,
  and "afterward" (CodeRabbit).

* Address review feedback

- Catch a failed live re-slice and show it in the panel instead of
  leaving an unhandled rejection (Claude review).
- Judge a dataset's map support by its worst variable, since the panel
  offers every variable of a dataset it lists as ready (Claude review).
- Share the re-slice queue across panel rebuilds and skip work queued
  by a disposed panel (CodeRabbit).
- Name a re-sliced layer after the variable it shows, not one chosen
  while the re-slice ran (CodeRabbit).

* Address review feedback

- Fall back to index labels only for a coordinate the repository does
  not have; a failed read now rejects and is not cached for the session
  (Claude review).
- Clear a re-slice failure from the status line once a later re-slice
  succeeds (CodeRabbit).

* Address Claude review feedback

- Use a collection's documentation link only when it is http(s), since
  it is rendered as a link; otherwise fall back to the catalog page.
- Trim the quotient before rounding a sampled range outward, so
  floating-point error cannot push a bound past its value (0.3 became
  0.29).

* fix(dynamical): keep the panel's spacing after a dataset is chosen

Un-hiding the editor set display to an empty string, which turned its
flex column into a block and dropped the gap above Forecast run,
Colormap and Add to map. Restore it as flex.
2026-10-07 14:23:44 -04:00
Qiusheng Wu 38ed979d3b docs: review the docs against the code and fix drift (#3008)
* docs: review the docs against the code and fix drift

Check every docs page against the current code and correct what no
longer matches: menu paths and labels, plugin placement (most plugins
now open in a docked panel), Cesium ion tokens being optional, desktop
OAuth sign-in for sharing, browser project save/open, project schema
fields, server and collaboration endpoints, Python and MCP APIs, and
stale paths in the source-code deep dives.

Fix the Spatial SQL tutorial's ST_Area_Spheroid query, which needs
ST_FlipCoordinates for lat/lon axis order. Document the Settings
sections that were listed but never described (Language, Appearance,
Interface, Geocoding, AI Providers), and add the USGS NLDI and ArcGIS
feature editing pages to the nav.

* docs: keep the NLDI and ArcGIS editing pages out of the nav

* Address CodeRabbit review feedback

- collaboration.md: give both snapshot-cap defaults, 10 MB for the
  relay and 1 MB in the Docker Compose setup.
- features.md, cesium-renderer.md: without an Ion token the globe still
  gets terrain from keyless Terrarium elevation (or a configured COG),
  not a bare ellipsoid (cesium-engine.ts enableWorldTerrain).

* Address CodeRabbit review feedback

- project-format.md: type blankBackgroundColor and selectedLayerId as
  string | null, matching ProjectFile in packages/core/src/types.ts.
2026-10-06 19:03:19 -04:00
Qiusheng Wu c42e054063 feat(plugins): add a Sentinel-2 Explorer under Web Services (#3001)
* feat(plugins): add a Sentinel-2 Explorer under Web Services

Ports Taylor Geospatial's s2-stac-geoparquet explorer to a GeoLibre
plugin (Plugins > Web Services > Sentinel-2 Explorer). It reads the
public catalog on Source Cooperative with no API in front of it:

- Colors the MGRS grid by monthly tile statistics over a date window
  (clearest scene, scene count, median cloud, or coverage), with max
  cloud, min coverage, and min scene filters. The grid is decoded once
  from the stats PMTiles and painted from the month slices.
- A tile click range-reads its scenes from the year's STAC-GeoParquet
  parts with hyparquet: the search sidecar or footer once per part,
  then only the column chunks of row groups whose tile range can hold
  the tile, fetched in parallel. The wide assets column is never read.
- Scenes list with thumbnails, cloud and coverage, sorted by cloud,
  coverage, or date, and filtered in memory.
- Add to map streams a scene's true color (TCI) or any single band as
  a COG layer; band stretches account for the processing-baseline
  offset.
- Supports both Collection 1 (default) and the original L2A index.

* Address review feedback

- Tag the decoded MGRS grid with the collection it was read for, and drop
  a superseded paint's grid, so a collection switch mid-load cannot keep
  the wrong grid on screen (Claude, CodeRabbit).
- Re-adding the overlays on styledata no longer throws mid basemap
  switch; the next styledata retries (Claude).
- Route the SCL stretch through bandRescale (0-19, one tab20 entry per
  class) and drop the unused SCL palette table; update the date inputs'
  minimum on a collection switch (Claude).
- Translate the band labels of the Add scenes as select (CodeRabbit).
- Check every chunk read against the cached part size and re-read a
  rewritten part's footer once before failing, and trust a part's 404 for
  five minutes only, so refreshed or newly published live tails are read
  correctly in a long session (CodeRabbit).

* feat(sentinel2-explorer): composites, indices, downloads, add/remove state

- Multi-file composites (false color IR, agriculture, SWIR) and NDVI/NDWI.
  An s2composite:// MapLibre protocol reads each band's window per map
  tile straight from the scene's COGs at the matching overview, warps it
  from UTM to Web Mercator, and paints it, so 10 m and 20 m bands
  combine at full resolution. Composites are plain XYZ store layers, and
  the protocol is registered at startup so saved projects restore them.
- Download lists a scene's COGs (TCI, every band, SCL, masks) and opens
  each in the browser, or the system browser on desktop.
- Add to map becomes Remove from map while the scene is on the map in
  the chosen display, shows Adding... while a COG loads, and follows
  the store, so removing the layer in the Layers panel resets it.
- The About text at the top of the panel is a collapsible section.

* Address Claude review feedback

- Keep a selected tile's search status (read plan or error) when the map
  repaints; the grid summary only shows with no tile selected.
- Fetch month slices at most six at a time, and memoize the aggregate by
  collection, months and metric, so filter drags read and aggregate
  nothing and a multi-year window does not fire a hundred requests.
- Reset the painted grid on deactivate so a reopened panel does not seed
  the map with the previous window's colors.
- Treat only 404 as an unpublished part or month; a 403 (blocked, rate
  limited) now surfaces as an error instead of "0 scenes".
- Clear the cached empty composite tile if its encode fails.
- Pin scene directories (and composite tile URLs) to Earth Search's two
  Sentinel-2 buckets instead of any amazonaws.com host.

* Address review feedback

- Remove the doubled comma in the Sentinel-2 Explorer features entry
  (CodeRabbit, Claude).
- Move the composite protocol registration above the Fields of the World
  comment it had orphaned in usePlugins.ts (Claude).

* Address review feedback

- Mark the composite protocol registered only after addProtocol
  succeeds, so a failed registration is retried (Claude).
- Load a result card's thumbnail only from https on Earth Search's
  Sentinel-2 buckets, the same check the COG reads use (Claude).
- Stop starting new month-slice fetches once one has failed
  (CodeRabbit).
2026-10-06 15:11:23 -04:00
9fe0ae9e2c feat(chrome-extension): make the GeoLibre base URL configurable (#2990)
* feat(chrome-extension): make the GeoLibre base URL configurable

Add an options page (Manifest V3 options_ui) where the extension's
target GeoLibre instance can be changed from the hosted default,
https://web.geolibre.app/, to a self-hosted one. The URL is validated,
normalized, and persisted via chrome.storage.sync in a new
base-url-settings.mjs module shared by the popup and the options page;
the popup already had a baseUrl parameter in buildGeoLibreUrl that
nothing wired up. Adds the storage permission, which PRIVACY.md,
STORE_LISTING.md, README.md, and the user guide are updated to
disclose.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* Address review feedback

- base-url-settings: parse the base URL first, drop its query string and
  fragment, and add the trailing slash to the pathname, so a subpath
  instance saved with ?query keeps its subpath (CodeRabbit).
- options: catch a failed reset and show an error status (CodeRabbit).
- PRIVACY.md: say selected URLs go to the configured destination, which
  may be a custom instance (CodeRabbit).
- STORE_LISTING.md: replace the "stores nothing" claim with the single
  stored setting (CodeRabbit).
- tests: type the chrome.storage stub so the test type-check ratchet
  (limit 0) passes again; cover query/fragment normalization.

---------

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
Co-authored-by: Qiusheng Wu <giswqs@gmail.com>
2026-10-06 13:47:12 -04:00
dependabot[bot]andgiswqs fca6dd46f6 build(deps): bump the npm-minor-patch group across 1 directory with 38 updates (#2982)
* build(deps): bump the npm-minor-patch group across 1 directory with 38 updates

Bumps the npm-minor-patch group with 38 updates in the / directory:

| Package | From | To |
| --- | --- | --- |
| [@types/node](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/node) | `26.6.3` | `26.6.4` |
| [eslint](https://github.com/eslint/eslint) | `10.11.0` | `10.12.0` |
| [typescript-eslint](https://github.com/typescript-eslint/typescript-eslint/tree/HEAD/packages/typescript-eslint) | `8.70.1` | `8.71.0` |
| [@anthropic-ai/sdk](https://github.com/anthropics/anthropic-sdk-typescript) | `0.128.0` | `0.131.0` |
| [@auth0/auth0-react](https://github.com/auth0/auth0-react) | `2.27.0` | `2.28.2` |
| [@aws-sdk/client-bedrock](https://github.com/aws/aws-sdk-js-v3/tree/HEAD/clients/client-bedrock) | `3.1141.0` | `3.1146.0` |
| [@carbonplan/zarr-layer](https://github.com/carbonplan/zarr-layer) | `0.10.0` | `0.11.0` |
| [@cereusdb/standard](https://github.com/tobilg/cereusdb/tree/HEAD/packages/standard) | `0.2.0` | `0.3.0` |
| [@cesium/engine](https://github.com/CesiumGS/cesium) | `26.3.0` | `26.4.0` |
| [@cesium/widgets](https://github.com/CesiumGS/cesium) | `16.2.0` | `16.3.0` |
| [@clerk/react](https://github.com/clerk/javascript/tree/HEAD/packages/react) | `6.17.2` | `6.17.5` |
| [@developmentseed/deck.gl-geotiff](https://github.com/developmentseed/deck.gl-raster) | `0.7.0` | `0.8.1` |
| [@developmentseed/deck.gl-raster](https://github.com/developmentseed/deck.gl-raster) | `0.7.0` | `0.8.1` |
| [@google/genai](https://github.com/googleapis/js-genai) | `2.24.0` | `2.27.0` |
| [@modelcontextprotocol/sdk](https://github.com/modelcontextprotocol/typescript-sdk) | `1.30.1` | `1.32.0` |
| [@tauri-apps/api](https://github.com/tauri-apps/tauri) | `2.11.1` | `2.12.1` |
| [@tauri-apps/plugin-deep-link](https://github.com/tauri-apps/plugins-workspace) | `2.4.10` | `2.6.1` |
| [@tauri-apps/plugin-dialog](https://github.com/tauri-apps/plugins-workspace) | `2.7.3` | `2.8.1` |
| [@tauri-apps/plugin-fs](https://github.com/tauri-apps/plugins-workspace) | `2.5.2` | `2.6.0` |
| [@tauri-apps/plugin-geolocation](https://github.com/tauri-apps/plugins-workspace) | `2.3.3` | `2.4.1` |
| [@tauri-apps/plugin-http](https://github.com/tauri-apps/plugins-workspace) | `2.6.1` | `2.8.0` |
| [@tauri-apps/plugin-opener](https://github.com/tauri-apps/plugins-workspace) | `2.5.5` | `2.7.0` |
| [cesium](https://github.com/CesiumGS/cesium) | `1.145.0` | `1.146.0` |
| [html2canvas-pro](https://github.com/yorickshan/html2canvas-pro) | `2.4.5` | `2.5.0` |
| [openai](https://github.com/openai/openai-node) | `7.23.0` | `7.27.0` |
| [@tauri-apps/cli](https://github.com/tauri-apps/tauri) | `2.11.5` | `2.12.1` |
| [vite](https://github.com/vitejs/vite/tree/HEAD/packages/vite) | `8.3.1` | `8.3.2` |
| [fast-xml-parser](https://github.com/NaturalIntelligence/fast-xml-parser) | `5.11.1` | `5.11.2` |
| [mapbox-gl](https://github.com/mapbox/mapbox-gl-js) | `3.31.0` | `3.32.0` |
| [zarr-cesium](https://github.com/NOC-OI/zarr-cesium) | `0.3.3` | `0.3.5` |
| [@developmentseed/morecantile](https://github.com/developmentseed/deck.gl-raster) | `0.7.0` | `0.8.1` |
| [hyparquet](https://github.com/hyparam/hyparquet) | `1.31.1` | `1.31.2` |
| [webdggrid](https://github.com/am2222/webDggrid) | `1.9.0` | `1.9.2` |
| [lucide-react](https://github.com/lucide-icons/lucide/tree/HEAD/packages/lucide-react) | `1.48.0` | `1.50.0` |
| [wrangler](https://github.com/cloudflare/workers-sdk/tree/HEAD/packages/wrangler) | `4.141.0` | `4.147.0` |
| [@cloudflare/workers-types](https://github.com/cloudflare/workerd) | `5.20260926.1` | `5.20261003.1` |
| [ws](https://github.com/websockets/ws) | `8.21.3` | `8.22.0` |
| [@types/ws](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/ws) | `8.18.1` | `8.18.2` |
| [@types/ws](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/ws) | `8.18.1` | `8.18.2` |



Updates `@types/node` from 26.6.3 to 26.6.4
- [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases)
- [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/node)

Updates `eslint` from 10.11.0 to 10.12.0
- [Release notes](https://github.com/eslint/eslint/releases)
- [Commits](https://github.com/eslint/eslint/compare/v10.11.0...v10.12.0)

Updates `typescript-eslint` from 8.70.1 to 8.71.0
- [Release notes](https://github.com/typescript-eslint/typescript-eslint/releases)
- [Changelog](https://github.com/typescript-eslint/typescript-eslint/blob/main/packages/typescript-eslint/CHANGELOG.md)
- [Commits](https://github.com/typescript-eslint/typescript-eslint/commits/v8.71.0/packages/typescript-eslint)

Updates `@anthropic-ai/sdk` from 0.128.0 to 0.131.0
- [Release notes](https://github.com/anthropics/anthropic-sdk-typescript/releases)
- [Changelog](https://github.com/anthropics/anthropic-sdk-typescript/blob/main/CHANGELOG.md)
- [Commits](https://github.com/anthropics/anthropic-sdk-typescript/compare/sdk-v0.128.0...sdk-v0.131.0)

Updates `@auth0/auth0-react` from 2.27.0 to 2.28.2
- [Release notes](https://github.com/auth0/auth0-react/releases)
- [Changelog](https://github.com/auth0/auth0-react/blob/main/CHANGELOG.md)
- [Commits](https://github.com/auth0/auth0-react/compare/v2.27.0...v2.28.2)

Updates `@aws-sdk/client-bedrock` from 3.1141.0 to 3.1146.0
- [Release notes](https://github.com/aws/aws-sdk-js-v3/releases)
- [Changelog](https://github.com/aws/aws-sdk-js-v3/blob/main/clients/client-bedrock/CHANGELOG.md)
- [Commits](https://github.com/aws/aws-sdk-js-v3/commits/v3.1146.0/clients/client-bedrock)

Updates `@carbonplan/zarr-layer` from 0.10.0 to 0.11.0
- [Release notes](https://github.com/carbonplan/zarr-layer/releases)
- [Commits](https://github.com/carbonplan/zarr-layer/compare/v0.10.0...v0.11.0)

Updates `@cereusdb/standard` from 0.2.0 to 0.3.0
- [Release notes](https://github.com/tobilg/cereusdb/releases)
- [Commits](https://github.com/tobilg/cereusdb/commits/v0.3.0/packages/standard)

Updates `@cesium/engine` from 26.3.0 to 26.4.0
- [Release notes](https://github.com/CesiumGS/cesium/releases)
- [Changelog](https://github.com/CesiumGS/cesium/blob/main/CHANGES.md)
- [Commits](https://github.com/CesiumGS/cesium/commits)

Updates `@cesium/widgets` from 16.2.0 to 16.3.0
- [Release notes](https://github.com/CesiumGS/cesium/releases)
- [Changelog](https://github.com/CesiumGS/cesium/blob/main/CHANGES.md)
- [Commits](https://github.com/CesiumGS/cesium/commits)

Updates `@clerk/react` from 6.17.2 to 6.17.5
- [Release notes](https://github.com/clerk/javascript/releases)
- [Changelog](https://github.com/clerk/javascript/blob/main/packages/react/CHANGELOG.md)
- [Commits](https://github.com/clerk/javascript/commits/@clerk/react@6.17.5/packages/react)

Updates `@developmentseed/deck.gl-geotiff` from 0.7.0 to 0.8.1
- [Release notes](https://github.com/developmentseed/deck.gl-raster/releases)
- [Changelog](https://github.com/developmentseed/deck.gl-raster/blob/main/CHANGELOG.md)
- [Commits](https://github.com/developmentseed/deck.gl-raster/compare/v0.7.0...v0.8.1)

Updates `@developmentseed/deck.gl-raster` from 0.7.0 to 0.8.1
- [Release notes](https://github.com/developmentseed/deck.gl-raster/releases)
- [Changelog](https://github.com/developmentseed/deck.gl-raster/blob/main/CHANGELOG.md)
- [Commits](https://github.com/developmentseed/deck.gl-raster/compare/v0.7.0...v0.8.1)

Updates `@google/genai` from 2.24.0 to 2.27.0
- [Release notes](https://github.com/googleapis/js-genai/releases)
- [Changelog](https://github.com/googleapis/js-genai/blob/main/CHANGELOG.md)
- [Commits](https://github.com/googleapis/js-genai/compare/v2.24.0...v2.27.0)

Updates `@modelcontextprotocol/sdk` from 1.30.1 to 1.32.0
- [Release notes](https://github.com/modelcontextprotocol/typescript-sdk/releases)
- [Commits](https://github.com/modelcontextprotocol/typescript-sdk/compare/1.30.1...1.32.0)

Updates `@tauri-apps/api` from 2.11.1 to 2.12.1
- [Release notes](https://github.com/tauri-apps/tauri/releases)
- [Commits](https://github.com/tauri-apps/tauri/compare/@tauri-apps/api-v2.11.1...@tauri-apps/api-v2.12.1)

Updates `@tauri-apps/plugin-deep-link` from 2.4.10 to 2.6.1
- [Release notes](https://github.com/tauri-apps/plugins-workspace/releases)
- [Commits](https://github.com/tauri-apps/plugins-workspace/compare/deep-link-v2.4.10...http-v2.6.1)

Updates `@tauri-apps/plugin-dialog` from 2.7.3 to 2.8.1
- [Release notes](https://github.com/tauri-apps/plugins-workspace/releases)
- [Commits](https://github.com/tauri-apps/plugins-workspace/compare/dialog-v2.7.3...dialog-v2.8.1)

Updates `@tauri-apps/plugin-fs` from 2.5.2 to 2.6.0
- [Release notes](https://github.com/tauri-apps/plugins-workspace/releases)
- [Commits](https://github.com/tauri-apps/plugins-workspace/compare/fs-v2.5.2...fs-v2.6.0)

Updates `@tauri-apps/plugin-geolocation` from 2.3.3 to 2.4.1
- [Release notes](https://github.com/tauri-apps/plugins-workspace/releases)
- [Commits](https://github.com/tauri-apps/plugins-workspace/compare/nfc-v2.3.3...fs-v2.4.1)

Updates `@tauri-apps/plugin-http` from 2.6.1 to 2.8.0
- [Release notes](https://github.com/tauri-apps/plugins-workspace/releases)
- [Commits](https://github.com/tauri-apps/plugins-workspace/compare/http-v2.6.1...log-v2.8.0)

Updates `@tauri-apps/plugin-opener` from 2.5.5 to 2.7.0
- [Release notes](https://github.com/tauri-apps/plugins-workspace/releases)
- [Commits](https://github.com/tauri-apps/plugins-workspace/compare/http-v2.5.5...log-v2.7.0)

Updates `cesium` from 1.145.0 to 1.146.0
- [Release notes](https://github.com/CesiumGS/cesium/releases)
- [Changelog](https://github.com/CesiumGS/cesium/blob/main/CHANGES.md)
- [Commits](https://github.com/CesiumGS/cesium/compare/1.145...1.146)

Updates `html2canvas-pro` from 2.4.5 to 2.5.0
- [Release notes](https://github.com/yorickshan/html2canvas-pro/releases)
- [Changelog](https://github.com/yorickshan/html2canvas-pro/blob/main/CHANGELOG.md)
- [Commits](https://github.com/yorickshan/html2canvas-pro/compare/v2.4.5...v2.5.0)

Updates `openai` from 7.23.0 to 7.27.0
- [Release notes](https://github.com/openai/openai-node/releases)
- [Changelog](https://github.com/openai/openai-node/blob/main/CHANGELOG.md)
- [Commits](https://github.com/openai/openai-node/compare/v7.23.0...v7.27.0)

Updates `@tauri-apps/cli` from 2.11.5 to 2.12.1
- [Release notes](https://github.com/tauri-apps/tauri/releases)
- [Commits](https://github.com/tauri-apps/tauri/compare/@tauri-apps/cli-v2.11.5...@tauri-apps/cli-v2.12.1)

Updates `vite` from 8.3.1 to 8.3.2
- [Release notes](https://github.com/vitejs/vite/releases)
- [Changelog](https://github.com/vitejs/vite/blob/main/packages/vite/CHANGELOG.md)
- [Commits](https://github.com/vitejs/vite/commits/v8.3.2/packages/vite)

Updates `fast-xml-parser` from 5.11.1 to 5.11.2
- [Release notes](https://github.com/NaturalIntelligence/fast-xml-parser/releases)
- [Changelog](https://github.com/NaturalIntelligence/fast-xml-parser/blob/master/CHANGELOG.md)
- [Commits](https://github.com/NaturalIntelligence/fast-xml-parser/compare/v5.11.1...v5.11.2)

Updates `mapbox-gl` from 3.31.0 to 3.32.0
- [Release notes](https://github.com/mapbox/mapbox-gl-js/releases)
- [Changelog](https://github.com/mapbox/mapbox-gl-js/blob/main/CHANGELOG.md)
- [Commits](https://github.com/mapbox/mapbox-gl-js/compare/v3.31.0...v3.32.0)

Updates `zarr-cesium` from 0.3.3 to 0.3.5
- [Release notes](https://github.com/NOC-OI/zarr-cesium/releases)
- [Commits](https://github.com/NOC-OI/zarr-cesium/compare/v0.3.3...v0.3.5)

Updates `@developmentseed/morecantile` from 0.7.0 to 0.8.1
- [Release notes](https://github.com/developmentseed/deck.gl-raster/releases)
- [Changelog](https://github.com/developmentseed/deck.gl-raster/blob/main/CHANGELOG.md)
- [Commits](https://github.com/developmentseed/deck.gl-raster/compare/v0.7.0...v0.8.1)

Updates `hyparquet` from 1.31.1 to 1.31.2
- [Changelog](https://github.com/hyparam/hyparquet/blob/master/CHANGELOG.md)
- [Commits](https://github.com/hyparam/hyparquet/compare/v1.31.1...v1.31.2)

Updates `webdggrid` from 1.9.0 to 1.9.2
- [Release notes](https://github.com/am2222/webDggrid/releases)
- [Changelog](https://github.com/am2222/webDggrid/blob/main/CHANGELOG.md)
- [Commits](https://github.com/am2222/webDggrid/compare/v1.9.0...v1.9.2)

Updates `lucide-react` from 1.48.0 to 1.50.0
- [Release notes](https://github.com/lucide-icons/lucide/releases)
- [Commits](https://github.com/lucide-icons/lucide/commits/1.50.0/packages/lucide-react)

Updates `wrangler` from 4.141.0 to 4.147.0
- [Release notes](https://github.com/cloudflare/workers-sdk/releases)
- [Commits](https://github.com/cloudflare/workers-sdk/commits/wrangler@4.147.0/packages/wrangler)

Updates `@cloudflare/workers-types` from 5.20260926.1 to 5.20261003.1
- [Release notes](https://github.com/cloudflare/workerd/releases)
- [Changelog](https://github.com/cloudflare/workerd/blob/main/RELEASE.md)
- [Commits](https://github.com/cloudflare/workerd/commits)

Updates `ws` from 8.21.3 to 8.22.0
- [Release notes](https://github.com/websockets/ws/releases)
- [Commits](https://github.com/websockets/ws/compare/8.21.3...8.22.0)

Updates `@types/ws` from 8.18.1 to 8.18.2
- [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases)
- [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/ws)

Updates `@types/ws` from 8.18.1 to 8.18.2
- [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases)
- [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/ws)

---
updated-dependencies:
- dependency-name: "@anthropic-ai/sdk"
  dependency-version: 0.131.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: npm-minor-patch
- dependency-name: "@auth0/auth0-react"
  dependency-version: 2.28.2
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: npm-minor-patch
- dependency-name: "@aws-sdk/client-bedrock"
  dependency-version: 3.1146.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: npm-minor-patch
- dependency-name: "@carbonplan/zarr-layer"
  dependency-version: 0.11.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: npm-minor-patch
- dependency-name: "@cereusdb/standard"
  dependency-version: 0.3.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: npm-minor-patch
- dependency-name: "@cesium/engine"
  dependency-version: 26.4.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: npm-minor-patch
- dependency-name: "@cesium/widgets"
  dependency-version: 16.3.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: npm-minor-patch
- dependency-name: "@clerk/react"
  dependency-version: 6.17.5
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: npm-minor-patch
- dependency-name: "@cloudflare/workers-types"
  dependency-version: 5.20261003.1
  dependency-type: direct:development
  update-type: version-update:semver-minor
  dependency-group: npm-minor-patch
- dependency-name: "@developmentseed/deck.gl-geotiff"
  dependency-version: 0.8.1
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: npm-minor-patch
- dependency-name: "@developmentseed/deck.gl-raster"
  dependency-version: 0.8.1
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: npm-minor-patch
- dependency-name: "@developmentseed/morecantile"
  dependency-version: 0.8.1
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: npm-minor-patch
- dependency-name: "@google/genai"
  dependency-version: 2.27.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: npm-minor-patch
- dependency-name: "@modelcontextprotocol/sdk"
  dependency-version: 1.32.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: npm-minor-patch
- dependency-name: "@tauri-apps/api"
  dependency-version: 2.12.1
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: npm-minor-patch
- dependency-name: "@tauri-apps/cli"
  dependency-version: 2.12.1
  dependency-type: direct:development
  update-type: version-update:semver-minor
  dependency-group: npm-minor-patch
- dependency-name: "@tauri-apps/plugin-deep-link"
  dependency-version: 2.6.1
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: npm-minor-patch
- dependency-name: "@tauri-apps/plugin-dialog"
  dependency-version: 2.8.1
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: npm-minor-patch
- dependency-name: "@tauri-apps/plugin-fs"
  dependency-version: 2.6.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: npm-minor-patch
- dependency-name: "@tauri-apps/plugin-geolocation"
  dependency-version: 2.4.1
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: npm-minor-patch
- dependency-name: "@tauri-apps/plugin-http"
  dependency-version: 2.8.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: npm-minor-patch
- dependency-name: "@tauri-apps/plugin-opener"
  dependency-version: 2.7.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: npm-minor-patch
- dependency-name: "@types/node"
  dependency-version: 26.6.4
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: npm-minor-patch
- dependency-name: "@types/ws"
  dependency-version: 8.18.2
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: npm-minor-patch
- dependency-name: "@types/ws"
  dependency-version: 8.18.2
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: npm-minor-patch
- dependency-name: cesium
  dependency-version: 1.146.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: npm-minor-patch
- dependency-name: eslint
  dependency-version: 10.12.0
  dependency-type: direct:development
  update-type: version-update:semver-minor
  dependency-group: npm-minor-patch
- dependency-name: fast-xml-parser
  dependency-version: 5.11.2
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: npm-minor-patch
- dependency-name: html2canvas-pro
  dependency-version: 2.5.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: npm-minor-patch
- dependency-name: hyparquet
  dependency-version: 1.31.2
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: npm-minor-patch
- dependency-name: lucide-react
  dependency-version: 1.50.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: npm-minor-patch
- dependency-name: mapbox-gl
  dependency-version: 3.32.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: npm-minor-patch
- dependency-name: openai
  dependency-version: 7.27.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: npm-minor-patch
- dependency-name: typescript-eslint
  dependency-version: 8.71.0
  dependency-type: direct:development
  update-type: version-update:semver-minor
  dependency-group: npm-minor-patch
- dependency-name: vite
  dependency-version: 8.3.2
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: npm-minor-patch
- dependency-name: webdggrid
  dependency-version: 1.9.2
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: npm-minor-patch
- dependency-name: wrangler
  dependency-version: 4.147.0
  dependency-type: direct:development
  update-type: version-update:semver-minor
  dependency-group: npm-minor-patch
- dependency-name: ws
  dependency-version: 8.22.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: npm-minor-patch
- dependency-name: zarr-cesium
  dependency-version: 0.3.5
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: npm-minor-patch
...

Signed-off-by: dependabot[bot] <support@github.com>

* fix(deps): adapt to Cesium 1.146, deck.gl-geotiff 0.8 and zarr-layer 0.11

- Import the math/core types CesiumJS 1.146 moved to @cesium/core
  (Cartesian3, Color, Event, ...) from there; @cesium/engine now
  re-exports them only as deprecated value shims.
- deck.gl-geotiff 0.8 exports inferTextureFormat at the top level
  instead of under a texture namespace.
- Refresh the allowlisted CesiumJS default Ion token hash (1.146).
- zarr-layer 0.11 routes opacity through optionalForCustom.
- Bump tauri-plugin-dialog to 2.8 to match the JS guest; keep
  @tauri-apps/plugin-geolocation on 2.3.x to match the vendored crate,
  and tell Dependabot not to move it.

---------

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: giswqs <giswqs@gmail.com>
2026-10-06 08:08:53 -04:00
Qiusheng Wu a70c2fe430 feat(plugins): add an ArcGIS Portal browser under Web Services (#2983)
* feat(plugins): add an ArcGIS Portal browser under Web Services

Signed-in ArcGIS users can now browse their own content, favorites,
groups, organization or the whole portal and add items to the map
instead of locating REST service URLs by hand. The panel shares the
Add Data sign-in session, and routes ArcGIS Online organization URLs
through www.arcgis.com because the org hosts refuse cross-origin reads.

* Address review feedback

- Send the portal token only to trusted service hosts (the portal's own
  host, or *.arcgis.com for ArcGIS Online): web map layer URLs and an
  item's registered service URL are author-controlled and could name any
  server. Untrusted ones load without credentials. Documented, with tests.
- Do not let a superseded search overwrite the panel's token after a
  newer search has started.

* Address Claude review feedback

- Treat a portal item whose search result has no service URL as
  untrusted, so the token never reaches a registered URL the panel could
  not check.
- Key the cached portal user by account as well as portal, so signing out
  in Add Data and back in as another user does not reuse the previous
  account's username, organization and favorites.
2026-10-06 01:15:18 -04:00
Qiusheng Wu 3d11b69740 docs(gallery): add NASA OPERA demos 101-105 (#2943)
Adds Alps radar, Valencia floods, Canada wildfire season 2023, Los
Angeles fire scars and Amazon water extent to the open data gallery and
bumps the demo count to 105.
2026-10-05 01:12:09 -04:00
Qiusheng Wu 5ae9762510 fix(app): app-UI follow-ups (language-pack race, palette memo/commands, vector panel race, focus, autosave indicator) (#2908)
* fix(app): app-UI follow-ups from #2869

- Language packs: guard download/import/remove with an operation token
  so a language switch mid-operation can't apply a stale result (#2866).
- Settings: drop the unused seededEnv block in the open effect (#2866).
- Command palette: memoize the command build on the values it reads at
  build time; handlers resolve through a view of the newest context
  (#2877).
- Palette: commands for the color-vision preview modes and for opening
  the starter examples expanded in New Project (#2884).
- Add Data -> Vector/Raster Layer: a restore pass for the same project
  (the basemap finishing its first load) no longer re-hides a panel the
  user just opened (#2895).
- Vector/Raster panels: move focus into the panel when opened and back to
  the trigger when closed (#2895).
- Status bar: memoize the autosave notice and show a distinct "Autosave
  off" indicator when IndexedDB is unavailable (#2860).

* test(app): cover the New Project starter-examples expansion

NewProjectDialog grew past its untested-module allowance; a component test
now loads it and checks the Examples section stays collapsed by default
and opens expanded with the first example focused when requested. The
dialog leaves the untested-module baseline, which is pruned to match.

* Address Claude review feedback

- useLanguagePack: a late import only writes the installed pack when no
  newer download/import/remove has started since, so it cannot overwrite a
  fresher result after a language switch away and back.

* Address Claude review feedback

- Vector/Raster panels: record the open's project generation only once the
  control exists, so a failed open never makes a later restore skip the
  project's saved panel state.
2026-10-04 09:53:00 -04:00
Qiusheng Wu a8692bdeef feat(graticule): MGRS/USNG grid overlay and grid references in Set View (#2907)
* feat(graticule): MGRS/USNG grid overlay and grid references in Set View

Add an MGRS / USNG mode to the Gridlines plugin. It draws, for the
viewport only, the grid zone boundaries (with the Norway 32V and
Svalbard 31X/33X/35X/37X exceptions) and their designations, the
100 km squares with their two-letter IDs, and 10 km then 1 km lines as
the ground resolution allows, labelled with their principal digits.
Lines are generated per zone in UTM and clipped to the zone's lng/lat
cell, and the finest tier keeps lines at least 24 px apart, so the
work stays proportional to the screen. The geometry lives in a new,
map-free mgrs-grid module; the grid type persists in plugin state and
older projects keep reading as geographic.

Share one zone rule (utmZoneNumber in mgrs-reference) between the MGRS
grid and the UTM readout, so lngLatToUtm now applies the Norway and
Svalbard exceptions and agrees with the MGRS readout (Bergen 32V,
Longyearbyen 33X).

The Set View dialog's paste box now accepts MGRS, USNG and UTM through
the place search's grid-reference parser, in addition to lat/lon.

Part of #2869

* Address review feedback

- Spell out the 10 km tier in the graticule line-width match so the
  taper (zone 2x, square 1.5x, 10 km 1x, 1 km 0.75x) does not read as a
  missed case (Claude review).
- Update the Gridlines description in scripts/plugin-reference.json, the
  source docs/user-guide/plugins.md is generated from, so the
  plugins:docs:check CI step passes.
2026-10-04 09:31:23 -04:00
Qiusheng Wu 84a1dc0b5e chore: pin the web CSP's jsDelivr paths and tighten loose types (#2906)
* chore: pin the web CSP's jsDelivr paths and tighten loose types

Follow-up cleanups from #2869:

- docker/nginx.conf: the app policy lists the same version-pinned
  jsDelivr script-src paths as tauri.conf.json instead of all of /npm/
  and /pyodide/. The JupyterLite policy drops /npm/ and keeps /pyodide/
  (its version comes from the pip-resolved kernel). tauri-csp.test.ts
  parses nginx.conf and checks both.
- CLAUDE.md: tile hosts need no CSP change; script-src is pinned.
- StoryChapterLocation pitch/bearing are optional. Cesium and
  applyStoryViewAndWait fill an absent value from the current camera
  (it was NaN on Cesium), Mapbox leaves the key out, and the story CSV
  writes an empty cell instead of "undefined".
- geojsonToCsv and shapefileFieldWarnings accept null geometries;
  shadow-style addLayer accepts an inline-source layer.
- Drop the test casts those worked around, type the root
  @mapbox/vector-tile copy locally in geojson-vt-tiling.test.ts, and
  lower the test type-check baseline to 0.
- A null registry bundleSha256 is treated as absent without a warning.
- Rename a variable that shadowed main() and skip a repeated existence
  check in upstream-contract.ts.

Part of #2869.

* Address review feedback

- Document that a story location's pitch/bearing are optional only in
  memory: loading a project or a CSV fills an absent value with 0, as
  it always has, so existing files keep playing north-up and flat.
- Assert the CSV round trip fills an empty pitch/bearing cell with 0.

* Address Claude review feedback

- Drop the LayerSpecification import shadow-style.test.ts no longer
  uses after the inline-layer cast was removed.
2026-10-04 09:30:48 -04:00
Qiusheng Wu c4ac813b50 docs: generated plugin reference and deeper user guide pages (#2891)
* docs: generate the plugin reference and deepen the user guide

Generate the built-in plugin table in docs/user-guide/plugins.md from
the plugin registry (scripts/gen-plugin-reference.mjs): ids and order
from BUILT_IN_PLUGINS, names from the plugin definitions and en.json,
menu location from PluginsMenu and the Web Services/DGGS groups, and
?plugin= link names from the deep-link rules. Only descriptions are
hand-written, in scripts/plugin-reference.json. npm run plugins:docs
regenerates it; plugins:docs:check (in npm run ci) and
tests/plugin-reference-docs.test.ts fail on drift. The table now covers
all 66 registered plugins in one place.

Expand the attribute table (joins, calculated and virtual fields, field
management, attribute forms, quick filters, selection) and
troubleshooting (Diagnostics categories, notifications and Report
issue, common errors, WebGL/GPU, self-hosting CSP) pages, and add
GPS Tracking & Field Collection, Print Layout, and Model Builder pages.
Document the raster histogram stretch, palette tool entries, Simplify
Interface, and notifications.

Part of #2858

* Address review feedback

- e2e/plugin-deep-link.spec.ts: read link names from the generated
  plugin reference table, which replaced the old link-name table (E2E
  core failure).
- model-builder.md: Cancel stops before the next node; a running
  Whitebox tool finishes first.
- projects.md: list the title, footer, and info block separately.
- styling.md: viewport stretch applies to unclassified single-band and
  index rasters only.
- troubleshooting.md: a private window points at the browser profile,
  not only the cache.
2026-10-04 07:41:52 -04:00
Qiusheng Wu 1b6f373104 ci(coverage): ratchet large modules that no test loads; lower lint cap to 242 (#2880)
* ci(coverage): ratchet large modules that no test loads

The frontend coverage floor only counts files a test imports, so a big
module with no test is invisible to it. The coverage run now also writes
an lcov report, and scripts/check-untested-modules.mjs compares its file
list with every source file under apps/*/src and packages/*/src. Any
untested file over 500 lines must sit in a committed baseline; a new one,
or a baseline entry growing more than 50 lines, fails ci:frontend.
Entries that gain a test can be pruned with --prune. Backend sidecar
modules with no test_<module>.py are reported, not gated.

Also lower the ESLint --max-warnings cap from 263 to the current 242.

Part of #2858.

* Address review feedback

- Credit each backend test file to the longest module name it matches,
  so test_vector_io.py no longer marks vector.py as tested (Claude).
- Pass module paths into backendModulesWithoutTests and return paths,
  documenting that same-basename modules match together (Claude).
- Report a malformed baseline file by path instead of throwing a raw
  SyntaxError (CodeRabbit).
- Add JSDoc to the remaining helpers.
2026-10-04 01:56:16 -04:00
Qiusheng Wu 9229c00454 build: ship h5wasm once and report bundle sizes in CI (#2870)
* build: ship h5wasm once and report bundle sizes in CI

The remote NetCDF worker is a separate Vite build, so its lazy
import("h5wasm") emitted a second 4.8 MB hdf5_hl chunk next to the main
thread's. The main build now emits h5wasm as an explicit chunk and
passes its URL to the worker with the open request; the worker build
resolves h5wasm to a shim that imports that chunk by URL. Dev is
unchanged. The build output shrinks by 4.79 MB.

Add scripts/bundle-report.mjs (npm run bundle:report): per-asset raw
and gzip sizes for every JS/WASM file, the boot set read from the built
index.html, and per-entry dist sizes, written as Markdown and JSON. The
CI build job appends it to the step summary and uploads it as an
artifact.

The boot budget guard gains a gzip limit (900 KiB; boot JS measures
~680 kB gzipped) next to the 3 MiB raw one. Both now live in
apps/geolibre-desktop/boot-budget.json, read by the guard and the
report, and are documented in docs/maintenance.md.

Part of #2858

* Address review feedback

- bundle-report: follow each boot chunk's static imports so the boot
  set stays complete even when index.html does not modulepreload the
  whole graph (CodeRabbit). Unchanged on the real build: 29 files.
- bundle-report: walk dist/assets recursively so nested JS/WASM files
  are listed and counted (CodeRabbit).
- Test that the worker's open request carries h5wasmUrl, which a
  production worker build needs to load h5wasm (Claude review).

* Address review feedback

- bundle-report: read static imports only from a chunk's leading import
  statements (after Vite's __vite__mapDeps line), so import-shaped text
  in strings or comments is not counted (CodeRabbit). With every
  modulepreload stripped from index.html, the traversal alone still
  recovers the same 29-file boot set.
- bundle-report: reject a non-numeric --top instead of silently
  rendering an empty table (Claude review).
- CI: make the bundle report step continue-on-error; it is diagnostic,
  and the build enforces the budgets (Claude review).
- vite.config: note that the worker h5wasm redirect applies to every
  worker build (Claude review).

* Address review feedback

- shared-h5wasm: fail the build unless exactly one hdf5_hl-*.js is
  emitted (worker outputs are in the main bundle by generateBundle), so
  a regression of the duplicate is caught in CI rather than only by the
  diagnostic report (Claude review). Verified: with the worker redirect
  removed, the build fails naming both old hdf5_hl chunks.
- bundle-report: note that the static-import scan is a text fallback
  that can drift with Rolldown's output shape (Claude review).

* Address review feedback

- shared-h5wasm: also fail the build when src/lib/h5wasm-chunk-url.ts
  was never replaced (for example after a move that breaks
  CHUNK_URL_MODULE). The one-chunk check alone would still pass, and
  the worker would only throw at runtime (Claude review). Verified by
  breaking the regex: the build fails with the new message.
2026-10-04 01:10:05 -04:00
Qiusheng Wu 2919e1193f test: contract tests for hand-mirrored upstream internals (#2867)
* test: add contract tests for hand-mirrored upstream internals

Add tests/upstream-contracts.test.ts, which checks the installed
packages for the exact shapes GeoLibre mirrors by hand (private fields,
class names, constants, method-body snippets) for every
docs/maintenance.md entry that had no real-package test: maplibre-gl,
mapbox-gl, zarr-layer, the Web Services controls, components, vector,
lidar, layer-control, splat, Geoman, planetary-computer, raster and
cesium. Failure messages name the section to read.

The new check caught a live drift: index.css hid the National Map dock
resize handle as .national-map-resizer, but the package renders
.national-map-resize-handle.

Add scripts/check-shared-deps.mjs (npm run check:shared-deps, run in
the CI lint job) to fail when a dependency shared by the desktop app
and packages/plugins carries different ranges.

Part of #2858.

* Address CodeRabbit review feedback

- check-shared-deps --json now prints only the JSON document and signals
  failure through the exit code, so stdout stays parseable on success.

* Address Claude review feedback

- Parse MAX_REMOTE_FILE_BYTES with a narrow N / B ** E - S pattern
  instead of evaluating the captured text with Function().
2026-10-03 23:38:28 -04:00
Qiusheng Wu 4a6d10ac64 ci: type-check the test suite with an error-count ratchet (#2862)
tsx strips types without checking them, so fakes in tests/ drifted from
the interfaces they stand in for and nothing noticed. Add tsconfig
projects for tests/ (a DOM project, plus two worker-runtime projects for
tests that import workers/tiles, workers/viewer and workers/ai-proxy,
whose runtime types cannot share a program with the DOM lib), and
`npm run typecheck:tests`, which runs tsc over them through
scripts/check-test-typecheck.mjs and fails when the error count in
test files rises above --max-errors.

Fix 450 of the 451 real errors: partial LayerStyle and project fixtures
now spread DEFAULT_LAYER_STYLE / createEmptyProject(), stale property
names and missing required fields are corrected, callback-assigned
variables keep their declared types, and deliberately partial fakes are
cast once through unknown. The one left is an untyped transitive
@mapbox/vector-tile import. The baseline is 1.

Run the gate in CI's lint job and in npm run ci / ci:web, document it in
docs/maintenance.md, and lower the lint warning limit to 263 now that
one any-warning is gone.

Part of #2858
2026-10-03 22:20:27 -04:00
Daniel Hofbauer 9dc4f0bdfb feat(docker): enforce sidecar and AI deployment policy (Part 5) (#2817)
* feat(docker): enforce sidecar and AI deployment policy (part 5)

* style(docker): apply policy Python formatting
2026-10-03 07:42:00 -04:00
Qiusheng Wu 1edd0b4d0d feat(plugins): dock USGS LiDAR panel under Web Services (#2813)
* feat(plugins): dock USGS LiDAR panel under Web Services

Host the USGS LiDAR control in the dockable side panel instead of a
floating map panel, and list it after USGS 3DEP in the Web Services
submenu. Also allowlist the unpatched braces advisory GHSA-vfj7-8cjw-p6xm
(build-time tooling only) so the dependency audit passes.

* Address Claude review feedback

- Document why the control's position option is still passed although the dock owns panel placement.
2026-10-02 22:04:39 -04:00
Daniel Hofbauer 540fc2e900 feat(docker): generate validated deployment.json at boot (#2783 part 3) (#2801)
* feat(docker): generate validated deployment.json at boot (#2783 part 3)

* fix(docker): strip services file path; reject query/fragment in file embedOrigins

* fix(docker): validate embed origin host and port in deployment.json

* fix(docker): address review: embed origin parsing, BOM, log redaction

* docs(docker): clarify strictness in deployment_policy docstring

* fix(docker): clean errors for malformed URL hosts; dedupe env embed origins

* fix(docker): keep query tokens out of URL validation errors

* refactor(docker): share service entry validation; name env var on whitespace errors
2026-10-02 16:34:35 -04:00
Qiusheng Wu dff1b123aa docs: list S3 Browser deep link; ci: allowlist unpatched node-forge advisory (#2798)
* docs: list the S3 Browser plugin and its ?plugin= link name

The S3 Browser (#2774) registered a deep-linkable built-in plugin, but
the Plugins page's link-name table did not list it, so the full E2E
spec plugin-deep-link.spec.ts failed on main and on every PR running
the full suite (seen on #2796).

* ci: allowlist unpatched node-forge advisory GHSA-86w9-cpqp-85rv

No patched node-forge exists (<=1.4.0, latest is 1.4.0). It reaches us
only through @google/earthengine's Node-only googleapis auth chain,
where google-p12-pem converts .p12 key files and never verifies an RSA
signature; the browser build contains no node-forge code.
2026-10-01 21:34:20 -04:00
Qiusheng Wu b3d91defa5 docs: add the open data gallery video tutorial (#2739)
Add "100 Interactive Maps from Open Data: Explore, Fork, and Build Your
Own with GeoLibre" (https://youtu.be/2r5OhvEa3AA, 24:37) to Video
Tutorials with an embed and summary, to the video lists in the README,
Getting Started, Tutorials, and Demos, and as a button on the Gallery
page (via scripts/gen-demo-gallery.mjs, then regenerated).
2026-09-28 22:23:30 -04:00
Qiusheng Wu 18dc662b9d docs: add a generated open data Gallery page grouped by theme (#2738)
* docs(demos): group the open data showcase by theme

Split the single 100-tile gallery into twelve theme sections (health,
natural hazards, climate, oceans and water, nature and land, transport,
cities and infrastructure, energy, space, history and culture, society
and economy, food and agriculture), each with its own table and a link to
the matching tag filter on share.geolibre.app. The intro now says "100
projects", explains the tags, and adds a jump list to the themes. The
README's Open data demos link lists the same themes.

* Address CodeRabbit review feedback

- Name the open-data tag in the showcase intro, linked to its share.geolibre.app
  filter, since all 100 projects carry it alongside their theme and features.

* docs: move the open data showcase to a generated Gallery page

The 100-tile showcase dominated the Demos page, burying its tour of
GeoLibre's own features, and loaded every screenshot up front. Move the
catalog to its own Gallery page and keep a short teaser on Demos.

- docs/gallery.md: all 100 demos in twelve theme sections, each with a
  count and a link to its share.geolibre.app tag filter; images load
  lazily.
- docs/demos.md: the Open data showcase section (same anchor) becomes six
  featured tiles plus buttons to the gallery and the collection.
- Both are generated by scripts/gen-demo-gallery.mjs from
  scripts/demo-gallery.json. npm run gallery regenerates them;
  npm run gallery:check runs in npm run ci and fails on drift.
- Add Gallery to the nav after Demos, point the README link at it, and
  document the generated files in maintenance.md.
2026-09-28 19:46:30 -04:00
Qiusheng Wu 8214841872 feat(plugins): add a US Federal GIS web service for federal agency portals (#2699)
* feat(plugins): add a US Federal GIS web service for federal agency portals

Add a "US Federal GIS" plugin under Plugins > Web Services that browses,
searches, and adds data from the public GIS portals of 24 US federal
agencies: pick a department, then an agency.

It is another createArcGisHubPlugin() instance next to US State GIS and
US Local GIS. Agencies with an ArcGIS Hub site (Forest Service, BLM,
NPS, FWS, FEMA, BTS, USACE, EIA, HUD, FAA, NIFC, NAIP) are searched
through the site's catalog groups; those without one (USGS, NOAA, EPA,
NRCS, NASA, Census Bureau, NGA, GeoPlatform) are searched across their
ArcGIS Online organization. Map and image services are included, so
NOAA radar, LANDFIRE and land cover rasters, and NAIP add as raster
layers.

TIGER boundaries live under Multi-agency > Esri U.S. Federal Datasets,
since the Census Bureau's own organization publishes mostly statistics.
HIFLD Open is left out: its Hub site no longer exists.

Every agency was verified live, and `npm run check:gis-portals --
federal` re-checks the list (not part of CI).

Closes #2698

* Address Claude review feedback

- Say 24 federal agencies in the web-services summary table, matching
  the catalog, instead of "about 25".

* Place US Federal GIS above US State GIS

List the three US tiers top-down in Plugins > Web Services (federal,
state, local): move US Federal GIS ahead of US State GIS in the plugin
registration order that drives the submenu, the web-service id list,
the UI profile, and the web-services guide. The State GIS placement test
now expects it right after US Federal GIS.
2026-09-26 16:16:46 -04:00
Qiusheng Wu e9a3d5a5e1 feat(plugins): add a US Local GIS web service for city and county portals (#2697)
* feat(plugins): add a US Local GIS web service for city and county portals

Add a "US Local GIS" plugin under Plugins > Web Services that browses,
searches, and adds data from the public GIS and open-data portals of
146 large US cities, counties, and regional agencies across 36 states:
pick a state, then a city or county.

Most portals are ArcGIS Hub sites and reuse the Hub catalog picker. Many
of the largest cities (New York, Chicago, San Francisco, Seattle,
Austin, Dallas) publish on Socrata instead, so the Hub factory learns a
second catalog kind:

- A catalog with a socrataDomain is searched through the Socrata
  Discovery API (search_context + domains, which a bare domains filter
  misses), keeping only the portal's own datasets with a geometry
  column. The catalog cannot filter by column type, so the scan reads
  batches until a page is full and reports "Datasets shown: N" rather
  than a total.
- A Socrata dataset adds as its GeoJSON export (capped at 50,000
  features) and zooms to the loaded data; Details opens its page. Data
  and page URLs are built from the configured domain, never from the
  response. The map-area filter is disabled, since Socrata has no
  spatial search.
- Portal ids derive from names, so a remembered choice survives list
  edits.

City and county ArcGIS Server sites often register a MapServer layer as
a "Feature Service" item (about 18% of those items across these
portals), which Add to map and Download rejected with "Enter an ArcGIS
FeatureServer layer URL". A portal item's MapServer/<n> layer is now
queried like a FeatureServer layer; the geometry-type check still
rejects one that is not a feature layer.

Every portal was verified live, and `npm run check:gis-portals`
re-checks both the state and local lists (not part of CI).

* fix(plugins): warn when a Socrata export is capped; normalize dataset ids

- A Socrata dataset loads through its GeoJSON export, capped at 50,000
  features. Add to map now says when the export filled the cap, and
  Download (which opens the export without reading it) names the cap,
  instead of reporting a partial dataset as a plain success
  (CodeRabbit).
- Lowercase a dataset id before validating it, so a mixed-case id is
  normalized rather than silently dropped (Claude).
2026-09-26 14:18:37 -04:00
Qiusheng Wu 46ceab531d fix(planetary): load collections and search despite missing STAC CORS (#2674)
The Planetary Computer STAC API sends no Access-Control-Allow-Origin on
GET /collections and answers the CORS preflight for a JSON POST /search
with a 405, so the Processing > Planetary Computer panel failed with
"Failed to fetch" in the browser and the Tauri webview.

Swap the control's STAC client for a subclass that sends search as a
preflight-free GET /search (collections, bbox, datetime, limit, query,
sortby, filter) and, when the live collection list cannot be read, falls
back to a bundled snapshot of the collection fields the panel uses. The
snapshot is a lazy chunk, regenerated by
scripts/gen-planetary-computer-collections.mjs.
2026-09-25 12:09:32 -04:00
Qiusheng Wu 379dc20547 feat(arcgis): add the ArcGIS Maps SDK for JavaScript as an optional CDN-loaded rendering engine (#2423)
* feat(arcgis): add the ArcGIS Maps SDK for JavaScript as an optional CDN-loaded rendering engine

Adds "arcgis" as a fourth renderer next to MapLibre, Mapbox and Cesium. Nothing is bundled: the engine imports the SDK's ES modules from Esri's versioned CDN at runtime (packages/map/src/arcgis-sdk.ts), so dist/, the wheel and the installers stay the same size.

- arcgis-layers.ts compiles store layers into SDK layer plans; GeoJSON symbology is evaluated per feature with the style-spec engine and baked into a unique-value renderer, filters and labels included. XYZ/WMTS templates, WMS GetMap templates, vector tiles, ArcGIS services and image overlays have translations.
- arcgis-engine.ts implements MapEngine: camera (rotation vs bearing), layer sync and ordering, basemap translation via the shared cesium-imagery catalog plus Esri basemap styles when an API key is present, hit-test identify, highlight, extent drawing, manual placement, capture, and the SDK widgets as built-in controls.
- ArcgisCanvas.tsx owns construction and the store subscription; PrimaryArcgisCanvas hosts it in the shell and split panes.
- Device-local ArcGIS API key under Settings → Environment Variables, ARCGIS_API_KEY bridge in vite.config.ts, getArcgisApiKey in core.
- Add Data greys out sources without an adapter; layer panels badge "No ArcGIS".
- CSP allow-lists js.arcgis.com in tauri.conf.json and nginx.conf; the service worker caches the SDK for offline reuse.
- Python, MCP, embed and agent-skill renderer lists accept "arcgis"; project format carries preferences.map.arcgisBasemap.
- Tests: arcgis-layers, arcgis-engine (fake SDK), arcgis-renderer; opt-in e2e/arcgis-renderer.spec.ts; translations for every locale; docs/arcgis-renderer.md and a maintenance note.

Refs #2421

* fix(arcgis): use the view's own attribution and stop rebuilding layers on opacity changes

- Drop the deprecated Attribution widget: the 5.x view draws attribution itself while `attributionVisible` is on (Esri requires it), so the engine sets that instead of mounting a widget, which also removes the deprecation warning.
- Ignore the SDK's "Aborted" layer-view errors and errors for layers the engine has already removed: a restyle rebuilds the native layer and the outgoing one's cancelled load was surfacing in the error banner once per slider tick.
- Compile vector-tile styles at full opacity and visible so the VectorTileLayer's native opacity/visibility carry those, and an opacity change no longer rebuilds the layer.
- `fullExtent` is read-only on VectorTileLayer; only tile and WMS layers receive it.

Refs #2421

* Address CodeRabbit review feedback

- useArcgisApiKey: read the device-local key directly as a fallback so an ArcGIS pane that mounts with the app gets the saved key before the first runtime-env projection (which fires no change event); the resolver still wins once it answers.
- vite.config.ts: give the ArcGIS SDK its own `geolibre-arcgis-sdk` CacheFirst cache scoped to the versioned `/5.1/` path, so its few hundred modules cannot evict the other CDN engines from the shared 400-entry cache.
- DesktopShell / e2e: anchor the file-drop target with `data-testid="desktop-shell"` and fail loudly in the spec when it is missing instead of falling back to `document.body`.
- arcgis-sdk.ts: reject HTTP error responses before installing the stylesheet, and tag each theme request with a generation so a stale fetch can neither overwrite a newer theme nor clear its pending state.
- ArcgisCanvas: accept a translated `closeLabel` for the identify popup's close button; the desktop caller passes `t("common.close")`.
- test_map.py: round-trip a mixed 1x3 layout so a MapLibre pane survives beside the ArcGIS primary and pane.

* Address CodeRabbit review feedback

- Allow `https://js.arcgis.com/` (and `data:`) in `font-src` in both the Tauri and nginx content-security policies: the SDK's inlined theme stylesheet loads its Avenir Next and icon WOFF2 files from the CDN, which `default-src 'self'` alone would block. Docs updated to match.

* Address CodeRabbit review feedback

- docs/arcgis-renderer.md: name the dedicated `geolibre-arcgis-sdk` service-worker cache instead of the shared `geolibre-cdn-engines` rule the SDK no longer uses.

* Address CodeRabbit review feedback

- docs/arcgis-renderer.md: limit the offline claim to the cached SDK bootstrap; remote basemaps and services still need the network.

* Address Claude review feedback

- arcgis-layers.ts: translate a FeatureServer layer's filters (quick filters, expression, time and embed filters, rule visibility) into an SQL `definitionExpression` via `filterToSql`; a filter with no SQL form is flagged on the plan and the engine reports that the service draws unfiltered instead of staying silent.
- arcgis-layers.ts / arcgis-engine.ts: place georeferenced images by their four corners through a `ControlPointsGeoreference` (image pixel size read on load) instead of collapsing them to an axis-aligned extent, so rotated and skewed fits match MapLibre.
- arcgis-engine.ts: destroy the outgoing custom basemap on every swap, including into a named Esri style, and re-apply the stored basemap visibility/opacity once a named style's layers have loaded; a style that fails to load is reported.
- arcgis-sdk.ts: redact `token=`/`apiKey=`/`api_key=` values without a leading `?`/`&` as well.

* Address CodeRabbit review feedback

- docker/nginx.conf: drop the Esri CDN from the JupyterLite prefix's CSP (script-src and the font-src directive added for it). The in-app notebook is an iframe whose client drives the parent GeoLibre document over postMessage, so the ArcGIS SDK only ever loads under the main application policy, which keeps its entries.

* Address Claude review feedback

- arcgis-layers.ts / arcgis-engine.ts: point markers now render. A Style-panel marker (shape or custom SVG) compiles to a placeholder carrying its per-class colour and scale; the engine bakes the same sprites MapLibre uses (`renderMarkerCanvas`) into `picture-marker` symbols and swaps them in, drawing the circle fallback until then. A KML feature's own icon becomes a `picture-marker` directly.
- arcgis-engine.ts: `identifyFeatures` performs a real synchronous query for any coordinate by testing the store's GeoJSON geometry (point-in-polygon, pixel-tolerance for points and lines, filters and visibility honoured), so the scripting, notebook and command bridges get answers; the cached hit-test result still serves the clicked location.
- arcgis-engine.ts / ArcgisCanvas.tsx: the async hit test bails out when the engine was destroyed mid-flight, guards the post-await `toMap`, and the canvas catches a rejection instead of leaving it unhandled.
- Tests for the marker placeholder and KML icon plans, the geometry hit tests, the synchronous identify, and a hit test that outlives the engine.

* Address Claude review feedback

- scripts/credential-patterns.json: list `VITE_ARCGIS_API_KEY` so redistributable builds strip it and the credential scan covers it, like the sibling tokens.
- store.ts: a basemap pick clears the pinned ArcGIS style whenever any pane draws with ArcGIS, not only the primary, so split panes follow the picker (Blank included).
- arcgis-engine.ts: identify falls back to the hit graphic's own geometry (converted from the view's spatial reference) and object id for service layers whose features never live in the store.
- arcgis-engine.ts: GeoJSON layers declare their three synthetic fields with explicit widths instead of letting the SDK infer them from sampled features, so long labels are not truncated.
- arcgis-sdk.ts: redact both Esri key prefixes (`AAPK` legacy API keys and `AAPT` API key credentials).
- add-data-renderer.ts: use the catalog's real ids (`deckgl-viz`, `gltf-model`) and also grey out the Vector and Raster panels, which are MapLibre controls with nowhere to mount on this engine.
- useArcgisApiKey.ts: the device key fills in only while the runtime environment has no ArcGIS entry at all, so an explicit empty project override still suppresses it.
- arcgis-layers.ts: remote GeoJSON-URL plans carry the resolver's `zoomDependent` flag so zoom-interpolated styles re-bake on zoom.
2026-09-16 08:23:24 -04:00
Qiusheng Wu c79b31009c chore(deps): patch npm audit advisories (#2417)
* chore(deps): patch npm audit advisories

Update browserslist, baseline-browser-mapping, hono, qs and js-yaml to
their patched releases, and override the fflate that @loaders.gl/compression
pins (0.7.4, unused by its code) to ^0.8.3.

js-yaml now installs 4.3.2/3.15.2, so its audit allowlist entry is dropped.
The image-size entries stay, with a refreshed rationale: the advisories
still list no patched version, and the new 2.0.3/2.0.4 npm releases have no
matching source upstream and break texture-compressor's API.

* chore(deps): patch image-size via a texture-compressor override

image-size 2.0.3 (codeberg.org/image-size/image-size e6e83a5578) fixes the
ICNS/JXL/HEIF infinite loops behind GHSA-w3rx-r6r6-pgpr and
GHSA-5p2g-fcmc-qvqq. Override texture-compressor's image-size to ^2.0.4 and
drop both allowlist entries, leaving npm audit at 0 vulnerabilities.

npm 12 omits an override-only package that no other dependency installs, so
the lockfile entry is written by hand; docs/maintenance.md records that and
the texture-compressor CLI API break (unused by GeoLibre).

* Address Claude review feedback

- Simplify the now-empty audit ALLOWLIST from `new Map([])` to `new Map()`.

* Address CodeRabbit review feedback

- docs/maintenance.md: the fflate override does not rewrite the declared
  range in package-lock.json (it still records `fflate: 0.7.4`); say that
  npm ls checks against the override's range instead.
2026-09-15 17:53:13 -04:00
Qiusheng Wu fbf39c21bb fix(zarr): keep Zarr layers visible past zoom 12 on Mesa GPUs (#2362)
On Intel and AMD integrated graphics (Mesa) a Zarr layer with a CRS vanished
as soon as MapLibre left the globe transition: at zoom 12 and above with the
globe projection, and at every zoom in plain Mercator. @carbonplan/zarr-layer
compiles its flat source-projected shader there, where shift_x, shift_y and
u_worldXOffset only feed a varying the fragment shader never reads. Mesa
eliminates them at link time and reports them inactive, the renderer's strict
uniform lookup threw on every frame, and MapLibre's frame loop swallowed the
exception, so nothing surfaced in the UI. NVIDIA keeps the uniforms active,
which is why it never reproduced there.

Carry the upstream fix (carbonplan/zarr-layer#91) as a patch-package patch
until a release ships it: look those three uniforms up without throwing, as
the renderer already does for its matrix uniforms. A test pins the installed
bundle so a dependency bump that silently drops the patch fails the suite.

Fixes #2357
2026-09-11 19:44:48 -07:00
Qiusheng Wu 58cbb14395 feat(labels): format numeric label fields with thousands separators (#2344)
* feat(labels): format numeric label fields with thousands separators

A numeric attribute rendered straight into a label reads as 1234567.5,
which is hard to compare at a glance. QGIS solves this with
format_number(field, places, language); this adds the equivalent to the
Labels section of the Style panel.

Three LabelStyle fields drive it — numberFormatEnabled, numberDecimals
and numberLocale — and one core module renders them two ways so every
label path agrees: labelFieldTextField() builds the MapLibre text-field
expression used by the 2D map and the style export, and
formatLabelNumber() does the same in JavaScript for the Cesium globe and
the duplicate-label collapsing modes.

Notes on the shape of it:

- Formatting applies to the label field only. A label expression formats
  its own output, so wrapping it would fight the author; number-format is
  now in the Expression Builder's function reference for that case.
- The locale choices are named by the separators they produce ("1,234,568",
  "1.234.568") rather than by a language tag, and the default follows the
  app language the way popup number fields do. The list is short on
  purpose: every entry groups with a character the map's glyph stack can
  draw, so a locale that groups with U+202F or uses non-Latin digits is
  left to the expression route rather than rendering as blank boxes.
- MapLibre's number-format ignores a falsy option, so zero fraction digits
  cannot be requested directly; the builder rounds first instead, which
  would otherwise leak the spec default of three.
- The text-field builder is now shared by layer-sync and the Mapbox style
  export, which were carrying the same expression twice.

Closes #2336

* Address review feedback

- Include the resolved locale in the deduplicated-label cache key
  (layer-sync). The key held the stored numberLocale, which is empty for
  the "Match app language" default, so switching the UI language served
  cached aggregate labels with the previous language's separators. The
  non-dedup path never had this, since it rebuilds the expression on every
  sync. Regression test added; it fails without the fix.
- Resolve every locale tag through a validating helper before it reaches
  Intl.NumberFormat or MapLibre's number-format. A malformed tag from a
  hand-edited project raised an uncaught RangeError that would take down a
  whole layer's labels; it now falls through to the caller's fallback and
  finally to the runtime default, the way an out-of-range decimals value
  already degraded. MapLibre builds its own Intl.NumberFormat per feature,
  so the expression path needed this too, not just the JS one.
- Cache the Intl formatters. formatLabelNumber runs once per labeled
  feature (per Cesium entity, per deduplicated point) and was constructing
  a formatter each call.
- Fix the English hint grammar: "Numeric values are labeled with ...".
- Say locale rather than language in the number-format function reference
  for it, fa, vi and ar, matching what the other catalogs already do.

Not changed: the report that the map and the globe disagree on rounding
negative halves at zero decimals. MapLibre's `round` is
`v < 0 ? -Math.round(-v) : Math.round(v)` — half away from zero, not
Math.round's half toward +Infinity — which is the rule Intl uses by
default, so the two paths already agree. Pinned that with tests over
negative halves and negative two-decimal values, which the parity test
did not cover.

* feat(labels): format numbers on control-rendered vector layers too

GeoLibre draws vector labels two ways, and the number formatting added in
this branch only reached one of them. A GeoParquet source (the Countries
and US cities samples, any .parquet URL) is handed to the
maplibre-gl-vector control, so syncLayer returns at its external-native
branch and nothing in layer-sync applies; those labels are built inside
the upstream package, which had no number formatting of its own. The
Format numbers checkbox was therefore a no-op on exactly the datasets
most likely to want it.

maplibre-gl-vector 0.11.0 adds labelNumberFormat / labelNumberDecimals /
labelNumberLocale to VectorLayerStyle (opengeos/maplibre-gl-vector#71),
building the same guarded `case` / `number-format` text-field this repo
builds for its own layers. Bump to it and extend the LabelStyle mapping
in vector-layer-sync: push the three settings to the control, seed them
back so a restored project shows what the control rendered, and include
them in vectorStylesEqual so a format change is not swallowed as a no-op.

Verified against the real Countries GeoParquet sample labelled by
POP_EST: 142257519 now reads 142,257,519, and switching Separators to
the German option at two decimals live-updates to 142.257.519,00.

* chore(audit): hold GHSA-2883-xcg3-v3hh (js-yaml) in the allowlist

Unrelated to this branch's feature work: the advisory was published while
the PR was open and fails the Dependency audit on main and every other
open PR too. Holding it here so the branch can go green.

This one bends the allowlist's own first rule, so the entry says why at
length. Patches do exist (3.15.2 and 4.3.2), but npm 12 will not install
them: an `overrides` entry is accepted and rewrites the declared ranges,
yet the tree is left permanently `invalid: "^4.3.2" from
node_modules/@changesets/parse` with the installed copies still at
4.3.1/3.15.1. That holds for a plain range override, an exact pin, and
the nested read-yaml-file form (that consumer needs 3.x, since v4 dropped
safeLoad). `npm audit fix` does reach it, but only by restructuring the
whole @loaders.gl tree — 362 insertions — which is not a change to make
from a labels PR.

What makes the hold safe is reachability: js-yaml is in the production
graph only because @placemarkio/geojson-rewind@1.0.3 declares
@changesets/cli as a runtime dependency, which is an upstream packaging
mistake. No GeoLibre code path parses YAML through it, and the built
bundle contains no js-yaml at all (grepped for js-yaml, YAMLException and
DUMPER_STATE).

* Address review feedback

- Persist the number-format settings (savedVectorStyle). The push and
  seed-back mappings were wired but the restore path was not, so a saved
  project came back with the settings reset to defaults. All three fields
  now round-trip, with the decimals value range-checked like the
  neighbouring guards so a hand-edited file cannot restore a fractional or
  out-of-range precision.
- Range-check labelNumberDecimals coming back from the control
  (vectorStyleToLayerStyle). The snapshot is untrusted input on the same
  grounds, and any JS number was reaching LabelStyle unchecked.
- Reject locales the map cannot draw, not just ones Intl rejects. The
  curated picker list was the only thing keeping U+202F and non-Latin
  digits out of the text-field, and it is not the only route in: a
  hand-edited project can pin any tag, and the "" default follows the app
  language, so a French or Arabic UI produced blank boxes. The check now
  tests the rendered sample rather than the tag, which covers both routes
  and needs no list kept in sync.
- Key the dedup cache on the effective locale. It carried the stored tag,
  so a tag the formatter rejects pinned the cache to a locale the labels
  were never formatted with. All three paths — the JS formatter, the
  MapLibre expression, and the cache key — now resolve through the same
  exported resolveLabelNumberLocale.

Not changed: the report that the map and the globe disagree on rounding
negative halves. MapLibre's `round` is `v < 0 ? -Math.round(-v) :
Math.round(v)`, half away from zero, which is the rule Intl uses by
default, so the two already agree. Pinned by the tests added in 82a157d5.

* Address review feedback

- Check the negative sample too when vetting a locale's glyph safety.
  sv-SE, fi-FI and nb-NO group with U+00A0 (safe) but write negatives with
  U+2212 MINUS SIGN, so the positive-only check passed them and would have
  blank-boxed every negative label.
- Read documentLocale() per call in the Cesium labeler instead of closing
  over it. A UI language switch does not change the layer object, so it
  never rebuilds the data source and the labeler outlives it; captured,
  "Match app language" labels stayed on the previous language's separators
  until some unrelated change forced a rebuild.
- Resolve the "match app language" sentinel before pushing it to the
  vector control. It was going over as the empty string, which the control
  hands to Intl as the runtime default -- the browser's locale, not
  GeoLibre's UI language -- so a control-managed layer could format
  differently from an inline one. Both paths now resolve identically.

documentLocale moved from @geolibre/map to @geolibre/core so the plugins
package can reach it: importing @geolibre/map pulls the barrel, and with
it MapCanvas and maplibre-gl's stylesheet, which the node test runner
cannot load. Core already reads browser globals (platform.ts,
runtime-env.ts), and the helper keeps its own typeof guard.

* Address CodeRabbit review feedback

- Stop asserting a literal in the rejected-fallback test. It compared
  against "1,234", which assumes the host's own default locale; it now
  compares with the same call made without a fallback, so the assertion
  holds on any CI host.

* Address Claude review feedback

- Guard the number-format branch on finiteness, not just typeof. NaN and
  +/-Infinity are both typeof "number", so they were reaching
  `number-format`: the map rendered Infinity as U+221E, a glyph the label
  font need not carry, and NaN arrived as null and threw "Could not
  convert null to number" out of the render -- while formatLabelNumber
  returned null for both and the caller fell back to String(value). All
  seven cases (NaN, both infinities, a finite value, a negative, text,
  null) now render identically on both paths.

  JSON carries no Infinity, so the test compares against 1e308, which
  doubles as the `to-number` fallback -- an unconvertible value then fails
  the same check instead of labelling as the fallback itself. A real
  attribute at or above 1e308 labels as plain text.

- Correct the formatLabelNumberSample doc comment: a rejected tag falls
  back to the runtime locale's formatting, not to an unformatted number.
  That stopped being true when the resolver started vetting tags.

- Add export-level coverage for the shared text-field builder in
  tests/mapbox-style-export.test.ts, formatted and unformatted, which the
  builder's own unit tests cannot catch a wiring break in.

Also confirmed for the dependency bump, per docs/maintenance.md: the
hand-mirrored MAX_VECTOR_BYTES in remote-file-formats.ts still matches
maplibre-gl-vector's internal MAX_REMOTE_FILE_BYTES (2 ** 31 - 1) in
0.11.0, so no update is needed there.
2026-09-08 20:55:55 -04:00
50e0fd4ffc chore(deps): bump the npm-minor-patch group across 1 directory with 33 updates (#2331)
* chore(deps): bump the npm-minor-patch group across 1 directory with 33 updates

Bumps the npm-minor-patch group with 32 updates in the / directory:

| Package | From | To |
| --- | --- | --- |
| [@playwright/test](https://github.com/microsoft/playwright) | `1.62.1` | `1.63.0` |
| [@types/node](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/node) | `26.4.0` | `26.4.1` |
| [eslint](https://github.com/eslint/eslint) | `10.9.1` | `10.10.0` |
| [tsx](https://github.com/privatenumber/tsx) | `4.23.12` | `4.23.13` |
| [typescript-eslint](https://github.com/typescript-eslint/typescript-eslint/tree/HEAD/packages/typescript-eslint) | `8.68.0` | `8.69.0` |
| [@anthropic-ai/sdk](https://github.com/anthropics/anthropic-sdk-typescript) | `0.122.0` | `0.124.0` |
| [@cesium/engine](https://github.com/CesiumGS/cesium) | `26.2.0` | `26.3.0` |
| [@cesium/widgets](https://github.com/CesiumGS/cesium) | `16.1.1` | `16.2.0` |
| [@clerk/react](https://github.com/clerk/javascript/tree/HEAD/packages/react) | `6.14.8` | `6.15.1` |
| [@google/genai](https://github.com/googleapis/js-genai) | `2.19.0` | `2.21.0` |
| [@strands-agents/sdk](https://github.com/strands-agents/harness-sdk) | `1.15.0` | `1.16.0` |
| [@tauri-apps/plugin-dialog](https://github.com/tauri-apps/plugins-workspace) | `2.7.2` | `2.7.3` |
| [@tauri-apps/plugin-fs](https://github.com/tauri-apps/plugins-workspace) | `2.5.1` | `2.5.2` |
| [@tauri-apps/plugin-geolocation](https://github.com/tauri-apps/plugins-workspace) | `2.3.2` | `2.3.3` |
| [@tauri-apps/plugin-http](https://github.com/tauri-apps/plugins-workspace) | `2.5.9` | `2.6.0` |
| [@tauri-apps/plugin-opener](https://github.com/tauri-apps/plugins-workspace) | `2.5.4` | `2.5.5` |
| [cesium](https://github.com/CesiumGS/cesium) | `1.144.0` | `1.145.0` |
| [html2canvas-pro](https://github.com/yorickshan/html2canvas-pro) | `2.4.0` | `2.4.1` |
| [i18next](https://github.com/i18next/i18next) | `26.4.0` | `26.4.2` |
| [maplibre-gl](https://github.com/maplibre/maplibre-gl-js) | `6.6.0` | `6.7.0` |
| [openai](https://github.com/openai/openai-node) | `7.8.0` | `7.10.0` |
| [react-i18next](https://github.com/i18next/react-i18next) | `17.0.12` | `17.0.13` |
| [zod](https://github.com/colinhacks/zod) | `4.5.2` | `4.5.4` |
| [@types/react-dom](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/react-dom) | `19.2.5` | `19.2.7` |
| [postcss](https://github.com/postcss/postcss) | `8.5.26` | `8.5.28` |
| [undici](https://github.com/nodejs/undici) | `8.10.0` | `8.10.2` |
| [tsdown](https://github.com/rolldown/tsdown) | `0.22.14` | `0.23.0` |
| copc | `0.0.8` | `0.0.9` |
| [laz-perf](https://github.com/hobuinc/laz-perf) | `0.0.6` | `0.0.7` |
| [proj4](https://github.com/proj4js/proj4js) | `2.21.0` | `2.22.0` |
| [lucide-react](https://github.com/lucide-icons/lucide/tree/HEAD/packages/lucide-react) | `1.35.0` | `1.41.0` |
| [wrangler](https://github.com/cloudflare/workers-sdk/tree/HEAD/packages/wrangler) | `4.127.1` | `4.129.0` |



Updates `@playwright/test` from 1.62.1 to 1.63.0
- [Release notes](https://github.com/microsoft/playwright/releases)
- [Commits](https://github.com/microsoft/playwright/compare/v1.62.1...v1.63.0)

Updates `@types/node` from 26.4.0 to 26.4.1
- [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases)
- [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/node)

Updates `eslint` from 10.9.1 to 10.10.0
- [Release notes](https://github.com/eslint/eslint/releases)
- [Commits](https://github.com/eslint/eslint/compare/v10.9.1...v10.10.0)

Updates `tsx` from 4.23.12 to 4.23.13
- [Release notes](https://github.com/privatenumber/tsx/releases)
- [Changelog](https://github.com/privatenumber/tsx/blob/master/release.config.cjs)
- [Commits](https://github.com/privatenumber/tsx/compare/v4.23.12...v4.23.13)

Updates `typescript-eslint` from 8.68.0 to 8.69.0
- [Release notes](https://github.com/typescript-eslint/typescript-eslint/releases)
- [Changelog](https://github.com/typescript-eslint/typescript-eslint/blob/main/packages/typescript-eslint/CHANGELOG.md)
- [Commits](https://github.com/typescript-eslint/typescript-eslint/commits/v8.69.0/packages/typescript-eslint)

Updates `@anthropic-ai/sdk` from 0.122.0 to 0.124.0
- [Release notes](https://github.com/anthropics/anthropic-sdk-typescript/releases)
- [Changelog](https://github.com/anthropics/anthropic-sdk-typescript/blob/main/CHANGELOG.md)
- [Commits](https://github.com/anthropics/anthropic-sdk-typescript/compare/sdk-v0.122.0...sdk-v0.124.0)

Updates `@cesium/engine` from 26.2.0 to 26.3.0
- [Release notes](https://github.com/CesiumGS/cesium/releases)
- [Changelog](https://github.com/CesiumGS/cesium/blob/main/CHANGES.md)
- [Commits](https://github.com/CesiumGS/cesium/commits)

Updates `@cesium/widgets` from 16.1.1 to 16.2.0
- [Release notes](https://github.com/CesiumGS/cesium/releases)
- [Changelog](https://github.com/CesiumGS/cesium/blob/main/CHANGES.md)
- [Commits](https://github.com/CesiumGS/cesium/commits)

Updates `@clerk/react` from 6.14.8 to 6.15.1
- [Release notes](https://github.com/clerk/javascript/releases)
- [Changelog](https://github.com/clerk/javascript/blob/main/packages/react/CHANGELOG.md)
- [Commits](https://github.com/clerk/javascript/commits/@clerk/react@6.15.1/packages/react)

Updates `@google/genai` from 2.19.0 to 2.21.0
- [Release notes](https://github.com/googleapis/js-genai/releases)
- [Changelog](https://github.com/googleapis/js-genai/blob/main/CHANGELOG.md)
- [Commits](https://github.com/googleapis/js-genai/compare/v2.19.0...v2.21.0)

Updates `@strands-agents/sdk` from 1.15.0 to 1.16.0
- [Release notes](https://github.com/strands-agents/harness-sdk/releases)
- [Commits](https://github.com/strands-agents/harness-sdk/compare/v1.15.0...v1.16.0)

Updates `@tauri-apps/plugin-dialog` from 2.7.2 to 2.7.3
- [Release notes](https://github.com/tauri-apps/plugins-workspace/releases)
- [Commits](https://github.com/tauri-apps/plugins-workspace/compare/dialog-v2.7.2...dialog-v2.7.3)

Updates `@tauri-apps/plugin-fs` from 2.5.1 to 2.5.2
- [Release notes](https://github.com/tauri-apps/plugins-workspace/releases)
- [Commits](https://github.com/tauri-apps/plugins-workspace/compare/fs-v2.5.1...fs-v2.5.2)

Updates `@tauri-apps/plugin-geolocation` from 2.3.2 to 2.3.3
- [Release notes](https://github.com/tauri-apps/plugins-workspace/releases)
- [Commits](https://github.com/tauri-apps/plugins-workspace/compare/os-v2.3.2...nfc-v2.3.3)

Updates `@tauri-apps/plugin-http` from 2.5.9 to 2.6.0
- [Release notes](https://github.com/tauri-apps/plugins-workspace/releases)
- [Commits](https://github.com/tauri-apps/plugins-workspace/compare/http-v2.5.9...log-v2.6.0)

Updates `@tauri-apps/plugin-opener` from 2.5.4 to 2.5.5
- [Release notes](https://github.com/tauri-apps/plugins-workspace/releases)
- [Commits](https://github.com/tauri-apps/plugins-workspace/compare/http-v2.5.4...http-v2.5.5)

Updates `cesium` from 1.144.0 to 1.145.0
- [Release notes](https://github.com/CesiumGS/cesium/releases)
- [Changelog](https://github.com/CesiumGS/cesium/blob/main/CHANGES.md)
- [Commits](https://github.com/CesiumGS/cesium/compare/1.144...1.145)

Updates `html2canvas-pro` from 2.4.0 to 2.4.1
- [Release notes](https://github.com/yorickshan/html2canvas-pro/releases)
- [Changelog](https://github.com/yorickshan/html2canvas-pro/blob/main/CHANGELOG.md)
- [Commits](https://github.com/yorickshan/html2canvas-pro/compare/v2.4.0...v2.4.1)

Updates `i18next` from 26.4.0 to 26.4.2
- [Release notes](https://github.com/i18next/i18next/releases)
- [Changelog](https://github.com/i18next/i18next/blob/master/CHANGELOG.md)
- [Commits](https://github.com/i18next/i18next/compare/v26.4.0...v26.4.2)

Updates `maplibre-gl` from 6.6.0 to 6.7.0
- [Release notes](https://github.com/maplibre/maplibre-gl-js/releases)
- [Changelog](https://github.com/maplibre/maplibre-gl-js/blob/main/CHANGELOG.md)
- [Commits](https://github.com/maplibre/maplibre-gl-js/compare/v6.6.0...v6.7.0)

Updates `openai` from 7.8.0 to 7.10.0
- [Release notes](https://github.com/openai/openai-node/releases)
- [Changelog](https://github.com/openai/openai-node/blob/main/CHANGELOG.md)
- [Commits](https://github.com/openai/openai-node/compare/v7.8.0...v7.10.0)

Updates `react-i18next` from 17.0.12 to 17.0.13
- [Changelog](https://github.com/i18next/react-i18next/blob/master/CHANGELOG.md)
- [Commits](https://github.com/i18next/react-i18next/compare/v17.0.12...v17.0.13)

Updates `zod` from 4.5.2 to 4.5.4
- [Release notes](https://github.com/colinhacks/zod/releases)
- [Commits](https://github.com/colinhacks/zod/compare/v4.5.2...v4.5.4)

Updates `@types/react-dom` from 19.2.5 to 19.2.7
- [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases)
- [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/react-dom)

Updates `postcss` from 8.5.26 to 8.5.28
- [Release notes](https://github.com/postcss/postcss/releases)
- [Changelog](https://github.com/postcss/postcss/blob/main/CHANGELOG.md)
- [Commits](https://github.com/postcss/postcss/compare/8.5.26...8.5.28)

Updates `undici` from 8.10.0 to 8.10.2
- [Release notes](https://github.com/nodejs/undici/releases)
- [Commits](https://github.com/nodejs/undici/compare/v8.10.0...v8.10.2)

Updates `tsdown` from 0.22.14 to 0.23.0
- [Release notes](https://github.com/rolldown/tsdown/releases)
- [Commits](https://github.com/rolldown/tsdown/compare/v0.22.14...v0.23.0)

Updates `copc` from 0.0.8 to 0.0.9

Updates `laz-perf` from 0.0.6 to 0.0.7
- [Release notes](https://github.com/hobuinc/laz-perf/releases)
- [Commits](https://github.com/hobuinc/laz-perf/commits)

Updates `proj4` from 2.21.0 to 2.22.0
- [Release notes](https://github.com/proj4js/proj4js/releases)
- [Changelog](https://github.com/proj4js/proj4js/blob/main/changelog.md)
- [Commits](https://github.com/proj4js/proj4js/compare/v2.21.0...v2.22.0)

Updates `lucide-react` from 1.35.0 to 1.41.0
- [Release notes](https://github.com/lucide-icons/lucide/releases)
- [Commits](https://github.com/lucide-icons/lucide/commits/1.41.0/packages/lucide-react)

Updates `wrangler` from 4.127.1 to 4.129.0
- [Release notes](https://github.com/cloudflare/workers-sdk/releases)
- [Commits](https://github.com/cloudflare/workers-sdk/commits/wrangler@4.129.0/packages/wrangler)

Updates `@cloudflare/workers-types` from 5.20260829.1 to 5.20260905.1
- [Release notes](https://github.com/cloudflare/workerd/releases)
- [Changelog](https://github.com/cloudflare/workerd/blob/main/RELEASE.md)
- [Commits](https://github.com/cloudflare/workerd/commits)

---
updated-dependencies:
- dependency-name: "@playwright/test"
  dependency-version: 1.63.0
  dependency-type: direct:development
  update-type: version-update:semver-minor
  dependency-group: npm-minor-patch
- dependency-name: "@types/node"
  dependency-version: 26.4.1
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: npm-minor-patch
- dependency-name: eslint
  dependency-version: 10.10.0
  dependency-type: direct:development
  update-type: version-update:semver-minor
  dependency-group: npm-minor-patch
- dependency-name: tsx
  dependency-version: 4.23.13
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: npm-minor-patch
- dependency-name: typescript-eslint
  dependency-version: 8.69.0
  dependency-type: direct:development
  update-type: version-update:semver-minor
  dependency-group: npm-minor-patch
- dependency-name: "@anthropic-ai/sdk"
  dependency-version: 0.124.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: npm-minor-patch
- dependency-name: "@cesium/engine"
  dependency-version: 26.3.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: npm-minor-patch
- dependency-name: "@cesium/widgets"
  dependency-version: 16.2.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: npm-minor-patch
- dependency-name: "@clerk/react"
  dependency-version: 6.15.1
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: npm-minor-patch
- dependency-name: "@google/genai"
  dependency-version: 2.21.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: npm-minor-patch
- dependency-name: "@strands-agents/sdk"
  dependency-version: 1.16.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: npm-minor-patch
- dependency-name: "@tauri-apps/plugin-dialog"
  dependency-version: 2.7.3
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: npm-minor-patch
- dependency-name: "@tauri-apps/plugin-fs"
  dependency-version: 2.5.2
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: npm-minor-patch
- dependency-name: "@tauri-apps/plugin-geolocation"
  dependency-version: 2.3.3
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: npm-minor-patch
- dependency-name: "@tauri-apps/plugin-http"
  dependency-version: 2.6.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: npm-minor-patch
- dependency-name: "@tauri-apps/plugin-opener"
  dependency-version: 2.5.5
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: npm-minor-patch
- dependency-name: cesium
  dependency-version: 1.145.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: npm-minor-patch
- dependency-name: html2canvas-pro
  dependency-version: 2.4.1
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: npm-minor-patch
- dependency-name: i18next
  dependency-version: 26.4.2
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: npm-minor-patch
- dependency-name: maplibre-gl
  dependency-version: 6.7.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: npm-minor-patch
- dependency-name: openai
  dependency-version: 7.10.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: npm-minor-patch
- dependency-name: react-i18next
  dependency-version: 17.0.13
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: npm-minor-patch
- dependency-name: zod
  dependency-version: 4.5.4
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: npm-minor-patch
- dependency-name: "@types/react-dom"
  dependency-version: 19.2.7
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: npm-minor-patch
- dependency-name: postcss
  dependency-version: 8.5.28
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: npm-minor-patch
- dependency-name: undici
  dependency-version: 8.10.2
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: npm-minor-patch
- dependency-name: tsdown
  dependency-version: 0.23.0
  dependency-type: direct:development
  update-type: version-update:semver-minor
  dependency-group: npm-minor-patch
- dependency-name: copc
  dependency-version: 0.0.9
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: npm-minor-patch
- dependency-name: laz-perf
  dependency-version: 0.0.7
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: npm-minor-patch
- dependency-name: proj4
  dependency-version: 2.22.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: npm-minor-patch
- dependency-name: lucide-react
  dependency-version: 1.41.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: npm-minor-patch
- dependency-name: wrangler
  dependency-version: 4.129.0
  dependency-type: direct:development
  update-type: version-update:semver-minor
  dependency-group: npm-minor-patch
- dependency-name: "@cloudflare/workers-types"
  dependency-version: 5.20260905.1
  dependency-type: direct:development
  update-type: version-update:semver-minor
  dependency-group: npm-minor-patch
...

Signed-off-by: dependabot[bot] <support@github.com>

* chore(deps): refresh the allowlisted CesiumJS token hash for 1.145

Cesium 1.145 ships a new built-in default Ion access token, so the SHA-256
in scripts/credential-patterns.json no longer matches and the pre-publish
credential scan fires on every build. Decoded the new value first, as
docs/maintenance.md requires: sub=CesiumJS, iss=https://api.cesium.com,
aud='1.145 Release - Delete on November 1, 2026'. It is the vendor's own
public token, so the hash is replaced rather than the finding silenced.

* ci: exclude package-lock.json from the large-file guard

The npm-minor-patch wave pushed package-lock.json past the 1MB
check-added-large-files limit (983 KB to 1,037 KB), failing pre-commit.ci
on a file npm generates. The guard exists to catch an accidentally
committed binary or data dump, and the lockfile only grows with the
dependency tree, so exclude it instead of raising the ceiling for every
file in the repo.

---------

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: giswqs <5016453+giswqs@users.noreply.github.com>
Co-authored-by: Qiusheng Wu <giswqs@gmail.com>
2026-09-08 11:48:58 -04:00
Siddik 0b0f6b6ba2 fix(tests): make the local quality gate pass off Linux (#2214)
CONTRIBUTING asks contributors to run `npm run ci` before opening a pull
request, but on macOS `npm run test:frontend` fails with 5 errors and
cancels 4 more tests, none of them the contributor's. CI does not catch
this because it runs Linux with GNU coreutils and bash 5.

Three independent causes, all in test-only or Linux-only helper code:

select-single-appimage.sh used `mapfile`, a builtin that arrived in bash
4. macOS still ships bash 3.2 as /bin/bash, where it is simply missing,
so all three of its tests failed. Read the lines with a `while IFS= read
-r` loop instead, which behaves identically on both shells.

render-linux-metainfo.sh validated its DATE with GNU `date -d`. BSD date
rejects that flag outright, so the check failed for every date and the
script always exited 1. Try GNU first, then BSD's `-j -f` spelling. BSD
date normalises an out-of-range day instead of rejecting it (2026-02-31
comes back as 2026-03-03 with a zero exit), so the result is compared
against the input rather than trusting the exit status. That is stricter
than the previous check on both implementations, and leaves the rendered
XML byte-identical.

The desktop-settings-url timeout test is not macOS-specific: it asserts
that AbortSignal.timeout fires, but Node leaves that timer unref'd, and
the stalled fetch it races settles only on that abort. With nothing else
pending the event loop drains first and the promise never settles, which
fails the test and cancels the three after it. Hold a ref'd timer across
the assertion so the abort is guaranteed to land.

macOS goes from 5 failed / 4 cancelled to 1 failed / 0 cancelled. The
remaining failure is the _frontend.js module-classification issue in
#2213, left out deliberately: its fix touches Python packaging.

Refs #2213
2026-09-02 11:51:58 -04:00
Qiusheng Wuandpre-commit-ci[bot] a0b256f876 fix: load rasters with large TIFF metadata (#2209)
* fix: load rasters with large TIFF metadata

Decode large ASCII TIFF tags in bounded chunks so browser argument limits do not reject otherwise valid cloud-hosted rasters.

* Address Copilot and Claude review feedback

- Apply dependency patches only when the raster dependency is installed.
- Stage patch inputs before Docker installs so web images receive the fix.
- Add a large TIFF metadata regression test and document sliced-view offsets.

* style: auto-format (ruff + oxfmt) [pre-commit.ci]

* Address CodeRabbit review feedback

- Assert the exact decoded GDAL metadata value.
- Cover ASCII decoding from a DataView with a non-zero byte offset.

---------

Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
2026-09-01 22:50:44 -04:00
Qiusheng Wu db849458a6 fix(build): keep build-machine credentials out of redistributable builds (#2130)
* fix(build): keep build-machine credentials out of redistributable builds

An artifact we hand to someone else -- the Jupyter wheel above all -- must
carry no credential of ours. Three properties of this build made that easy
to get wrong:

1. vite.config.ts bridges bare shell vars into their VITE_ names
   (GOOGLE_MAPS_API_KEY, MAPBOX_TOKEN, CESIUM_TOKEN), so the build
   machine's shell is build input.
2. Something in the graph reads `import.meta.env` as a whole object, so
   Vite stops replacing per key and inlines the entire env record into
   every chunk that read reaches. Ours was
   packages/core/src/runtime-env.ts; the one we cannot fix is
   @clerk/shared's getEnvVariable.mjs, which does `import.meta.env[name]`
   with a computed name -- so the inlined record lands in ClerkGate-*.js.
3. hatch_build.py skips the JS build when static/app already exists and
   GEOLIBRE_FORCE_JS_BUILD is unset, so `python -m build` packages
   whatever an earlier `npm run build:embed` left staged, with no
   JavaScript running at all.

Changes:

- vite.config.ts gains BUILD_ENV_KEYS (allowlist of VITE_ names that may
  reach a bundle) and CREDENTIAL_ENV_KEYS (the subset that bills to
  whoever built). pruneBuildEnv() deletes unrecognized VITE_ vars from
  process.env and blanks credentials in redistributable builds, before
  Vite reads it -- so a whole-object read has nothing sensitive to
  inline, including Clerk's. The web deploy is unaffected and keeps its
  own referrer-restricted keys.
- runtime-env.ts reads the allowlisted __GEOLIBRE_BUILD_ENV__ define
  instead of import.meta.env, and exports getBuildEnvironment() for the
  five call sites that took a whole-object dependency.
- scripts/scan-credentials.mjs verifies build OUTPUT, wired into
  build-embed.mjs (before staging) and hatch_build.py (before packaging
  any wheel/sdist, needing no Node -- the path (3) above that no JS-side
  guard can cover). Both read scripts/credential-patterns.json so they
  cannot drift; it is force-included into the sdist so sdist -> wheel is
  gated too.
- CesiumJS's own public default Ion token is hash-allowlisted so the
  guard does not fire on every release.

Verified: an embed build on a machine with those shell vars set now emits
VITE_*:"" for every credential and no raw token shapes; the web build is
unchanged; a planted key makes `python -m build` refuse.

Stripped credentials still resolve at runtime through
getRuntimeEnvironment()'s Settings -> Environment variables overlay.

* Address review feedback

- pruneBuildEnv() now covers `.env*` files, not just `process.env`. Only 4
  of the 11 credential keys have a bare->prefixed shell bridge; the other 7
  are documented in getting-started.md as `.env.local` entries. A key set
  only in a file was never in `process.env` at prune time, so the loop
  skipped it and Vite's own loadEnv then resolved the raw value into
  `import.meta.env` -- reaching the @clerk/shared computed lookup in a
  redistributable build. Confirmed by planting a file-only
  VITE_STADIA_API_KEY: it was inlined verbatim, and only the output scan
  caught it. The sweep now unions process.env and FILE_ENV keys, and blanks
  rather than deletes file-backed ones, because process.env wins over a
  .env value only when the key is present (including as ""). The
  __GEOLIBRE_BUILD_ENV__ record falls back to FILE_ENV too, so a
  .env.local-configured var still reaches getBuildEnvironment().

- CREDENTIAL_ENV_KEYS is now read from scripts/credential-patterns.json
  instead of being a second hand-maintained copy, so the name this config
  strips and the name the scanners look for cannot drift.

- The Google API key pattern ends with a lookahead instead of `\b`. Its
  charset includes `-`, and `\b` needs a word character on the left, so a
  key ending in `-` did not match before a quote and both scanners missed
  it. Verified in both JavaScript RegExp and Python re; regression fixtures
  added on both sides.

- _load_patterns() raises instead of returning None. It previously made
  scan_for_credentials return [] and report "Credential scan clean.", which
  is indistinguishable in the build log from a genuine clean scan -- failing
  open, the opposite of the documented intent.

- Added python/tests/test_credential_scan.py (9 tests), including one that
  runs both scanners over the same fixture and asserts identical output.
  The Python scanner previously had no coverage. The hatchling import in
  hatch_build.py is now guarded so the scanner is importable standalone.
2026-08-27 10:06:36 -04:00
Qiusheng Wu d507b05280 docs: split the maintenance notes out of CLAUDE.md (#2120)
* docs: split the maintenance notes out of CLAUDE.md

CLAUDE.md had grown to 30 KB, most of it rules that only matter when
bumping a dependency or touching CI and release plumbing. Those move to
docs/maintenance.md, kept verbatim, so the file agents read on every task
stays focused on repo shape, commands, and architecture.

* Address Claude review feedback

- docs/maintenance.md: restore the note that Whitebox translations ship as
  external language packs rather than generated locale entries, dropped when
  the Processing menu catalog bullet moved out of CLAUDE.md.
- CLAUDE.md: restore the consequence clause on the MCP paragraph, so the
  reason publish-python.yml must install the mcp extra stays recorded.
2026-08-26 07:58:31 -04:00
Qiusheng Wuandpre-commit-ci[bot] 3b54b06add fix: proxy local raster URLs in Colab (#2108)
* fix: proxy local raster URLs in Colab

Route session-scoped raster files through Colab's kernel proxy so xarray layers can load remotely. Always strip outputs from every tracked notebook during pre-commit runs.

* Address Claude review feedback

- Support local raster rewriting through jupyter-server-proxy as well as Colab.
- Rewrite sourcePath independently when it references a registered local file.
- Preserve git stderr when notebook discovery fails.

* Address CodeRabbit review feedback

- Terminate nbstripout option parsing before passing tracked notebook paths.

* fix: rebase stale Colab raster proxy URLs

Colab gives separate widget views distinct proxy origins. Rebase tokenized raster URLs from an older view onto the current view before restoring layers.

* fix(python): preserve raster range requests through Colab proxy

* style: auto-format (ruff + oxfmt) [pre-commit.ci]

* fix(python): tunnel partial raster responses through Colab

* style: auto-format (ruff + oxfmt) [pre-commit.ci]

* fix(python): render local rasters as XYZ tiles in Colab

* Address review feedback

- _server.py: gate the Content-Range/X-GeoLibre-Content-Range headers on a
  newly parsed range rather than on the presence of a Range header, so a
  non-bytes unit (e.g. "items=0-1") no longer emits a spec-invalid
  Content-Range alongside a full-file 200.
- _server.py: send Access-Control-Expose-Headers for
  X-GeoLibre-Content-Range, without which a cross-origin fetch cannot read it
  and the app leaves the response at 200 instead of rebuilding the 206.
- _frontend.js: require an empty port on the Colab proxy hostname, so a
  look-alike host on a foreign network port does not enter the rewrite path.
- _frontend.js: extract the transport gate into an exported
  canProxyLocalFiles(base, port) and cover it in tests, so a future
  proxyBase/resolveBase refactor cannot silently stop the rewrite.
- geolibre.py: resolve the local raster path once and hand the resolved path
  to both register_raster_tiles and rasterio.open — GDAL does not expand "~",
  so a "~/dem.tif" source silently lost its computed bounds.
- kernel-proxy-range.ts: export KERNEL_CONTENT_RANGE_HEADER and cross-reference
  the three mirrored literals from _frontend.js and _server.py; a new test
  asserts both Python sources against the exported constants.
- python-api.md: fix the duplicated "JupyterHub when" clause, matching the
  wording in docs/python.md.
- tests: cover the malformed-range 200, the exposed range header, and both the
  populated and None bounds paths of add_cog's Colab branch.

* Address Claude review feedback

- add_cog: the `url:` docstring still claimed a local raster is only reachable
  when the browser runs on the kernel's host, which this PR made stale. Restate
  it the way docs/python.md now does: direct reads in local Jupyter/VS Code,
  kernel-rendered PNG XYZ tiles in Colab, the kernel port via
  jupyter-server-proxy on JupyterHub, and a hosted URL for an
  extension-only deployment.

* Address Claude review feedback

- Remove the Colab range-proxy bridge, which the PNG-tile path in 45599eff
  superseded and left unreachable: `register_local_file` is called only from
  `_resolve_raster_source`, which only `add_cog`'s non-Colab branch reaches, so
  no `/_geolibre_local/` URL is ever emitted under Colab and nothing ever set
  the `__geolibre_range_proxy` marker the bridge keys on. Deleted
  `kernel-proxy-range.ts` and its global `fetch` wrapper (installed in
  `main.tsx` for every GeoLibre user), the marker logic in `_frontend.js`, and
  the `__geolibre_range` / `X-GeoLibre-Content-Range` transport in `_server.py`,
  along with their tests. Ordinary `Range` header support — what local Jupyter,
  VS Code, and jupyter-server-proxy actually use — is unchanged, including the
  `range_matched` fix from 8af3e91c.
- Document the notebook hook's blast radius in docs/contributing.md and expand
  the comment in .pre-commit-config.yaml: it rewrites tracked notebooks in
  place, so unstaged locally-executed outputs are erased by any commit, not
  merely excluded from it.

---------

Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
2026-08-25 14:48:36 -04:00
95ea999ed3 feat (npm_package): pack core and map into npm package (#2084)
* feat (npm_package): pack core and map into npm package

* fix maplibregl and desktop config

* style: auto-format (ruff + oxfmt) [pre-commit.ci]

* Address review feedback

- packages/core/package.json: move the dist `main`/`types`/`exports` out of
  `publishConfig` semantics that npm ignores. npm only honors publish-time
  *config* there (npm/cli#7586), so the tarball advertised `./src/index.ts`
  while `files` shipped only `dist`. The published entries now go through
  `scripts/prepare-npm-package.mjs`, which hoists them just before publish;
  `publishConfig` also gains the missing `access: public` for a scoped
  package. Fixes the Copilot and CodeRabbit threads on this file.
- packages/core/package.json: `types` pointed at `./dist/index.d.ts`, but
  `tsdown --dts` emits `index.d.mts`. Published consumers would have gotten no
  types at all. Answers the Claude review question about the extension.
- packages/map/package.json: point `main`/`types`/`exports` back at
  TypeScript source. Pointing them at `dist` is what broke CI: `dist` is
  gitignored and nothing builds it, so 12 frontend tests failed with
  ERR_MODULE_NOT_FOUND on `@geolibre/map/derived-geometry` and friends. The
  published dist entries live under `publishConfig` and register every subpath
  the repo exports (`.`, `./headless`, `./derived-geometry`,
  `./pmtiles-layer`), so nothing 404s on npm.
- packages/map/package.json: restore the `^19.0.0` react/react-dom peer ranges.
  Narrowing them to `^19.2.8` was unrelated to packaging and only constrains
  consumers.
- apps/geolibre-desktop/tsconfig.json, vite.config.ts: revert the
  `@geolibre/map` path/alias and the `../../packages/map/src` include. With the
  manifest resolving to source again they are unnecessary, and the include made
  `tsc -b` emit `.js`/`.d.ts` beside the map sources. Fixes the CodeRabbit
  thread on tsconfig.json.
- packages/map/src/headless.ts: restore the requested stack order when a layer
  moves. `syncLayer` on an existing layer leaves it where MapLibre put it, so a
  reorder was silently ignored and the documented bottom-to-top order stopped
  holding. Rebuild from the lowest position whose occupant changed.
- Add scripts/prepare-npm-package.mjs plus tests/prepare-npm-package.test.ts,
  which also guards that every published path is one the package's own tsdown
  entries actually emit.
- Add tests/headless-layer-sync.test.ts covering ordering, insertion, removal
  and dispose.
- Add .github/workflows/publish-packages.yml: build, prepare and publish
  @geolibre/core then @geolibre/map on each GitHub Release, via npm Trusted
  Publishing with provenance, skipping a version already on the registry.
- Add READMEs for both packages (npm renders them) and document the
  source-vs-dist split in CLAUDE.md.

* Address CodeRabbit review feedback

- .github/workflows/publish-packages.yml: read each package's own version for
  the registry check instead of using core's for both. The two move in lockstep
  today, but the moment they diverge the map check would skip an existing
  release or attempt one npm has already seen.
- CLAUDE.md: correct the claim that no workflow runs the package builds. The
  release workflow does build both; what it does not do is verify that every
  path the manifest publishes names a file the build emitted, which is what
  tests/prepare-npm-package.test.ts covers.

---------

Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
Co-authored-by: giswqs <giswqs@gmail.com>
2026-08-24 00:04:25 -04:00
5d29009ede feat(i18n): localize processing tools in Simplified Chinese (#2049)
* Complete Simplified Chinese (zh) locale catalog to 100% coverage

Add 696 missing translations to zh.json covering Iceberg data source UI, processing tool metadata (vector, raster, network, statistics), tool group labels, STAC plugin messages, and GeoEditor strings.

Also add i18n guidance note to CLAUDE.md and a gradle ignore rule.

* Translate Whitebox menu tool names and subcategory labels via i18n

Add processing.whitebox.menuTool.{id} and processing.whitebox.menuSubcategory.{key} keys to en.json and zh.json for all 1066 Whitebox tools and 45 subcategory labels. Update ProcessingMenu.tsx to render them through t() instead of raw catalog strings. Other locales fall back to English automatically.

* feat(processing): localize Whitebox toolbox metadata

Generate the English baseline for 733 unlocked Whitebox tools and their parameters, and add Simplified Chinese tool names. Render Processing dialog tool names and parameter help through i18n while keeping manifest heuristics on raw metadata, and include localized Whitebox names in Model Builder search.

* feat(processing): add Whitebox reprojection method option

Expose the optional auto_reproject_method parameter in 34 Whitebox tools and regenerate their English metadata. Add Simplified Chinese labels and descriptions for the new control.

* fix(processing): localize Whitebox parameters

* feat(processing): complete Whitebox translation coverage

Merge local WASM manifests into the generated Whitebox baseline, adding 333 local-mode tools and 50 category labels. Translate toolbox categories and status text, cover every Simplified Chinese tool and parameter label, and avoid appending untranslated help text to translated labels.

* fix(processing): translate Whitebox parameter help

* fix(processing): address i18n review feedback

* style: auto-format (ruff + oxfmt) [pre-commit.ci]

* chore(ci): allow generated locale catalogs

* fix(build): avoid deep Trans component inference

* fix(build): narrow remaining Trans inference

* fix(processing): address i18n review issues

* style: auto-format (ruff + oxfmt) [pre-commit.ci]

* fix(processing): harden i18n review fixes

* fix(processing): pass active locale explicitly

* fix(processing): generate Whitebox menu baselines

* style: auto-format (ruff + oxfmt) [pre-commit.ci]

* feat(i18n): load Whitebox translations as language packs

* Address review feedback

- i18n/index.ts: move `loadInstalledLanguagePack` inside a try so a rejected
  `indexedDB.open()` (Safari private browsing, a sandboxed embed iframe)
  degrades to the base catalog instead of rejecting `i18nReady` and leaving
  `main.tsx` without a render.
- language-pack.ts: abort a pack download after 30s so a host that accepts the
  connection and never answers cannot leave the Settings buttons disabled for
  the session; only short-circuit on a *declared* content-length, since
  `Number(null)` read a chunked body as zero bytes.
- language-pack.ts / SettingsDialog.tsx: derive the "browse the catalog" link
  from the configured host via the new `languagePackBaseUrl()` instead of
  hardcoding `languages.geolibre.app`, so a `__NO_EXTERNAL_CDN__` build hides
  it and a self-hosted mirror is linked rather than the public host. Replaces
  the now-unused `officialLanguagePackDownloadsEnabled()`.
- SettingsDialog.tsx: validate an installed pack's `installedAt` before
  formatting it; an unparseable timestamp made `Intl.DateTimeFormat.format`
  throw during render and take the Settings pane down. New
  `settings.languagePack.installedDetailNoDate` string in en/zh.
- processing-tool-i18n.ts: `translateWhiteboxParameterDescription` now takes the
  active language and suppresses an untranslated description outside English,
  matching `whiteboxParameterLabel` — the same text was hidden inline but shown
  in English on hover.
- processing-tool-i18n.ts / ProcessingDialog.tsx: unify the two identical
  humanizers behind `humanizeIdentifier(value, fallback)`; the extent-group
  field labels now use the parameter fallback rather than the tool one.
- docs/i18n.md: the Model Builder palette section said a heading is translated
  when a tool comes from "an owned catalog", which stopped meaning "vector only"
  once `modelProviderCatalog` began returning `"whitebox"`. Say the vector
  registry outright.
- tests: cover the abort signal, the missing/oversized content-length paths, and
  the suppressed non-English description.

* Address CodeRabbit review feedback

- language-pack.ts / tests: the timeout test dispatched a bare `abort` event,
  which does not set `signal.aborted`, so it took the generic failure path and
  asserted nothing about the timeout. `fetchLanguagePack` now takes an optional
  `timeoutMs` (defaulting to `LANGUAGE_PACK_TIMEOUT_MS`) and the test drives a
  never-resolving host with a 5ms timeout, asserting the timeout message.
- SettingsDialog.tsx: the privacy notice hardcoded `languages.geolibre.app` in
  its copy, so a self-hosted `VITE_LANGUAGE_PACK_BASE_URL` deployment named the
  wrong host and a downloads-disabled build named one it never contacts. The
  notice now interpolates the configured host, and falls back to a new
  `settings.languagePack.privacyLocalOnly` string when there is none.

* Address Claude review feedback

- SettingsDialog.tsx: check a picked language-pack file's `size` against
  `LANGUAGE_PACK_MAX_BYTES` before `file.text()`. `parseLanguagePack` enforces
  the same limit, but only after the whole file has been buffered, so a
  mistakenly picked multi-gigabyte file paid the full read before the check
  ran. Reuses the existing `settings.languagePack.errorTooLarge` string.

---------

Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
Co-authored-by: giswqs <giswqs@gmail.com>
2026-08-22 22:54:52 -04:00
Qiusheng Wu 61aad916f5 docs: document and brand the download mirror (#2055) 2026-08-22 09:49:46 -04:00
Qiusheng Wu e113401da1 fix: hide AppImage update metadata from downloads (#2054) 2026-08-22 09:45:55 -04:00
Qiusheng Wu f4053a5d8b fix: omit maintainer-only MSIX from download mirror (#2053) 2026-08-22 09:43:30 -04:00
Qiusheng Wuandpre-commit-ci[bot] 889082a680 Mirror release downloads to GitHub Pages (#2052)
* ci: mirror release downloads to GitHub Pages

* style: auto-format (ruff + oxfmt) [pre-commit.ci]

* fix: omit maintainer-only store packages from mirror

* perf: skip unchanged mirror downloads

---------

Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
2026-08-22 09:40:58 -04:00