Keep the page you're on in the address

Each page has an address (#filament, #printers, #collection/12), so a refresh
or bookmark returns to it and Back/Forward move between pages. The search
and filter bar is hidden on the Filament page, where it did nothing.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
dlawler489 2026-09-18 00:19:13 +10:00
parent ff2b0bc11f
commit 2aff27cbdd

View file

@ -71,6 +71,7 @@ function startApp() {
document.getElementById('authScreen').style.display = 'none'; document.getElementById('authScreen').style.display = 'none';
document.querySelector('.container').style.display = ''; document.querySelector('.container').style.display = '';
if (!appStarted) { setupEventListeners(); appStarted = true; } if (!appStarted) { setupEventListeners(); appStarted = true; }
selectedScope = scopeFromHash();
loadData(); loadData();
} }
@ -460,8 +461,9 @@ function scopeTitle() {
} }
function updateScopeChrome() { function updateScopeChrome() {
syncAddress(); // e.g. a deleted collection falling back to All Models
contentTitle.textContent = scopeTitle(); contentTitle.textContent = scopeTitle();
const panel = ['duplicates', 'ai', 'watch', 'printers'].includes(selectedScope); // pages without the model grid const panel = ['duplicates', 'ai', 'watch', 'filament', 'printers'].includes(selectedScope); // pages without the model grid
document.getElementById('filterBar').hidden = panel; document.getElementById('filterBar').hidden = panel;
document.querySelector('.view-options').hidden = panel; document.querySelector('.view-options').hidden = panel;
emptyTrashBtn.hidden = !(selectedScope === 'trash' && trashModels.length); emptyTrashBtn.hidden = !(selectedScope === 'trash' && trashModels.length);
@ -977,12 +979,40 @@ function setupEventListeners() {
document.getElementById('addFilesInput').addEventListener('change', handleAddFiles); document.getElementById('addFilesInput').addEventListener('change', handleAddFiles);
} }
async function selectScope(scope) { // The page you're on lives in the address — #filament, #collection/12 — so a
// refresh or a bookmark comes back to it, and Back and Forward move between pages.
function scopeHash(scope) {
if (scope === 'all') return '';
return SPECIAL_SCOPES.includes(scope) ? `#${scope}` : `#collection/${scope}`;
}
function scopeFromHash() {
const h = decodeURIComponent(location.hash.slice(1));
const collection = h.match(/^collection\/(\d+)$/);
if (collection) return collection[1];
return SPECIAL_SCOPES.includes(h) ? h : 'all';
}
// Keeps the address in step with the page shown; `push` makes a Back step.
function syncAddress(push = false) {
const hash = scopeHash(String(selectedScope));
if (location.hash === hash || (!hash && !location.hash)) return;
history[push ? 'pushState' : 'replaceState'](null, '', `${location.pathname}${location.search}${hash}`);
}
async function selectScope(scope, { push = true } = {}) {
selectedScope = scope; selectedScope = scope;
syncAddress(push);
if (scope === 'trash') await loadTrash(); if (scope === 'trash') await loadTrash();
renderModels(); renderModels();
} }
window.addEventListener('popstate', () => {
if (!appStarted) return;
const scope = scopeFromHash();
if (String(scope) !== String(selectedScope)) selectScope(scope, { push: false });
});
// Tracks the file currently shown in the detail viewer (for thumbnail regen). // Tracks the file currently shown in the detail viewer (for thumbnail regen).
let currentDetailFile = null; let currentDetailFile = null;
let currentDetailThumb = null; // model image, used as a viewer fallback let currentDetailThumb = null; // model image, used as a viewer fallback