From a2c9e3b111bd2d3f758c4332a7164b69b365cb97 Mon Sep 17 00:00:00 2001 From: Mark Randall Havens Date: Sun, 9 Nov 2025 14:47:26 +0000 Subject: [PATCH] update with harmonozer --- package.json | 2 +- public/app.jss | 313 ++++++++++++++++++++++++++++++++++++++++++++++ public/styles.css | 54 ++++++++ 3 files changed, 368 insertions(+), 1 deletion(-) create mode 100644 public/app.jss diff --git a/package.json b/package.json index b6559af..9e3d97f 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "the-fold-within", - "version": "3.1.0", + "version": "3.2.0", "dependencies": { "pdf-parse": "^1.1.1" } diff --git a/public/app.jss b/public/app.jss new file mode 100644 index 0000000..4f3d0e2 --- /dev/null +++ b/public/app.jss @@ -0,0 +1,313 @@ +const els = { + menuBtn: document.getElementById("menuBtn"), + primaryNav: document.getElementById("primaryNav"), + subNav: document.getElementById("subNav"), + sectionSelect: document.getElementById("sectionSelect"), + tagSelect: document.getElementById("tagSelect"), + sortSelect: document.getElementById("sortSelect"), + searchMode: document.getElementById("searchMode"), + searchBox: document.getElementById("searchBox"), + postList: document.getElementById("postList"), + viewer: document.getElementById("viewer"), + content: document.getElementById("content"), + toggleControls: document.getElementById("toggleControls"), + filterPanel: document.getElementById("filterPanel") +}; + +let indexData = null; +let sidebarOpen = false; +let currentParent = null; +let indexFiles = null; + +async function init() { + try { + indexData = await (await fetch("index.json")).json(); + indexFiles = indexData.flat.filter(f => f.isIndex); + populateNav(); + populateSections(); + populateTags(); + wireUI(); + renderList(); + handleHash(); + window.addEventListener("hashchange", handleHash); + } catch (e) { + els.viewer.innerHTML = "

Error

Failed to load site data.

"; + } +} + +function populateNav() { + els.primaryNav.innerHTML = 'Home'; + const navSections = [...new Set( + indexData.flat + .filter(f => f.isIndex && f.path.split("/").length > 1) + .map(f => f.path.split("/")[0]) + )].sort(); + navSections.forEach(s => { + els.primaryNav.innerHTML += `${s.charAt(0).toUpperCase() + s.slice(1)}`; + }); +} + +function populateSections() { + els.sectionSelect.innerHTML = ''; + indexData.sections.forEach(s => { + const opt = document.createElement("option"); + opt.value = s; opt.textContent = s; + els.sectionSelect.appendChild(opt); + }); + + if (indexData.sections.includes("posts")) { + els.sectionSelect.value = "posts"; + } else if (indexData.sections.length > 0) { + els.sectionSelect.value = indexData.sections[0]; + } +} + +function populateTags() { + indexData.tags.forEach(t => { + const opt = document.createElement("option"); + opt.value = t; opt.textContent = t; + els.tagSelect.appendChild(opt); + }); +} + +function formatTimestamp(ms) { + const d = new Date(ms); + return `${d.getFullYear()}-${String(d.getMonth()+1).padStart(2,'0')}-${String(d.getDate()).padStart(2,'0')} ${String(d.getHours()).padStart(2,'0')}:${String(d.getMinutes()).padStart(2,'0')}`; +} + +function wireUI() { + els.menuBtn.addEventListener("click", () => { + sidebarOpen = !sidebarOpen; + document.body.classList.toggle("sidebar-open", sidebarOpen); + }); + + els.toggleControls.addEventListener("click", () => { + const open = els.filterPanel.open; + els.filterPanel.open = !open; + els.toggleControls.textContent = open ? "Filters" : "Hide"; + }); + + els.sectionSelect.addEventListener("change", () => { + renderList(); + if (els.sectionSelect.value !== "all") loadDefaultForSection(els.sectionSelect.value); + }); + + [els.tagSelect, els.sortSelect, els.searchMode].forEach(el => el.addEventListener("change", renderList)); + els.searchBox.addEventListener("input", renderList); + + els.content.addEventListener("click", (e) => { + if (window.innerWidth < 1024 && document.body.classList.contains("sidebar-open")) { + if (!e.target.closest("#sidebar")) { + document.body.classList.remove("sidebar-open"); + sidebarOpen = false; + } + } + }); +} + +function renderList() { + const section = els.sectionSelect.value; + const tags = Array.from(els.tagSelect.selectedOptions).map(o => o.value.toLowerCase()); + const sort = els.sortSelect.value; + const mode = els.searchMode.value; + const query = els.searchBox.value.toLowerCase(); + + let posts = indexData.flat.filter(p => !p.isIndex); + if (section !== "all") posts = posts.filter(p => p.path.split('/')[0] === section); + if (tags.length) posts = posts.filter(p => tags.every(t => p.tags.includes(t))); + if (query) { + posts = posts.filter(p => { + const text = mode === "content" ? p.title + " " + p.excerpt : p.title; + return text.toLowerCase().includes(query); + }); + } + posts.sort((a, b) => sort === "newest" ? b.mtime - a.mtime : a.mtime - b.mtime); + + els.postList.innerHTML = posts.length ? "" : "
  • No posts found.
  • "; + posts.forEach(p => { + const li = document.createElement("li"); + const pin = p.isPinned ? "Star " : ""; + const time = formatTimestamp(p.ctime); + li.innerHTML = `${pin}${p.title}${time}`; + els.postList.appendChild(li); + }); +} + +function loadDefaultForSection(section) { + const posts = indexData.flat.filter(p => p.path.split('/')[0] === section && !p.isIndex); + if (!posts.length) { + els.viewer.innerHTML = `

    ${section}

    No content yet.

    `; + return; + } + const pinned = posts.find(p => p.isPinned) || posts.sort((a,b) => b.mtime - a.mtime)[0]; + location.hash = `#/${pinned.path}`; +} + +function renderSubNav(parent) { + const subnav = els.subNav; + subnav.innerHTML = ""; + subnav.classList.remove("visible"); + + if (!parent || !indexData.hierarchies?.[parent]) return; + + const subs = indexData.hierarchies[parent]; + subs.forEach(child => { + const link = document.createElement("a"); + link.href = `#/${parent}/${child}/`; + link.textContent = child.charAt(0).toUpperCase() + child.slice(1); + subnav.appendChild(link); + }); + + requestAnimationFrame(() => { + subnav.classList.add("visible"); + }); +} + +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) { + currentParent = currentParentPath; + renderSubNav(currentParent); + } + + const topSection = parts[0] || null; + if (topSection && indexData.sections.includes(topSection)) { + els.sectionSelect.value = topSection; + renderList(); + } + + if (!rel) return renderDefault(); + + if (rel.endsWith('/')) { + const currentPath = parts.join("/"); + const indexFile = indexFiles.find(f => { + const dir = f.path.split("/").slice(0, -1).join("/"); + return dir === currentPath; + }); + + if (indexFile) { + try { + if (indexFile.ext === ".md") { + const src = await fetch(indexFile.path).then(r => r.ok ? r.text() : ""); + const html = marked.parse(src || `# ${currentPath.split("/").pop()}\n\nNo content yet.`); + els.viewer.innerHTML = `
    ${html}
    `; + } else { + await renderIframe("/" + indexFile.path); + } + } catch (e) { + els.viewer.innerHTML = `

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

    No content yet.

    `; + } + } else { + if (topSection) { + els.sectionSelect.value = topSection; + renderList(); + loadDefaultForSection(topSection); + } else { + els.viewer.innerHTML = `

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

    No content yet.

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

    404

    Not found.

    "; + return; + } + file.ext === ".md" ? await renderMarkdown(file.path) : await renderIframe("/" + file.path); + } +} + +async function renderMarkdown(rel) { + const src = await fetch(rel).then(r => r.ok ? r.text() : ""); + els.viewer.innerHTML = `
    ${marked.parse(src || "# Untitled")}
    `; +} + +// === HARMONIZER ENGINE === +async function renderIframe(rel) { + const mode = await detectHarmonizerMode(rel); + if (mode === 'full') return renderIframeFull(rel); + return renderIframeHarmonized(rel, mode); +} + +async function detectHarmonizerMode(rel) { + try { + const res = await fetch(rel); + const html = await res.text(); + const match = html.match(/]*>[\s\S]*?<\/head>/gi, "") + .replace(//gi, "") + .replace(//gi, ""); + } else if (mode === 'enhanced') { + html = html + .replace(/]+(youtube|soundcloud|player)).*?<\/script>/gi, "") + .replace(//gi, ""); + } + + const bodyMatch = html.match(/]*>([\s\S]*)<\/body>/i); + const bodyContent = bodyMatch ? bodyMatch[1].trim() : html.trim(); + + els.viewer.innerHTML = ` +
    + +
    +
    ${bodyContent}
    + `; + applyHarmonizerStyles(); + + const btn = els.viewer.querySelector(".popout-btn"); + btn.addEventListener("click", e => window.open(e.target.dataset.src, "_blank")); + } catch (e) { + els.viewer.innerHTML = `

    Error loading

    ${rel}

    `; + } +} + +function renderIframeFull(rel) { + els.viewer.innerHTML = ` +
    + +
    + + `; + const btn = els.viewer.querySelector(".popout-btn"); + btn.addEventListener("click", e => window.open(e.target.dataset.src, "_blank")); +} + +function applyHarmonizerStyles() { + const el = document.querySelector(".harmonized"); + if (!el) return; + el.querySelectorAll("*").forEach(node => { + node.style.background = "transparent"; + node.style.color = "inherit"; + node.style.fontFamily = "'Inter', system-ui, sans-serif"; + }); +} + +function renderDefault() { + const defaultSection = indexData.sections.includes("posts") ? "posts" : (indexData.sections[0] || null); + if (defaultSection) { + els.sectionSelect.value = defaultSection; + renderList(); + loadDefaultForSection(defaultSection); + } else { + els.viewer.innerHTML = "

    Welcome

    Add content to begin.

    "; + } +} + +init(); diff --git a/public/styles.css b/public/styles.css index 667e7aa..77847b6 100755 --- a/public/styles.css +++ b/public/styles.css @@ -213,3 +213,57 @@ body.sidebar-open #sidebar { transform: translateX(0); } margin: 0; display: block; } + +/* HARMONIZER HEADER */ +.harmonizer-header { + display: flex; + justify-content: flex-end; + align-items: center; + padding: 0.5rem 1rem; + background: rgba(255, 215, 0, 0.05); + border-bottom: 1px solid #333; + position: sticky; + top: 0; + z-index: 10; +} + +.popout-btn { + background: none; + border: 1px solid var(--accent); + color: var(--accent); + border-radius: 6px; + padding: 0.25rem 0.75rem; + font-size: 0.85rem; + cursor: pointer; + transition: background 0.2s ease, color 0.2s ease; +} + +.popout-btn:hover { + background: var(--accent); + color: var(--bg); +} + +/* HARMONIZED BODY CONTEXT */ +.harmonized { + background: transparent; + color: var(--fg); + font-family: 'Inter', system-ui, sans-serif; + max-width: 90ch; + margin: auto; + padding: 3rem 4vw; + line-height: 1.7; +} + +.harmonized h1, .harmonized h2, .harmonized h3, .harmonized h4 { + color: var(--accent); + border-bottom: 1px solid #333; + padding-bottom: 0.3em; + margin-top: 2em; +} + +.harmonized img, .harmonized video, .harmonized iframe { + display: block; + margin: 2rem auto; + max-width: 100%; + height: auto; +}