You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
feat(export): embed the final map state in the saved chat log (#324) (#326)
The 💾 export dropped the map entirely. Capture the map as it stood at
Save time — one map per log — as an interactive MapLibre embed (live,
pannable) rather than a raster screenshot, so the transcript shows the
layers/styling/filters that were on screen and stays inspectable.
- MapManager.getExportState(): serializable {style, camera, projection};
terrain (keyed MapTiler DEM) stripped so its token can't leak.
- chat-ui buildMapEmbedHtml(): pure helper that re-hydrates a map from the
captured state via the same pinned CDN builds the app uses
(maplibre-gl@5.22.0, pmtiles@3.0.7). Runs the state through the existing
credential scrub + redacts MapTiler key= params; escapes < so a
</script> in a source name/URL can't break out of the JSON block.
- Wire into exportHtml() (guarded — a ChatUI without a map still exports)
and pass mapManager into ChatUI from main.js.
- Tests for both helpers; docs updated (Chat export § Embedded map).
Copy file name to clipboardExpand all lines: docs/guide/configuration.md
+11-2Lines changed: 11 additions & 2 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -719,12 +719,21 @@ The checkpoint is the only per-turn cap on tool use. Setting a value to `0` remo
719
719
720
720
A 💾 save button in the chat footer saves the current conversation as a self-contained HTML document you can share or print. The button is disabled until the first user message and enables automatically after. No configuration — it's always present.
721
721
722
-
The saved file mirrors what the user sees in the live chat: user prompts, assistant prose, and tool-call rows with collapsible SQL and result blocks. Everything is in a single `.html` with inlined CSS — no external assets, no JavaScript required to view it.
722
+
The saved file mirrors what the user sees in the live chat: user prompts, assistant prose, and tool-call rows with collapsible SQL and result blocks, plus the **map as it stood when Save was clicked** (see below).
723
723
724
724
Two guarantees apply to the export:
725
725
726
726
-**Reproducible SQL.** Every `s3://bucket/...` URL inside a SQL block is rewritten to `https://s3-west.nrp-nautilus.io/bucket/...`. Pasting the SQL into any DuckDB with `INSTALL httpfs; LOAD httpfs;` will run it against the public endpoint without secret configuration (public buckets only).
727
-
-**Credential scrubbing.** On top of the live-chat redaction described in the agent-loop docs, the export pass replaces credential-shaped tokens with `[REDACTED]` — DuckDB `CREATE SECRET` key/value pairs, AWS access keys (`aws_access_key_id`, `aws_secret_access_key`), `Authorization: Bearer …` tokens, and pre-signed-URL `X-Amz-Signature` / `X-Amz-Credential` / `X-Amz-Security-Token` query parameters.
727
+
-**Credential scrubbing.** On top of the live-chat redaction described in the agent-loop docs, the export pass replaces credential-shaped tokens with `[REDACTED]` — DuckDB `CREATE SECRET` key/value pairs, AWS access keys (`aws_access_key_id`, `aws_secret_access_key`), `Authorization: Bearer …` tokens, and pre-signed-URL `X-Amz-Signature` / `X-Amz-Credential` / `X-Amz-Security-Token` query parameters. This scrubbing also covers the embedded map state (below).
728
+
729
+
### Embedded map
730
+
731
+
The export captures the final map state — one map per saved log — as an **interactive MapLibre map**, not a static image. It serializes `map.getStyle()` (all sources, layers, and their current paint / filter / visibility) plus the camera (center, zoom, bearing, pitch, and globe-vs-mercator projection), embeds it in the HTML, and re-renders a live, pannable map when the file is opened. Because it's the real style rather than a screenshot, the recipient sees exactly the layers and styling that were on screen and can zoom and inspect them.
732
+
733
+
Trade-offs, by design:
734
+
735
+
-**Not fully offline.** The embedded map loads MapLibre GL JS and PMTiles from the same pinned CDN builds the app uses (`maplibre-gl@5.22.0`, `pmtiles@3.0.7`) and fetches tiles from the original public sources at view time. Without a network connection, the map area shows an error message; the rest of the transcript still renders.
736
+
-**Public layers only.** Terrain (whose DEM source is keyed to a private MapTiler token) is stripped, and any signed or private tile URL is redacted by the credential scrub — so such layers may not appear for a recipient. The transcript text remains complete.
0 commit comments