From bafd4f29affe213d1840c2824e58bcc8943abfd4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mark=20Randall=20Havens=20=E2=96=B3=20The=20Empathic=20Tec?= =?UTF-8?q?hnologist=20=E2=9F=81=20Doctor=20Who=2042?= Date: Sat, 8 Nov 2025 12:32:47 -0600 Subject: [PATCH] Update app.js --- public/app.js | 53 +++++++++++++++++++++++++++++++++++++++++---------- 1 file changed, 43 insertions(+), 10 deletions(-) diff --git a/public/app.js b/public/app.js index efae277..0356ff2 100755 --- a/public/app.js +++ b/public/app.js @@ -1,5 +1,5 @@ /* ============================================================ - Self-Organizing Static Site Framework v2.3.2 + Self-Organizing Static Site Framework v2.3.3 ============================================================ */ let INDEX, CURRENT_PATH = null, PATH_TO_EL = new Map(); @@ -17,12 +17,30 @@ const sidebar = document.querySelector(".sidebar"); const navToggle = document.getElementById("navToggle"); const overlay = document.querySelector(".overlay"); -/* --- Navigation toggle --- */ +/* ------------------------------------------------------------ + Utility: ensure external libs are ready before running +------------------------------------------------------------- */ +async function ensureLibsReady() { + let tries = 0; + while ((!window.marked || !window.DOMPurify) && tries < 40) { + await new Promise(r => setTimeout(r, 100)); + tries++; + } + if (!window.marked) console.warn("⚠️ marked.js not detected — markdown will show as plain text."); + if (!window.DOMPurify) console.warn("⚠️ DOMPurify not detected — HTML not sanitized."); +} + +/* ------------------------------------------------------------ + Sidebar toggle and overlay +------------------------------------------------------------- */ navToggle.addEventListener("click", () => sidebar.classList.toggle("open")); overlay.addEventListener("click", () => sidebar.classList.remove("open")); -/* --- Index load --- */ +/* ------------------------------------------------------------ + Load and render index.json +------------------------------------------------------------- */ async function loadIndex() { + await ensureLibsReady(); const res = await fetch("/index.json", { cache: "no-store" }); INDEX = await res.json(); populateFilters(); @@ -49,7 +67,9 @@ function populateFilters() { } } -/* --- Tree build --- */ +/* ------------------------------------------------------------ + Build directory tree +------------------------------------------------------------- */ function rebuildTree() { treeEl.innerHTML = ""; PATH_TO_EL.clear(); @@ -111,7 +131,9 @@ function renderNode(n) { function iconForExt(ext){return ext===".md"?"📝":"🧩";} function fmtDate(ms){return new Date(ms).toISOString().slice(0,10);} -/* --- Path openers --- */ +/* ------------------------------------------------------------ + Path navigation +------------------------------------------------------------- */ function findDir(p){ p=p.replace(/\/$/,''); function search(n){ @@ -145,7 +167,9 @@ async function openPath(path){ if(window.innerWidth<900) sidebar.classList.remove("open"); } -/* --- Markdown renderer (v2.3.2 fix) --- */ +/* ------------------------------------------------------------ + Markdown rendering (stable version) +------------------------------------------------------------- */ async function renderMarkdown(path){ mdView.innerHTML="

Loading…

"; htmlView.style.display="none"; @@ -164,6 +188,7 @@ async function renderMarkdown(path){ requestAnimationFrame(()=>{ mdView.innerHTML=safe; + mdView.scrollTop=0; // reset scroll to top mdView.classList.add("fade-in"); mdView.style.display="block"; }); @@ -174,14 +199,18 @@ async function renderMarkdown(path){ } } -/* --- HTML viewer --- */ +/* ------------------------------------------------------------ + HTML rendering +------------------------------------------------------------- */ function renderHTML(path){ htmlView.src="/"+path; htmlView.style.display="block"; mdView.style.display="none"; } -/* --- Active / Pager --- */ +/* ------------------------------------------------------------ + Active state + pager +------------------------------------------------------------- */ function setActive(path){ document.querySelectorAll(".file.active").forEach(el=>el.classList.remove("active")); const el=PATH_TO_EL.get(path); @@ -212,7 +241,9 @@ function updatePager(){ nextBtn.onclick=()=>i{ clearTimeout(searchTimer); @@ -221,7 +252,9 @@ searchBox.addEventListener("input",()=>{ sortSel.addEventListener("change",rebuildTree); filterSel.addEventListener("change",rebuildTree); -/* --- Internal link interception --- */ +/* ------------------------------------------------------------ + Internal link interception +------------------------------------------------------------- */ document.body.addEventListener("click",e=>{ const a=e.target.closest("a[href]"); if(!a) return;