maputnik-layer
Recipe card from the charly-versa plugin (Images — the deployable catalog).
maputnik — visual MapLibre style editor
Section titled “maputnik — visual MapLibre style editor”Maputnik is a pure-JS SPA for visually editing MapLibre GL JS style
JSON files. Built from upstream source (felt/maputnik) via npm at
image build time, served as a static dist by Python’s stdlib
http.server. Composed by /charly-versa:versa so operators can
iterate on the streets-map style against the in-pod martin tile
server (/charly-versa:osm-tools-layer).
Layer properties
Section titled “Layer properties”| Property | Value |
|---|---|
| Dependencies | supervisord |
| Distros | fedora (sole; needs node + npm + git for build) |
| Build deps | nodejs, npm, git |
| Ports | 8000 (host-mapped to 28000) |
| Service | maputnik (supervisord, restart: always) |
| Static dist | /opt/maputnik/build/ (Vite dist/ renamed) |
Service spec
Section titled “Service spec”maputnik: candy: service: - name: maputnik exec: /usr/bin/python3 -m http.server 8000 --directory /opt/maputnik/build restart: always working_directory: /opt/maputnik priority: 34Pure stdlib server — no marimo-pixi-env coupling. The system
python3 from supervisord’s own RPM dep handles the static serve.
Critical: Vite –base=/ override
Section titled “Critical: Vite –base=/ override”Maputnik’s package.json defines "build": "tsc && vite build".
Vite’s default base path for builds is --base=/maputnik/. That
default bakes /maputnik/assets/* URL references into the emitted
index.html — but we serve the dist at root (/) via
python -m http.server, so all those baked URLs 404 in the
browser.
Symptom (before fix): the maputnik UI loads at
http://127.0.0.1:28000/ showing a blank page; browser dev-tools
shows asset 404s for /maputnik/assets/index-*.js etc.
Fix in the build cmd — an ordered plan: step:
plan: - run: build maputnik from upstream source with the Vite --base=/ override command: | set -euo pipefail git clone --depth 1 https://github.com/maplibre/maputnik /tmp/maputnik cd /tmp/maputnik npm ci --no-audit --no-fund # Override Vite's default --base=/maputnik/ so asset URLs are # root-relative and resolve against our serve path. The `--` # forwards the flag through npm to vite. npm run build -- --base=/ if [ -d dist ]; then mkdir -p /opt/maputnik cp -r dist /opt/maputnik/build else echo "maputnik build did not produce ./dist directory" >&2 ls -la exit 1 fi cd / rm -rf /tmp/maputnik /root/.npm run_as: rootCheck lock-in
Section titled “Check lock-in”A deploy-context check: step greps the served HTML for the
(forbidden) /maputnik/ prefix and fails if present. It is a check:
step, so it is a deterministic
acceptance step that locks in the fix against a future revert to the
Vite default. The step is an ordered plan: entry; its former node name
becomes the step’s id::
plan: - check: maputnik serves a root-relative SPA id: maputnik-asset-base-not-prefixed exit_status: 0 context: [deploy] command: command: | ! curl -fsS http://localhost:8000/ | grep -q '"/maputnik/' in_container: truePlus the standard probe steps (also context: [deploy]):
maputnik-running— supervisord program is RUNNINGmaputnik-port-reachable— TCP 8000 reachablemaputnik-http-up—GET /returns 200
Use case in versa
Section titled “Use case in versa”The streets-map style in /charly-versa:notebook-osm cell 7 is a
small inline JSON. Operators iterating on richer styles can:
- Open maputnik at
http://127.0.0.1:28000/ - Connect to martin’s TileJSON URL
http://127.0.0.1:23000/monaco - Visually edit polygon/line/circle layer paint properties
- Export the style JSON
- Paste into the notebook cell (or save to a server-mounted file
and load via
style: 'http://...'in the MapLibre constructor)
Cross-references
Section titled “Cross-references”/charly-versa:versa— image composing this layer/charly-versa:osm-tools-layer— companion martin tile server/charly-versa:notebook-osm— uses an inline style; maputnik can author richer ones/charly-infrastructure:supervisord— service runtime