From 99f9f648ba10c6e1b51035d409f8ca93cb1bb6ab Mon Sep 17 00:00:00 2001 From: Arne Baeumler Date: Sun, 14 Jun 2026 11:19:46 +0200 Subject: [PATCH] feat: add fullscreen download action --- src/client/app.ts | 17 +++++++++++++++++ src/server/app-assets.ts | 34 +++++++++++++++++++++++++++++++++- src/server/page-template.ts | 13 ++++++++++--- 3 files changed, 60 insertions(+), 4 deletions(-) diff --git a/src/client/app.ts b/src/client/app.ts index 68182dd..3789270 100644 --- a/src/client/app.ts +++ b/src/client/app.ts @@ -75,6 +75,7 @@ const overlayLoadingText = mustGet("overlay-loading-text"); const overlayMedia = mustGet("overlay-media"); const overlayPrev = mustGet("overlay-prev"); const overlayNext = mustGet("overlay-next"); +const overlayDownload = mustGet("overlay-download"); const closeOverlay = mustGet("close-overlay"); const routeToggle = mustGet("route-toggle"); const routeToggleIcon = mustGet("route-toggle-icon"); @@ -274,6 +275,20 @@ function syncPhotoNameInLocation(photoName) { } } +function syncOverlayDownload(photo) { + if (!photo) { + overlayDownload.setAttribute("href", "#"); + overlayDownload.setAttribute("aria-disabled", "true"); + overlayDownload.removeAttribute("download"); + return; + } + + overlayDownload.href = photo.fullUrl; + overlayDownload.download = photo.name; + overlayDownload.setAttribute("aria-label", "Download photo"); + overlayDownload.setAttribute("aria-disabled", "false"); +} + function getOverlayPhotos() { if (!state.visiblePhotos.length) { return state.photos; @@ -566,6 +581,7 @@ function openOverlay(photo) { overlay.setAttribute("aria-hidden", "false"); overlayTitle.textContent = photo.name; overlayImage.alt = photo.name; + syncOverlayDownload(photo); overlay.classList.add("loading"); overlayLoading.classList.add("active"); overlayLoading.classList.remove("error"); @@ -600,6 +616,7 @@ function closeOverlayView() { overlayLoading.classList.remove("error"); overlayLoadingText.textContent = "Loading full-size image..."; overlayImage.removeAttribute("src"); + syncOverlayDownload(null); syncPhotoNameInLocation(""); overlaySwipeState = null; } diff --git a/src/server/app-assets.ts b/src/server/app-assets.ts index 617470e..e8be8b5 100644 --- a/src/server/app-assets.ts +++ b/src/server/app-assets.ts @@ -105,7 +105,7 @@ export const APP_STYLES = ` :root { --overlay-media-bg: rgba(248, 250, 252, 0.94); --overlay-media-border: rgba(148, 163, 184, 0.26); --overlay-media-shadow: 0 10px 24px rgba(15, 23, 42, 0.08); - --overlay-caption-text: rgba(248, 250, 252, 0.95); + --overlay-caption-text: #303030; --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); @@ -730,6 +730,38 @@ export const APP_STYLES = ` :root { border-color: var(--timeline-border-strong); } + .overlay-download { + position: fixed; + top: 16px; + right: 64px; + 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); + text-decoration: none; + } + + .overlay-action { + display: inline-flex; + align-items: center; + } + + .overlay-download:hover { + background: var(--timeline-surface-strong); + border-color: var(--timeline-border-strong); + } + + .overlay-download[aria-disabled="true"] { + opacity: 0.45; + pointer-events: none; + } + .overlay-nav { position: fixed; top: 50%; diff --git a/src/server/page-template.ts b/src/server/page-template.ts index 5b9d870..1184745 100644 --- a/src/server/page-template.ts +++ b/src/server/page-template.ts @@ -147,9 +147,16 @@ ${APP_STYLES} - + + + +