diff --git a/static/js/app.js b/static/js/app.js
index ee08ffb..29444e5 100644
--- a/static/js/app.js
+++ b/static/js/app.js
@@ -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
/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);
}