feat(places): photo map view (MapLibre + PMTiles, Approach A)

Adds a Places tab to the Photos section: a self-hosted MapLibre GL map of
the user's geotagged photos.

- places.js: lazy-loads vendored MapLibre + pmtiles.js on first open; draws
  the server-aggregated clusters (GET /api/photos/geo) as HTML thumbnail
  markers (no glyphs/sprites, no client-side clustering); pan/zoom refetches
  for the new viewport; click a cluster to zoom in, or a single photo to
  open it in the lightbox.
- Optional vector basemap read directly from /basemaps/basemap.pmtiles over
  HTTP Range (label-light Protomaps style, light/dark aware); falls back to
  a themed background when no basemap is present. ODbL attribution shown.
- "Moments | Places" sub-nav wired into the Photos section.
- enable_places now defaults on, since the route + UI are ready.

No new backend serving code: tower-http ServeDir already serves static/
with Range, so the .pmtiles basemap is just a static file.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JW6ghFMDtnRYuYNzZhb47M
This commit is contained in:
Claude
2026-06-19 11:17:56 +00:00
parent bb3d73926a
commit 513b6224b4
6 changed files with 479 additions and 3 deletions
+2
View File
@@ -36,6 +36,7 @@
<link rel="stylesheet" href="/css/views/trash.css">
<link rel="stylesheet" href="/css/views/photos.css">
<link rel="stylesheet" href="/css/views/photosLightbox.css">
<link rel="stylesheet" href="/css/views/places.css">
<link rel="stylesheet" href="/css/views/music.css">
<!-- Scripts (defer: download in parallel, execute in order, after HTML parsed) -->
@@ -57,6 +58,7 @@
<script defer type="module" src="/js/features/library/favorites.js"></script>
<script defer type="module" src="/js/features/library/recent.js"></script>
<script defer type="module" src="/js/features/library/photos.js"></script>
<script defer type="module" src="/js/features/library/places.js"></script>
<script defer type="module" src="/js/features/library/music.js"></script>
<script defer type="module" src="/js/features/sharing/fileSharing.js"></script>
<script defer type="module" src="/js/model/recentModel.js"></script>