summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorKen D'Ambrosio <ken@jots.org>2026-05-12 12:33:17 +0000
committerKen D'Ambrosio <ken@jots.org>2026-05-12 12:33:17 +0000
commitca717d9625cdd60272226db50f0f148c949565c7 (patch)
tree2479d3770390490a3cae04ade1891f44a57e4408
parent3e1a71be6e63696dfe9792f123f24ece1da8116a (diff)
Move slideshow interval control to album page; fix mobile viewport clipping
- Interval input (default 5 s) now lives beside Shuffle/Full screen on the album page; passed as ?interval= param to the slideshow and seeded into the hidden ss-interval input on load. - Added "Interval" label text next to the input. - Fixed slideshow controls being pushed off-screen on mobile by using 100dvh (dynamic viewport height) with 100vh as a fallback, so the layout accounts for mobile browser chrome. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
-rw-r--r--public/css/style.css9
-rw-r--r--public/js/album.js3
-rw-r--r--public/js/slideshow.js4
-rw-r--r--views/album.erb1
-rw-r--r--views/slideshow.erb5
5 files changed, 13 insertions, 9 deletions
diff --git a/public/css/style.css b/public/css/style.css
index a142f8a..b0c73dd 100644
--- a/public/css/style.css
+++ b/public/css/style.css
@@ -247,10 +247,10 @@ main { max-width: 1400px; margin: 0 auto; padding: 24px; }
.lb-next { right: 0; border-radius: var(--radius) 0 0 var(--radius); }
/* ── Slideshow ─────────────────────────────────────────────────────────── */
-.slideshow-page { background: #000; overflow: hidden; height: 100vh; display: flex; }
+.slideshow-page { background: #000; overflow: hidden; height: 100vh; height: 100dvh; display: flex; }
#slideshow {
- width: 100%; height: 100vh;
+ width: 100%; height: 100vh; height: 100dvh;
display: flex; flex-direction: column;
background: #000;
}
@@ -289,9 +289,8 @@ main { max-width: 1400px; margin: 0 auto; padding: 24px; }
flex-shrink: 0;
}
.ss-opt-label { display: inline-flex; align-items: center; gap: 5px; font-size: .9rem; color: var(--text-dim); cursor: pointer; }
-.ss-opt-label input { cursor: pointer; }
-.ss-interval-label { color: var(--text-dim); font-size: .85rem; display: flex; align-items: center; gap: 4px; margin-left: auto; }
-.ss-interval-label input { width: 48px; background: var(--bg3); border: 1px solid var(--border); color: var(--text); border-radius: var(--radius); padding: 2px 6px; }
+.ss-opt-label input[type=checkbox] { cursor: pointer; }
+.ss-opt-label input[type=number] { width: 44px; background: var(--bg3); border: 1px solid var(--border); color: var(--text); border-radius: var(--radius); padding: 2px 6px; }
#ss-counter { position: absolute; top: 12px; right: 16px; color: rgba(255,255,255,.5); font-size: .8rem; pointer-events: none; }
diff --git a/public/js/album.js b/public/js/album.js
index a2301c9..c3f775f 100644
--- a/public/js/album.js
+++ b/public/js/album.js
@@ -129,6 +129,8 @@ window.addEventListener('DOMContentLoaded', () => {
const p = [];
if (document.getElementById('ss-opt-shuffle')?.checked) p.push('shuffle=1');
if (document.getElementById('ss-opt-fullscreen')?.checked) p.push('fullscreen=1');
+ const iv = parseFloat(document.getElementById('ss-opt-interval')?.value);
+ if (iv && iv !== 5) p.push('interval=' + iv);
if (input && input.value.trim()) {
const visible = [...document.querySelectorAll('#album-grid .album-card')]
.filter(c => c.style.display !== 'none')
@@ -153,6 +155,7 @@ window.addEventListener('DOMContentLoaded', () => {
['ss-opt-shuffle', 'ss-opt-fullscreen'].forEach(id =>
document.getElementById(id)?.addEventListener('change', updateSsLink)
);
+ document.getElementById('ss-opt-interval')?.addEventListener('input', updateSsLink);
})();
// Touch swipe
diff --git a/public/js/slideshow.js b/public/js/slideshow.js
index d1a0a95..ddcaa3c 100644
--- a/public/js/slideshow.js
+++ b/public/js/slideshow.js
@@ -203,6 +203,10 @@ const ssParams = new URLSearchParams(location.search);
if (ssParams.get('shuffle') === '1') {
ssQueue = shuffle(SS_ENTRIES);
}
+const ivParam = parseFloat(ssParams.get('interval'));
+if (ivParam >= 1 && ivParam <= 60) {
+ document.getElementById('ss-interval').value = ivParam;
+}
if (ssQueue.length > 0) {
ssShow(0, true);
diff --git a/views/album.erb b/views/album.erb
index 603aa80..ee6e548 100644
--- a/views/album.erb
+++ b/views/album.erb
@@ -15,6 +15,7 @@
<a href="/slideshow/<%= @rel %>" id="ss-launch" data-base="/slideshow/<%= @rel %>" class="btn">Slideshow</a>
<label class="ss-opt-label"><input type="checkbox" id="ss-opt-shuffle"> Shuffle</label>
<label class="ss-opt-label"><input type="checkbox" id="ss-opt-fullscreen"> Full screen</label>
+ <label class="ss-opt-label">Interval <input type="number" id="ss-opt-interval" value="5" min="1" max="60" step="1"> s</label>
<% end %>
</div>
</div>
diff --git a/views/slideshow.erb b/views/slideshow.erb
index dfdf523..24a487b 100644
--- a/views/slideshow.erb
+++ b/views/slideshow.erb
@@ -22,10 +22,7 @@
<button onclick="ssToggle()" id="ss-play-btn" class="btn btn-sm">⏸ Pause</button>
<button onclick="ssNext()" class="btn btn-sm">Next ›</button>
<button onclick="ssFsToggle()" class="btn btn-sm" title="Toggle full screen (F)">⛶</button>
- <label class="ss-interval-label">
- Interval
- <input type="number" id="ss-interval" value="4" min="1" max="60" step="1"> s
- </label>
+ <input type="number" id="ss-interval" value="5" min="1" max="60" step="1" hidden>
</div>
<div id="ss-counter"></div>
</div>