fix architectural issues: SPA nav, error handling, CSS bugs, perf

- responsive.js: fix scroll race condition (scroll after innerHTML),
  add error handling for fetch failures, fix implicit global `eid`,
  dispatch `beforenavigate` event for cleanup hooks
- chessbed.js: fix implicit global `ratings` variable
- status.js: clear polling interval on SPA navigation via
  `beforenavigate` event to prevent leak
- App.css: add font-display:swap to all @font-face, fix broken
  media query (missing px unit), consolidate duplicate selectors
  (.concentratedHead, .relative, strong), fix hardcoded bookshelf
  background-image path to use relative URL
- header.html: defer chessbed.js, use p5.min.js instead of p5.js
- monitor.py: use ThreadPoolExecutor for concurrent service checks
- config.py: fix __import__('envs.py') → __import__('envs')
- app.py: rename misleading error handlers (page404→handle_http_error,
  page500→handle_generic_error), fix error info leakage by not passing
  raw exception to InternalServerError, fix hardcoded canonical "404"

https://claude.ai/code/session_01FUhPqQLahEoL6FMxhXkDKa
This commit is contained in:
Claude
2026-02-12 14:39:43 +00:00
parent 9b6e29a15c
commit c8b1f124f2
8 changed files with 77 additions and 55 deletions

View File

@@ -13,30 +13,46 @@ function toggleMenu(collapse=false) {
}
async function goto(location, { push = true } = {}) {
let a = await fetch("/api/goto/" + location, {
credentials: "include",
method: "GET",
mode: "cors",
});
let a;
try {
a = await fetch("/api/goto/" + location, {
credentials: "include",
method: "GET",
mode: "cors",
});
if (!a.ok) {
console.error(`Navigation failed: HTTP ${a.status}`);
return;
}
} catch (err) {
console.error("Navigation fetch failed:", err);
return;
}
document.dispatchEvent(new Event('beforenavigate'));
const response = await a.json();
const metadata = response[0];
const content = response[1];
const root = document.getElementById("root");
root.innerHTML = content;
root.querySelectorAll("script").forEach((oldScript) => {
const newScript = document.createElement("script");
Array.from(oldScript.attributes).forEach(attr => {
newScript.setAttribute(attr.name, attr.value);
});
newScript.textContent = oldScript.textContent;
oldScript.parentNode.replaceChild(newScript, oldScript);
});
if (!window.location.href.includes("#")) {
window.scrollTo({top: 0, left: 0, behavior:"instant"});
} else {
eid = decodeURIComponent(window.location.hash.substring(1))
document.getElementById(eid).scrollIntoView()
const eid = decodeURIComponent(window.location.hash.substring(1));
const el = document.getElementById(eid);
if (el) el.scrollIntoView();
}
const metadata = response[0];
const content = response[1];
let root = document.getElementById("root");
root.innerHTML = content;
root.querySelectorAll("script").forEach((oldScript) => {
const newScript = document.createElement("script");
Array.from(oldScript.attributes).forEach(attr => {
newScript.setAttribute(attr.name, attr.value);
});
newScript.textContent = oldScript.textContent;
oldScript.parentNode.replaceChild(newScript, oldScript);
});
toggleMenu(collapse=true);
document.querySelector("title").textContent = metadata["title"];
if (push) {