Notebook panel¶
GeoLibre can run a Jupyter notebook beside the map, in a resizable,
collapsible panel (open it from Processing → Jupyter Notebook). It works in
both the web and desktop builds, with a different Python runtime behind the same
<iframe> panel:
- Web build → JupyterLite. A full
Jupyter UI running entirely in the browser on a Pyodide
(WebAssembly) kernel — no server. Install extra packages on demand from a cell
with
%pip install geopandas. - Desktop build → a real JupyterLab server. The app launches
jupyter labfrom a uv-managed Python environment (the same mechanism as the FastAPI sidecar) on127.0.0.1:8766, token-authenticated, and embeds it. This gives full CPython with the native geospatial stack (geopandas, rasterio, GDAL, …).
This is distinct from the geolibre Python package, which does the
inverse — embedding the whole GeoLibre app inside a notebook cell.
Scripting the map from a cell¶
A lightweight geolibre client is preloaded into the notebook kernel — on web
it is bundled into JupyterLite's filesystem, so no pip install is needed;
on desktop it is placed on the kernel's import path. Just import it:
import geolibre
m = geolibre.connect() # or geolibre.Map()
m.fly_to(-122.4, 37.8, zoom=11) # animate the live map in the left pane
m.add_geojson(gdf, name="My layer") # GeoDataFrame, dict, or JSON string
m.fit_bounds([-123, 37, -122, 38])
m.set_basemap("https://…/style.json")
m.set_visibility(layer_id, False)
m.remove_layer(layer_id)
Calls are fire-and-forget: each posts a command to the host app over the
shared scripting protocol (the same createScriptingHandlers surface used by the
in-app Python console and the Jupyter widget) and returns immediately, so the
client behaves identically on the in-browser kernel and a real server. The
app-side endpoint is useNotebookBridge in
apps/geolibre-desktop/src/hooks/. Canonical client source:
backend/geolibre_server/notebook_client.py.
Read-back queries (e.g.
get_center) are not exposed by this fire-and-forget client; they need the blocking request/reply path thegeolibrewidget uses.
Theme¶
On the web build the notebook follows the app's light/dark theme live (no
reload). JupyterLite is built with exposeAppInBrowser (see
apps/geolibre-desktop/jupyterlite/jupyter-lite.json), which puts its app object
on window.jupyterapp; since the JupyterLite iframe is same-origin,
useNotebookThemeSync reaches in and runs the built-in apputils:change-theme
command whenever the app theme changes. On the desktop build the JupyterLab
server is a different origin, so this cross-origin call is a no-op and the
notebook keeps its own theme (syncing it there would need a small JupyterLab
extension — a future enhancement).
Building the web JupyterLite site¶
The web build embeds a self-hosted JupyterLite site under
apps/geolibre-desktop/public/jupyterlite/. It is not committed (≈70 MB) and
is generated by a best-effort prebuild step that needs the jupyter lite CLI:
pip install -r apps/geolibre-desktop/jupyterlite/requirements.txt
npm run build:jupyterlite
Both npm run dev and npm run build run this automatically:
npm run devbuilds it once on first run via apredevhook (--if-missing), then is instant on subsequent runs.npm run buildalways rebuilds it (viaprebuild) so a changed client/config is picked up.
Both skip gracefully when jupyter lite is not installed — a Node-only build
still succeeds and the web Notebook panel shows a "not built" message until the
site is generated (install the deps above and re-run). The desktop (Tauri) dev
and build paths skip it entirely (they use the real JupyterLab server), so the
static site never bloats the installer.
Build config lives in apps/geolibre-desktop/jupyterlite/ (a
jupyter_lite_config.json, the build requirements.txt, and a starter
files/Welcome.ipynb). The generated directory is excluded from the PWA
precache (see pwaPlugin in apps/geolibre-desktop/vite.config.ts).
Desktop server¶
- Backend extra:
notebookinbackend/geolibre_server/pyproject.toml(jupyterlab,jupyter-server), synced into its own uv project environment so it never disturbs the sidecar's env. - Launcher / lifecycle:
start_jupyter_server/stop_jupyter_serverTauri commands inapps/geolibre-desktop/src-tauri/src/lib.rs(mirrors the sidecar launcher), with the TS wrapper inapps/geolibre-desktop/src/lib/jupyter.ts. - Framing:
backend/geolibre_server/jupyter_server_config.pyrelaxesContent-Security-Policy: frame-ancestorsto the Tauri webview / loopback origins so the app can embed the server; the Tauri CSP (tauri.conf.json) adds the loopback origins toframe-src/child-src.