A lightweight, cloud-native GIS platform for visualizing, exploring, and analyzing geospatial data. It runs in the web browser, on the desktop, on mobile, and inside Jupyter notebooks.
| Files | Last commit | Last update |
|---|---|---|
chore(deps): bump anthropics/claude-code-action (#2316) Bumps the actions-minor-patch group with 1 update: [anthropics/claude-code-action](https://github.com/anthropics/claude-code-action). Updates `anthropics/claude-code-action` from 1.0.210 to 1.0.216 - [Release notes](https://github.com/anthropics/claude-code-action/releases) - [Commits](https://github.com/anthropics/claude-code-action/compare/v1.0.210...v1.0.216) --- updated-dependencies: - dependency-name: anthropics/claude-code-action dependency-version: 1.0.216 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: actions-minor-patch ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> | 7 days ago | |
fix: improve Add Data compatibility with Mapbox (#2392) * fix: make Add Data panels respect Mapbox renderer support * Address review feedback - Throw a clear "no resolved tile templates" error when an ArcGIS vector source reaches the Mapbox compiler with only its REST `url` and no `tiles`, so isMapboxSupportedLayer badges it instead of Mapbox failing later on a non-TileJSON fetch; add a regression test - Attach a rejection handler to the lazy vector-panel import used by the FlatGeobuf action on Mapbox so a failed chunk load is logged instead of surfacing as an unhandled rejection * Address Claude review feedback - Export mapboxSourceId/mapboxFillLayerId/mapboxLineLayerId from style-layer-ids so the Mapbox compiler and the STAC footprint picker share one id scheme at compile time instead of the plugin hardcoding the `geolibre-mapbox-<id>-geojson-fill/-line` literals | 14 hours ago | |
feat(vector): let the buffer tool run inward or across the boundary (#2242) * feat(vector): let the buffer tool run inward or across the boundary The Buffer tool only ever grew a feature, so there was no way to ask for a setback inside a polygon. It now takes a `side` parameter: Outside grows each feature (the previous behavior, still the default), Inside shrinks it, and Both sides keeps only the zone within the distance on either side of the boundary. Both engines implement it, so the client (Turf.js), sidecar (GeoPandas) and Pyodide engines agree. An inward buffer can consume a feature entirely, and on a point or line it always does; those features are dropped and reported rather than emitted as ring-less polygons no renderer can draw. Distance stays non-negative in both engines, since direction now belongs to `side`, and an unrecognized `side` is rejected by both rather than silently falling back. Adds three shared golden fixtures (inside, both, points-emptied) plus a rejection case, backend unit tests, the regenerated en.json baseline, and translations for all 18 locales. Closes https://github.com/opengeos/GeoLibre/discussions/2235 Claude-Session: https://claude.ai/code/session_01V2NKL3Eh9yGmmYcfgRuBCr * test(vector): drop the added buffer-side golden fixtures Removes the four shared golden cases added with the buffer `side` parameter (inside, both-sides, points-emptied, unknown-side rejection). The backend unit tests in test_vector_ops.py continue to cover the same behaviors. Claude-Session: https://claude.ai/code/session_01V2NKL3Eh9yGmmYcfgRuBCr * Address CodeRabbit review feedback - vector_ops.py: reject a non-finite buffer distance. `json.loads` accepts NaN/Infinity, so a raw sidecar payload can carry one, and NaN compares False against the `>= 0` bound rather than tripping it, reaching Shapely and buffering every geometry away to nothing. - vector-tools.ts: reject a negative buffer distance. The dialog's `min: 0` binds the form, not a programmatic caller (Model Builder, the assistant, a replayed history entry), so a negative value silently eroded features on the client while the Python engine already rejected it. The two engines now agree. - docs/features.md: say that Inside shrinks polygons only, and that a point or line is emptied by the inward buffer, dropped, and reported in the run log. - test_vector_ops.py: cover the non-finite distance rejection for NaN and both infinities. Claude-Session: https://claude.ai/code/session_01V2NKL3Eh9yGmmYcfgRuBCr * Address Claude and CodeRabbit review feedback - vector-tools.ts / vector_ops.py: reword the drop message to "Dropped N feature(s) the buffer left empty". It fires whenever a feature is dropped, not only on an inward buffer, so naming the inward buffer misattributed a drop caused by a degenerate input geometry on an outward run. - vector-tools.ts: wrap the per-feature buffer in try/catch. `side: "both"` runs buffer + erode + difference on every feature, and jsts can throw on a degenerate or self-intersecting geometry the erosion produces; a throw now counts as a drop instead of aborting the batch and discarding every feature already buffered. - vector-tools.ts: reject an explicitly non-finite distance. `numberParam` folds NaN/Infinity/unparseable values into its fallback, so a programmatic caller silently got a 1-unit buffer where the Python engine raises. Checking the raw parameter keeps the two engines' distance contracts aligned; a missing or null distance still takes the documented default of 1. Claude-Session: https://claude.ai/code/session_01V2NKL3Eh9yGmmYcfgRuBCr * Address Claude and CodeRabbit review feedback - vector-tools.ts: count a null-geometry feature as dropped instead of skipping it silently. The Python engine loads it into the GeoDataFrame and its `isna()` filter reports it, so the two engines disagreed on the totals for a layer mixing null and normal geometries. Verified both now report "Buffered 1 / Dropped 1" for the same input. - vector-tools.ts: report a buffering error separately from an empty result. A thrown geometry was folded into the "left empty" count, which read as if the buffer had consumed it; it now logs "Skipped N feature(s) the buffer could not process". This path is reachable, not hypothetical: a 2-point polygon, an empty ring, or a NaN coordinate all make turf throw. - vector-tools.ts: reject a whitespace-only distance string. `Number(" ")` is 0 here while the Python engine's `float(" ")` raises. An empty string is deliberately left alone: both engines read that as 0, so rejecting it would have created the divergence rather than closed it. Claude-Session: https://claude.ai/code/session_01V2NKL3Eh9yGmmYcfgRuBCr * Address Claude review feedback - Restore the four shared golden fixtures for the buffer `side` parameter (inside, both-sides, points-emptied, unknown-side rejection) that a previous commit dropped. `tests/fixtures/vector/SPEC.md` sets these up precisely to catch drift between the Turf and GeoPandas buffer implementations, and both harnesses pass them, so dropping them gave up the only CI check on the new inward/band behavior agreeing across the two engines. - Add `tests/vector-buffer-side.test.ts`, client-engine coverage for the parts a JSON fixture cannot express: the `Buffered`/`Dropped` log lines, the band's outer ring plus hole, property restoration through turf's `difference`, a null-geometry feature counted as dropped, and the negative / NaN / Infinity / whitespace-only / unparseable distance guards. - Reject an explicitly empty `side` in both engines instead of falling back to `outside`. An empty `units` already reaches its lookup and is rejected there, so the two parameters now behave alike, and the PR's stated intent — an unrecognized side is an error, not a silent default — no longer has a blank string slipping through it. Only an absent `side` defaults. Covered by a new golden case plus a unit test on each engine. - Document the one deliberate asymmetry in SPEC.md: the client engine's per-feature `try`/`catch` reports `Skipped N feature(s)`, which the vectorized GeoPandas path has no counterpart for. The engines agree on `Dropped` and diverge only here, by design. Claude-Session: https://claude.ai/code/session_0132d1a3smsbEn4QkKyx3cQa * Address Claude review feedback - Validate the buffer parameters in the Python engine's order (units, side, distance finiteness, distance sign). Both engines already rejected the same inputs, but a call with several bad parameters at once reported different first errors — `side: "bogus", distance: -5` raised "Unknown buffer side" on the sidecar and logged the negative-distance error on the client. - Validate `units` in the client engine, which never did. This turned into a real regression once this PR added the per-feature try/catch: turf throws on a unit it does not know, and the catch swallowed that into the `Skipped` count, so `units: "furlongs"` logged "Buffered 0 feature(s) by 1 furlongs" and handed back an empty result layer where the Python engine raises "Unknown unit". It is now rejected up front, and an explicitly empty `units` is rejected too, matching the `side` treatment in the previous commit. - Cover both with `error-buffer-unknown-unit.json` (a shared golden case, so the two engines are pinned to the same verdict) plus order-asserting unit tests on each engine, since a golden case asserts rejection but not which message wins. Claude-Session: https://claude.ai/code/session_0132d1a3smsbEn4QkKyx3cQa * Address CodeRabbit and Claude review feedback - Hold a string `distance` to Python's decimal-float grammar instead of `Number()`'s. `Number("0x10")` is 16 while `float("0x10")` raises, so a hex, binary or octal string buffered by 16/2/8 on the client and errored on the sidecar. The new DECIMAL_NUMBER contract is deliberately the narrower of the two grammars, so it also refuses Python's digit separators, which JavaScript already refused. Subsumes the whitespace-only check it replaces. - Widen `nonEmptyBuffer`'s empty test from `coordinates.length === 0` to a recursive search for a real position. Zero rings is the usual shape jsts returns for an emptied geometry, but a `Polygon` with one empty ring (`[[]]`) and a `MultiPolygon` of individually degenerate parts both have a non-zero length while still being undrawable — the exact case the drop exists to prevent. Extracted as `hasPositions` and exported for tests: turf rejects a degenerate geometry on the way in, so this guard cannot be reached through the tool's own parameters. - Cover both with unit tests plus `error-buffer-hex-distance.json`, a shared golden case pinning the two engines to the same verdict on a hex distance. Claude-Session: https://claude.ai/code/session_0132d1a3smsbEn4QkKyx3cQa * Address Claude review feedback - Parse the buffer `distance` only after the `units` and `side` checks in the Python engine. The ordering claim made in the previous commit was wrong: `float(parameters.get("distance", 1) or 0)` ran first, so an unparseable distance pre-empted both checks and `{distance: "abc", units: "furlongs"}` raised on the distance where the client reports the unit. The two engines now really do report the same first error. - Raise the tool's own "Buffer distance must be a finite number" for an unparseable distance instead of letting `float`'s raw "could not convert string to float: 'abc'" escape to the client, which never produces that. - Add `buffer-point-line-both.json` plus a client-side assertion for `side: "both"` on a feature with no interior to erode. The behavior was documented above `bufferOneFeature` but untested: a point and a line have nothing to erode, so the band across the boundary is the grown shape and both engines return it whole, solid and with the source attributes, rather than dropping it. The fixture pins the counts and attributes across both engines; the unit test pins the single ring, which a golden case cannot express. Claude-Session: https://claude.ai/code/session_0132d1a3smsbEn4QkKyx3cQa * Address Claude review feedback Require the buffer `distance` to be a number or a numeric string in both engines, rejecting every other JSON type instead of coercing it. The two languages coerce those types differently, and four of them diverged: distance client (before) sidecar (before) false 1 0 [5] 1 error {} error 0 [] 1 0 `Number(false)` is 0 and `Number([5])` is 5, both of which pass the finite pre-check and are then discarded by `numberParam` for its fallback of 1, while Python's `raw or 0` reads false/[]/{} as 0 and `float` raises on [5]. There is no coercion the two share, so the type is now checked before the value. `bool` is excluded explicitly on the Python side because it is an `int` subclass. An empty string stays the one non-number both engines agree on — Python's `"" or 0` is 0 and `Number("")` is 0 — and is now pinned by a test on each engine rather than left implicit. Covered by parametrized tests on both engines plus `error-buffer-boolean-distance.json` for the shared verdict. Claude-Session: https://claude.ai/code/session_0132d1a3smsbEn4QkKyx3cQa * Address Claude review feedback Treat an explicit JSON `null` as an absent parameter for `units` and `distance` in the Python engine, as `side` already did and as the client does for all three. `parameters.get("units", "kilometers")` only defaults on a missing key, so `units: null` reached the lookup as `str(None)` — the unit "None" — and `distance: null` was rejected by the type check added in the previous commit, while the client defaulted both. The function was also inconsistent with itself: `side: null` already defaulted. Parametrized over all three fields on each engine, so an omitted parameter and a nulled one are pinned to the same buffer. Claude-Session: https://claude.ai/code/session_0132d1a3smsbEn4QkKyx3cQa * test(vector): drop the buffer-side golden fixtures Removes the nine shared golden cases added on this branch for the buffer `side` parameter: the four behavioral ones (inside, both-sides, both on a point/line, points emptied by an inward buffer) and the five rejection ones (unknown side, empty side, unknown unit, hex distance, boolean distance). The 26 fixtures inherited from main are untouched, and `buffer-points-km.json` still covers an outward buffer, so the golden harness keeps running. What this gives up: nothing now checks that the Turf and GeoPandas engines agree on `inside`, `both`, or any of the rejection paths. That behavior is still tested, but per engine rather than against each other — `tests/vector-buffer-side.test.ts` for the client and `test_vector_ops.py` for the sidecar — so drift between the two implementations would no longer be caught by CI. Claude-Session: https://claude.ai/code/session_0132d1a3smsbEn4QkKyx3cQa | 11 days ago | |
feat: add GPT-native disaster search route (#2090) * feat: add GPT-native disaster search route Separate GPT web search from explicitly requested Tavily queries so plugins can use the native search capability by default. * Address review feedback - workers/ai-proxy/wrangler.jsonc: drop TAVILY_API_KEY from `secrets.required`. The README says the Worker deploys without either search secret, and `/tavily` answers 503 when it is missing, so listing it as required contradicted both the docs and the code (CodeRabbit). - workers/ai-proxy/src/index.ts: declare TAVILY_API_KEY as an optional Env field alongside the SEARCH_MESSAGES_* ones, now that it is no longer a wrangler-declared secret, and rewrite the comment above the block so it describes the fields that are actually there. - workers/ai-proxy/worker-configuration.d.ts: regenerate with `wrangler types` for the wrangler.jsonc change (also picks up the already-drifted ALLOWED_ORIGINS value and a workerd runtime refresh). - workers/ai-proxy/README.md: retitle the stale "Serving `/tavily` from a model's own web search" subsection around `/search`, which is the route the messages backend now serves unconditionally, and drop the "keeps its name" / "Tavily remains the default ... until" wording left over from the removed SEARCH_BACKEND toggle (Claude, CodeRabbit). - workers/ai-proxy/README.md: the settings table's Required column now reads "for `/search`" rather than "for `messages`", matching the rest of the doc (Claude). - workers/ai-proxy/README.md: re-indent the search paragraphs and fenced blocks to 3 spaces so they stay nested under ordered-list item 2 on strict CommonMark renderers (Claude). * Address Claude review feedback - workers/ai-proxy/README.md: call out the upgrade as a breaking change. `SEARCH_BACKEND` exists on main, so a deployment running with `SEARCH_BACKEND=messages` today has `/tavily` answered by the messages backend with no `TAVILY_API_KEY` set; after this Worker version deploys that route calls Tavily again and 503s. The NASA OPERA plugin loads from outside this repo and deploys on its own schedule, so the README now states the deploy order: move the plugin to `/search` first. - docker/entrypoint.sh: the comments above `GEOLIBRE_NASA_OPERA_NEWS_PROXY_ENDPOINT` still described the published base as one the plugin appends `/tavily` to, which was the pre-PR contract where `/tavily` could transparently be the messages engine. They now name both routes and use `/search` in the doubled-slash example. Comments only -- the published value is a base either way, so no behavior changes. * Address CodeRabbit review feedback - workers/ai-proxy/README.md: say how to set `SEARCH_MESSAGES_URL`. The step showed only `wrangler secret put SEARCH_MESSAGES_API_KEY` and the variable is not in the checked-in `wrangler.jsonc`, so an operator following the steps verbatim got `503 Search is not configured` from `/search` with nothing pointing at the missing piece. It and `SEARCH_MESSAGES_MODEL` are read off `Env` like the key, so either a `vars` entry or `wrangler secret put` works; the README now says so and states that the 503 lifts only when the URL and the key are both set (`proxyMessagesSearch` checks both). * Address Claude review feedback - workers/ai-proxy/README.md: the upgrade note covered only the loud failure. An operator who ran `SEARCH_BACKEND=messages` with a leftover `TAVILY_API_KEY` still configured gets no 503 at all -- `/tavily` quietly starts serving real Tavily results in place of GPT-native search, which is harder to notice than an error. Added the callout to delete the stray secret unless that route is meant to stay open. | 21 days ago | |
fix: improve Add Data compatibility with Mapbox (#2392) * fix: make Add Data panels respect Mapbox renderer support * Address review feedback - Throw a clear "no resolved tile templates" error when an ArcGIS vector source reaches the Mapbox compiler with only its REST `url` and no `tiles`, so isMapboxSupportedLayer badges it instead of Mapbox failing later on a non-TileJSON fetch; add a regression test - Attach a rejection handler to the lazy vector-panel import used by the FlatGeobuf action on Mapbox so a failed chunk load is logged instead of surfacing as an unhandled rejection * Address Claude review feedback - Export mapboxSourceId/mapboxFillLayerId/mapboxLineLayerId from style-layer-ids so the Mapbox compiler and the STAC footprint picker share one id scheme at compile time instead of the plugin hardcoding the `geolibre-mapbox-<id>-geojson-fill/-line` literals | 14 hours ago | |
fix: improve Add Data compatibility with Mapbox (#2392) * fix: make Add Data panels respect Mapbox renderer support * Address review feedback - Throw a clear "no resolved tile templates" error when an ArcGIS vector source reaches the Mapbox compiler with only its REST `url` and no `tiles`, so isMapboxSupportedLayer badges it instead of Mapbox failing later on a non-TileJSON fetch; add a regression test - Attach a rejection handler to the lazy vector-panel import used by the FlatGeobuf action on Mapbox so a failed chunk load is logged instead of surfacing as an unhandled rejection * Address Claude review feedback - Export mapboxSourceId/mapboxFillLayerId/mapboxLineLayerId from style-layer-ids so the Mapbox compiler and the STAC footprint picker share one id scheme at compile time instead of the plugin hardcoding the `geolibre-mapbox-<id>-geojson-fill/-line` literals | 14 hours ago | |
fix: make URL load error banners dismissible (#2353) * fix: make URL load error banners dismissible * Address CodeRabbit review feedback Prefix URL error banner keys with their source to keep dismissal state independent. * chore: bump Chrome extension to 0.3.1 | 5 days ago | |
fix: improve Add Data compatibility with Mapbox (#2392) * fix: make Add Data panels respect Mapbox renderer support * Address review feedback - Throw a clear "no resolved tile templates" error when an ArcGIS vector source reaches the Mapbox compiler with only its REST `url` and no `tiles`, so isMapboxSupportedLayer badges it instead of Mapbox failing later on a non-TileJSON fetch; add a regression test - Attach a rejection handler to the lazy vector-panel import used by the FlatGeobuf action on Mapbox so a failed chunk load is logged instead of surfacing as an unhandled rejection * Address Claude review feedback - Export mapboxSourceId/mapboxFillLayerId/mapboxLineLayerId from style-layer-ids so the Mapbox compiler and the STAC footprint picker share one id scheme at compile time instead of the plugin hardcoding the `geolibre-mapbox-<id>-geojson-fill/-line` literals | 14 hours ago | |
Open GeoLibre projects from the desktop (#2163) * Open projects from desktop file associations * Use native project events on macOS | 16 days ago | |
fix: render vector and raster plugins correctly in Mapbox (#2390) * fix: render vector and raster plugins correctly in Mapbox Recognize Mapbox control corners so importer panels stay beside Layers. Use the compatible GPU raster backend and Mapbox projection API, and let plugin-managed rasters render without unsupported-layer errors. * fix: add Mapbox importer control spacing | 16 hours ago | |
Add optional Mapbox rendering engine (#2389) * feat: add optional Mapbox rendering engine * Add Mapbox token shortcut to environment settings * Fix Mapbox zoom jumps from stale split-view camera feedback * Smooth Mapbox mouse-wheel zoom across successive ticks * Revert experimental Mapbox zoom fixes * Align Mapbox token settings with Cesium device-local credentials * Address review feedback - Clear a stored Mapbox source error once the source finishes loading (`sourcedata` with `content` + `isSourceLoaded`) and when its layer is removed, so the error banner and image capture recover (CodeRabbit) - Resolve `identifyFeatures` ids through the layer's own GeoJSON: Mapbox's `generateId` reports the source index, which is mapped back to `String(feature.id ?? index)` so selection and highlighting match (CodeRabbit) - Fall back to the field-based label text when a label expression is not valid JSON instead of dropping the whole layer (CodeRabbit) - Badge layers the Mapbox adapter cannot compile in the per-pane layer toggle and the primary layer panel via a new `isMapboxSupportedLayer` helper, with `mapGrid.noMapbox` / `renderer.layerMapboxUnsupported` keys in every catalog (Claude review) - Add `tests/mapbox-engine.test.ts`: behavior tests for `MapboxEngine` against a fake mapbox-gl map (sync diffing, error record, picking, camera and preferences) (Claude review) - Rename `bridgeVectorControlToCesium` to `bridgeVectorControlToStore` and document its renderer-agnostic scope (Claude review) - Document the Mapbox GL JS v3 license and account/data-collection terms in docs/mapbox-renderer.md and packages/map/README.md (CodeRabbit) - Use "çizim motoru" in the Turkish Mapbox token hint (CodeRabbit) - List `mapbox` as a renderer choice in the agent skill references (Claude review summary) * Address review feedback - Match Multi* geometry types in the Mapbox fill/circle filters and exclude MultiPoint from the line layer, so MultiPolygons get a fill and MultiPoints draw as circles (Claude review) - Clamp views against the project preferences in MapboxEngine.applyView and easeToView before the camera move, mirroring constrainMapView in the MapLibre engine, to avoid a one-frame snap (Claude review) - Update the shared mapView before the pane view in MapboxCanvas so a synchronized pane never jumps to a stale camera (CodeRabbit) - Fix the German mapboxTokenTitle copy-paste ("Mapbox-Token") (Claude review) - Drop the unused key parameter of addEnvironmentVariable (Claude review) - Add tests for the geometry filters, the label-expression fallback and the view clamp * Add label-free Mapbox Satellite basemap * Make background picker replace the active Mapbox basemap override * Address Claude review feedback - Fall back to the default basemap in a Mapbox pane when the resolved inline style uses a MapLibre-only source protocol (the offline PMTiles basemap's pmtiles:// source is registered with maplibre-gl only), via a new styleUsesUnsupportedSource helper, with a console warning, a doc note in docs/mapbox-renderer.md and a unit test * Address Claude review feedback - Update the View-menu visibility comment in TopToolbar to describe the generalized primaryRenderer !== "maplibre" guard (Cesium or Mapbox) * Address CodeRabbit review feedback - Include a GeoJSON source's string `data` URL in styleUsesUnsupportedSource so a MapLibre-only protocol there also triggers the Mapbox fallback - Redact credentials from the basemap fallback warning * Link the Mapbox token hint to Settings and address review feedback - Make "Settings → Environment variables" in the Mapbox token hint a button that opens the Settings dialog at the Environment section with the Mapbox token field focused (new "mapboxToken" focus target), so first-time setup is one click; the hint text now uses <Trans> with a <settingsLink> tag in every catalog - Translate the Mapbox token input placeholder via settings.env.mapboxTokenPlaceholder (Claude review) - Give each pane's rendering-engine trigger a pane-specific aria-label via mapGrid.renderingEngineLabel with {{number}} (Claude review) - Memoize isMapboxSupportedLayer per layer object with a WeakMap so the layer panels do not recompile every layer on every render (Claude review) * Address Claude review feedback and update the globe E2E spec - Move the dev-server WMS proxy helpers into a shared wms-proxy.ts and route Mapbox WMS raster tiles through it like the MapLibre path, so a WMS layer works in both panes under npm run dev (Claude review) - Add a why-disabled tooltip (renderer.pluginUnsupported) to plugin menu items greyed out for the active renderer, matching the command palette's disabledReason (Claude review) - Update e2e/cesium-globe.spec.ts to pick the pane renderer from the new rendering-engine dropdown; the per-pane 2D/3D toggle buttons it clicked were replaced by that menu in this PR * Address Claude review feedback - Move the basemap label-font resolver out of layer-sync.ts into a shared text-font.ts and have MapboxEngine resolve the font from the loaded basemap style on every style.load, passing it to compileMapboxLayer via a new textFont option; "Open Sans Regular" is now only the fallback for styles without a text symbol layer, so labels render on third-party basemaps whose glyph catalog lacks it | 20 hours ago | |
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 | 3 days ago | |
Add optional Mapbox rendering engine (#2389) * feat: add optional Mapbox rendering engine * Add Mapbox token shortcut to environment settings * Fix Mapbox zoom jumps from stale split-view camera feedback * Smooth Mapbox mouse-wheel zoom across successive ticks * Revert experimental Mapbox zoom fixes * Align Mapbox token settings with Cesium device-local credentials * Address review feedback - Clear a stored Mapbox source error once the source finishes loading (`sourcedata` with `content` + `isSourceLoaded`) and when its layer is removed, so the error banner and image capture recover (CodeRabbit) - Resolve `identifyFeatures` ids through the layer's own GeoJSON: Mapbox's `generateId` reports the source index, which is mapped back to `String(feature.id ?? index)` so selection and highlighting match (CodeRabbit) - Fall back to the field-based label text when a label expression is not valid JSON instead of dropping the whole layer (CodeRabbit) - Badge layers the Mapbox adapter cannot compile in the per-pane layer toggle and the primary layer panel via a new `isMapboxSupportedLayer` helper, with `mapGrid.noMapbox` / `renderer.layerMapboxUnsupported` keys in every catalog (Claude review) - Add `tests/mapbox-engine.test.ts`: behavior tests for `MapboxEngine` against a fake mapbox-gl map (sync diffing, error record, picking, camera and preferences) (Claude review) - Rename `bridgeVectorControlToCesium` to `bridgeVectorControlToStore` and document its renderer-agnostic scope (Claude review) - Document the Mapbox GL JS v3 license and account/data-collection terms in docs/mapbox-renderer.md and packages/map/README.md (CodeRabbit) - Use "çizim motoru" in the Turkish Mapbox token hint (CodeRabbit) - List `mapbox` as a renderer choice in the agent skill references (Claude review summary) * Address review feedback - Match Multi* geometry types in the Mapbox fill/circle filters and exclude MultiPoint from the line layer, so MultiPolygons get a fill and MultiPoints draw as circles (Claude review) - Clamp views against the project preferences in MapboxEngine.applyView and easeToView before the camera move, mirroring constrainMapView in the MapLibre engine, to avoid a one-frame snap (Claude review) - Update the shared mapView before the pane view in MapboxCanvas so a synchronized pane never jumps to a stale camera (CodeRabbit) - Fix the German mapboxTokenTitle copy-paste ("Mapbox-Token") (Claude review) - Drop the unused key parameter of addEnvironmentVariable (Claude review) - Add tests for the geometry filters, the label-expression fallback and the view clamp * Add label-free Mapbox Satellite basemap * Make background picker replace the active Mapbox basemap override * Address Claude review feedback - Fall back to the default basemap in a Mapbox pane when the resolved inline style uses a MapLibre-only source protocol (the offline PMTiles basemap's pmtiles:// source is registered with maplibre-gl only), via a new styleUsesUnsupportedSource helper, with a console warning, a doc note in docs/mapbox-renderer.md and a unit test * Address Claude review feedback - Update the View-menu visibility comment in TopToolbar to describe the generalized primaryRenderer !== "maplibre" guard (Cesium or Mapbox) * Address CodeRabbit review feedback - Include a GeoJSON source's string `data` URL in styleUsesUnsupportedSource so a MapLibre-only protocol there also triggers the Mapbox fallback - Redact credentials from the basemap fallback warning * Link the Mapbox token hint to Settings and address review feedback - Make "Settings → Environment variables" in the Mapbox token hint a button that opens the Settings dialog at the Environment section with the Mapbox token field focused (new "mapboxToken" focus target), so first-time setup is one click; the hint text now uses <Trans> with a <settingsLink> tag in every catalog - Translate the Mapbox token input placeholder via settings.env.mapboxTokenPlaceholder (Claude review) - Give each pane's rendering-engine trigger a pane-specific aria-label via mapGrid.renderingEngineLabel with {{number}} (Claude review) - Memoize isMapboxSupportedLayer per layer object with a WeakMap so the layer panels do not recompile every layer on every render (Claude review) * Address Claude review feedback and update the globe E2E spec - Move the dev-server WMS proxy helpers into a shared wms-proxy.ts and route Mapbox WMS raster tiles through it like the MapLibre path, so a WMS layer works in both panes under npm run dev (Claude review) - Add a why-disabled tooltip (renderer.pluginUnsupported) to plugin menu items greyed out for the active renderer, matching the command palette's disabledReason (Claude review) - Update e2e/cesium-globe.spec.ts to pick the pane renderer from the new rendering-engine dropdown; the per-pane 2D/3D toggle buttons it clicked were replaced by that menu in this PR * Address Claude review feedback - Move the basemap label-font resolver out of layer-sync.ts into a shared text-font.ts and have MapboxEngine resolve the font from the loaded basemap style on every style.load, passing it to compileMapboxLayer via a new textFont option; "Open Sans Regular" is now only the fallback for styles without a text symbol layer, so labels render on third-party basemaps whose glyph catalog lacks it | 20 hours ago | |
fix: improve Add Data compatibility with Mapbox (#2392) * fix: make Add Data panels respect Mapbox renderer support * Address review feedback - Throw a clear "no resolved tile templates" error when an ArcGIS vector source reaches the Mapbox compiler with only its REST `url` and no `tiles`, so isMapboxSupportedLayer badges it instead of Mapbox failing later on a non-TileJSON fetch; add a regression test - Attach a rejection handler to the lazy vector-panel import used by the FlatGeobuf action on Mapbox so a failed chunk load is logged instead of surfacing as an unhandled rejection * Address Claude review feedback - Export mapboxSourceId/mapboxFillLayerId/mapboxLineLayerId from style-layer-ids so the Mapbox compiler and the STAC footprint picker share one id scheme at compile time instead of the plugin hardcoding the `geolibre-mapbox-<id>-geojson-fill/-line` literals | 14 hours ago | |
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> | 6 days ago | |
feat(server): add a self-hostable projects API and collaboration relay (#1690) * feat(server): add a self-hostable projects API and collaboration relay Sharing, accounts, and live collaboration could only ever run on share.geolibre.app and Cloudflare, so a self-hoster running the Docker image got the map and the sidecar but none of it. Write the projects and identity contract down, add a FastAPI reference implementation of it, lift the collab session logic out of the Durable Object into a shared core, and host that core from a plain Node relay as well. One conformance suite runs against both relays so their permission behavior cannot drift. Closes #1685 Closes #1686 * fix(server): unbreak the Docker stack found by an end-to-end run Three defects only a real `docker compose up` surfaces. The entrypoint embeds its runtime-config program in a single-quoted `python -c`, so the apostrophes in `'/'.join(...)` closed that argument and every web container died at boot on a SyntaxError. The server image created only /data, so a named volume mounted at /data/objects came up root-owned and the unprivileged user could not write any project. Forking required a request body despite documenting a private default, so a bodyless "fork this" answered 422. * Address Copilot, Claude, and CodeRabbit review feedback Relay, crash and correctness: - Reject non-object JSON before reading .type. `null` is valid JSON, so one frame from any client threw inside the ws listener and killed the process, taking every other session with it. Reproduced against the container, which Docker then restarted. handleMessage is also wrapped defensively. - Answer presence frames before the per-message SQLite read. It is a synchronous SELECT * over the snapshot and chat blobs, and this relay hosts every session in one process, so a cursor burst added latency to unrelated sessions. - Add an error listener to the session-create request stream and destroy the request past the size cap, rather than reading on forever. - Sweep sessions that were created but never joined; only the socket-close path deleted rows, so an unjoined code lived in SQLite forever. - Add a ping/pong heartbeat, so a connection dropped without a close frame stops pinning the roster and blocking idle cleanup, and terminate sockets that never finish the closing handshake instead of hanging shutdown. - Make saveSnapshot atomic with UPDATE ... RETURNING rev. - Trim in sanitizeDisplayName: a whitespace-only name is truthy and reached the roster, participant broadcasts, and chat author fields. Server correctness and hardening: - USERNAME_RE accepted a 1-character name; the middle group was optional. - Bound username and password length. Both endpoints taking them are unauthenticated and feed scrypt, so size drove CPU and memory. - Never pair a wildcard CORS origin with allow_credentials. - Enable SQLite foreign keys, so deleting an account no longer strands tokens. - Reject explicit null visibility/tags in PATCH, which returned 500 not 422. - Increment views and forkCount in SQL; the contract promises atomicity. - Allocate version numbers from max(number) with retry, so concurrent content updates cannot collide on a key and overwrite each other. - Stream thumbnail uploads instead of buffering the whole body before the check. - Build the app in a uvicorn factory; importing the module created a stray database and storage directory, including under pytest. - Return the documented account shape from /api/users/me. Packaging and docs: - Run the relay container as non-root with a healthcheck, and document the one-time chown for volumes created by an earlier root-owned image. - Re-exclude collab-node build artifacts after the .dockerignore negations. - Extend tsconfig.base.json, use the workspace "*" dependency convention, drop the duplicated SocketAttachment members and the dead socketByClientId, and share HEX_COLOR_RE/finite from one internal module. - Document `mine=true`, correct the users/{username}/projects auth description to the filtered-200 behavior the code and its tests implement, and state plainly that rate limiting and token expiry are left to the operator. - Cover unlisted visibility, explicit-null PATCH, username length, the configurable snapshot ceiling, and sanitizeView; drop the fixed sleep and the hardcoded /tmp path from the relay test. * Address CodeRabbit review feedback - Count the session-create body in UTF-8 bytes including the current chunk, and check before appending. Testing the accumulated length first accepted one final chunk of any size once the running total was still under the cap. - Answer the 413 before dropping the connection. Destroying the request immediately tore down the socket before the response could flush, so a client saw a reset instead of the status; verified against the container. - Use a level-two heading in the relay README (MD001). * Address Claude review feedback - Reject a declared Content-Length past the largest accepted body before reading it. The JSON `content` routes let Pydantic materialize the whole payload before parse_content could answer 413, the exposure the thumbnail route already avoided by streaming. - Retry slug allocation on IntegrityError in create_project. unique_slug SELECTs and the insert follows, so two concurrent creates with one title from the same account raced into a 500 rather than a clean retry. - Match the Worker in the Node relay's comment-mutation path: validate the action shape before checking permissions, so a malformed frame from a view-only guest answers bad-message on both relays rather than forbidden on one, and bound the mutated project against the snapshot ceiling before persisting it. The per-comment caps alone allow a worst case far past it. - Cover both in the relay suite, which did not exercise comment-mutation at all. * Address CodeRabbit review feedback - Size the Content-Length ceiling for worst-case JSON escaping. parse_content bounds the decoded string, and JSON may spend six wire bytes on one ASCII byte via \u00XX, so the 2x slack would have refused valid uploads at the documented limit before they were ever parsed. - Stop allocating a 101 MiB payload in the oversized-body test. The middleware decides from the header, so the request now carries two bytes and only claims to be huge. Drops the unused monkeypatch argument (ARG001). - Add a test that a fully \u-escaped document within the decoded limit is accepted, with the limits shrunk so the multiplier is what decides the outcome; it fails against the previous 2x ceiling. * Address CodeRabbit review feedback - Reject an oversized POST /sessions body on the declared Content-Length before registering the body handlers, so a request claiming a large body cannot hold the connection open while it uploads slowly. The running byte count stays for clients that omit or understate the header, and both paths now share one MAX_SESSION_BODY_BYTES constant. - Add the integration test that was asked for. It writes the request over a raw socket, because fetch refuses to send fewer bytes than it declared, and that mismatch is exactly the case under test; without the guard it times out waiting for a response instead of getting an immediate 413. This was an outside-diff-range comment in the review body rather than an inline thread, which is why it was missed in the earlier rounds. * Address Claude review feedback - Translate an IntegrityError on account creation into the documented 409. The uniqueness check and the commit are not atomic, and no IntegrityError handler is registered, so the loser of a race escaped as a raw 500. - Eager-load owner and versions on the two listing queries. project_json reads both lazily, so a page fired one query per row; measured 28 statements for 25 projects before, 4 after. - Hash on a dummy password when the account is not found, so login costs the same either way. Short-circuiting skipped scrypt for an unknown username, which enumerates accounts by response time regardless of a request-count limiter. - Handle SIGTERM and SIGINT in the relay entrypoint. Nothing outside the tests called close(), so docker stop killed the process outright and the graceful shutdown path never ran in production. - Hoist the stored-chat validators into @geolibre/collab-core and use them in both relays. The Node store only JSON-parsed the column, so a corrupt record would reach joiners and crash a client on coordinate.lat.toFixed; the Worker had guarded this alone. Covered in the conformance suite. - Warn that POSTGRES_PASSWORD is substituted into a DSN verbatim. Verified the two characters that actually break: "@" reparses the host, and "%" silently percent-decodes to a different password. "/" and ":" are fine, so the note names only what genuinely fails. * Address CodeRabbit review feedback - Register CORSMiddleware after limit_body so it stays outermost. Starlette wraps in reverse registration order, so the 413 was returned without CORS headers and a browser could not read the documented error body. Confirmed before and after, and pinned in the test. - Pin the geolibre uid/gid to 1000, which the documented volume-repair chown assumes; the base image merely happened to allocate it. - Require POSTGRES_PASSWORD instead of committing a shared default for the account that owns all project metadata, and document that Postgres only applies it at volume initialization, so rotating it later needs ALTER USER or a fresh volume. - Document binding the projects server and relay to loopback behind a proxy, with an override file; pointing the browser URLs at a proxy does not stop direct access to those listeners. - Deduplicate reply ids in validateComment. The relay's reply action already skipped a duplicate id, leaving inline replies as the one path that could persist two replies sharing an id. - Use COPY --chown in the relay image instead of a recursive chown of /app, which rewrote node_modules into a second layer. - Declare engines.node >=22.13, below which node:sqlite is flag-gated. - Reuse normalizeMode for the stored mode rather than an inline ternary that would silently downgrade a future third mode. - Document GEOLIBRE_HOST and GEOLIBRE_PORT, and that the fork request body is optional. - Pin the override-specific refusal message in the conformance suite, and correct two comments in the Worker that described code the extraction removed. * Address Claude review feedback - Filter non-object entries out of project.comments and target.replies in the Node relay. Snapshot content is opaque, so a client could plant null with an ordinary snapshot frame and every subsequent .id read threw, leaving commenting permanently broken for that session while the Worker tolerated the same data. Reproduced against the container, then covered in the relay suite. - Attach an error listener to the raw upgrade socket before writing to it. A net.Socket with no listener throws on the next TCP error, which is uncaught and stops the relay, and this is the pre-auth path most exposed to arbitrary traffic. - Retry the slug and version allocations on OperationalError too. On the SQLite default a concurrent writer surfaces as "database is locked" rather than IntegrityError, which the loops did not catch. - Add a catch-all exception handler returning the documented JSON error shape. Only HTTPException and RequestValidationError were registered, so anything else escaped as a plain-text 500. - Document that the Content-Length guard sees only a declared length, so a chunked or HTTP/2 request is still parsed in full, and that request size should also be capped at the proxy. * style: auto-format (ruff + oxfmt) [pre-commit.ci] --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> | 1 month ago | |
ci(fmt): add ruff + oxfmt auto-format workflow and config (#1327) * ci(fmt): add ruff + oxfmt auto-format workflow and config Adds a GitHub Actions workflow that runs ruff format/check on Python and notebooks and oxfmt on JS/TS/JSON/CSS/YAML/TOML, then pushes a style commit back to the branch. Mirrors the local pre-commit hooks so CI and local runs produce identical output. - .github/workflows/format.yml: pinned ruff==0.15.22, oxfmt@0.59.0 - ruff.toml: line-length 100, rules F/I/W/E, py310 target - .oxfmtrc.json: width 100, double quotes, lockfile/generated excludes - .pre-commit-config.yaml: local ruff + oxfmt hooks (offline-friendly) - .gitattributes: normalize all text to LF, explicit binary rules - docs/contributing.md: document the new formatting pipeline * ci(fmt): switch auto-format from GitHub Actions to pre-commit.ci pre-commit.ci authenticates via its own GitHub App, so the auto-format bot can push fixes back on fork PRs (where GITHUB_TOKEN is read-only). It only runs on PRs, so auto-formatted commits never bypass review. - Delete .github/workflows/format.yml - Add autofix/autoupdate config to .pre-commit-config.yaml; the ruff and oxfmt version pins there are now the single source of truth - Trim ignorePatterns comments in .oxfmtrc.json - Rewrite the coding-conventions section in docs/contributing.md * ci(fmt): address review feedback on ruff.toml and .gitattributes - .gitattributes: add explicit binary rules for *.icns, *.pbf, *.pmtiles (icon.icns, MapLibre glyph pbfs, osm.pbf fixtures, and mini.pmtiles are all tracked in-repo; closes the explicit-safety-net gap flagged in review) - ruff.toml: drop the misleading lines-after-imports = -1 (no-op default) and its comment; add .ruff_cache to extend-exclude so the intent survives any future change to ruff's default exclusion list - .gitignore: ignore .ruff_cache/ - .pre-commit-config.yaml: drop the redundant `files: \.(py|ipynb)$` regex (types_or already scopes the ruff hooks to python/pyi/jupyter) and fix the nbstripout hook indentation * ci(fmt): exclude generated whitebox-menu-catalog from oxfmt The file is auto-generated by scripts/gen-whitebox-menu-catalog.mjs (its header says 'do not hand-edit'), so add it to .oxfmtrc.json's ignorePatterns alongside the other generated catalogs. * ci(fmt): run ruff check --fix before ruff format Lint autofixes (import sorting via I, unused-import removal via F) can change layout; the formatter must run last so a single pre-commit pass stabilizes the file. Matches the order recommended by ruff's docs and astral-sh/ruff-pre-commit. * Update .gitattributes Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> * style: auto-format (ruff + oxfmt) [pre-commit.ci] * Address Claude review feedback - docs/contributing.md: note that oxfmt also sorts package.json keys into its conventional order, so contributors expect key reordering (not just whitespace changes) in future package.json diffs. * Address Claude review feedback - .oxfmtrc.json: drop the "python/**" ignore. It was a no-op for .py/.ipynb (the hook's types_or never passes them, and *.ipynb is excluded globally) while wrongly hiding real JS/TOML under python/ from oxfmt; the generated python/src/geolibre/static stays excluded via "**/static/**". Format the newly covered python/pyproject.toml and python/src/geolibre/_frontend.js. - ruff.toml: delete the [lint.pycodestyle] max-line-length block; it equaled the top-level line-length, which is already the default, so behavior is unchanged. Reworded the E-rules comment that pointed at it. - docs/contributing.md: restore the 2-space list-continuation indent on the mixed-line-ending bullet so the inline code span isn't split oddly. --------- Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> Co-authored-by: Qiusheng Wu <giswqs@gmail.com> Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> | 1 month ago | |
chore: ignore Android Gradle cache (#2050) | 24 days ago | |
ci(fmt): add ruff + oxfmt auto-format workflow and config (#1327) * ci(fmt): add ruff + oxfmt auto-format workflow and config Adds a GitHub Actions workflow that runs ruff format/check on Python and notebooks and oxfmt on JS/TS/JSON/CSS/YAML/TOML, then pushes a style commit back to the branch. Mirrors the local pre-commit hooks so CI and local runs produce identical output. - .github/workflows/format.yml: pinned ruff==0.15.22, oxfmt@0.59.0 - ruff.toml: line-length 100, rules F/I/W/E, py310 target - .oxfmtrc.json: width 100, double quotes, lockfile/generated excludes - .pre-commit-config.yaml: local ruff + oxfmt hooks (offline-friendly) - .gitattributes: normalize all text to LF, explicit binary rules - docs/contributing.md: document the new formatting pipeline * ci(fmt): switch auto-format from GitHub Actions to pre-commit.ci pre-commit.ci authenticates via its own GitHub App, so the auto-format bot can push fixes back on fork PRs (where GITHUB_TOKEN is read-only). It only runs on PRs, so auto-formatted commits never bypass review. - Delete .github/workflows/format.yml - Add autofix/autoupdate config to .pre-commit-config.yaml; the ruff and oxfmt version pins there are now the single source of truth - Trim ignorePatterns comments in .oxfmtrc.json - Rewrite the coding-conventions section in docs/contributing.md * ci(fmt): address review feedback on ruff.toml and .gitattributes - .gitattributes: add explicit binary rules for *.icns, *.pbf, *.pmtiles (icon.icns, MapLibre glyph pbfs, osm.pbf fixtures, and mini.pmtiles are all tracked in-repo; closes the explicit-safety-net gap flagged in review) - ruff.toml: drop the misleading lines-after-imports = -1 (no-op default) and its comment; add .ruff_cache to extend-exclude so the intent survives any future change to ruff's default exclusion list - .gitignore: ignore .ruff_cache/ - .pre-commit-config.yaml: drop the redundant `files: \.(py|ipynb)$` regex (types_or already scopes the ruff hooks to python/pyi/jupyter) and fix the nbstripout hook indentation * ci(fmt): exclude generated whitebox-menu-catalog from oxfmt The file is auto-generated by scripts/gen-whitebox-menu-catalog.mjs (its header says 'do not hand-edit'), so add it to .oxfmtrc.json's ignorePatterns alongside the other generated catalogs. * ci(fmt): run ruff check --fix before ruff format Lint autofixes (import sorting via I, unused-import removal via F) can change layout; the formatter must run last so a single pre-commit pass stabilizes the file. Matches the order recommended by ruff's docs and astral-sh/ruff-pre-commit. * Update .gitattributes Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> * style: auto-format (ruff + oxfmt) [pre-commit.ci] * Address Claude review feedback - docs/contributing.md: note that oxfmt also sorts package.json keys into its conventional order, so contributors expect key reordering (not just whitespace changes) in future package.json diffs. * Address Claude review feedback - .oxfmtrc.json: drop the "python/**" ignore. It was a no-op for .py/.ipynb (the hook's types_or never passes them, and *.ipynb is excluded globally) while wrongly hiding real JS/TOML under python/ from oxfmt; the generated python/src/geolibre/static stays excluded via "**/static/**". Format the newly covered python/pyproject.toml and python/src/geolibre/_frontend.js. - ruff.toml: delete the [lint.pycodestyle] max-line-length block; it equaled the top-level line-length, which is already the default, so behavior is unchanged. Reworded the E-rules comment that pointed at it. - docs/contributing.md: restore the 2-space list-continuation indent on the mixed-line-ending bullet so the inline code span isn't split oddly. --------- Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> Co-authored-by: Qiusheng Wu <giswqs@gmail.com> Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> | 1 month ago | |
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> | 6 days ago | |
chore(release): v3.0.0 (#2378) | 1 day ago | |
fix: restore Windows sidecar connectivity (#2133) * fix: bypass WebView restrictions for desktop sidecar * ci: add temporary Windows sidecar test build * Address automated review feedback - Wait for the native sidecar transport before mounting the app. - Keep custom sidecar URL overrides on browser fetch. - Cover the exact native sidecar capability scope. * Address review feedback - Disable native redirects for sidecar requests (`maxRedirections: 0`). The per-launch `X-GeoLibre-Token` is attached before the transport runs, and reqwest replays custom headers across cross-host redirects, so a redirecting response could have leaked the token off-host. - Extract `createNativeSidecarFetch()` from `installNativeSidecarFetch()` so the transport is testable without stubbing the `@tauri-apps/plugin-http` dynamic import; add regression tests for the redirect block and the out-of-scope browser-fetch fallback. - Export `LOCAL_SIDECAR_URL` from `@geolibre/processing` and derive `NATIVE_SIDECAR_ORIGIN` from it instead of redeclaring the loopback literal in the desktop adapter. * Address Claude review feedback - Cover the multipart path through the native sidecar transport: assert the adapter hands a FormData body to Tauri's fetch by reference, so the `{ ...init, maxRedirections: 0 }` spread can never clobber the body that plugin-http's `new Request(...)` serializes into a multipart payload. * Address CodeRabbit review feedback - Bypass the system proxy for native sidecar requests. reqwest's Windows system-proxy reader copies the registry `ProxyOverride` list verbatim and does not expand the `<local>` entry Windows writes for "bypass proxy server for local addresses", so on a machine behind a corporate proxy every loopback sidecar request — body and `X-GeoLibre-Token` included — would have gone to that proxy, where WebView2 connected directly. Verified `system-proxy` is in the resolved feature set (tauri-plugin-http's default `macos-system-configuration` enables it) so this was reachable, not theoretical. The plugin has no "no proxy" switch; supplying a proxy clears reqwest's automatic lookup and `noProxy: "*"` stops the supplied one from intercepting. * Address Claude review feedback - Install the native sidecar transport on Windows only. The WebView2 CORS/Local Network Access restriction this PR works around is Windows-specific, but the install was unconditional for every Tauri build, so macOS and Linux — which reach the sidecar directly — also started paying the native client's cost. @tauri-apps/plugin-http serializes request bodies with `Array.from(new Uint8Array(buffer))` before sending them over Tauri IPC, which is proportional to body size and would tax mlSegment's image uploads on platforms that were never broken. - Add `isWindows()` beside the existing user-agent platform checks in `is-mobile.ts`, matching their signature and testing convention, so the gate needs no new plugin, Rust crate, or capability wiring. * Address review feedback - Derive the native fetch types from `typeof import("@tauri-apps/plugin-http").fetch` instead of re-declaring the subset this adapter uses, so a renamed or dropped plugin option fails `npm run typecheck` rather than drifting silently. The type-only import emits nothing, so web/embedded bundles still never pull in the Tauri HTTP plugin. - Document the two upstream *behaviors* the transport depends on in docs/maintenance.md, with the reqwest/hyper-util/tauri-plugin-http sources to re-check on a bump. `maxRedirections: 0` and the `noProxy: "*"` proxy bypass are implementation behavior, not API contracts; a unit test can pin the options passed but cannot exercise the Rust side, and both failure modes (a leaked token, a sidecar unreachable only for proxied users) are invisible in CI. * Address review feedback - List `@tauri-apps/plugin-http` in CLAUDE.md's bump-check bullet, so the maintenance note added in 57a7e021 is actually reached before a Dependabot bump changes behavior the compiler cannot check. * ci: remove the temporary Windows sidecar test build The workflow existed only to produce a portable Windows x64 artifact for verifying the WebView2 sidecar fix against discussion #2132. Its push trigger is scoped to this branch, so it would be dead config on `main` while leaving a `workflow_dispatch`-triggerable build duplicating `release.yml`/`test-build.yml`. The final artifact was built from b9016a6e (run 33115833612). | 18 days ago | |
docs: fix Title Case section headings and clone URL placeholder in docs/contributing.md (#1408) * docs: fix Title Case section heading and fork clone URL placeholder in CONTRIBUTING.md * docs: fix Title Case section headings and clone URL placeholder in docs/contributing.md --------- Co-authored-by: ferkans-amir <amir.rezaei@tu-berlin.de> Co-authored-by: giswqs <giswqs@gmail.com> | 1 month ago | |
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> | 13 days ago | |
chore: add MIT license (#202) Co-authored-by: giswqs <giswqs@users.noreply.github.com> | 3 months ago | |
docs: add three more video tutorials (#2303) Adds the Nepal floods mapping walkthrough, the cloud-native GIS workflows webinar, and the browser image georeferencing tutorial to the video tutorials page, with summaries, chapter lists, and sample-data links, plus the short link lists in the README, Getting Started, Tutorials index, and Demos. | 7 days ago | |
Add an optional Auth0 sign-in gate alongside Clerk (#1853) * feat(auth): add an optional Auth0 sign-in gate alongside Clerk Hosted deployments can already gate the web app behind Clerk. This adds Auth0 as an alternative provider, configured independently and loaded instead of Clerk, for deployments that already run an Auth0 tenant. Auth0 has no embedded sign-in card, so the gate uses Universal Login: the visitor is redirected to the tenant's hosted page and returned to the app. The URL they arrived on travels in the transaction's appState, so a shared `?project=...` link still opens its project after signing in, and the single-use `code`/`state` parameters are stripped from the address bar on return. Provider selection lives in one place (lib/auth-gate.ts) and follows the precedence the individual settings already use: a provider named in the deployment env beats one baked into the build, and naming both at the same level keeps Clerk so an existing gated image never switches on its own. The Docker entrypoint refuses to boot when both are passed at runtime, and validates the Auth0 pair together rather than serving an ungated app from a half configuration. Each provider stays in its own dynamically imported chunk, both excluded from the service-worker precache, so an ungated deployment downloads neither SDK. The gate remains a property of the build, not the request: the Tauri, mobile, and embedded builds are compiled without it, and `?embed=1` cannot switch it off. Also apply the initial theme in main.tsx when a gate is configured. The signed-out screen paints before <App /> mounts, and App is where useThemeMode adds the `dark` class, so a dark-mode visitor was shown a white sign-in screen that only flipped after signing in. This affected the existing Clerk gate too. Verified against the running app with the tenant endpoints stubbed: the sign-in screen replaces the app, /authorize carries the configured client_id, the registered redirect_uri and PKCE, the app renders with a clean URL once signed in, the account menu offers sign-out, a reload stays signed in without another trip to the tenant, and a deep link's query survives. An ungated build still boots straight to the map and requests neither SDK chunk. * Address CodeRabbit review feedback - auth-gate.ts: count either half of the Auth0 pair as naming Auth0 in the deployment env. The pair can be split across the runtime and build tiers, so a deployment supplying only the client ID at runtime had still named Auth0 there, but checking the domain alone handed it back to Clerk. Covered by a new precedence test. - auth0-auth.ts: decide whether to log an incomplete configuration from the raw environment values rather than the normalized ones. Two malformed values both normalize to undefined, so the loudest misconfiguration — nothing usable at all — was the one case that disabled the gate silently. The half/malformed test now covers both-malformed and each single-malformed value. - docs/getting-started.md: Allowed Web Origins takes the origin only, with no trailing slash and no path, unlike Allowed Callback/Logout URLs. The previous text told operators to paste the same trailing-slash URL into all three, which for a subpath deployment would break silent authentication. * Address Claude review feedback - Restore a deep link's query before the boot-time settings read it. Auth0 returns to the registered callback URL, so `?locale=`/`?theme=` were gone on the load that followed. `onRedirectCallback` put the address bar back, but it runs in a React effect — after `getInitialLanguage()` has already resolved the UI language at module scope during `import "./i18n"`. The query is now stashed before the redirect leaves and merged back in on the callback load, ahead of that read (lib/auth-return-url.ts, imported for its side effect above ./i18n). Confirmed with a negative control: with the restore disabled, a visit to `?theme=dark&locale=fr` comes back English. The theme self-healed already, since useThemeMode re-reads the URL when App mounts after the callback, but it now also avoids a light-to-dark flash. - Document the localStorage session cache for operators, and say in the code what bounds it: no API audience and no refresh token are requested, so the cached entry is a short-lived identity assertion that cannot be renewed. It does outlive the tab and is readable by same-origin plugin code, which the Auth0 section of the docs now states. * Restore the deep link on a refused login too The guard required both `code` and `state`, so it matched only a successful return from Auth0. A refusal — an Action calling api.access.deny(), a declined consent — comes back as `error`/`error_description` with `state` and no `code`, which fell through and discarded the stash. The theme and language the visitor asked for were then missing from the one screen that has something to tell them. Extracted the check as `isSignInCallback`, which now requires `state` (Auth0's CSRF token, present on every return) plus either `code` or `error`, and unit tested both returns along with the loads that must not qualify. Verified against the running app with the tenant stubbed to refuse: the denial lands on the error screen showing Auth0's own reason, with the requested language and theme applied. Note the auth.* strings themselves are English in every non-English catalog today — pre-existing, and equally true of the Clerk gate — so this restores the language, not yet the translation. * Address review feedback - Keep one login attempt out of the next. auth0-react cleans the callback URL only after a callback it accepted, so a refusal leaves `error` in the address bar; "Try again" then stashed it, the restore merged it into the next, successful callback, and the SDK rejected a login that had actually worked — a permanent lockout, since every retry re-armed it. The stash now strips Auth0's own parameters (CALLBACK_PARAMS, shared with onRedirectCallback so the list has one home). Confirmed with a negative control: without the strip the retry never reaches the map. - Correct what the localStorage session cache is claimed to be. Without `useRefreshTokens` the SDK still renews through silent authentication while the Auth0 session cookie answers, so "cannot be renewed" was wrong. The docs and the code comment now say no refresh token is stored and that renewal depends on that flow, which a browser blocking the cookie will fail. The missing API audience still means no upstream access on its own. - Log the normalized Auth0 host at boot, not the raw variable, so the line matches the host actually written to the runtime config and the CSP frame-src. Uses two scheme-specific sed expressions rather than a case-insensitive match, which is a GNU extension. | 1 month ago | |
Add persistent layer filters from Select by Expression (#2364) * feat(layers): add persistent expression filters * style: auto-format (ruff + oxfmt) [pre-commit.ci] * fix(layers): restore persistent vector filters * Address review feedback - Bound the clustered-filter cache in `authoredClusterInput` to the current filter only. Iterating on a filter against the same `geojson` object no longer retains one filtered copy of the dataset per expression tried (Claude + CodeRabbit). - Document that a persisted expression filter resolves the Expression Builder's `@` variables to literals at apply time, and that `["zoom"]` is the live alternative. Covered in `docs/project-format.md`, `docs/user-guide/styling.md`, and a comment at the substitution site. - Name both filter kinds in the Layers panel funnel tooltip when a layer has an expression filter and Quick Filters at once, via a new `selection.layerFilteredBothHint` string. - Add a map-controller test covering the bounded cache: an unchanged filter keeps a stable data reference, and switching filters re-derives the result. * Address Claude review feedback - Stop a previous layer's seeded filter expression from following the user to an unfiltered layer, where "Filter layer" would have persisted the wrong expression onto the wrong layer. The panel now tracks whether the textarea holds text seeded from a layer's saved filter or text the user authored: authored text still follows the target so it can be re-run elsewhere, a stale seed is cleared. Applied to both the open-time seeding effect and the target-layer dropdown. - Extract that decision into a pure `retargetExpressionSource` in the new `lib/expression-source.ts` (kept out of `expression-inputs.ts`, which pulls in the map runtime and so cannot be imported from a node test) and cover it with `tests/retarget-expression-source.test.ts`. - Refresh the stale "Clear filters" comment in `LayerPanel`: the action now deletes the persistent expression filter outright and only empties the Quick Filter answers. * Address CodeRabbit review feedback Make a clustered layer's pre-filter honour the live zoom. MapLibre clusters at the source, so `authoredClusterInput` narrows the source data rather than letting the renderer evaluate the filter. That evaluation ran without a zoom, i.e. at zoom 0, and nothing re-ran it, so a persisted `[">=", ["zoom"], 8]` filter hid the layer at every zoom forever -- while the same filter on an unclustered layer tracks the camera, and the docs added earlier in this branch point at `["zoom"]` as the way to stay live. - Evaluate the pre-filter at the map's current zoom and join that zoom to the cache key, but only for a filter that actually reads `["zoom"]`, so an ordinary filter keeps one cache entry across camera moves. - Attach a `zoomend` resync in `MapController` exactly while some clustered layer holds a zoom-dependent filter, and detach it when none does. When a zoom changes no outcome the pre-filter hands back its previous collection, so an attached listener does not re-cluster on every step. - Cover the threshold crossing and the listener's attach/detach in `tests/map-controller.test.ts`; give the two other clustering fakes a `getZoom`. * Address review feedback Never overwrite hand-authored expression text. Two reviewers found the same gap from opposite directions: retargeting at a layer that has a saved filter replaced whatever was in the textarea, so switching to an already-filtered layer discarded a typed expression (CodeRabbit), and re-running the open-time seed on an unchanged target discarded an in-progress edit (Claude). `retargetExpressionSource` now seeds the target's saved filter only into an empty textarea or over a previous seed; text the user typed or built is left alone. The other direction is unchanged: a seed still does not follow to a layer with no filter of its own. Four cases added to `tests/retarget-expression-source.test.ts`. * Correct the cluster-filter docs for source pre-filtering Both pages still said cluster bubbles and counts describe unfiltered data, which this branch made false for the two filters it pre-applies to a clustered layer's source: the persistent expression filter and Quick Filters. Say which filters reach the source and which stay per-feature render filters (Time Slider window, rule-based filter, embed `setFilter`), so the boundary is readable in both directions. * Address Claude review feedback - Stop Select by Expression swallowing a failed run. Both buttons are enabled from `validation`, memoized against the variable snapshot taken when the panel opened, while the panel is non-modal and both handlers re-check against the live camera before running. When the two disagreed the click was a silent no-op. A `runError` state now shows the compiler's message, on the filter path flagged in review and on the selection path that swallowed `matchFeaturesByExpression` the same way. - Say what the layer row's clear-filters action discards. Quick Filter controls survive with their values emptied, but a persistent expression is deleted outright, so with one active the item reads "Clear filters and saved expression" and takes the FilterX icon. * Address Claude review feedback - Stop `hasZoomDependentClusterFilter` scanning geometry on every sync pass. It ran `detectGeometryProfile` (O(features), no early exit) for every layer carrying `.geojson`, on every `syncLayers` call including ones with no filter in sight. The cheap filter test now comes first, so the scan is paid only by a layer that already has a zoom-dependent authored filter — which also skips it for clustered layers whose filter never reads the zoom. - Stop `expressionUsesZoom` mistaking a data value for the zoom operator. A categorical Quick Filter compiles to `["in", ["get", f], ["literal", vals]]`, so a field with a value of `"zoom"` was reported zoom-dependent, needlessly attaching a listener and varying the cluster cache key. It no longer walks into a `["literal", …]` payload, and requires the operator's own arity. - Cover both in `tests/zoom-dependent-cluster-filter.test.ts`, including a zoom operator sitting beside a literal that mentions it. * Address Claude review feedback - Reattach the two stacked JSDoc blocks in `LayerPanel`. Inserting `layerClearFiltersKey` above `layerFilteredHintKey` left the hint helper's doc orphaned in front of the new one, so neither described the function under it. - Persist the layer filter only once both compile passes have succeeded. `setLayerFilterExpression` ran before `matchFeaturesByExpression` was checked, so a failure there would have saved the filter while the panel reported an error. Unreachable today (both passes compile the same source with the same variables), but the ordering should not depend on that. * Show the filtered indicator in the read-only viewer A persistent expression filter reaches a shared link but renders no control in `?layout=viewer`, so a viewer saw a layer quietly missing features with nothing saying why. That is the same failure the funnel icon was added to the authoring panel to prevent, and this branch is what makes it reachable from a share. - Extract `layerFilteredHintKey` out of `LayerPanel` into `lib/layer-filter-hint.ts` so both panels label the icon identically, and cover it with `tests/layer-filtered-hint.test.ts`. - Render the funnel icon on a filtered layer's row in `ViewerLayerPanel`. Indicator only: the viewer stays read-only, with no clear action. - Say so in `docs/user-guide/styling.md`, which until now implied the viewer surfaced Quick Filters alone. * fix(layers): apply a restored layer filter to control-owned vector layers Reopening a project whose Add Vector Layer layer (a GeoParquet import, say) carried a persistent filter rendered the full dataset. The filter was saved and loaded correctly; it was never applied. A control-owned layer's MapLibre layers are created by the control itself, asynchronously, after the sync pass that follows the project load. `syncLayer` skips a native layer that is not on the map (`if (!nativeLayer) continue`), so the filter had nowhere to go. Restore then replays the layer into a store record identical to the saved one, so `syncVectorLayersToStore` finds nothing changed and issues no update, and `MapCanvas` only syncs on a `layers`/`layerGroups` change. No second pass ever ran. `hasPendingExternalNativeFilters` reports a layer whose filters have nowhere to be applied yet, and `MapController` watches `styledata` while that holds, waiting until every named native layer exists before spending one more sync. Nothing is attached for a layer with no filter, or once the layers have arrived. * Address Claude review feedback - Stop the row's clear-filters action requiring edit rights to empty Quick Filter answers. On main the item had no permission gate at all; folding the persistent expression into it gated the whole thing, so a read-only collaborator lost a view-narrowing action they previously had (and still have from `QuickFiltersSection` and the viewer rail). Only the expression half is gated now: a collaborator without edit rights clears the Quick Filter values and the authored expression stays, with the label and icon following what will actually be discarded. - Move the `filterExpression` section in `docs/project-format.md` below the `quickFilters` field reference, so that explanation stays contiguous with the JSON example it describes. --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Co-authored-by: giswqs <5016453+giswqs@users.noreply.github.com> | 1 day ago | |
docs: add a Web Services page to the user guide (#2251) * docs: add a Web Services page covering all 17 submenu plugins * docs: add screenshots to the Web Services page * docs: point Web Services screenshots at the published asset URLs * docs: drop the USGS NLDI nav entry The page stays reachable from the Web Services and Plugins pages, so it does not need its own User Guide nav slot. * Address CodeRabbit review feedback - Reword "Each one is documented on its own page" to say all seventeen browsers live on the single Web Services page. - Note that Web Services panels dock rather than float, so the blanket "you can also set their on-map position" sentence does not apply to them (Vantor Open Data and Planet Open Data included). * Address CodeRabbit review feedback Scope the "activated from the Plugins menu" sentence to the three tables it actually covers. Planetary Computer and Earth Engine are reached from the Processing menu and STAC from Add Data, so the blanket "all of these" was wrong for the top of the page. | 10 days ago | |
Add optional Mapbox rendering engine (#2389) * feat: add optional Mapbox rendering engine * Add Mapbox token shortcut to environment settings * Fix Mapbox zoom jumps from stale split-view camera feedback * Smooth Mapbox mouse-wheel zoom across successive ticks * Revert experimental Mapbox zoom fixes * Align Mapbox token settings with Cesium device-local credentials * Address review feedback - Clear a stored Mapbox source error once the source finishes loading (`sourcedata` with `content` + `isSourceLoaded`) and when its layer is removed, so the error banner and image capture recover (CodeRabbit) - Resolve `identifyFeatures` ids through the layer's own GeoJSON: Mapbox's `generateId` reports the source index, which is mapped back to `String(feature.id ?? index)` so selection and highlighting match (CodeRabbit) - Fall back to the field-based label text when a label expression is not valid JSON instead of dropping the whole layer (CodeRabbit) - Badge layers the Mapbox adapter cannot compile in the per-pane layer toggle and the primary layer panel via a new `isMapboxSupportedLayer` helper, with `mapGrid.noMapbox` / `renderer.layerMapboxUnsupported` keys in every catalog (Claude review) - Add `tests/mapbox-engine.test.ts`: behavior tests for `MapboxEngine` against a fake mapbox-gl map (sync diffing, error record, picking, camera and preferences) (Claude review) - Rename `bridgeVectorControlToCesium` to `bridgeVectorControlToStore` and document its renderer-agnostic scope (Claude review) - Document the Mapbox GL JS v3 license and account/data-collection terms in docs/mapbox-renderer.md and packages/map/README.md (CodeRabbit) - Use "çizim motoru" in the Turkish Mapbox token hint (CodeRabbit) - List `mapbox` as a renderer choice in the agent skill references (Claude review summary) * Address review feedback - Match Multi* geometry types in the Mapbox fill/circle filters and exclude MultiPoint from the line layer, so MultiPolygons get a fill and MultiPoints draw as circles (Claude review) - Clamp views against the project preferences in MapboxEngine.applyView and easeToView before the camera move, mirroring constrainMapView in the MapLibre engine, to avoid a one-frame snap (Claude review) - Update the shared mapView before the pane view in MapboxCanvas so a synchronized pane never jumps to a stale camera (CodeRabbit) - Fix the German mapboxTokenTitle copy-paste ("Mapbox-Token") (Claude review) - Drop the unused key parameter of addEnvironmentVariable (Claude review) - Add tests for the geometry filters, the label-expression fallback and the view clamp * Add label-free Mapbox Satellite basemap * Make background picker replace the active Mapbox basemap override * Address Claude review feedback - Fall back to the default basemap in a Mapbox pane when the resolved inline style uses a MapLibre-only source protocol (the offline PMTiles basemap's pmtiles:// source is registered with maplibre-gl only), via a new styleUsesUnsupportedSource helper, with a console warning, a doc note in docs/mapbox-renderer.md and a unit test * Address Claude review feedback - Update the View-menu visibility comment in TopToolbar to describe the generalized primaryRenderer !== "maplibre" guard (Cesium or Mapbox) * Address CodeRabbit review feedback - Include a GeoJSON source's string `data` URL in styleUsesUnsupportedSource so a MapLibre-only protocol there also triggers the Mapbox fallback - Redact credentials from the basemap fallback warning * Link the Mapbox token hint to Settings and address review feedback - Make "Settings → Environment variables" in the Mapbox token hint a button that opens the Settings dialog at the Environment section with the Mapbox token field focused (new "mapboxToken" focus target), so first-time setup is one click; the hint text now uses <Trans> with a <settingsLink> tag in every catalog - Translate the Mapbox token input placeholder via settings.env.mapboxTokenPlaceholder (Claude review) - Give each pane's rendering-engine trigger a pane-specific aria-label via mapGrid.renderingEngineLabel with {{number}} (Claude review) - Memoize isMapboxSupportedLayer per layer object with a WeakMap so the layer panels do not recompile every layer on every render (Claude review) * Address Claude review feedback and update the globe E2E spec - Move the dev-server WMS proxy helpers into a shared wms-proxy.ts and route Mapbox WMS raster tiles through it like the MapLibre path, so a WMS layer works in both panes under npm run dev (Claude review) - Add a why-disabled tooltip (renderer.pluginUnsupported) to plugin menu items greyed out for the active renderer, matching the command palette's disabledReason (Claude review) - Update e2e/cesium-globe.spec.ts to pick the pane renderer from the new rendering-engine dropdown; the per-pane 2D/3D toggle buttons it clicked were replaced by that menu in this PR * Address Claude review feedback - Move the basemap label-font resolver out of layer-sync.ts into a shared text-font.ts and have MapboxEngine resolve the font from the loaded basemap style on every style.load, passing it to compileMapboxLayer via a new textFont option; "Open Sans Regular" is now only the fallback for styles without a text symbol layer, so labels render on third-party basemaps whose glyph catalog lacks it | 20 hours ago | |
chore(release): v3.0.0 (#2378) | 1 day ago | |
3D globe: build a bare CesiumWidget, import the engine directly, and smoke-test the globe (#2208) * feat(cesium): build a bare CesiumWidget, import the engine directly, and smoke-test the globe Three parts of #2189: slim the globe's runtime, and give it the automated coverage it has never had. **CesiumWidget instead of Viewer.** The pane constructed `Cesium.Viewer` and then switched off ten widgets one by one. `Viewer` is a wrapper that builds the base-layer picker, geocoder, home button, scene-mode picker, help button, timeline, animation dial, fullscreen button, info box and selection indicator on top of `CesiumWidget`; constructing the widget directly skips building them and still exposes everything the pane uses (`scene`, `camera`, `canvas`, `imageryLayers`, `dataSources`, `terrainProvider`, `screenSpaceEventHandler`). The issue expected this to need a hand-wired `DataSourceCollection` + `DataSourceDisplay` on `scene.postUpdate`; modern `CesiumWidget` owns both, so `CesiumLayerSync` needed only a type change. The pane also drops its explicit `removeInputAction(LEFT_DOUBLE_CLICK)`: that gesture is a `Viewer` feature the widget never installs. **Import `@cesium/engine`, not `cesium`.** The class swap alone changed the bundle by zero bytes — `import("cesium")` pulls the barrel that re-exports `@cesium/widgets`, and that defeats tree-shaking, so Knockout and the widget chrome shipped regardless of which class was constructed. Importing the engine directly is what actually removes them: before 4,896,009 B (cesium-*.js) + 47,342 B (Cesium-*.js facade) after 4,593,633 B, no facade chunk saved 349,718 B (~341 KB, 7%) `BaseLayerPicker`, `InfoBox` and `knockout` are now absent from the chunk. The `cesium` package stays a dependency: copy-cesium-assets stages the runtime Workers/Assets from its prebuilt `Build/Cesium`, and the two versions are a matched pair. The linked stylesheet also narrows from the 32 KB `Widgets/widgets.css` to `Widgets/CesiumWidget/CesiumWidget.css`. **A keyless e2e smoke test.** `e2e/cesium-globe.spec.ts` splits the grid, toggles a pane to 3D, asserts the real engine mounts, then drags the globe and asserts the primary MapLibre pane's bbox moved — proving the whole camera round trip against the actual engine rather than a fake. It was written *before* the refactor so it guarded it. Running keyless in CI is also the standing guard on #2180: if the token gating returns, this fails at the toggle rather than silently skipping. It asserts nothing about tiles, so a runner with no egress still passes. `docs/maintenance.md` gains a `cesium` / `@cesium/engine` entry covering the asset copy, the `CESIUM_BASE_URL` contract, the stylesheet path, and the PWA globs — none of which fail the build when they drift. Verified: 7453 unit tests, all 48 e2e specs, lint and build clean; canvas sizing and the rendered globe checked by eye against the narrower stylesheet. * Address review feedback - Point `optimizeDeps.include` at `@cesium/engine` instead of `cesium` (Claude, Copilot — same finding). This commit's parent moved the globe's dynamic import to `@cesium/engine` and updated `manualChunks`, but left the dev-server pre-bundle list naming the wrapper. Nothing imports `cesium` as a module any more, so the list was optimizing the wrong graph: `@cesium/engine` would be discovered on first open of the globe, triggering exactly the full-page reload (and the raw-CJS `mersenne-twister` default import) the list exists to prevent. Verified on a cold cache (`rm -rf node_modules/.vite` + `npm run dev`): a marker set on `window` before opening the globe survives, so no reload occurs, the console is error-free, and Vite writes `.vite/deps/@cesium_engine.js`. * Address CodeRabbit review feedback - Correct the asset-copy failure mode in `docs/maintenance.md` (CodeRabbit). I wrote that a directory renamed upstream is "silently dropped"; `cpSync` throws `ENOENT` from `buildStart`, so that case stops the dev server and the build loudly. The genuinely silent case is the opposite: a runtime directory *added* upstream is not in `RUNTIME_DIRS`, so it is never copied and only shows up as a 404 at runtime. Reworded to say that. - Make the globe e2e deterministically keyless (CodeRabbit). The spec claimed to run keyless, but `vite.config.ts` bakes `CESIUM_TOKEN`/`VITE_CESIUM_TOKEN` into the build, so on a developer machine with a token it silently exercised the tokened path instead. `playwright.config.ts`'s webServer now blanks both for the build, and the spec asserts the tokenless hint is present — the one signal that separates the two paths. Verified both directions: with `CESIUM_TOKEN` set in the shell it passes with the override and fails on that assertion without it, so the assertion is not vacuous. - Exercise the 2D teardown the comment claimed (CodeRabbit). The spec asserted only that the "Show map 2 as a 2D map" button was visible while its comment described round-trip teardown coverage. It now clicks back to 2D and asserts the MapLibre pane mounts and the globe is gone, which actually runs CesiumCanvas's cleanup (`viewer.destroy()`, layer-sync and basemap teardown). * Address CodeRabbit review feedback - Correct the `CESIUM_BASE_URL` claim in `optimizeDeps` (CodeRabbit). The comment (pre-existing, carried over in this branch) asserted Cesium locates its Workers/Assets via the global "never `import.meta.url`". That is not true of the package: `buildModuleUrl.js` falls back to `getAbsoluteUri(".", import.meta.url)` when the global is undefined. Reworded to state the application invariant instead — this app always defines `CESIUM_BASE_URL` in `prepareCesiumEnvironment()` before importing the engine, which is what makes pre-bundling safe, and dropping the global would make this entry unsafe rather than merely change the paths. * Address review feedback - Raise the globe spec's own test budget (Claude). The per-assertion timeouts (60s for the engine chunk, 30s for the camera poll and the 2D toggle-back) could never be reached: `playwright.config.ts`'s `timeout: 60_000` caps the whole test, not each step, and `waitForMap` + the split + the toggle click spend part of it first. `test.setTimeout(180_000)` in the test body so the budgets it relies on are actually available on a cold runner. - Stop reusing an already-running server (CodeRabbit). With `reuseExistingServer: !process.env.CI`, Playwright skips the webServer command entirely when a server is up, so the `env` override was not applied and a run could silently test a stale or tokened bundle. Now `false`. Verified the failure mode is actionable rather than confusing: with a stray preview on 4173 Playwright reports "http://localhost:4173 is already used…" instead of quietly reusing it. - Stop overclaiming what the env override covers (Claude). It blanks the shell only: `vite.config.ts`'s bridge falls through to `loadEnv()` when the prefixed name is falsy, so a token in `apps/geolibre-desktop/.env.local` is read off disk and wins. Rather than assume, the config comment and the spec header now say so, and the tokenless-hint assertion carries a failure message naming that file — a tokened build fails loudly with the reason instead of silently exercising the wrong path. * Address Claude review feedback - Restore `reuseExistingServer: !process.env.CI` (Claude). Setting it unconditionally false in 29f1a6ed was a blanket change motivated by one new spec: it forced a full rebuild on every local `npm run test:e2e`, including runs iterating on the other 47 specs, and it contradicted this file's own header describing local reuse. It was also unnecessary — the stale/tokened build it guarded against is already caught by `cesium-globe.spec.ts`'s tokenless-hint assertion, and CI (where determinism matters) already has reuse off, so the env override always applies there. - Document the reuse/env interaction where it is load-bearing instead. The config now states that a reused server does not get `webServer.env`, and the spec header and its assertion message name both cases the override cannot reach — a `.env.local` token and a reused preview — with the fix for each. | 13 days ago | |
docs: migrate documentation build from MkDocs to Zensical (#442) * docs: migrate the documentation build from MkDocs to Zensical Mkdocs and Material for MkDocs are now in maintenance mode; the same team's successor is Zensical, which natively reads the existing mkdocs.yml. Switch the docs toolchain over: - Replace mkdocs-material with zensical in requirements-docs.txt - Build with `zensical build --strict` in the Pages workflow and the Netlify build (Zensical's default site_dir is already `site`) - Update contributing docs to use `zensical serve` / `zensical build` - Escape the `[::1]` IPv6 literal in plugins.md so Zensical's stricter link checker no longer treats it as an unresolved reference - Refresh the extra.css comment to note the selectors target Zensical's output Closes #441 * Address review feedback - Pin zensical to >=0.0.45,<0.1 in requirements-docs.txt so the docs build doesn't silently pull a future 0.0.x (or 0.1+) release that breaks the build. zensical is pre-1.0, so any minor/patch bump can be breaking; the lower bound matches the locally verified version and the upper bound caps within the tested 0.0.x series. Addresses the Claude and CodeRabbit comments on the unpinned dependency. * Address review feedback - Set site_dir: site explicitly in mkdocs.yml. zensical build has no --site-dir flag (only -f/-c/-s), but it honors the site_dir key from mkdocs.yml (verified: setting a temp value redirected the output). This pins the build target to site/, matching the hard-coded upload path in the Pages workflow and the netlify.toml publish dir, so a future 0.0.x default-dir shift can't silently upload an empty directory. Addresses the Claude comment about the implicit output directory. | 2 months ago | |
ci(fmt): add ruff + oxfmt auto-format workflow and config (#1327) * ci(fmt): add ruff + oxfmt auto-format workflow and config Adds a GitHub Actions workflow that runs ruff format/check on Python and notebooks and oxfmt on JS/TS/JSON/CSS/YAML/TOML, then pushes a style commit back to the branch. Mirrors the local pre-commit hooks so CI and local runs produce identical output. - .github/workflows/format.yml: pinned ruff==0.15.22, oxfmt@0.59.0 - ruff.toml: line-length 100, rules F/I/W/E, py310 target - .oxfmtrc.json: width 100, double quotes, lockfile/generated excludes - .pre-commit-config.yaml: local ruff + oxfmt hooks (offline-friendly) - .gitattributes: normalize all text to LF, explicit binary rules - docs/contributing.md: document the new formatting pipeline * ci(fmt): switch auto-format from GitHub Actions to pre-commit.ci pre-commit.ci authenticates via its own GitHub App, so the auto-format bot can push fixes back on fork PRs (where GITHUB_TOKEN is read-only). It only runs on PRs, so auto-formatted commits never bypass review. - Delete .github/workflows/format.yml - Add autofix/autoupdate config to .pre-commit-config.yaml; the ruff and oxfmt version pins there are now the single source of truth - Trim ignorePatterns comments in .oxfmtrc.json - Rewrite the coding-conventions section in docs/contributing.md * ci(fmt): address review feedback on ruff.toml and .gitattributes - .gitattributes: add explicit binary rules for *.icns, *.pbf, *.pmtiles (icon.icns, MapLibre glyph pbfs, osm.pbf fixtures, and mini.pmtiles are all tracked in-repo; closes the explicit-safety-net gap flagged in review) - ruff.toml: drop the misleading lines-after-imports = -1 (no-op default) and its comment; add .ruff_cache to extend-exclude so the intent survives any future change to ruff's default exclusion list - .gitignore: ignore .ruff_cache/ - .pre-commit-config.yaml: drop the redundant `files: \.(py|ipynb)$` regex (types_or already scopes the ruff hooks to python/pyi/jupyter) and fix the nbstripout hook indentation * ci(fmt): exclude generated whitebox-menu-catalog from oxfmt The file is auto-generated by scripts/gen-whitebox-menu-catalog.mjs (its header says 'do not hand-edit'), so add it to .oxfmtrc.json's ignorePatterns alongside the other generated catalogs. * ci(fmt): run ruff check --fix before ruff format Lint autofixes (import sorting via I, unused-import removal via F) can change layout; the formatter must run last so a single pre-commit pass stabilizes the file. Matches the order recommended by ruff's docs and astral-sh/ruff-pre-commit. * Update .gitattributes Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> * style: auto-format (ruff + oxfmt) [pre-commit.ci] * Address Claude review feedback - docs/contributing.md: note that oxfmt also sorts package.json keys into its conventional order, so contributors expect key reordering (not just whitespace changes) in future package.json diffs. * Address Claude review feedback - .oxfmtrc.json: drop the "python/**" ignore. It was a no-op for .py/.ipynb (the hook's types_or never passes them, and *.ipynb is excluded globally) while wrongly hiding real JS/TOML under python/ from oxfmt; the generated python/src/geolibre/static stays excluded via "**/static/**". Format the newly covered python/pyproject.toml and python/src/geolibre/_frontend.js. - ruff.toml: delete the [lint.pycodestyle] max-line-length block; it equaled the top-level line-length, which is already the default, so behavior is unchanged. Reworded the E-rules comment that pointed at it. - docs/contributing.md: restore the 2-space list-continuation indent on the mixed-line-ending bullet so the inline code span isn't split oddly. --------- Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> Co-authored-by: Qiusheng Wu <giswqs@gmail.com> Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> | 1 month ago | |
feat: add GeoLibre desktop MVP with Tauri, React, and MapLibre (#2) * feat: add GeoLibre desktop MVP with Tauri, React, and MapLibre Full monorepo scaffold with Tauri v2 desktop shell, MapLibre GL map canvas, layer/style panels, attribute table, plugin system, processing toolbox, shared UI components, and optional Python FastAPI sidecar. * fix: address Copilot review feedback - Fix waitAndSyncLayers accumulating style.load listeners on every call by removing the previous handler before re-adding - Remove tsconfig.tsbuildinfo and src-tauri/gen/ build artifacts from tracking and add them to .gitignore - Remove dead syncAllLayers function from layer-sync.ts - Change bundle targets from Linux-only to "all" for cross-platform builds - Replace alert() with Dialog component for layer metadata display - Fix file dialog filter extensions to use single-dot .geolibre extension * chore: update icon assets and configuration * feat: integrate maplibre-gl-layer-control for enhanced map functionality - Added maplibre-gl-layer-control as a dependency in package.json and package-lock.json. - Implemented layer control in MapController, allowing for dynamic layer management. - Updated MapCanvas to utilize the new layer control features and ensure proper style handling. - Introduced default projection enforcement and layer control addition/removal methods. * fix: update default map view settings - Changed default map center coordinates from [-98.5795, 39.8283] to [-100, 40]. - Adjusted default zoom level from 3 to 2 in both createDefaultMapView and MapController. | 3 months ago |
GeoLibre
一个免费开源、轻量级、云原生的 GIS 平台,用于地理空间数据的可视化、探索与分析。它可在你使用的任何环境中运行,包括网页浏览器、桌面、移动端和 Jupyter Notebook,同时确保数据保持本地且私密。
它还内置 1,000+ 个地理处理工具,均可基于 WebAssembly 完全在你的浏览器中 运行——涵盖地形、水文、LiDAR、遥感与矢量分析,无需服务器、无需安装,数据也从不离开你的设备。
GeoLibre 基于 Tauri v2、React、TypeScript、MapLibre GL JS、DuckDB-WASM Spatial 和 deck.gl 构建。同一套工作区可运行于原生桌面应用、原生 Android 与 iOS 应用以及任意现代网页浏览器,并能响应式适配移动端和小屏幕。
- 启动 GeoLibre 网页版 ——完整应用就在浏览器中,无需任何安装
- 下载桌面应用 ——提供 Windows、macOS 和 Linux 安装包
- 在 Mac App Store 获取 ——沙盒化的 macOS 版本
- 在 App Store 获取 ——适用于 iPhone 和 iPad 的原生 iOS 应用
- 在 Google Play 获取 ——原生 Android 应用
- 获取 Chrome 扩展 ——在 GeoLibre 中打开你在任意网页上发现的数据集
- 使用 Python 包 ——在 Jupyter Notebook 中嵌入并控制完整应用
- 使用 R 包 ——在 RStudio、Quarto、R Markdown 和 Shiny 中构建交互式地图
- 1,000+ 地理处理工具 ——完整工具箱,尽在浏览器
- 快速上手 ——安装、从源码运行以及配置
- 功能 ——完整功能列表
演示
点击任意截图以原图分辨率打开,或点击任意动画播放全画质视频。
3D Tiles
纽约市建筑与地铁
曼哈顿建筑轮廓以三维方式拉伸,并按建造年代着色;上方叠加 MTA 地铁线路与站点,图例根据图层符号化自动生成。
下方动画让 Time Slider 沿建筑建造年代从 1850 年运行到 2025 年,使曼哈顿按年代逐渐显现。点击播放全画质视频。
行星底图
GeoLibre 不仅限于地球。来自 OpenPlanetaryMap 和 USGS Astrogeology 的行星底图覆盖月球、火星、水星、金星、伽利略卫星(木卫一、木卫二、木卫三、木卫四)、土卫六、冥王星和卡戎,并支持按项目指定椭球体,使距离、面积和比例尺量测与当前映射的天体相匹配。每个天体模型背后的深空星空来自 Atmosphere Effects 插件。
![]() |
![]() |
![]() |
| 地球 | 月球 | 火星 |
![]() |
![]() |
![]() |
| 水星 | 冥王星 | 金星 |
![]() |
![]() |
![]() |
| 木卫二 | 木卫四 | 卡戎 |
可通过图层面板中的天体切换器切换天体。更多示例见演示。
视频教程
- GeoLibre 1.0:可运行于任何环境的免费开源云原生 GIS(浏览器、桌面端和 Jupyter)
- 浏览器中的地理处理:GeoLibre 中的 700+ 免费 GIS 工具,零安装
- 在浏览器中免费获取高分辨率灾害卫星影像
- 使用 GeoLibre 在浏览器中规整化建筑物轮廓
- GeoLibre + GeoLens:用于自托管地理空间数据的现代 GIS 技术栈
- 使用 GeoLibre 模型构建器和 AI 助手创建可复用的 GIS 工作流
- 使用免费高分辨率卫星影像绘制 2026 年尼泊尔洪灾地图
- 使用 GeoLibre 构建云原生 GIS 工作流
- 在浏览器中使用 GeoLibre 进行影像地理配准
上述视频均配有章节和摘要,并收录于视频教程页面。
地理处理:1,000+ 工具,零安装
Processing → Whitebox Toolbox 打开一个包含 1,000+ 地理处理工具 的工具箱, 这些工具通过 WebAssembly 运行时在浏览器中执行,支持原生栅格与矢量 I/O。 无需安装 Python 辅助组件,也无需调用服务器——工具、你的数据和结果都保留在本机, 因此完整工具箱在 GeoLibre Web、桌面应用和 Android 上均可使用。
这些工具来自 Whitebox Next Gen 工具包,以及 GeoLibre 自研的 WASM 工具,并可直接从 Processing 菜单按类别浏览:
| 类别 | 工具数 | 示例 |
|---|---|---|
| 矢量 | 313 | 叠加、缓冲、连接、清理、拓扑、综合 |
| 栅格 | 256 | 代数、滤波、重分类、区域统计和焦点统计 |
| 遥感 | 154 | 光谱指数、波段运算、分类、变化检测 |
| 水文 | 100 | 流量累积、流域、水系网络、洼地填充 |
| 地形 | 99 | 坡度、坡向、晕渲、曲率、崎岖度、通视 |
| LiDAR | 65 | 点云滤波、地面分类、DEM/DSM 生成 |
| 转换 | 49 | 转换为云原生 GeoParquet、PMTiles 和 COG 格式 |
| 网络 | 26 | 连通性、成本距离和路径分析 |
| 投影 | 4 | 栅格和矢量数据的重投影 |
任何工具都可通过 ?tool= URL 参数直接链接,该参数会预先选择该工具并填充其表单。
详情请参见处理工具指南,
并观看浏览器中的地理处理视频演示。
文档
完整文档,包括用户指南和教程,发布在 geolibre.app。
- 快速开始 - 在网页、桌面、Android、iOS 或 Jupyter 中使用 GeoLibre;从源码运行;使用 Docker 运行;并配置可选凭据。
- 功能 - 逐项列出 GeoLibre 当前可实现的完整功能清单。
- 演示 - 可视化导览:3D Tiles、3D 城市数据、行星底图、SQL 工作台以及嵌入。
- 下载 - Windows、macOS 和 Linux 的安装程序与包管理器。
- 用户指南 - 涵盖界面、数据添加、图层、样式、属性表、地图控件、处理、SQL 工作台、数据集成、插件、设置和嵌入的逐项功能参考。
- 教程 - 端到端的实操工作流:第一张地图、云原生数据、矢量分析、地形分析、空间 SQL,以及分享和嵌入。
- 参考
- 架构
- Android
- iOS
- 项目格式
- 插件 API
- UI 配置文件
- 国际化
- Python 包(Jupyter) — 同样支持
from geolibre import DashMap用于 Dash(安装geolibre[dash]) - R 包(RStudio、Quarto 和 Shiny)
- Notebook 面板
- 路线图
- 贡献指南
- 引用方式
- 成为赞助者
欢迎贡献。请参阅 贡献指南 了解开发环境、仓库结构和质量门禁。
赞助
GeoLibre 免费开源,并将保持这一状态。如果您或您的团队认为它有用,赞助是支持开发、托管和跨平台分发持续开展的最直接方式。
- GitHub Sponsors - 可每月或一次性付款,通过您的 GitHub 账户结算。
- Buy Me a Coffee - 快速的一次性赞助,无需账户。
请参阅 成为赞助者 页面,了解赞助支持的内容以及其他免费参与方式。
致谢
GeoLibre 建立在地理空间与 Web 领域免费且开源的社区之上——包括 MapLibre GL JS、deck.gl、DuckDB-WASM Spatial、Turf.js、Tauri、React,以及许多其他项目。关于完整的项目与社区贡献者列表,请参阅致谢页面。
- Atmosphere Effects 插件(深空背景、视差星空、彗星以及地球大气光晕)借鉴并改编了 Leonel Dias 的文章 Globe atmosphere, halo, and comets 中的技术与视觉设计——包括分层 Canvas 2D 方案、光晕渐变与“screen”混合模式、用于在俯仰时保持光晕对齐的临边采样,以及星空/彗星参数。
- 社区贡献者——感谢 Ryanphoenix 做出的许多宝贵贡献,包括问题报告、反馈和改进。
- Beta 测试人员——感谢 René van der Velde(荷兰)进行早期测试、提交详细缺陷报告以及提出功能请求。
引用
如果你在研究中使用 GeoLibre,请引用它。GeoLibre 已归档于 Zenodo,每次发布都会生成一个 DOI。下方的概念 DOI 始终指向最新版本。
Wu, Q. (2026). GeoLibre: A lightweight, cloud-native GIS platform for visualizing, exploring, and analyzing geospatial data. Zenodo. https://doi.org/10.5281/zenodo.20785400
你也可以使用 GitHub 的 “Cite this repository” 按钮(该按钮会读取 CITATION.cff)复制现成的 APA 或 BibTeX 条目。更多格式请参阅 如何引用 页面。












