| 文件 | 最后提交记录 | 最后更新时间 |
|---|---|---|
docs: document and brand the download mirror (#2055) | 1 个月前 | |
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 | 17 天前 | |
chore(deps): patch npm audit advisories (#2417) * chore(deps): patch npm audit advisories Update browserslist, baseline-browser-mapping, hono, qs and js-yaml to their patched releases, and override the fflate that @loaders.gl/compression pins (0.7.4, unused by its code) to ^0.8.3. js-yaml now installs 4.3.2/3.15.2, so its audit allowlist entry is dropped. The image-size entries stay, with a refreshed rationale: the advisories still list no patched version, and the new 2.0.3/2.0.4 npm releases have no matching source upstream and break texture-compressor's API. * chore(deps): patch image-size via a texture-compressor override image-size 2.0.3 (codeberg.org/image-size/image-size e6e83a5578) fixes the ICNS/JXL/HEIF infinite loops behind GHSA-w3rx-r6r6-pgpr and GHSA-5p2g-fcmc-qvqq. Override texture-compressor's image-size to ^2.0.4 and drop both allowlist entries, leaving npm audit at 0 vulnerabilities. npm 12 omits an override-only package that no other dependency installs, so the lockfile entry is written by hand; docs/maintenance.md records that and the texture-compressor CLI API break (unused by GeoLibre). * Address Claude review feedback - Simplify the now-empty audit ALLOWLIST from `new Map([])` to `new Map()`. * Address CodeRabbit review feedback - docs/maintenance.md: the fflate override does not rewrite the declared range in package-lock.json (it still records `fflate: 0.7.4`); say that npm ls checks against the override's range instead. | 13 天前 | |
docs: document and brand the download mirror (#2055) | 1 个月前 | |
fix(build): keep build-machine credentials out of redistributable builds (#2130) * fix(build): keep build-machine credentials out of redistributable builds An artifact we hand to someone else -- the Jupyter wheel above all -- must carry no credential of ours. Three properties of this build made that easy to get wrong: 1. vite.config.ts bridges bare shell vars into their VITE_ names (GOOGLE_MAPS_API_KEY, MAPBOX_TOKEN, CESIUM_TOKEN), so the build machine's shell is build input. 2. Something in the graph reads `import.meta.env` as a whole object, so Vite stops replacing per key and inlines the entire env record into every chunk that read reaches. Ours was packages/core/src/runtime-env.ts; the one we cannot fix is @clerk/shared's getEnvVariable.mjs, which does `import.meta.env[name]` with a computed name -- so the inlined record lands in ClerkGate-*.js. 3. hatch_build.py skips the JS build when static/app already exists and GEOLIBRE_FORCE_JS_BUILD is unset, so `python -m build` packages whatever an earlier `npm run build:embed` left staged, with no JavaScript running at all. Changes: - vite.config.ts gains BUILD_ENV_KEYS (allowlist of VITE_ names that may reach a bundle) and CREDENTIAL_ENV_KEYS (the subset that bills to whoever built). pruneBuildEnv() deletes unrecognized VITE_ vars from process.env and blanks credentials in redistributable builds, before Vite reads it -- so a whole-object read has nothing sensitive to inline, including Clerk's. The web deploy is unaffected and keeps its own referrer-restricted keys. - runtime-env.ts reads the allowlisted __GEOLIBRE_BUILD_ENV__ define instead of import.meta.env, and exports getBuildEnvironment() for the five call sites that took a whole-object dependency. - scripts/scan-credentials.mjs verifies build OUTPUT, wired into build-embed.mjs (before staging) and hatch_build.py (before packaging any wheel/sdist, needing no Node -- the path (3) above that no JS-side guard can cover). Both read scripts/credential-patterns.json so they cannot drift; it is force-included into the sdist so sdist -> wheel is gated too. - CesiumJS's own public default Ion token is hash-allowlisted so the guard does not fire on every release. Verified: an embed build on a machine with those shell vars set now emits VITE_*:"" for every credential and no raw token shapes; the web build is unchanged; a planted key makes `python -m build` refuse. Stripped credentials still resolve at runtime through getRuntimeEnvironment()'s Settings -> Environment variables overlay. * Address review feedback - pruneBuildEnv() now covers `.env*` files, not just `process.env`. Only 4 of the 11 credential keys have a bare->prefixed shell bridge; the other 7 are documented in getting-started.md as `.env.local` entries. A key set only in a file was never in `process.env` at prune time, so the loop skipped it and Vite's own loadEnv then resolved the raw value into `import.meta.env` -- reaching the @clerk/shared computed lookup in a redistributable build. Confirmed by planting a file-only VITE_STADIA_API_KEY: it was inlined verbatim, and only the output scan caught it. The sweep now unions process.env and FILE_ENV keys, and blanks rather than deletes file-backed ones, because process.env wins over a .env value only when the key is present (including as ""). The __GEOLIBRE_BUILD_ENV__ record falls back to FILE_ENV too, so a .env.local-configured var still reaches getBuildEnvironment(). - CREDENTIAL_ENV_KEYS is now read from scripts/credential-patterns.json instead of being a second hand-maintained copy, so the name this config strips and the name the scanners look for cannot drift. - The Google API key pattern ends with a lookahead instead of `\b`. Its charset includes `-`, and `\b` needs a word character on the left, so a key ending in `-` did not match before a quote and both scanners missed it. Verified in both JavaScript RegExp and Python re; regression fixtures added on both sides. - _load_patterns() raises instead of returning None. It previously made scan_for_credentials return [] and report "Credential scan clean.", which is indistinguishable in the build log from a genuine clean scan -- failing open, the opposite of the documented intent. - Added python/tests/test_credential_scan.py (9 tests), including one that runs both scanners over the same fixture and asserts identical output. The Python scanner previously had no coverage. The hatchling import in hatch_build.py is now guarded so the scanner is importable standalone. | 1 个月前 | |
fix(docker): build and serve JupyterLite in the official image (#1852) * fix(docker): build and serve JupyterLite in the official image Processing > Jupyter Notebook opened a second copy of the GeoLibre map instead of a notebook in the Docker image. Two independent faults, both of which had to be fixed for the panel to work. 1. The site was never built. The build stage was node:22-alpine, which has no Python, so the `prebuild` hook (scripts/build-jupyterlite.mjs) could not find the `jupyter lite` CLI. That script is best-effort: it warns and exits 0, the build succeeds, and apps/geolibre-desktop/public/jupyterlite/ is git-ignored, so a clean CI checkout never had it either. nginx then answered the panel's iframe URL through its SPA fallback (`try_files $uri /index.html`) with the app, which is the duplicated view users saw. This is the same failure GeoLibre #1658 fixed for the Mac App Store build, where Tauri's asset resolver does exactly what nginx does here. 2. The CSP blocked it even when present. JupyterLab bootstraps from an inline <script>, which the app policy forbids. Under it the site loads its HTML and then never boots: no shell, no launcher, one "Executing inline script violates..." violation and no other symptom. Changes: - Dockerfile: build stage moves to node:22-bookworm-slim and installs Python in a venv, so the JupyterLite dependencies resolve to manylinux wheels rather than compiling against musl. Only dist/ is copied into the runtime image, so the larger builder does not affect the shipped image. Sets GEOLIBRE_JUPYTERLITE_REQUIRED=1 and asserts dist/jupyterlite/lab/index.html exists, so this cannot regress silently again. - scripts/build-jupyterlite.mjs: generalises the existing MAS hard-failure into GEOLIBRE_JUPYTERLITE_REQUIRED, for any build that serves the site and so cannot degrade. MAS behaviour is unchanged. - docker/nginx.conf: a `location ^~ /jupyterlite/` block giving the site the app policy plus 'unsafe-inline' for script-src only, scoped to that prefix so the app keeps forbidding inline script. It also drops the SPA fallback there (`try_files $uri $uri/ =404`), so a missing site 404s instead of silently returning the app. - docs/notebook.md: the claim that the panel "shows a not built message" was wrong, which is part of why this went unnoticed. It documents the real behaviour and the new flag. Verified against real containers, not just the source: - Published ghcr.io/opengeos/geolibre:latest serves /jupyterlite/lab/index.html as <title>GeoLibre</title> containing id="root" - the bug, in the shipped artifact. - The image built from this branch serves <title>JupyterLite</title>, and driving the running container through Processing > Jupyter Notebook in Playwright shows a real JupyterLab shell and launcher, not a second app, in both light and dark themes, with zero CSP violations. - The /jupyterlite/ response carries the relaxed script-src while / keeps the strict one, confirming the relaxation is scoped. - nginx -t passes on the rendered template. - The required flag exits 1 with the CLI absent, exits 0 when unset, and MAS still exits 1. Fixes #1851 * Address review feedback - docker/nginx.conf: give the content-hashed JupyterLite assets back the immutable cache policy the `^~ /jupyterlite/` prefix match took from them (Claude review). A nested regex location matches only filenames carrying a >=7-hex content hash, measured against a real `jupyter lite build`: 276 of 355 static files, ~12 MB of a 58 MB site. Matching the hash rather than the extension is deliberate -- the build also emits ~80 stable-named files (service-worker.js, lab/bundle.js, bootstrap.js, the bundled themes, the MathJax/FontAwesome fonts) that must keep revalidating, for the same reason the `location = /sw.js` block exists. The pattern is quoted because nginx otherwise reads `{7,}` as a block delimiter and refuses to start. - docker/nginx.conf: redirect a bare `/jupyterlite` to `/jupyterlite/` so a hand-typed path cannot fall through to the SPA fallback and reproduce #1851 (Claude review, quality note). `absolute_redirect off` keeps the Location relative so a TLS proxy in front of the container is not downgraded. - docs/notebook.md: the duplicated-app failure mode is no longer what this image does -- the new block 404s instead. Attribute the duplicate to Tauri's asset resolver and SPA-fallback hosts, and document the Docker 404 and the cache split alongside the scoped CSP (CodeRabbit). Verified by rendering docker/nginx.conf into a real nginx (1.31.3) serving a real `jupyter lite build` output: `nginx -t` passes; a hashed chunk returns `public, max-age=31536000, immutable`; service-worker.js, build/lab/bundle.js and the theme CSS return `no-cache, must-revalidate`; a missing path 404s; `/jupyterlite` 301s to `/jupyterlite/`; `/` keeps the strict app CSP. Booted the site in headless Chromium through that config: JupyterLab shell and launcher render, `isSecondCopyOfApp: false`, 0 CSP violations, 63 of 86 requests now immutable (previously 0). * Address Claude review feedback - docker/nginx.conf: the nested hashed-asset location dropped the relaxed Content-Security-Policy from those responses, since add_header stops inheriting once a block sets any header of its own. Replace the nested location with a `map $uri $geolibre_jupyterlite_cache` and a single per-request `add_header Cache-Control $geolibre_jupyterlite_cache` in the /jupyterlite/ block. Every response under the prefix now carries all four headers, hashed assets still get the immutable policy, and the long CSP string is not duplicated where the two copies could drift apart. Re-verified against real nginx serving a real `jupyter lite build`: `nginx -t` passes with the map at http level (entrypoint.sh renders this file into conf.d/, which Debian's nginx includes inside `http`); a hashed chunk returns `public, max-age=31536000, immutable` *and* the relaxed CSP; service-worker.js, build/lab/bundle.js and the FontAwesome fonts return `no-cache, must-revalidate` with the same CSP; a missing path still 404s; `/` still returns the strict app policy with no 'unsafe-inline'. JupyterLab still boots in headless Chromium through the config: shell + launcher, 0 CSP violations, 63 of 86 requests immutable. | 1 个月前 | |
feat(plugins): add a US Federal GIS web service for federal agency portals (#2699) * feat(plugins): add a US Federal GIS web service for federal agency portals Add a "US Federal GIS" plugin under Plugins > Web Services that browses, searches, and adds data from the public GIS portals of 24 US federal agencies: pick a department, then an agency. It is another createArcGisHubPlugin() instance next to US State GIS and US Local GIS. Agencies with an ArcGIS Hub site (Forest Service, BLM, NPS, FWS, FEMA, BTS, USACE, EIA, HUD, FAA, NIFC, NAIP) are searched through the site's catalog groups; those without one (USGS, NOAA, EPA, NRCS, NASA, Census Bureau, NGA, GeoPlatform) are searched across their ArcGIS Online organization. Map and image services are included, so NOAA radar, LANDFIRE and land cover rasters, and NAIP add as raster layers. TIGER boundaries live under Multi-agency > Esri U.S. Federal Datasets, since the Census Bureau's own organization publishes mostly statistics. HIFLD Open is left out: its Hub site no longer exists. Every agency was verified live, and `npm run check:gis-portals -- federal` re-checks the list (not part of CI). Closes #2698 * Address Claude review feedback - Say 24 federal agencies in the web-services summary table, matching the catalog, instead of "about 25". * Place US Federal GIS above US State GIS List the three US tiers top-down in Plugins > Web Services (federal, state, local): move US Federal GIS ahead of US State GIS in the plugin registration order that drives the submenu, the web-service id list, the UI profile, and the web-services guide. The State GIS placement test now expects it right after US Federal GIS. | 2 天前 | |
docs: split the maintenance notes out of CLAUDE.md (#2120) * docs: split the maintenance notes out of CLAUDE.md CLAUDE.md had grown to 30 KB, most of it rules that only matter when bumping a dependency or touching CI and release plumbing. Those move to docs/maintenance.md, kept verbatim, so the file agents read on every task stays focused on repo shape, commands, and architecture. * Address Claude review feedback - docs/maintenance.md: restore the note that Whitebox translations ship as external language packs rather than generated locale entries, dropped when the Processing menu catalog bullet moved out of CLAUDE.md. - CLAUDE.md: restore the consequence clause on the MCP paragraph, so the reason publish-python.yml must install the mcp extra stays recorded. | 1 个月前 | |
feat(arcgis): add the ArcGIS Maps SDK for JavaScript as an optional CDN-loaded rendering engine (#2423) * feat(arcgis): add the ArcGIS Maps SDK for JavaScript as an optional CDN-loaded rendering engine Adds "arcgis" as a fourth renderer next to MapLibre, Mapbox and Cesium. Nothing is bundled: the engine imports the SDK's ES modules from Esri's versioned CDN at runtime (packages/map/src/arcgis-sdk.ts), so dist/, the wheel and the installers stay the same size. - arcgis-layers.ts compiles store layers into SDK layer plans; GeoJSON symbology is evaluated per feature with the style-spec engine and baked into a unique-value renderer, filters and labels included. XYZ/WMTS templates, WMS GetMap templates, vector tiles, ArcGIS services and image overlays have translations. - arcgis-engine.ts implements MapEngine: camera (rotation vs bearing), layer sync and ordering, basemap translation via the shared cesium-imagery catalog plus Esri basemap styles when an API key is present, hit-test identify, highlight, extent drawing, manual placement, capture, and the SDK widgets as built-in controls. - ArcgisCanvas.tsx owns construction and the store subscription; PrimaryArcgisCanvas hosts it in the shell and split panes. - Device-local ArcGIS API key under Settings → Environment Variables, ARCGIS_API_KEY bridge in vite.config.ts, getArcgisApiKey in core. - Add Data greys out sources without an adapter; layer panels badge "No ArcGIS". - CSP allow-lists js.arcgis.com in tauri.conf.json and nginx.conf; the service worker caches the SDK for offline reuse. - Python, MCP, embed and agent-skill renderer lists accept "arcgis"; project format carries preferences.map.arcgisBasemap. - Tests: arcgis-layers, arcgis-engine (fake SDK), arcgis-renderer; opt-in e2e/arcgis-renderer.spec.ts; translations for every locale; docs/arcgis-renderer.md and a maintenance note. Refs #2421 * fix(arcgis): use the view's own attribution and stop rebuilding layers on opacity changes - Drop the deprecated Attribution widget: the 5.x view draws attribution itself while `attributionVisible` is on (Esri requires it), so the engine sets that instead of mounting a widget, which also removes the deprecation warning. - Ignore the SDK's "Aborted" layer-view errors and errors for layers the engine has already removed: a restyle rebuilds the native layer and the outgoing one's cancelled load was surfacing in the error banner once per slider tick. - Compile vector-tile styles at full opacity and visible so the VectorTileLayer's native opacity/visibility carry those, and an opacity change no longer rebuilds the layer. - `fullExtent` is read-only on VectorTileLayer; only tile and WMS layers receive it. Refs #2421 * Address CodeRabbit review feedback - useArcgisApiKey: read the device-local key directly as a fallback so an ArcGIS pane that mounts with the app gets the saved key before the first runtime-env projection (which fires no change event); the resolver still wins once it answers. - vite.config.ts: give the ArcGIS SDK its own `geolibre-arcgis-sdk` CacheFirst cache scoped to the versioned `/5.1/` path, so its few hundred modules cannot evict the other CDN engines from the shared 400-entry cache. - DesktopShell / e2e: anchor the file-drop target with `data-testid="desktop-shell"` and fail loudly in the spec when it is missing instead of falling back to `document.body`. - arcgis-sdk.ts: reject HTTP error responses before installing the stylesheet, and tag each theme request with a generation so a stale fetch can neither overwrite a newer theme nor clear its pending state. - ArcgisCanvas: accept a translated `closeLabel` for the identify popup's close button; the desktop caller passes `t("common.close")`. - test_map.py: round-trip a mixed 1x3 layout so a MapLibre pane survives beside the ArcGIS primary and pane. * Address CodeRabbit review feedback - Allow `https://js.arcgis.com/` (and `data:`) in `font-src` in both the Tauri and nginx content-security policies: the SDK's inlined theme stylesheet loads its Avenir Next and icon WOFF2 files from the CDN, which `default-src 'self'` alone would block. Docs updated to match. * Address CodeRabbit review feedback - docs/arcgis-renderer.md: name the dedicated `geolibre-arcgis-sdk` service-worker cache instead of the shared `geolibre-cdn-engines` rule the SDK no longer uses. * Address CodeRabbit review feedback - docs/arcgis-renderer.md: limit the offline claim to the cached SDK bootstrap; remote basemaps and services still need the network. * Address Claude review feedback - arcgis-layers.ts: translate a FeatureServer layer's filters (quick filters, expression, time and embed filters, rule visibility) into an SQL `definitionExpression` via `filterToSql`; a filter with no SQL form is flagged on the plan and the engine reports that the service draws unfiltered instead of staying silent. - arcgis-layers.ts / arcgis-engine.ts: place georeferenced images by their four corners through a `ControlPointsGeoreference` (image pixel size read on load) instead of collapsing them to an axis-aligned extent, so rotated and skewed fits match MapLibre. - arcgis-engine.ts: destroy the outgoing custom basemap on every swap, including into a named Esri style, and re-apply the stored basemap visibility/opacity once a named style's layers have loaded; a style that fails to load is reported. - arcgis-sdk.ts: redact `token=`/`apiKey=`/`api_key=` values without a leading `?`/`&` as well. * Address CodeRabbit review feedback - docker/nginx.conf: drop the Esri CDN from the JupyterLite prefix's CSP (script-src and the font-src directive added for it). The in-app notebook is an iframe whose client drives the parent GeoLibre document over postMessage, so the ArcGIS SDK only ever loads under the main application policy, which keeps its entries. * Address Claude review feedback - arcgis-layers.ts / arcgis-engine.ts: point markers now render. A Style-panel marker (shape or custom SVG) compiles to a placeholder carrying its per-class colour and scale; the engine bakes the same sprites MapLibre uses (`renderMarkerCanvas`) into `picture-marker` symbols and swaps them in, drawing the circle fallback until then. A KML feature's own icon becomes a `picture-marker` directly. - arcgis-engine.ts: `identifyFeatures` performs a real synchronous query for any coordinate by testing the store's GeoJSON geometry (point-in-polygon, pixel-tolerance for points and lines, filters and visibility honoured), so the scripting, notebook and command bridges get answers; the cached hit-test result still serves the clicked location. - arcgis-engine.ts / ArcgisCanvas.tsx: the async hit test bails out when the engine was destroyed mid-flight, guards the post-await `toMap`, and the canvas catches a rejection instead of leaving it unhandled. - Tests for the marker placeholder and KML icon plans, the geometry hit tests, the synchronous identify, and a hit test that outlives the engine. * Address Claude review feedback - scripts/credential-patterns.json: list `VITE_ARCGIS_API_KEY` so redistributable builds strip it and the credential scan covers it, like the sibling tokens. - store.ts: a basemap pick clears the pinned ArcGIS style whenever any pane draws with ArcGIS, not only the primary, so split panes follow the picker (Blank included). - arcgis-engine.ts: identify falls back to the hit graphic's own geometry (converted from the view's spatial reference) and object id for service layers whose features never live in the store. - arcgis-engine.ts: GeoJSON layers declare their three synthetic fields with explicit widths instead of letting the SDK infer them from sampled features, so long labels are not truncated. - arcgis-sdk.ts: redact both Esri key prefixes (`AAPK` legacy API keys and `AAPT` API key credentials). - add-data-renderer.ts: use the catalog's real ids (`deckgl-viz`, `gltf-model`) and also grey out the Vector and Raster panels, which are MapLibre controls with nowhere to mount on this engine. - useArcgisApiKey.ts: the device key fills in only while the runtime environment has no ArcGIS entry at all, so an explicit empty project override still suppresses it. - arcgis-layers.ts: remote GeoJSON-URL plans carry the resolver's `zoomDependent` flag so zoom-interpolated styles re-bake on zoom. | 12 天前 | |
docs: add a generated open data Gallery page grouped by theme (#2738) * docs(demos): group the open data showcase by theme Split the single 100-tile gallery into twelve theme sections (health, natural hazards, climate, oceans and water, nature and land, transport, cities and infrastructure, energy, space, history and culture, society and economy, food and agriculture), each with its own table and a link to the matching tag filter on share.geolibre.app. The intro now says "100 projects", explains the tags, and adds a jump list to the themes. The README's Open data demos link lists the same themes. * Address CodeRabbit review feedback - Name the open-data tag in the showcase intro, linked to its share.geolibre.app filter, since all 100 projects carry it alongside their theme and features. * docs: move the open data showcase to a generated Gallery page The 100-tile showcase dominated the Demos page, burying its tour of GeoLibre's own features, and loaded every screenshot up front. Move the catalog to its own Gallery page and keep a short teaser on Demos. - docs/gallery.md: all 100 demos in twelve theme sections, each with a count and a link to its share.geolibre.app tag filter; images load lazily. - docs/demos.md: the Open data showcase section (same anchor) becomes six featured tiles plus buttons to the gallery and the collection. - Both are generated by scripts/gen-demo-gallery.mjs from scripts/demo-gallery.json. npm run gallery regenerates them; npm run gallery:check runs in npm run ci and fails on drift. - Add Gallery to the nav after Demos, point the README link at it, and document the generated files in maintenance.md. | 6 小时前 | |
feat(release): embed AppImage update information and publish zsync (#1498) * feat(release): embed AppImage update information and publish zsync Tauri's bundler leaves the AppImage runtime's 1 KiB `.upd_info` ELF section zeroed, so AppImageUpdate, AppImageLauncher, AppManager and AM all report "Could not find update information in the AppImage" and cannot update it. Add scripts/embed-appimage-update-info.sh, which writes a `gh-releases-zsync|<owner>|<repo>|latest|<bundle>_*_amd64.AppImage.zsync` string into that section and generates the matching `.zsync` from the patched image. The release workflow runs it on the built AppImage and uploads both the patched image and the `.zsync`, so updates transfer only the blocks that changed instead of the whole ~120 MB file. The version in the bundle name becomes a glob so the update information keeps matching future releases; the script fails loudly if the bundle is ever renamed so that cannot silently degrade into a pattern that matches nothing. test-build runs the same patch (without uploading) so a break surfaces on a test build rather than halfway through a real release. Fixes #1495 * Address CodeRabbit review feedback - Extract the duplicated "pick exactly one AppImage from artifactPaths" block out of release.yml and test-build.yml into scripts/select-single-appimage.sh, so the two workflows cannot drift. - Cover the extracted script in tests/appimage-update-info.test.ts (picks the AppImage past the other bundles, fails on zero and on more than one). The suite skips itself where jq is not installed. Writing the test caught a bash parse error: an apostrophe inside ${1:?...} swallowed the rest of the file. | 2 个月前 | |
feat(pmtiles): styled offline basemaps + consolidate offline menus + CORS worker (#1259) * feat(pmtiles): styled offline basemaps + consolidate Project offline menus Turn an extracted PMTiles archive into a proper styled basemap (roads, water, landcover, buildings, labels) instead of a flat single-colour overlay, and unify the offline story under one Project menu. - @geolibre/map: buildProtomapsBasemapStyle() generates a MapLibre style from @protomaps/basemaps for a pmtiles source in a chosen flavor (light/dark/ white/grayscale/black). Applied as the basemap via an offline-basemap sentinel that map-controller's resolveMapStyle expands from a registry (mirrors the planetary-basemap pattern; falls back gracefully if the session-scoped archive is gone on reload). - Bundle Protomaps glyphs (Latin/Cyrillic/Greek ranges) + all flavor sprites under public/basemaps-assets (1.3 MB) so it renders fully offline; refresh with scripts/fetch-basemaps-assets.mjs. - BasemapExtractPanel: a "Basemap style" flavor picker. A Protomaps-schema vector archive is applied as the styled basemap; anything else (or "None") is added as the flat overlay layer. - Project menu: replace "Download offline areas" + "Manage offline areas" (service-worker tile cache) with a single "Offline Basemap…" opening the extract panel. Removes OfflineRegionDialog/OfflineManagerDialog. - workers/tiles: add a /pmtiles/<name>.pmtiles CORS range proxy for the Protomaps daily planet builds (scoped to build.protomaps.com, range-only), so the extractor can pull them from any origin. Verified in a real browser: extracted Florence z0-15 from the live planet build and applied it as a dark Protomaps basemap — roads, water, parks, POI icons, and labels all render from the bundled offline assets. * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * fix(pmtiles): extract panel polish — default proxy URL, resize, dark dropdown - Default the Archive URL to the Protomaps planet build via the deployed CORS proxy (tiles.geolibre.app/pmtiles/<recent-date>.pmtiles), computed at open time so it stays fresh; widen the panel so the full URL is visible. - Fix the dark-mode flavor dropdown contrast by using the themed @geolibre/ui Select instead of a bare <select>. - Don't auto-seed the bbox on open (a globe/world view produced a near-global box whose overlay drew a stray diagonal line); guard the overlay for near-global boxes too. The user draws a box or clicks "Use map view". - Make the panel resizable via a bottom-end grip; fix a position jump by pinning the parent-relative top-start corner (matching the drag). - Show the estimated output size + tile count during download, highlighted when large. - Default the panel to the top-start corner with a small gap; equal-width "Draw on map" / "Use map view" buttons; drop the now-redundant URL hint. * feat(pmtiles): manage saved offline basemaps (list, rename, delete, re-apply) Add a device-local catalogue of extracted basemaps and a "Saved basemaps" section in the panel: - lib/offline-basemaps.ts: a localStorage manifest (name, bbox, zoom range, flavor, size, saved path, date) with load/upsert/rename/delete + a change subscription. Each extract records an entry. - The panel lists saved basemaps with size/zoom/flavor and per-row actions: use-as-basemap, rename (inline), delete. Re-apply regenerates the Protomaps style and reuses the live in-session archive, or (on desktop) reloads its bytes from the saved file; raster archives can't be styled. - @geolibre/map: export hasPMTilesArchive and readLocalFileBytes for the live-vs-reload decision. * feat(pmtiles): reload saved basemaps on web, per-entry flavor, clear-bbox - "Use as basemap" now reloads a saved basemap whose in-memory archive is gone this session: from the remembered path on desktop, or via a file picker on web (browsers give no stable path to a downloaded file). Removes the dead-end "reopen on desktop" error — the affordance is the button itself. - Each saved basemap has its own flavor dropdown; changing it re-styles the basemap (instantly if its archive is loaded, else on next apply) since the archive's tiles are flavor-independent. Adds setOfflineBasemapFlavor. - Add a Clear (eraser) button beside Draw on map / Use map view that clears the drawn bounding box. * fix(pmtiles): re-styling the active offline basemap now takes effect registerOfflineBasemapStyle keyed the sentinel by id alone, so changing the flavor of the currently active basemap re-registered the style under the same URL. MapCanvas early-returns when basemapStyleUrl is unchanged, so map.setStyle never fired and the map kept the old flavor. Give each apply a unique sentinel (an incrementing suffix, dropping the id's prior entry) so the store value changes and the basemap re-renders. Verified: switching a loaded basemap Light -> Dark now restyles the map live. * feat(pmtiles): latest.pmtiles proxy route, static default URL, delete confirm - workers/tiles: /pmtiles/latest.pmtiles resolves to the newest daily Protomaps build by probing backward from today (no "latest" alias or index upstream) and caching the resolved date, so every range request in one extraction hits the same file. Deployed. - Default the extract URL to the static latest.pmtiles proxy URL — always current with no client-side date to go stale. - Deleting a saved basemap now asks for inline confirmation instead of removing on the first click. * feat(basemap): style a PMTiles URL as a basemap; open local PMTiles; tidy menus - BasemapPickerDialog + NewProjectDialog: a custom URL ending in .pmtiles (or a pmtiles:// URL) now reveals a Protomaps flavor selector and applies the archive as a styled offline basemap (new lib/pmtiles-basemap-url.ts). - Fix: an active offline-basemap sentinel no longer seeds the custom URL field and trips the "invalid URL" error — treat it as its own choice (export isOfflineBasemapSentinel from @geolibre/map). - BasemapExtractPanel: Saved basemaps section is always visible with an "Open file…" action to apply a local .pmtiles as a basemap without downloading; default archive URL seeds to latest.pmtiles unless a non-proxy URL was last used. - Remove "Offline Basemap Extract" from the Add Data menu (the flow lives under Project → Offline Basemap); drop the now-dead addLayer.basemapExtract wiring. - Rename the extract panel's "Archive URL" label to "Basemap URL". * fix(basemap): persist the custom URL and PMTiles flavor in the picker Applying a PMTiles URL swaps the basemap to an opaque offline sentinel, so reopening the picker left the custom URL field blank. Remember the last applied custom URL (and flavor) in localStorage and re-seed the field on open. * fix(basemap): register the pmtiles protocol for a remote PMTiles basemap A raw basemap style never runs the layer-sync path that registers the pmtiles:// protocol and its archive, so a styled remote PMTiles basemap applied no tiles — MapLibre had no handler for its source. Add ensureRemotePMTilesArchive() (mirrors ensurePMTilesProtocol for a FetchSource- backed remote URL) and call it when building the basemap style. Verified against data.source.coop/opengeos/protomaps/planet/latest.pmtiles: range GETs return 206 and the light basemap renders land, water, and place labels. * Address Claude and CodeRabbit review feedback - workers/tiles: bound the PMTiles range proxy — reject open-ended (`bytes=0-`) or oversized ranges (>32 MB) so it can't stream a 100+ GB planet build through the Worker; the presence-of-Range check alone did not cap the span. - workers/tiles: fold the forwarded `Range` header into the sub-fetch cacheKey so a cached 206 for one byte range can't be served for a different range of the same file (default CF cache key is URL-only). - protomaps-basemap: resolve bundled glyphs/sprites against `import.meta.env.BASE_URL` (caller passes assetsBaseUrl) so styled offline basemaps keep labels/icons when served under a sub-path. - BasemapExtractPanel: free the previous styled basemap's in-memory PMTiles archive (and evict its style-registry entry) when a different one is applied or the active one is deleted — styled basemaps aren't GeoLibreLayers, so removeLayerFromMap never freed them (memory leak). - BasemapExtractPanel: tighten the Protomaps-schema heuristic to require a distinctive `earth`/`water` layer (plus ≥2 schema layers), so a generic vector archive with only `roads`/`places` isn't styled with Protomaps expressions and rendered blank. - BasemapExtractPanel: "Use as basemap" now also requires a non-null flavor, so overlay-saved entries can't be forced through the styled path into a broken basemap. - BasemapExtractPanel: make the resize grip work in RTL layouts (invert the drag delta, measure available width to the left, pin the right edge via pos.x). - packages/map: revert the incidental react/react-dom dependency widening (^19.0.0 → ^19.2.7) to match sibling workspaces. * Address second-round review feedback - workers/tiles: document that the /pmtiles range proxy is intentionally not origin-gated (Jupyter/embed builds on arbitrary origins need it), and that abuse is bounded by the per-request range cap plus Cloudflare platform/zone rate limiting — mirroring the OAM route's throttling note. - tests: add unit coverage for the new pure-logic modules — offline-basemaps catalogue CRUD (upsert/rename/flavor/delete + malformed data) and the Protomaps offline-basemap style builder + sentinel registry (register/get/evict/isSentinel, sub-path assets base). * Address third-round review feedback - BasemapExtractPanel: hide the per-entry flavor <select> (and no-op handleSavedFlavorChange) for overlay-saved entries (flavor === null), so a flavor change can't force an incompatible archive through the styled-basemap path that applySaved's guard was added to block. - BasemapExtractPanel: openPmtilesAsBasemap ("Open file…") now also requires isProtomapsCompatible, matching handleExtract — a non-Protomaps vector archive no longer gets force-styled into a blank basemap. - pmtiles-basemap-url: buildRemotePmtilesBasemap now passes assetsBaseUrl = `${BASE_URL}basemaps-assets`, so remote-PMTiles basemaps applied via the New Project / Basemap Picker dialogs keep labels/icons under a sub-path deployment (GEOLIBRE_APP_BASE). - NewProjectDialog: translate the custom-basemap flavor picker via t() (basemapExtract.style / basemapExtract.flavor.*), matching BasemapPickerDialog instead of hardcoded English. - workers/tiles: drop `cf.cacheEverything` from the range proxy — caching 206s under a URL-only key risks serving the wrong range's bytes, and `cf.cacheKey` only works on Enterprise plans, so don't rely on it; refuse a 200 (range-ignored) response so the whole file can't stream through; reject ranges with offsets past 2^53 (Number precision). * Address fourth-round review feedback - BasemapExtractPanel: deleting the currently-applied styled basemap now resets the basemap to DEFAULT_BASEMAP before freeing its archive, so the live MapLibre style isn't left pointing at a pmtiles:// source whose bytes were just released (which would 404 on pan/zoom). - BasemapExtractPanel: "Open file…" now derives the saved-basemap name via layerNameFromPath, so a desktop absolute path yields "tuscany" rather than a mangled "home-alice-Downloads-tuscany". - BasemapExtractPanel: move the active-styled-basemap tracker onto globalThis (matching the style registry / archive-key set this PR adds) so it survives a Vite HMR reload and doesn't leak the live archive. * Address fifth-round review feedback - workers/tiles: stop edge-caching the resolveLatestBuildDate existence probe. `cacheEverything` cached the 1-byte `bytes=0-0` 206 under the URL-only cache key for `<date>.pmtiles` — the same URL the range proxy fetches next — so a later real range read could be served the 1-byte body. The resolved date is already memoised in `latestCache`, so no edge cache is needed; this reintroduced the exact corruption the main-proxy fix removed. - NewProjectDialog / BasemapPickerDialog: use the themed @geolibre/ui Select for the custom-basemap flavor picker instead of a bare native <select>, matching the fix already applied to the main extract-panel picker (dark-mode contrast + consistency). - NewProjectDialog: translate the "Enter a valid style.json or .pmtiles URL." validation message via t() (new newProject.invalidCustomUrl key), matching BasemapPickerDialog's basemapPicker.invalidUrl. * Address sixth-round review feedback - BasemapExtractPanel: compare against the live store `basemapStyleUrl` (via a new isLiveOfflineBasemap helper + exported OFFLINE_BASEMAP_SENTINEL_PREFIX) instead of the panel-local tracker, which only sees applies made from this panel and goes stale once the basemap is switched via the picker / New Project dialog. Fixes two bugs: (1) deleting a saved entry could reset an unrelated, currently- active basemap to the default; (2) changing a non-displayed entry's flavor could silently swap it in as the live basemap. Now the default- reset on delete and the live re-style on flavor change only fire when the entry is actually the basemap on the map. * Address seventh-round review feedback - map-controller: resolveMapStyle now returns a structuredClone of the registered offline-basemap style instead of the shared registry object (matching the planetary path, which builds a fresh object). In split/compare view two Map instances resolve the same sentinel; MapLibre mutates the style it's handed, so sharing one object let the two panes corrupt each other's style state. - pmtiles-basemap-url: use the percent-encoded URL as the remote-PMTiles registry id instead of a 32-bit rolling hash, removing the (unlikely but real) chance that two distinct URLs collide and one silently evicts the other's registry entry. * fix(pmtiles): absolute basemap-assets URL + skip layer-control fetch of offline sentinel Two runtime errors surfaced when a styled offline basemap was applied on the deploy preview (relative "./" base): - "Invalid sprite URL ... must be absolute": the sub-path fix built the glyphs/sprites base as `${import.meta.env.BASE_URL}basemaps-assets`, which is `./basemaps-assets` under the embed/demo build's relative base — and MapLibre rejects non-absolute sprite/glyph URLs. Resolve it to a fully-qualified URL via `new URL(base, document.baseURI).href` in both BasemapExtractPanel and pmtiles-basemap-url; this stays absolute for "/", a "/sub-path/", and a relative "./" base alike. - "Failed to fetch geolibre://offline-basemap/…": the layer control fetches the basemap style URL to introspect its layers, but an offline basemap uses a non-fetchable `geolibre://` sentinel. MapController already substitutes BLANK_BASEMAP for planetary sentinels; extend that to offline-basemap sentinels so the control skips the fetch (and shows a single background entry) instead of throwing. Verified in a real browser: applying a remote styled Protomaps basemap now renders labels/water/landcover with 0 console errors (previously the sprite + sentinel-fetch errors). --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> | 2 个月前 | |
docs: add the open data gallery video tutorial (#2739) Add "100 Interactive Maps from Open Data: Explore, Fork, and Build Your Own with GeoLibre" (https://youtu.be/2r5OhvEa3AA, 24:37) to Video Tutorials with an embed and summary, to the video lists in the README, Getting Started, Tutorials, and Demos, and as a button on the Gallery page (via scripts/gen-demo-gallery.mjs, then regenerated). | 3 小时前 | |
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> | 2 个月前 | |
fix(planetary): load collections and search despite missing STAC CORS (#2674) The Planetary Computer STAC API sends no Access-Control-Allow-Origin on GET /collections and answers the CORS preflight for a JSON POST /search with a 405, so the Processing > Planetary Computer panel failed with "Failed to fetch" in the browser and the Tauri webview. Swap the control's STAC client for a subclass that sends search as a preflight-free GET /search (collections, bbox, datetime, limit, query, sortby, filter) and, when the live collection list cannot be read, falls back to a bundled snapshot of the collection fields the panel uses. The snapshot is a lazy chunk, regenerated by scripts/gen-planetary-computer-collections.mjs. | 3 天前 | |
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> | 2 个月前 | |
feat(i18n): localize processing tools in Simplified Chinese (#2049) * Complete Simplified Chinese (zh) locale catalog to 100% coverage Add 696 missing translations to zh.json covering Iceberg data source UI, processing tool metadata (vector, raster, network, statistics), tool group labels, STAC plugin messages, and GeoEditor strings. Also add i18n guidance note to CLAUDE.md and a gradle ignore rule. * Translate Whitebox menu tool names and subcategory labels via i18n Add processing.whitebox.menuTool.{id} and processing.whitebox.menuSubcategory.{key} keys to en.json and zh.json for all 1066 Whitebox tools and 45 subcategory labels. Update ProcessingMenu.tsx to render them through t() instead of raw catalog strings. Other locales fall back to English automatically. * feat(processing): localize Whitebox toolbox metadata Generate the English baseline for 733 unlocked Whitebox tools and their parameters, and add Simplified Chinese tool names. Render Processing dialog tool names and parameter help through i18n while keeping manifest heuristics on raw metadata, and include localized Whitebox names in Model Builder search. * feat(processing): add Whitebox reprojection method option Expose the optional auto_reproject_method parameter in 34 Whitebox tools and regenerate their English metadata. Add Simplified Chinese labels and descriptions for the new control. * fix(processing): localize Whitebox parameters * feat(processing): complete Whitebox translation coverage Merge local WASM manifests into the generated Whitebox baseline, adding 333 local-mode tools and 50 category labels. Translate toolbox categories and status text, cover every Simplified Chinese tool and parameter label, and avoid appending untranslated help text to translated labels. * fix(processing): translate Whitebox parameter help * fix(processing): address i18n review feedback * style: auto-format (ruff + oxfmt) [pre-commit.ci] * chore(ci): allow generated locale catalogs * fix(build): avoid deep Trans component inference * fix(build): narrow remaining Trans inference * fix(processing): address i18n review issues * style: auto-format (ruff + oxfmt) [pre-commit.ci] * fix(processing): harden i18n review fixes * fix(processing): pass active locale explicitly * fix(processing): generate Whitebox menu baselines * style: auto-format (ruff + oxfmt) [pre-commit.ci] * feat(i18n): load Whitebox translations as language packs * Address review feedback - i18n/index.ts: move `loadInstalledLanguagePack` inside a try so a rejected `indexedDB.open()` (Safari private browsing, a sandboxed embed iframe) degrades to the base catalog instead of rejecting `i18nReady` and leaving `main.tsx` without a render. - language-pack.ts: abort a pack download after 30s so a host that accepts the connection and never answers cannot leave the Settings buttons disabled for the session; only short-circuit on a *declared* content-length, since `Number(null)` read a chunked body as zero bytes. - language-pack.ts / SettingsDialog.tsx: derive the "browse the catalog" link from the configured host via the new `languagePackBaseUrl()` instead of hardcoding `languages.geolibre.app`, so a `__NO_EXTERNAL_CDN__` build hides it and a self-hosted mirror is linked rather than the public host. Replaces the now-unused `officialLanguagePackDownloadsEnabled()`. - SettingsDialog.tsx: validate an installed pack's `installedAt` before formatting it; an unparseable timestamp made `Intl.DateTimeFormat.format` throw during render and take the Settings pane down. New `settings.languagePack.installedDetailNoDate` string in en/zh. - processing-tool-i18n.ts: `translateWhiteboxParameterDescription` now takes the active language and suppresses an untranslated description outside English, matching `whiteboxParameterLabel` — the same text was hidden inline but shown in English on hover. - processing-tool-i18n.ts / ProcessingDialog.tsx: unify the two identical humanizers behind `humanizeIdentifier(value, fallback)`; the extent-group field labels now use the parameter fallback rather than the tool one. - docs/i18n.md: the Model Builder palette section said a heading is translated when a tool comes from "an owned catalog", which stopped meaning "vector only" once `modelProviderCatalog` began returning `"whitebox"`. Say the vector registry outright. - tests: cover the abort signal, the missing/oversized content-length paths, and the suppressed non-English description. * Address CodeRabbit review feedback - language-pack.ts / tests: the timeout test dispatched a bare `abort` event, which does not set `signal.aborted`, so it took the generic failure path and asserted nothing about the timeout. `fetchLanguagePack` now takes an optional `timeoutMs` (defaulting to `LANGUAGE_PACK_TIMEOUT_MS`) and the test drives a never-resolving host with a 5ms timeout, asserting the timeout message. - SettingsDialog.tsx: the privacy notice hardcoded `languages.geolibre.app` in its copy, so a self-hosted `VITE_LANGUAGE_PACK_BASE_URL` deployment named the wrong host and a downloads-disabled build named one it never contacts. The notice now interpolates the configured host, and falls back to a new `settings.languagePack.privacyLocalOnly` string when there is none. * Address Claude review feedback - SettingsDialog.tsx: check a picked language-pack file's `size` against `LANGUAGE_PACK_MAX_BYTES` before `file.text()`. `parseLanguagePack` enforces the same limit, but only after the whole file has been buffered, so a mistakenly picked multi-gigabyte file paid the full read before the check ran. Reuses the existing `settings.languagePack.errorTooLarge` string. --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Co-authored-by: giswqs <giswqs@gmail.com> | 1 个月前 | |
feat(i18n): localize processing tools in Simplified Chinese (#2049) * Complete Simplified Chinese (zh) locale catalog to 100% coverage Add 696 missing translations to zh.json covering Iceberg data source UI, processing tool metadata (vector, raster, network, statistics), tool group labels, STAC plugin messages, and GeoEditor strings. Also add i18n guidance note to CLAUDE.md and a gradle ignore rule. * Translate Whitebox menu tool names and subcategory labels via i18n Add processing.whitebox.menuTool.{id} and processing.whitebox.menuSubcategory.{key} keys to en.json and zh.json for all 1066 Whitebox tools and 45 subcategory labels. Update ProcessingMenu.tsx to render them through t() instead of raw catalog strings. Other locales fall back to English automatically. * feat(processing): localize Whitebox toolbox metadata Generate the English baseline for 733 unlocked Whitebox tools and their parameters, and add Simplified Chinese tool names. Render Processing dialog tool names and parameter help through i18n while keeping manifest heuristics on raw metadata, and include localized Whitebox names in Model Builder search. * feat(processing): add Whitebox reprojection method option Expose the optional auto_reproject_method parameter in 34 Whitebox tools and regenerate their English metadata. Add Simplified Chinese labels and descriptions for the new control. * fix(processing): localize Whitebox parameters * feat(processing): complete Whitebox translation coverage Merge local WASM manifests into the generated Whitebox baseline, adding 333 local-mode tools and 50 category labels. Translate toolbox categories and status text, cover every Simplified Chinese tool and parameter label, and avoid appending untranslated help text to translated labels. * fix(processing): translate Whitebox parameter help * fix(processing): address i18n review feedback * style: auto-format (ruff + oxfmt) [pre-commit.ci] * chore(ci): allow generated locale catalogs * fix(build): avoid deep Trans component inference * fix(build): narrow remaining Trans inference * fix(processing): address i18n review issues * style: auto-format (ruff + oxfmt) [pre-commit.ci] * fix(processing): harden i18n review fixes * fix(processing): pass active locale explicitly * fix(processing): generate Whitebox menu baselines * style: auto-format (ruff + oxfmt) [pre-commit.ci] * feat(i18n): load Whitebox translations as language packs * Address review feedback - i18n/index.ts: move `loadInstalledLanguagePack` inside a try so a rejected `indexedDB.open()` (Safari private browsing, a sandboxed embed iframe) degrades to the base catalog instead of rejecting `i18nReady` and leaving `main.tsx` without a render. - language-pack.ts: abort a pack download after 30s so a host that accepts the connection and never answers cannot leave the Settings buttons disabled for the session; only short-circuit on a *declared* content-length, since `Number(null)` read a chunked body as zero bytes. - language-pack.ts / SettingsDialog.tsx: derive the "browse the catalog" link from the configured host via the new `languagePackBaseUrl()` instead of hardcoding `languages.geolibre.app`, so a `__NO_EXTERNAL_CDN__` build hides it and a self-hosted mirror is linked rather than the public host. Replaces the now-unused `officialLanguagePackDownloadsEnabled()`. - SettingsDialog.tsx: validate an installed pack's `installedAt` before formatting it; an unparseable timestamp made `Intl.DateTimeFormat.format` throw during render and take the Settings pane down. New `settings.languagePack.installedDetailNoDate` string in en/zh. - processing-tool-i18n.ts: `translateWhiteboxParameterDescription` now takes the active language and suppresses an untranslated description outside English, matching `whiteboxParameterLabel` — the same text was hidden inline but shown in English on hover. - processing-tool-i18n.ts / ProcessingDialog.tsx: unify the two identical humanizers behind `humanizeIdentifier(value, fallback)`; the extent-group field labels now use the parameter fallback rather than the tool one. - docs/i18n.md: the Model Builder palette section said a heading is translated when a tool comes from "an owned catalog", which stopped meaning "vector only" once `modelProviderCatalog` began returning `"whitebox"`. Say the vector registry outright. - tests: cover the abort signal, the missing/oversized content-length paths, and the suppressed non-English description. * Address CodeRabbit review feedback - language-pack.ts / tests: the timeout test dispatched a bare `abort` event, which does not set `signal.aborted`, so it took the generic failure path and asserted nothing about the timeout. `fetchLanguagePack` now takes an optional `timeoutMs` (defaulting to `LANGUAGE_PACK_TIMEOUT_MS`) and the test drives a never-resolving host with a 5ms timeout, asserting the timeout message. - SettingsDialog.tsx: the privacy notice hardcoded `languages.geolibre.app` in its copy, so a self-hosted `VITE_LANGUAGE_PACK_BASE_URL` deployment named the wrong host and a downloads-disabled build named one it never contacts. The notice now interpolates the configured host, and falls back to a new `settings.languagePack.privacyLocalOnly` string when there is none. * Address Claude review feedback - SettingsDialog.tsx: check a picked language-pack file's `size` against `LANGUAGE_PACK_MAX_BYTES` before `file.text()`. `parseLanguagePack` enforces the same limit, but only after the whole file has been buffered, so a mistakenly picked multi-gigabyte file paid the full read before the check ran. Reuses the existing `settings.languagePack.errorTooLarge` string. --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Co-authored-by: giswqs <giswqs@gmail.com> | 1 个月前 | |
Add GEOLIBRE_DUCKDB_WASM_CDN to load DuckDB-WASM from jsDelivr (#1844) * Add GEOLIBRE_DUCKDB_WASM_CDN to load DuckDB-WASM from jsDelivr duckdb-mvp.wasm (~40 MB) and duckdb-eh.wasm (~35 MB) both exceed the 25 MiB per-asset limit on Cloudflare Pages and Workers static assets, which rejects the upload outright. Nothing else in the build is close (next largest ~22 MB), so those two files alone are what stop the web build from being hosted there. GitHub Pages allows 100 MB per file and needs none of this. GEOLIBRE_DUCKDB_WASM_CDN=1 selects a third duckdb-wasm-bundles variant that resolves the engine from jsDelivr, alongside the existing web and Tauri ones. Build output goes ~251 MB -> ~176 MB with no file over the ceiling. Opt-in, the reverse of GEOLIBRE_PGLITE_CDN: DuckDB is on the critical path for opening a local vector file, so making that need the network is a real behaviour change rather than a default. Ignored for a Tauri build, which must stay offline-capable. URLs come from duckdb-wasm's own getJsDelivrBundles(), which pins @version from the same constant the bundled JS was compiled from, so the fetched engine cannot drift from the loader that instantiates it. A CDN worker script cannot go to `new Worker` (it must be same-origin), so the CDN variant wraps it in a same-origin blob that importScripts the real one -- classic, not { type: "module" }, since importScripts does not exist in a module worker. Worker creation therefore moves behind a createDuckDbWorker export that each variant supplies, rather than being inlined at the call site. The service worker's geolibre-cdn-engines rule now covers /npm/@duckdb/, so the web build still works offline after first use. That path also matches maplibre-gl-duckdb, which already fetches its own DuckDB from jsDelivr in every build, so this adds no new external origin and needs no CSP change. Verified in a real browser against both builds, driving the app's own duckdb-vector-loader chunk (its getDatabase() export, INSTALL/LOAD spatial, a ST_Point query): - CDN build: resolves POINT (3 4), fetches the worker and .wasm from jsDelivr, zero same-origin duckdb .wasm requests. - Default build: resolves POINT (3 4), zero CDN requests, fetches same-origin duckdb-eh-*.wasm. Unchanged. * Add `npm run lite:build` for size-capped hosts Wraps GEOLIBRE_DUCKDB_WASM_CDN=1 so the flag does not have to be remembered, and so it works on Windows -- the `VAR=value npm run build` prefix is POSIX-only, which is why this follows the scripts/build-embed.mjs pattern of a node script that spawns the build with an env override rather than an inline shell assignment in package.json. It also carries the guard that pattern exists for. The build's whole purpose is to fit under Cloudflare's 25 MiB per-asset limit, so afterwards it walks the output and fails if anything exceeds it, naming the offenders. Without that, the module swap breaking -- or any *other* asset growing past the ceiling later -- produces a build that looks fine locally and is rejected at upload time, with nothing pointing at the cause. The error distinguishes the two: a DuckDB file points at the vite plugin and module pair, anything else says it needs the same CDN treatment. Forwards extra args (`-- --outDir dist-lite`) as build-embed does, and resolves the guard against whichever outDir was chosen. Verified: `npm run lite:build` reports "1586 files, 172 MB, no file over 25 MiB" and exits 0; the guard logic exits 1 on a planted 25.7 MiB duckdb-mvp-*.wasm and fires the DuckDB-specific hint. (The guard was exercised against a planted directory rather than a real build, since Vite empties outDir and a planted file cannot survive one.) * Address review feedback - duckdb-wasm-bundles.cdn.ts: stop revoking the blob url immediately after `new Worker`. Per the HTML spec the constructor returns before the worker fetches its script, so that was a real race: the fetch can land after the revoke and the worker then silently never loads. The blob now revokes its own url as its first statement, which cannot run until the fetch has already succeeded, and the caller only revokes synchronously if the constructor threw (nothing would run the self-revoke then). My previous comment asserting the constructor "has already resolved the URL" was wrong. Raised by both CodeRabbit and the Claude bot. - duckdb-wasm-bundles.cdn.ts: throw a named error when `bundle.mainWorker` is absent. It is optional on the type, and interpolating it would have emitted `importScripts(null)`, surfacing as a worker that never answers rather than as the actual cause. Preferred to the suggested `!` assertion, which only silences the compiler and leaves the runtime failure just as opaque. - vite.config.ts: exclude the embed build from the CDN flag as well as Tauri (`!PWA_DISABLED` rather than `!IS_TAURI_BUILD`). An embed build ships no service worker, so the engine would be refetched every notebook session with no runtime cache behind it, and a wheel has none of the size ceiling this exists for. Required moving the declaration below PWA_DISABLED, since referencing IS_EMBED from its old position would have been a TDZ error. Verified after the change, driving the app's own duckdb-vector-loader chunk in a real browser: the CDN build still resolves POINT (3 4) from jsDelivr with zero same-origin duckdb .wasm requests, and the emitted blob does contain the self-revoke. `GEOLIBRE_EMBED=1 GEOLIBRE_DUCKDB_WASM_CDN=1` now emits both bundled duckdb .wasm files and no CDN shim, confirming the flag is ignored there. * Address CodeRabbit review feedback - scripts/lite-build.mjs: parse `--outDir=<path>` as well as `--outDir <path>`. Vite's parser (cac) accepts either, and reading only the space-separated form was worse than not reading it at all: with the equals form the guard checked `dist` rather than the directory just built, so on any machine with a previous build sitting there it passed while saying nothing about the new output. Last occurrence wins, matching how cac resolves a repeated flag, and an empty value (`--outDir=` from an unset shell var) is ignored rather than resolving to the app directory and walking node_modules. Verified: a 7-case table over the parser (both forms, repeats in either order, bare `--outDir`, empty value) all pass, and `npm run lite:build -- --outDir= dist-lite` with a stale `dist/` deliberately left in place now reports "apps/geolibre-desktop/dist-lite: 1586 files, 172 MB, no file over 25 MiB" — the directory it actually built. * Address Claude review feedback - duckdb-wasm-bundles.cdn.ts: revoke the blob url on the worker's error event too. The catch only covers a synchronous throw; if construction succeeds but the script never executes (blocked blob worker, or an importScripts that cannot reach the CDN) the self-revoke never runs and the url leaked for the life of the page. Revoking twice is a no-op, so this is harmless on the ordinary path. - duckdb-wasm-bundles.cdn.ts: record that the CSP claim is tested, not assumed. The reviewer was right to challenge it: the comment asserted that the nested importScripts is matched against script-src rather than worker-src (which is only `blob: 'self'`), which is Chromium behaviour that Firefox has historically not shared. Served the built app under the exact production CSP from docker/nginx.conf and ran the app's own duckdb-vector-loader chunk in both engines: Chromium and Firefox each instantiate DuckDB from jsDelivr and answer a query, with no CSP violation reported. WebKit could not be tested here (its Playwright build needs Ubuntu libs unavailable on this host), so the comment says which engines were covered rather than claiming all three. - docs/architecture.md: say the flag is ignored for embed builds as well as Tauri, with the reason (no service worker, so the engine would be refetched every notebook session), mirroring what vite.config.ts already documents. Also record the script-src/worker-src finding. | 1 个月前 | |
fix(chrome): find map services without broad host permissions (#1998) * fix(chrome): find map services without broad host permissions The Chrome Web Store flags `http://*/*` and `https://*/*` for in-depth review, and those existed only so `webRequest` could watch map requests. The popup now reads each frame's Resource Timing buffer under activeTab instead, leaving the extension with activeTab and scripting alone. MapLibre fetches vector tiles from a worker, which that buffer never records, so such a tileset is recovered from the TileJSON or style the main thread did fetch; GeoLibre now accepts a vector tiles deep link carrying only a style. * Address Claude review feedback - Assert at compile time that GeoLibreCogRenderEngine still matches the RenderEngine union it hand-mirrors from maplibre-gl-raster. types.ts is the public plugin API and must not hard-depend on that package's types, so the check lives next to the real import and fails typecheck on drift rather than letting a stale identifier reach control.setEngine(). - Record the mirror in CLAUDE.md alongside the others it documents. * Address review feedback - Reserve room under MAX_SERVICE_CANDIDATES for the style fallbacks: a page varied enough to fill the cap is mostly repeating layers of a few endpoints, while a fallback is the only trace its origin leaves at all. - Recognize the singular `tile.json` alongside `tiles.json` and `tilejson.json`, with a regression test over all three spellings. - Document in the README what the TileJSON sniff does not reach (a server that names its metadata otherwise, and a raster TileJSON, which cannot be told apart without reading a body this design cannot fetch). * Address review feedback - Offer a style as a candidate of its own only when its path names it a map style (`…/style.json`, `…/styles.json`, an ArcGIS `…/resources/styles/<name> .json`). The looser `…/styles/<name>.json` is an ordinary theme route too, so a style matched that way still explains a tileset at its origin but no longer surfaces on its own, where a page's theme file would appear as a layer. - State the raster-TileJSON failure precisely in the README: Add Data resolves the document on submit and refuses it when no source layers come out, rather than every selection opening on an empty dialog. * Address review feedback - Keep a self-naming style over a generic one from the same origin. 65552354 made the fallback depend on that flag, but the map kept only the last style seen per origin, so a theme file fetched after `/style.json` stranded a worker-only tileset and handed an existing one the wrong style document. A test covers both request orders and fails without the fix. * Address review feedback - Document that `activeTab` does not reach cross-origin frames. Chrome grants the tab's main frame origin only and deliberately withholds that grant from a frame of another origin, so `allFrames: true` covers the top frame and its same-origin frames. The README described the scan as reading "each frame" and claimed the MapLibre row "covers services a page reaches only through an embedded frame", which over-claimed: that example's iframe is `src= "../display-a-map.html"`, same-origin with the docs page, so the live test never exercised a cross-origin frame. Added a third bullet to the buffer consequences, narrowed the two summary sentences and the table row, and recorded the boundary next to the `allFrames` call in popup.mjs. No code change: reaching such a frame needs standing host permission, which is exactly what this PR removes, so the limit is documented, not worked around. | 1 个月前 | |
feat (npm_package): pack core and map into npm package (#2084) * feat (npm_package): pack core and map into npm package * fix maplibregl and desktop config * style: auto-format (ruff + oxfmt) [pre-commit.ci] * Address review feedback - packages/core/package.json: move the dist `main`/`types`/`exports` out of `publishConfig` semantics that npm ignores. npm only honors publish-time *config* there (npm/cli#7586), so the tarball advertised `./src/index.ts` while `files` shipped only `dist`. The published entries now go through `scripts/prepare-npm-package.mjs`, which hoists them just before publish; `publishConfig` also gains the missing `access: public` for a scoped package. Fixes the Copilot and CodeRabbit threads on this file. - packages/core/package.json: `types` pointed at `./dist/index.d.ts`, but `tsdown --dts` emits `index.d.mts`. Published consumers would have gotten no types at all. Answers the Claude review question about the extension. - packages/map/package.json: point `main`/`types`/`exports` back at TypeScript source. Pointing them at `dist` is what broke CI: `dist` is gitignored and nothing builds it, so 12 frontend tests failed with ERR_MODULE_NOT_FOUND on `@geolibre/map/derived-geometry` and friends. The published dist entries live under `publishConfig` and register every subpath the repo exports (`.`, `./headless`, `./derived-geometry`, `./pmtiles-layer`), so nothing 404s on npm. - packages/map/package.json: restore the `^19.0.0` react/react-dom peer ranges. Narrowing them to `^19.2.8` was unrelated to packaging and only constrains consumers. - apps/geolibre-desktop/tsconfig.json, vite.config.ts: revert the `@geolibre/map` path/alias and the `../../packages/map/src` include. With the manifest resolving to source again they are unnecessary, and the include made `tsc -b` emit `.js`/`.d.ts` beside the map sources. Fixes the CodeRabbit thread on tsconfig.json. - packages/map/src/headless.ts: restore the requested stack order when a layer moves. `syncLayer` on an existing layer leaves it where MapLibre put it, so a reorder was silently ignored and the documented bottom-to-top order stopped holding. Rebuild from the lowest position whose occupant changed. - Add scripts/prepare-npm-package.mjs plus tests/prepare-npm-package.test.ts, which also guards that every published path is one the package's own tsdown entries actually emit. - Add tests/headless-layer-sync.test.ts covering ordering, insertion, removal and dispose. - Add .github/workflows/publish-packages.yml: build, prepare and publish @geolibre/core then @geolibre/map on each GitHub Release, via npm Trusted Publishing with provenance, skipping a version already on the registry. - Add READMEs for both packages (npm renders them) and document the source-vs-dist split in CLAUDE.md. * Address CodeRabbit review feedback - .github/workflows/publish-packages.yml: read each package's own version for the registry check instead of using core's for both. The two move in lockstep today, but the moment they diverge the map check would skip an existing release or attempt one npm has already seen. - CLAUDE.md: correct the claim that no workflow runs the package builds. The release workflow does build both; what it does not do is verify that every path the manifest publishes names a file the build emitted, which is what tests/prepare-npm-package.test.ts covers. --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Co-authored-by: giswqs <giswqs@gmail.com> | 1 个月前 | |
ci(aur): publish geolibre-bin to the AUR on each release (#645) * ci(aur): publish geolibre-bin to the AUR on each release Add a binary AUR package that repackages the Linux .deb already attached to every release (no source build, full desktop integration from the .deb's binary/.desktop/icons). - scripts/render-aur-pkgbuild.sh renders the PKGBUILD for a version + deb checksum (mirrors render-homebrew-cask.sh). - packaging/aur/PKGBUILD is a generated reference for the first manual import; packaging/aur/README.md documents account/SSH/secret setup. - release.yml gains an `aur` job that downloads the release .deb, renders the PKGBUILD, and pushes PKGBUILD + .SRCINFO to the AUR. The job is isolated: it only `needs` build, nothing depends on it, it skips when AUR_SSH_PRIVATE_KEY is unset (forks), and runs as continue-on-error so an AUR failure never fails the release or affects the asset build or Homebrew update. * Address Claude review feedback - Anchor the VERSION regex at both ends so a hyphen/suffix tag (e.g. 1.2.3-rc1) is rejected rather than producing an invalid AUR pkgver. - Guard the data.tar.* glob in package(): capture it and abort if no data member exists, instead of passing a literal name to bsdtar. - Pin KSXGitHub/github-actions-deploy-aur to a commit SHA (v4.1.3) rather than a mutable tag, since the step receives the AUR SSH private key. | 3 个月前 | |
ci(copr): publish geolibre to Fedora COPR + add AppStream metainfo (#646) * ci(copr): publish geolibre to Fedora COPR + add AppStream metainfo Add a Fedora COPR binary package (the "AUR for Fedora") that repackages the official release .rpm, plus a shared AppStream metainfo file (reusable for Flathub/Snap later) and a Linux install section in the docs. - scripts/render-linux-metainfo.sh + packaging/linux/org.geolibre.desktop.metainfo.xml: AppStream metadata (name, description, screenshot, release); validated with appstreamcli. - scripts/render-copr-spec.sh + packaging/copr/geolibre.spec: an RPM spec that unpacks the upstream .rpm, renames the desktop entry to the app-id with proper categories, installs the metainfo, and lets rpm auto-generate the library Requires. Verified end-to-end with a Fedora-container rpmbuild. - packaging/copr/README.md: COPR account/token/secret setup. - release.yml: a `copr` job, isolated like the AUR job (needs build only, continue-on-error, skips when COPR_API_TOKEN is unset). - docs/downloads.md: Linux installation section (AUR, COPR, .deb, .rpm, AppImage), and dropped em dashes from the AUR README. * ci(copr): target the giswqs/geolibre project namespace The COPR project lives under the user namespace, not a group, so point COPR_PROJECT, the docs (dnf copr enable), and the README at giswqs/geolibre. * ci(copr): inject rich into copr-cli copr-cli imports rich but does not always declare it as a dependency, so a plain pipx install fails at runtime with ModuleNotFoundError. Verified the fix end-to-end against the live COPR token in CI. * Address review feedback - Validate DATE as a real calendar date (date -d), not just YYYY-MM-DD format, in both render scripts; rejects e.g. 2026-13-40. - Use space-padded %e (not %d) for the RPM %changelog day, matching the rpmlint-expected convention for single-digit days. - Drop the duplicate <url type="help"> from the metainfo (it pointed at the homepage); a missing help URL is cleaner than a duplicate. * Address Claude review feedback - Guard against an empty SRPM path before calling copr-cli, so a future rpmbuild output change fails loudly instead of with a cryptic error. - Ship the project LICENSE in the RPM via %license (Source2); the upstream bundle omits it. Verified the package now carries /usr/share/licenses. - Create the COPR token file with a restrictive umask atomically instead of chmod-after-write. - Find the upstream .desktop dynamically instead of hardcoding its name, so an upstream rename fails clearly rather than silently dropping it. - Rename the `date` shell var to `release_date` (avoid shadowing the builtin); log the repackaged RPM's sha256 to the step summary; note that `date -d` is GNU-only; document yum/zypper install in the docs. * Address Claude review feedback - Assert exactly one .desktop in the upstream payload (not just non-zero), so an unexpected extra fails with a clear message instead of a cryptic unpackaged-files error. - Add Conflicts: geo-libre-desktop (the upstream release RPM's actual Name, which drops the same files) for a clean dnf conflict message. | 3 个月前 | |
docs: simplify Homebrew install now that the cask is in the official tap (#948) GeoLibre is published in Homebrew/homebrew-cask (Casks/g/geolibre.rb), so installation no longer needs a custom tap or 'brew trust'. Update README and docs/downloads.md to 'brew install --cask geolibre', add an uninstall snippet, and refresh the stale 'not yet submitted' comments in the cask render scripts. Keep the self-hosted tap (opengeos/homebrew-geolibre) and its release job as a fallback. | 3 个月前 | |
fix(tests): make the local quality gate pass off Linux (#2214) CONTRIBUTING asks contributors to run `npm run ci` before opening a pull request, but on macOS `npm run test:frontend` fails with 5 errors and cancels 4 more tests, none of them the contributor's. CI does not catch this because it runs Linux with GNU coreutils and bash 5. Three independent causes, all in test-only or Linux-only helper code: select-single-appimage.sh used `mapfile`, a builtin that arrived in bash 4. macOS still ships bash 3.2 as /bin/bash, where it is simply missing, so all three of its tests failed. Read the lines with a `while IFS= read -r` loop instead, which behaves identically on both shells. render-linux-metainfo.sh validated its DATE with GNU `date -d`. BSD date rejects that flag outright, so the check failed for every date and the script always exited 1. Try GNU first, then BSD's `-j -f` spelling. BSD date normalises an out-of-range day instead of rejecting it (2026-02-31 comes back as 2026-03-03 with a zero exit), so the result is compared against the input rather than trusting the exit status. That is stricter than the previous check on both implementations, and leaves the rendered XML byte-identical. The desktop-settings-url timeout test is not macOS-specific: it asserts that AbortSignal.timeout fires, but Node leaves that timer unref'd, and the stalled fetch it races settles only on that abort. With nothing else pending the event loop drains first and the promise never settles, which fails the test and cancels the three after it. Hold a ref'd timer across the assertion so the abort is guaranteed to land. macOS goes from 5 failed / 4 cancelled to 1 failed / 0 cancelled. The remaining failure is the _frontend.js module-classification issue in #2213, left out deliberately: its fix touches Python packaging. Refs #2213 | 26 天前 | |
feat(desktop): add a sandboxed Mac App Store build variant (#1581) * feat(desktop): add a sandboxed Mac App Store build variant Adds a `mas` build of GeoLibre Desktop that satisfies App Store review rules (App Sandbox, guideline 2.5.2), alongside the existing Developer ID notarized builds: - `mas` cargo feature compiles out everything that downloads or spawns external code: the Python sidecar, Jupyter server, martin tile server, the uv bootstrap, and external plugin installation. Invoke-compatible stubs keep the command registry unchanged; `compile_error!` forbids combining with `native-duckdb` (runtime extension loading). - `GEOLIBRE_MAS_BUILD=1` frontend flag (Vite define, DCE-friendly) hides the sidecar-only UI (AI Segmentation, PostgreSQL, File Geodatabase, plugin marketplace) and routes processing dialogs to their client-side engines (Whitebox WASM, browser conversions, client raster tools, Turf/Pyodide vector tools, CereusDB SQL). The Notebook panel keeps working via JupyterLite, which stays in the MAS bundle. - App Sandbox entitlements template + render script, tauri.mas.conf.json overlay (Apple Distribution signing, embedded provisioning profile, no sidecar resources), and `npm run tauri:build:mas`. - Shapefile companions: the sandbox denies reading unselected siblings of a picked .shp, so the MAS build matches .dbf/.prj/.shx/.cpg out of the same multi-selection instead. - mas-store.yml workflow (manual dispatch, mirrors msix-store.yml): builds the universal sandboxed app, verifies the sandbox entitlement, and uploads a signed .pkg artifact for Transporter submission. - docs/mac-app-store.md documents the variant, its limitations, the required secrets, and the submission steps. Also fixes cargo check --features native-duckdb, broken by the locked duckdb crate making Value non-exhaustive. * Address review feedback - Widen the MAS shapefile companion set to the Rust command's full 16-extension list and make companion extensions selectable in the MAS file dialog (they were excluded by the vector filter, so the multi-select fallback could not receive them). - Add APPLE_MAS_CERTIFICATE_PASSWORD to the workflow's secrets check so a missing password fails with the actionable message. - Set persist-credentials: false on the mas-store.yml checkout. - Pin dtolnay/rust-toolchain to the current stable commit SHA; the job later holds signing certificates in its keychain. - Replace envsubst with sed in render-mas-entitlements.sh (gettext is not preinstalled on macOS runners) and validate the team ID format. - Filter MAS-hidden Add Data kinds from the command palette and reject them in the OPEN_ADD_DATA_EVENT handler. - Disable the Segmentation form inputs under MAS via formUnavailable, kept separate from webUnavailable so the desktop-download CTA does not appear on a desktop build. - Move the hardcoded raster sidecar message into i18n (toolbar.rasterTool.needsDesktopSidecar). - Document the security-scoped-bookmark project-restore limitation in docs/mac-app-store.md. | 1 个月前 | |
docs: simplify Homebrew install now that the cask is in the official tap (#948) GeoLibre is published in Homebrew/homebrew-cask (Casks/g/geolibre.rb), so installation no longer needs a custom tap or 'brew trust'. Update README and docs/downloads.md to 'brew install --cask geolibre', add an uninstall snippet, and refresh the stale 'not yet submitted' comments in the cask render scripts. Keep the self-hosted tap (opengeos/homebrew-geolibre) and its release job as a fallback. | 3 个月前 | |
fix(build): keep build-machine credentials out of redistributable builds (#2130) * fix(build): keep build-machine credentials out of redistributable builds An artifact we hand to someone else -- the Jupyter wheel above all -- must carry no credential of ours. Three properties of this build made that easy to get wrong: 1. vite.config.ts bridges bare shell vars into their VITE_ names (GOOGLE_MAPS_API_KEY, MAPBOX_TOKEN, CESIUM_TOKEN), so the build machine's shell is build input. 2. Something in the graph reads `import.meta.env` as a whole object, so Vite stops replacing per key and inlines the entire env record into every chunk that read reaches. Ours was packages/core/src/runtime-env.ts; the one we cannot fix is @clerk/shared's getEnvVariable.mjs, which does `import.meta.env[name]` with a computed name -- so the inlined record lands in ClerkGate-*.js. 3. hatch_build.py skips the JS build when static/app already exists and GEOLIBRE_FORCE_JS_BUILD is unset, so `python -m build` packages whatever an earlier `npm run build:embed` left staged, with no JavaScript running at all. Changes: - vite.config.ts gains BUILD_ENV_KEYS (allowlist of VITE_ names that may reach a bundle) and CREDENTIAL_ENV_KEYS (the subset that bills to whoever built). pruneBuildEnv() deletes unrecognized VITE_ vars from process.env and blanks credentials in redistributable builds, before Vite reads it -- so a whole-object read has nothing sensitive to inline, including Clerk's. The web deploy is unaffected and keeps its own referrer-restricted keys. - runtime-env.ts reads the allowlisted __GEOLIBRE_BUILD_ENV__ define instead of import.meta.env, and exports getBuildEnvironment() for the five call sites that took a whole-object dependency. - scripts/scan-credentials.mjs verifies build OUTPUT, wired into build-embed.mjs (before staging) and hatch_build.py (before packaging any wheel/sdist, needing no Node -- the path (3) above that no JS-side guard can cover). Both read scripts/credential-patterns.json so they cannot drift; it is force-included into the sdist so sdist -> wheel is gated too. - CesiumJS's own public default Ion token is hash-allowlisted so the guard does not fire on every release. Verified: an embed build on a machine with those shell vars set now emits VITE_*:"" for every credential and no raw token shapes; the web build is unchanged; a planted key makes `python -m build` refuse. Stripped credentials still resolve at runtime through getRuntimeEnvironment()'s Settings -> Environment variables overlay. * Address review feedback - pruneBuildEnv() now covers `.env*` files, not just `process.env`. Only 4 of the 11 credential keys have a bare->prefixed shell bridge; the other 7 are documented in getting-started.md as `.env.local` entries. A key set only in a file was never in `process.env` at prune time, so the loop skipped it and Vite's own loadEnv then resolved the raw value into `import.meta.env` -- reaching the @clerk/shared computed lookup in a redistributable build. Confirmed by planting a file-only VITE_STADIA_API_KEY: it was inlined verbatim, and only the output scan caught it. The sweep now unions process.env and FILE_ENV keys, and blanks rather than deletes file-backed ones, because process.env wins over a .env value only when the key is present (including as ""). The __GEOLIBRE_BUILD_ENV__ record falls back to FILE_ENV too, so a .env.local-configured var still reaches getBuildEnvironment(). - CREDENTIAL_ENV_KEYS is now read from scripts/credential-patterns.json instead of being a second hand-maintained copy, so the name this config strips and the name the scanners look for cannot drift. - The Google API key pattern ends with a lookahead instead of `\b`. Its charset includes `-`, and `\b` needs a word character on the left, so a key ending in `-` did not match before a quote and both scanners missed it. Verified in both JavaScript RegExp and Python re; regression fixtures added on both sides. - _load_patterns() raises instead of returning None. It previously made scan_for_credentials return [] and report "Credential scan clean.", which is indistinguishable in the build log from a genuine clean scan -- failing open, the opposite of the documented intent. - Added python/tests/test_credential_scan.py (9 tests), including one that runs both scanners over the same fixture and asserts identical output. The Python scanner previously had no coverage. The hatchling import in hatch_build.py is now guarded so the scanner is importable standalone. | 1 个月前 | |
fix(tests): make the local quality gate pass off Linux (#2214) CONTRIBUTING asks contributors to run `npm run ci` before opening a pull request, but on macOS `npm run test:frontend` fails with 5 errors and cancels 4 more tests, none of them the contributor's. CI does not catch this because it runs Linux with GNU coreutils and bash 5. Three independent causes, all in test-only or Linux-only helper code: select-single-appimage.sh used `mapfile`, a builtin that arrived in bash 4. macOS still ships bash 3.2 as /bin/bash, where it is simply missing, so all three of its tests failed. Read the lines with a `while IFS= read -r` loop instead, which behaves identically on both shells. render-linux-metainfo.sh validated its DATE with GNU `date -d`. BSD date rejects that flag outright, so the check failed for every date and the script always exited 1. Try GNU first, then BSD's `-j -f` spelling. BSD date normalises an out-of-range day instead of rejecting it (2026-02-31 comes back as 2026-03-03 with a zero exit), so the result is compared against the input rather than trusting the exit status. That is stricter than the previous check on both implementations, and leaves the rendered XML byte-identical. The desktop-settings-url timeout test is not macOS-specific: it asserts that AbortSignal.timeout fires, but Node leaves that timer unref'd, and the stalled fetch it races settles only on that abort. With nothing else pending the event loop drains first and the promise never settles, which fails the test and cancels the three after it. Hold a ref'd timer across the assertion so the abort is guaranteed to land. macOS goes from 5 failed / 4 cancelled to 1 failed / 0 cancelled. The remaining failure is the _frontend.js module-classification issue in #2213, left out deliberately: its fix touches Python packaging. Refs #2213 | 26 天前 | |
fix: proxy local raster URLs in Colab (#2108) * fix: proxy local raster URLs in Colab Route session-scoped raster files through Colab's kernel proxy so xarray layers can load remotely. Always strip outputs from every tracked notebook during pre-commit runs. * Address Claude review feedback - Support local raster rewriting through jupyter-server-proxy as well as Colab. - Rewrite sourcePath independently when it references a registered local file. - Preserve git stderr when notebook discovery fails. * Address CodeRabbit review feedback - Terminate nbstripout option parsing before passing tracked notebook paths. * fix: rebase stale Colab raster proxy URLs Colab gives separate widget views distinct proxy origins. Rebase tokenized raster URLs from an older view onto the current view before restoring layers. * fix(python): preserve raster range requests through Colab proxy * style: auto-format (ruff + oxfmt) [pre-commit.ci] * fix(python): tunnel partial raster responses through Colab * style: auto-format (ruff + oxfmt) [pre-commit.ci] * fix(python): render local rasters as XYZ tiles in Colab * Address review feedback - _server.py: gate the Content-Range/X-GeoLibre-Content-Range headers on a newly parsed range rather than on the presence of a Range header, so a non-bytes unit (e.g. "items=0-1") no longer emits a spec-invalid Content-Range alongside a full-file 200. - _server.py: send Access-Control-Expose-Headers for X-GeoLibre-Content-Range, without which a cross-origin fetch cannot read it and the app leaves the response at 200 instead of rebuilding the 206. - _frontend.js: require an empty port on the Colab proxy hostname, so a look-alike host on a foreign network port does not enter the rewrite path. - _frontend.js: extract the transport gate into an exported canProxyLocalFiles(base, port) and cover it in tests, so a future proxyBase/resolveBase refactor cannot silently stop the rewrite. - geolibre.py: resolve the local raster path once and hand the resolved path to both register_raster_tiles and rasterio.open — GDAL does not expand "~", so a "~/dem.tif" source silently lost its computed bounds. - kernel-proxy-range.ts: export KERNEL_CONTENT_RANGE_HEADER and cross-reference the three mirrored literals from _frontend.js and _server.py; a new test asserts both Python sources against the exported constants. - python-api.md: fix the duplicated "JupyterHub when" clause, matching the wording in docs/python.md. - tests: cover the malformed-range 200, the exposed range header, and both the populated and None bounds paths of add_cog's Colab branch. * Address Claude review feedback - add_cog: the `url:` docstring still claimed a local raster is only reachable when the browser runs on the kernel's host, which this PR made stale. Restate it the way docs/python.md now does: direct reads in local Jupyter/VS Code, kernel-rendered PNG XYZ tiles in Colab, the kernel port via jupyter-server-proxy on JupyterHub, and a hosted URL for an extension-only deployment. * Address Claude review feedback - Remove the Colab range-proxy bridge, which the PNG-tile path in 45599eff superseded and left unreachable: `register_local_file` is called only from `_resolve_raster_source`, which only `add_cog`'s non-Colab branch reaches, so no `/_geolibre_local/` URL is ever emitted under Colab and nothing ever set the `__geolibre_range_proxy` marker the bridge keys on. Deleted `kernel-proxy-range.ts` and its global `fetch` wrapper (installed in `main.tsx` for every GeoLibre user), the marker logic in `_frontend.js`, and the `__geolibre_range` / `X-GeoLibre-Content-Range` transport in `_server.py`, along with their tests. Ordinary `Range` header support — what local Jupyter, VS Code, and jupyter-server-proxy actually use — is unchanged, including the `range_matched` fix from 8af3e91c. - Document the notebook hook's blast radius in docs/contributing.md and expand the comment in .pre-commit-config.yaml: it rewrites tracked notebooks in place, so unstaged locally-executed outputs are erased by any commit, not merely excluded from it. --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> | 1 个月前 | |
fix(ios): ship the real app icon instead of the Tauri placeholder (#1558) Build 2.4.0 (5) reached App Store Connect showing a yellow/cyan swirl that appears nowhere in this repository. It is cargo-mobile2's template icon, baked into the Tauri CLI binary: `tauri ios init` populates gen/apple/Assets.xcassets/AppIcon.appiconset/ from that placeholder and ignores src-tauri/icons/ios entirely. Nothing catches this. A wrong icon produces no build error, no warning in the archive or export, and passes upload validation, so it survived all the way to "Waiting for Review" and was only caught by looking at the listing. And because gen/apple is git-ignored and regenerated on every run, every future build would have shipped the placeholder too. - Add scripts/sync-ios-app-icon.sh, which copies the real icons over the generated catalog and then asserts two invariants: every source icon found a counterpart (so a future Tauri CLI that renames catalog entries fails loudly instead of silently restoring the placeholder), and no icon carries an alpha channel. - Run it in CI right after `tauri ios init`. - Flatten the 18 committed icons in src-tauri/icons/ios to opaque RGB. They all had hasAlpha=yes, and Apple rejects app icons with transparency ("Invalid large app icon ... can't be transparent") at upload, after a full build. This would have blocked the next upload even with the copy fixed. The artwork was already drawn on white with alpha effectively opaque (extrema 254-255, zero fully transparent pixels), so flattening is visually lossless; partially transparent edge pixels are composited over white for determinism. Verified by rebuilding for the simulator: the shipped icons are opaque, the artwork is the OpenGeos globe, and it renders correctly masked on the simulator home screen. Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com> | 1 个月前 | |
feat(desktop): add a sandboxed Mac App Store build variant (#1581) * feat(desktop): add a sandboxed Mac App Store build variant Adds a `mas` build of GeoLibre Desktop that satisfies App Store review rules (App Sandbox, guideline 2.5.2), alongside the existing Developer ID notarized builds: - `mas` cargo feature compiles out everything that downloads or spawns external code: the Python sidecar, Jupyter server, martin tile server, the uv bootstrap, and external plugin installation. Invoke-compatible stubs keep the command registry unchanged; `compile_error!` forbids combining with `native-duckdb` (runtime extension loading). - `GEOLIBRE_MAS_BUILD=1` frontend flag (Vite define, DCE-friendly) hides the sidecar-only UI (AI Segmentation, PostgreSQL, File Geodatabase, plugin marketplace) and routes processing dialogs to their client-side engines (Whitebox WASM, browser conversions, client raster tools, Turf/Pyodide vector tools, CereusDB SQL). The Notebook panel keeps working via JupyterLite, which stays in the MAS bundle. - App Sandbox entitlements template + render script, tauri.mas.conf.json overlay (Apple Distribution signing, embedded provisioning profile, no sidecar resources), and `npm run tauri:build:mas`. - Shapefile companions: the sandbox denies reading unselected siblings of a picked .shp, so the MAS build matches .dbf/.prj/.shx/.cpg out of the same multi-selection instead. - mas-store.yml workflow (manual dispatch, mirrors msix-store.yml): builds the universal sandboxed app, verifies the sandbox entitlement, and uploads a signed .pkg artifact for Transporter submission. - docs/mac-app-store.md documents the variant, its limitations, the required secrets, and the submission steps. Also fixes cargo check --features native-duckdb, broken by the locked duckdb crate making Value non-exhaustive. * Address review feedback - Widen the MAS shapefile companion set to the Rust command's full 16-extension list and make companion extensions selectable in the MAS file dialog (they were excluded by the vector filter, so the multi-select fallback could not receive them). - Add APPLE_MAS_CERTIFICATE_PASSWORD to the workflow's secrets check so a missing password fails with the actionable message. - Set persist-credentials: false on the mas-store.yml checkout. - Pin dtolnay/rust-toolchain to the current stable commit SHA; the job later holds signing certificates in its keychain. - Replace envsubst with sed in render-mas-entitlements.sh (gettext is not preinstalled on macOS runners) and validate the team ID format. - Filter MAS-hidden Add Data kinds from the command palette and reject them in the OPEN_ADD_DATA_EVENT handler. - Disable the Segmentation form inputs under MAS via formUnavailable, kept separate from webUnavailable so the desktop-download CTA does not appear on a desktop build. - Move the hardcoded raster sidecar message into i18n (toolbar.rasterTool.needsDesktopSidecar). - Document the security-scoped-bookmark project-restore limitation in docs/mac-app-store.md. | 1 个月前 |
| 文件 | 最后提交记录 | 最后更新时间 |
|---|---|---|
| 1 个月前 | ||
| 17 天前 | ||
| 13 天前 | ||
| 1 个月前 | ||
| 1 个月前 | ||
| 1 个月前 | ||
| 2 天前 | ||
| 1 个月前 | ||
| 12 天前 | ||
| 6 小时前 | ||
| 2 个月前 | ||
| 2 个月前 | ||
| 3 小时前 | ||
| 2 个月前 | ||
| 3 天前 | ||
| 2 个月前 | ||
| 1 个月前 | ||
| 1 个月前 | ||
| 1 个月前 | ||
| 1 个月前 | ||
| 1 个月前 | ||
| 3 个月前 | ||
| 3 个月前 | ||
| 3 个月前 | ||
| 26 天前 | ||
| 1 个月前 | ||
| 3 个月前 | ||
| 1 个月前 | ||
| 26 天前 | ||
| 1 个月前 | ||
| 1 个月前 | ||
| 1 个月前 |