feat: implement automatic static map image generation

- Add html-to-image for capturing map container with markers
- Auto-generate map image on page/marker save via hooks
- Use flag system (.regenerate-map-image) to trigger generation on Panel reload
- Create file using Kirby API for proper indexing
- Add mapStaticImage field in blueprint to display generated image
- Wait for map to be fully loaded before capture
- Capture entire container (map + custom markers)
- Filter MapLibre controls from capture

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit is contained in:
isUnknown 2026-02-06 11:48:05 +01:00
parent 1d74105910
commit 9193ac8900
8 changed files with 474 additions and 92 deletions

View file

@ -12,6 +12,7 @@
import { ref, watch, onMounted, onBeforeUnmount, nextTick } from "vue";
import maplibregl from "maplibre-gl";
import "maplibre-gl/dist/maplibre-gl.css";
import { toPng } from "html-to-image";
export default {
props: {
@ -97,6 +98,7 @@ export default {
try {
map.value = new maplibregl.Map({
container: mapContainer.value,
preserveDrawingBuffer: true, // Required for canvas.toDataURL()
style: {
version: 8,
sources: {
@ -326,12 +328,55 @@ export default {
}
}
async function captureMapImage() {
console.log('[MapPreview] captureMapImage called');
if (!map.value) {
throw new Error("Map is not initialized");
}
if (!map.value.loaded()) {
throw new Error("Map is not loaded");
}
if (!mapContainer.value) {
throw new Error("Map container not found");
}
console.log('[MapPreview] Map is loaded, capturing container...');
try {
// Capture the entire map container (includes canvas + markers)
const imageDataUrl = await toPng(mapContainer.value, {
quality: 0.95,
pixelRatio: 2, // Higher quality
cacheBust: true,
filter: (node) => {
// Exclude MapLibre controls (zoom buttons, etc.)
if (node.classList && node.classList.contains('maplibregl-ctrl')) {
return false;
}
return true;
}
});
console.log('[MapPreview] Container captured, image size:', imageDataUrl?.length);
return imageDataUrl;
} catch (error) {
console.error("[MapPreview] Error capturing map image:", error);
throw error;
}
}
return {
mapContainer,
loading,
map,
getCurrentCenter,
getCurrentZoom,
centerOnPosition
centerOnPosition,
captureMapImage
};
}
};