diff --git a/src/client/app.ts b/src/client/app.ts
index 4613fa4..44fbd18 100644
--- a/src/client/app.ts
+++ b/src/client/app.ts
@@ -32,6 +32,7 @@ let scheduledRenderTimer = null;
let scheduledRenderOptions = { fitMap: false };
let lastRenderAt = 0;
const SHARE_PARAM_NAME = "share";
+let overlayMapView = null;
const map = L.map("map", { zoomControl: true }).setView([52.5208, 13.4095], 13);
const lightMapLayer = L.tileLayer("https://{s}.basemaps.cartocdn.com/light_all/{z}/{x}/{y}{r}.png", {
@@ -248,6 +249,27 @@ function setMapTheme(theme) {
});
}
+function captureMapView() {
+ const center = map.getCenter();
+ return {
+ center: [center.lat, center.lng],
+ zoom: map.getZoom()
+ };
+}
+
+function rememberOverlayMapView() {
+ overlayMapView = captureMapView();
+}
+
+function restoreOverlayMapView() {
+ if (!overlayMapView) {
+ return;
+ }
+
+ map.setView(overlayMapView.center, overlayMapView.zoom, { animate: false });
+ overlayMapView = null;
+}
+
function setRouteVisible(visible) {
routeVisible = Boolean(visible);
localStorage.setItem(ROUTE_VISIBLE_STORAGE_KEY, routeVisible ? "true" : "false");
@@ -494,6 +516,7 @@ function closeOverlayView() {
overlayLoading.classList.remove("error");
overlayLoadingText.textContent = "Loading full-size image...";
overlayImage.removeAttribute("src");
+ restoreOverlayMapView();
}
closeOverlay.addEventListener("click", closeOverlayView);
@@ -503,7 +526,7 @@ overlay.addEventListener("click", (event) => {
}
});
-function updateRouteView(photos) {
+function updateRouteView(photos, options = {}) {
const routePoints = photos
.filter((photo) => photo.latitude !== null && photo.longitude !== null && photo.capturedAt)
.sort((a, b) => String(a.capturedAt).localeCompare(String(b.capturedAt)))
@@ -511,6 +534,10 @@ function updateRouteView(photos) {
route.setLatLngs(routePoints);
+ if (options.preserveMapView) {
+ return;
+ }
+
if (routePoints.length === 1) {
map.setView(routePoints[0], 13);
} else if (routePoints.length > 1) {
@@ -671,7 +698,8 @@ function renderVisiblePhotos(options = {}) {
marker.on("mouseover", () => marker.openPopup());
marker.on("click", () => {
state.activePhotoId = photo.id;
- renderVisiblePhotos({ fitMap: false });
+ rememberOverlayMapView();
+ renderVisiblePhotos({ fitMap: false, preserveMapView: true });
openOverlay(photo);
});
}
@@ -685,14 +713,15 @@ function renderVisiblePhotos(options = {}) {
'';
item.addEventListener("click", () => {
state.activePhotoId = photo.id;
- renderVisiblePhotos({ fitMap: false });
+ rememberOverlayMapView();
+ renderVisiblePhotos({ fitMap: false, preserveMapView: true });
openOverlay(photo);
});
photoList.appendChild(item);
}
}
- updateRouteView(visiblePhotos);
+ updateRouteView(visiblePhotos, options);
renderTimeline();
if (options.fitMap && visiblePhotos.length) {
diff --git a/src/server/app-assets.ts b/src/server/app-assets.ts
index 2e1ab72..63644fb 100644
--- a/src/server/app-assets.ts
+++ b/src/server/app-assets.ts
@@ -37,7 +37,7 @@ export const APP_STYLES = ` :root {
--photo-active-bg: rgba(51, 65, 85, 0.76);
--photo-active-border: rgba(148, 163, 184, 0.34);
--empty-border: rgba(148, 163, 184, 0.22);
- --overlay-bg: rgba(2, 6, 23, 0.9);
+ --overlay-bg: rgba(2, 6, 23, 0.7);
--card-border: rgba(148, 163, 184, 0.16);
--timeline-surface: rgba(15, 23, 42, 0.88);
--timeline-surface-soft: rgba(15, 23, 42, 0.76);
@@ -97,7 +97,7 @@ export const APP_STYLES = ` :root {
--photo-active-bg: rgba(100, 116, 139, 0.12);
--photo-active-border: rgba(100, 116, 139, 0.32);
--empty-border: rgba(15, 23, 42, 0.16);
- --overlay-bg: rgba(8, 12, 18, 0.78);
+ --overlay-bg: rgba(8, 12, 18, 0.7);
--card-border: rgba(15, 23, 42, 0.11);
--timeline-surface: rgba(255, 255, 255, 0.84);
--timeline-surface-soft: rgba(255, 255, 255, 0.94);
@@ -587,6 +587,7 @@ export const APP_STYLES = ` :root {
display: none;
place-items: center;
background: var(--overlay-bg);
+ backdrop-filter: blur(4px);
z-index: 1000;
padding: 24px;
}
@@ -598,11 +599,13 @@ export const APP_STYLES = ` :root {
.overlay-card {
display: inline-grid;
width: fit-content;
- max-width: 100%;
+ max-width: min(100%, calc(100vw - 48px));
+ gap: 12px;
overflow: hidden;
border-radius: 0;
- background: var(--timeline-surface-soft);
- border-color: var(--timeline-border);
+ background: transparent;
+ border: 0;
+ box-shadow: none;
color: var(--timeline-text);
}
@@ -610,25 +613,32 @@ export const APP_STYLES = ` :root {
position: relative;
width: fit-content;
max-width: 100%;
- min-height: 48vh;
- background: var(--timeline-surface-strong);
+ min-height: 0;
+ background: transparent;
}
.overlay-card header {
- padding: 16px 18px;
- border-bottom: 1px solid var(--timeline-border);
+ padding: 0 4px;
display: flex;
- justify-content: space-between;
+ justify-content: flex-start;
align-items: center;
- background: var(--timeline-surface-strong);
+ background: transparent;
color: var(--timeline-text);
}
+ .overlay-card header strong {
+ display: inline-flex;
+ padding: 8px 12px;
+ background: var(--timeline-surface-soft);
+ border: 1px solid var(--timeline-border);
+ backdrop-filter: blur(14px);
+ }
+
.overlay-card img {
display: block;
width: auto;
- max-width: min(1120px, calc(100vw - 48px));
- max-height: 78vh;
+ max-width: min(1440px, calc(100vw - 48px));
+ max-height: calc(100vh - 112px);
object-fit: contain;
background: var(--timeline-surface-strong);
transition: opacity 120ms ease;
@@ -677,6 +687,27 @@ export const APP_STYLES = ` :root {
color: var(--timeline-text);
}
+ .overlay-close {
+ position: fixed;
+ top: 16px;
+ right: 16px;
+ z-index: 1001;
+ width: 40px;
+ height: 40px;
+ padding: 0;
+ justify-content: center;
+ background: var(--timeline-surface-soft);
+ color: var(--timeline-text);
+ border: 1px solid var(--timeline-border);
+ box-shadow: var(--shadow-soft);
+ backdrop-filter: blur(14px);
+ }
+
+ .overlay-close:hover {
+ background: var(--timeline-surface-strong);
+ border-color: var(--timeline-border-strong);
+ }
+
.thumb {
width: min(210px, 100%);
box-sizing: border-box;
diff --git a/src/server/page-template.ts b/src/server/page-template.ts
index fd8d15c..51442d9 100644
--- a/src/server/page-template.ts
+++ b/src/server/page-template.ts
@@ -140,12 +140,12 @@ ${APP_STYLES}