feat(lab): phase 4 -- hash-router for sim deep-links
URL #sim/<name> deep-links: F5 restores sim, browser back/forward switches between sims, click on sim-card updates URL. 34 sims mapped via _SIM_HASH_MAP (built dynamically from SIMS array). Unknown hash -> console.warn fallback. Recursion guard prevents double-activation on programmatic navigate. closeSim clears hash via history.pushState (no hashchange loop). Embed mode excluded from hash updates (?embed=1 workflow unaffected). Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -818,8 +818,71 @@
|
||||
} else {
|
||||
renderSims();
|
||||
if (_autoSim) openSim(_autoSim);
|
||||
// hash-router: activate sim from URL fragment after catalogue renders
|
||||
else _activateFromHash();
|
||||
}
|
||||
});
|
||||
lucide.createIcons();
|
||||
LS.notif.init();
|
||||
}
|
||||
|
||||
/* ─── Hash router for sim deep-links ─────────────────────────────────────
|
||||
URL pattern: /lab#sim/<name>
|
||||
<name> matches SIMS[i].id (e.g. 'projectile', 'graph', 'chemsandbox').
|
||||
F5 restores sim. Browser back/forward switches between sims.
|
||||
Click on sim-card updates URL via wrapped openSim.
|
||||
──────────────────────────────────────────────────────────────────────── */
|
||||
|
||||
// Build valid-id set from SIMS catalogue (filters out "coming soon" entries)
|
||||
const _SIM_HASH_MAP = {};
|
||||
SIMS.forEach(function(s) { if (s.id) { _SIM_HASH_MAP[s.id] = s.id; } });
|
||||
|
||||
var _routerNavigating = false;
|
||||
|
||||
function _activateFromHash() {
|
||||
var m = (location.hash || '').match(/^#sim\/([\w-]+)/);
|
||||
if (!m) return false;
|
||||
var simName = m[1];
|
||||
if (!_SIM_HASH_MAP[simName]) {
|
||||
// eslint-disable-next-line no-console
|
||||
window.console && window.console.warn('lab-router: unknown sim', simName);
|
||||
return false;
|
||||
}
|
||||
openSim(simName);
|
||||
return true;
|
||||
}
|
||||
|
||||
// Intercept openSim to push URL hash on user-initiated navigation
|
||||
var _origOpenSim = openSim;
|
||||
openSim = function(id) {
|
||||
_origOpenSim(id);
|
||||
if (!_routerNavigating && !_embedMode) {
|
||||
var baseId = id.includes(':') ? id.split(':')[0] : id;
|
||||
if (_SIM_HASH_MAP[baseId]) {
|
||||
_routerNavigating = true;
|
||||
location.hash = '#sim/' + baseId;
|
||||
// use setTimeout so hashchange fires after flag is set
|
||||
setTimeout(function() { _routerNavigating = false; }, 0);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// Intercept closeSim to clear hash when returning to home grid
|
||||
var _origCloseSim = closeSim;
|
||||
closeSim = function() {
|
||||
_origCloseSim();
|
||||
if (!_embedMode) {
|
||||
_routerNavigating = true;
|
||||
history.pushState(null, '', location.pathname + location.search);
|
||||
setTimeout(function() { _routerNavigating = false; }, 0);
|
||||
}
|
||||
};
|
||||
|
||||
// Browser back/forward navigation
|
||||
window.addEventListener('hashchange', function() {
|
||||
if (_routerNavigating) return;
|
||||
var hasHash = _activateFromHash();
|
||||
if (!hasHash && document.getElementById('lab-sim').classList.contains('open')) {
|
||||
_origCloseSim();
|
||||
}
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user