fix(lightbox): slideshow navigation writes browser history

goToSlide() was replaceState()-ing, so paging through the viewer left a
single history entry — browser Back skipped every plate you'd looked at.
It now pushState()s on each user-driven slide (guarded to fire only when the
index actually changes), popstate slides the viewer back/forward without
stacking entries, and the first entry on a directly-loaded plate page is
tagged with its slug so Back to it is handled in-place.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DZPmxGywFnAhmYJB1eh9fm
This commit is contained in:
2026-08-29 23:34:51 +02:00
co-authored by Claude Sonnet 5
parent 2dc7a0063f
commit 0b0fb8d199
+18 -7
View File
@@ -289,9 +289,14 @@
preloadNeighbors(lbIdx);
}
function goToSlide(idx, smooth = true) {
// smooth — animate the track (false = jump, used when first opening)
// record — push a history entry + sync <title>/meta (default follows `smooth`;
// popstate passes false so back/forward doesn't stack entries)
function goToSlide(idx, smooth = true, record = smooth) {
if (!lbTrack) return;
lbIdx = Math.max(0, Math.min(idx, lbList.length - 1));
const next = Math.max(0, Math.min(idx, lbList.length - 1));
const changed = next !== lbIdx;
lbIdx = next;
lbTrack.style.transition = smooth ? '' : 'none';
lbTrack.style.transform = `translateX(-${lbIdx * 100}%)`;
if (!smooth) lbTrack.getBoundingClientRect(); // force reflow
@@ -302,17 +307,17 @@
lbBuildMeta(p);
syncThumbs();
preloadNeighbors(lbIdx);
if (p && smooth) {
if (p) {
const postTitle = p.title + ' — Roux';
const postUrl = new URL(p.url, location.origin).href;
document.title = postTitle;
history.replaceState({ slug: p.slug }, '', p.url);
setMeta('name', 'description', p.description || null);
setMeta('property', 'og:title', postTitle);
setMeta('property', 'og:description', p.description || null);
setMeta('property', 'og:url', postUrl);
const canon = document.querySelector('link[rel="canonical"]');
if (canon) canon.href = postUrl;
if (record && changed) history.pushState({ slug: p.slug }, '', p.url);
}
}
@@ -440,15 +445,19 @@
// Handle browser back/forward.
window.addEventListener('popstate', e => {
const slug = e.state && e.state.slug;
// Entry points at a plate we already know about → show it in the lightbox now,
// no page fetch needed (covers sliding + card-opened plates).
if (slug && POSTS.some(p => p.slug === slug)) {
// Already in the viewer with this plate in the list → just slide to it.
if (lb && lb.dataset.open === 'true') {
const i = lbList.findIndex(p => p.slug === slug);
if (i !== -1) { goToSlide(i, true, false); return; }
}
// Otherwise (re)open the viewer at this plate — no page fetch needed.
const opened = POSTS.find(p => p.slug === slug);
const scoped = POSTS.filter(p => p.issue === opened.issue);
lbOpen(slug, scoped.length ? scoped : POSTS);
return;
}
// Otherwise re-render #content for the URL we landed on (also closes the lightbox).
// Not a plate → re-render #content for the URL we landed on (also closes the viewer).
navigate(location.href, { push: false });
});
@@ -562,6 +571,8 @@
const opened = POSTS.find(p => p.slug === window.__ROUX_OPEN_SLUG);
const issueId = opened ? opened.issue : null;
const scoped = issueId ? POSTS.filter(p => p.issue === issueId) : POSTS;
// Tag this first history entry so back-navigation to it is handled by the viewer.
if (opened) history.replaceState({ slug: opened.slug }, '', location.href);
lbOpen(window.__ROUX_OPEN_SLUG, scoped.length ? scoped : POSTS);
}