diff --git a/public/app.jss b/public/app.jss index 4f3d0e2..0393ac2 100644 --- a/public/app.jss +++ b/public/app.jss @@ -17,12 +17,12 @@ const els = { let indexData = null; let sidebarOpen = false; let currentParent = null; -let indexFiles = null; +let indexFiles = null; // Cached async function init() { try { indexData = await (await fetch("index.json")).json(); - indexFiles = indexData.flat.filter(f => f.isIndex); + indexFiles = indexData.flat.filter(f => f.isIndex); // Cache populateNav(); populateSections(); populateTags(); @@ -92,7 +92,7 @@ function wireUI() { if (els.sectionSelect.value !== "all") loadDefaultForSection(els.sectionSelect.value); }); - [els.tagSelect, els.sortSelect, els.searchMode].forEach(el => el.addEventListener("change", renderList)); + [els.tagSelect, els.sortNou, els.searchMode].forEach(el => el.addEventListener("change", renderList)); els.searchBox.addEventListener("input", renderList); els.content.addEventListener("click", (e) => { @@ -143,6 +143,7 @@ function loadDefaultForSection(section) { location.hash = `#/${pinned.path}`; } +// NESTED HORIZON: Deep-Aware Sub-Navigation function renderSubNav(parent) { const subnav = els.subNav; subnav.innerHTML = ""; @@ -167,6 +168,7 @@ async function handleHash() { els.viewer.innerHTML = ""; const rel = location.hash.replace(/^#\//, ""); const parts = rel.split("/").filter(Boolean); + const currentParentPath = parts.slice(0, -1).join("/") || parts[0] || null; if (currentParentPath !== currentParent) { @@ -184,6 +186,7 @@ async function handleHash() { if (rel.endsWith('/')) { const currentPath = parts.join("/"); + const indexFile = indexFiles.find(f => { const dir = f.path.split("/").slice(0, -1).join("/"); return dir === currentPath; @@ -196,7 +199,7 @@ async function handleHash() { const html = marked.parse(src || `# ${currentPath.split("/").pop()}\n\nNo content yet.`); els.viewer.innerHTML = `
${html}
`; } else { - await renderIframe("/" + indexFile.path); + await renderIframe("/" + indexFile.path); // Now uses Harmonizer } } catch (e) { els.viewer.innerHTML = `

${currentPath.split("/").pop()}

No content yet.

`; @@ -210,7 +213,8 @@ async function handleHash() { els.viewer.innerHTML = `

${currentPath.split("/").pop()}

No content yet.

`; } } - } else { + } + else { const file = indexData.flat.find(f => f.path === rel); if (!file) { els.viewer.innerHTML = "

404

Not found.

"; @@ -225,13 +229,14 @@ async function renderMarkdown(rel) { els.viewer.innerHTML = `
${marked.parse(src || "# Untitled")}
`; } -// === HARMONIZER ENGINE === +// === HARMONIZER ENGINE CORE === async function renderIframe(rel) { const mode = await detectHarmonizerMode(rel); if (mode === 'full') return renderIframeFull(rel); return renderIframeHarmonized(rel, mode); } +// Detect async function detectHarmonizerMode(rel) { try { const res = await fetch(rel); @@ -243,20 +248,23 @@ async function detectHarmonizerMode(rel) { } } +// Harmonized loader (safe/enhanced) async function renderIframeHarmonized(rel, mode = 'safe') { try { const res = await fetch(rel); if (!res.ok) throw new Error(`HTTP ${res.status}`); let html = await res.text(); + // Strip scripts and styles if (mode === 'safe') { html = html .replace(/]*>[\s\S]*?<\/head>/gi, "") .replace(//gi, "") .replace(//gi, ""); } else if (mode === 'enhanced') { + // Allow YouTube/SoundCloud embeds html = html - .replace(/]+(youtube|soundcloud|player)).*?<\/script>/gi, "") + .replace(/]+(youtube\.com|soundcloud\.com|player)).*?<\/script>/gi, "") .replace(//gi, ""); } @@ -265,7 +273,7 @@ async function renderIframeHarmonized(rel, mode = 'safe') { els.viewer.innerHTML = `
- +
${bodyContent}
`; @@ -278,10 +286,11 @@ async function renderIframeHarmonized(rel, mode = 'safe') { } } +// Full mode: preserve original script behavior inside sandbox function renderIframeFull(rel) { els.viewer.innerHTML = `
- +
`; @@ -289,6 +298,7 @@ function renderIframeFull(rel) { btn.addEventListener("click", e => window.open(e.target.dataset.src, "_blank")); } +// Harmonizer aesthetic pass function applyHarmonizerStyles() { const el = document.querySelector(".harmonized"); if (!el) return; @@ -298,6 +308,7 @@ function applyHarmonizerStyles() { node.style.fontFamily = "'Inter', system-ui, sans-serif"; }); } +// === END HARMONIZER === function renderDefault() { const defaultSection = indexData.sections.includes("posts") ? "posts" : (indexData.sections[0] || null); diff --git a/public/styles.css b/public/styles.css index 77847b6..f2486df 100755 --- a/public/styles.css +++ b/public/styles.css @@ -195,7 +195,7 @@ body.sidebar-open #sidebar { transform: translateX(0); } .viewer > * { width: 100%; margin: 0; - padding: 3rem 4vw; + padding: 0; animation: fadeIn .4s ease-out; } @@ -204,16 +204,6 @@ body.sidebar-open #sidebar { transform: translateX(0); } to { opacity: 1; transform: translateY(0); } } -.viewer iframe { - flex: 1; - width: 100vw; - min-height: 100vh; - border: none; - border-radius: 0; - margin: 0; - display: block; -} - /* HARMONIZER HEADER */ .harmonizer-header { display: flex; @@ -266,4 +256,16 @@ body.sidebar-open #sidebar { transform: translateX(0); } margin: 2rem auto; max-width: 100%; height: auto; + border-radius: var(--radius); +} + +.harmonized a { + color: var(--accent); + text-decoration: underline; + text-decoration-thickness: 1px; + text-underline-offset: 2px; +} + +.harmonized a:hover { + text-shadow: 0 0 6px var(--accent); }