2025-10-16 19:45:05 -05:00
|
|
|
// main.js — client router + markdown renderer for The Fold Within
|
2025-10-16 17:25:32 -05:00
|
|
|
|
2025-10-16 18:24:10 -05:00
|
|
|
const state = {
|
|
|
|
|
posts: [],
|
2025-10-16 19:45:05 -05:00
|
|
|
bySlug: new Map(),
|
2025-10-16 18:24:10 -05:00
|
|
|
};
|
2025-10-16 17:52:19 -05:00
|
|
|
|
2025-10-16 19:45:05 -05:00
|
|
|
function $(sel) {
|
|
|
|
|
return document.querySelector(sel);
|
|
|
|
|
}
|
2025-10-16 18:24:10 -05:00
|
|
|
|
|
|
|
|
window.addEventListener("hashchange", router);
|
|
|
|
|
document.addEventListener("DOMContentLoaded", init);
|
|
|
|
|
|
|
|
|
|
async function init() {
|
|
|
|
|
try {
|
|
|
|
|
const res = await fetch("posts/posts.json", { cache: "no-cache" });
|
|
|
|
|
if (!res.ok) throw new Error("Could not load posts index.");
|
|
|
|
|
state.posts = await res.json();
|
2025-10-16 19:45:05 -05:00
|
|
|
state.bySlug = new Map(state.posts.map((p) => [p.slug, p]));
|
2025-10-16 18:24:10 -05:00
|
|
|
router();
|
|
|
|
|
} catch (err) {
|
2025-10-16 19:45:05 -05:00
|
|
|
$("#posts").innerHTML = `<p class="error">⚠️ ${err.message}</p>`;
|
2025-10-16 18:24:10 -05:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function router() {
|
|
|
|
|
const hash = location.hash.replace(/^#/, "");
|
|
|
|
|
const parts = hash.split("/").filter(Boolean);
|
|
|
|
|
|
|
|
|
|
if (parts[0] === "post" && parts[1]) {
|
|
|
|
|
renderPost(parts[1]);
|
|
|
|
|
} else {
|
|
|
|
|
renderIndex();
|
|
|
|
|
}
|
|
|
|
|
}
|
2025-10-16 17:25:32 -05:00
|
|
|
|
2025-10-16 18:24:10 -05:00
|
|
|
function renderIndex() {
|
|
|
|
|
const postsContainer = $("#posts");
|
|
|
|
|
if (!postsContainer) return;
|
2025-10-16 19:45:05 -05:00
|
|
|
|
|
|
|
|
// Clear any loading message
|
2025-10-16 18:24:10 -05:00
|
|
|
postsContainer.innerHTML = "";
|
2025-10-16 17:25:32 -05:00
|
|
|
|
2025-10-16 19:45:05 -05:00
|
|
|
if (!state.posts.length) {
|
|
|
|
|
postsContainer.innerHTML = `<p class="error">⚠️ No posts found.</p>`;
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
state.posts.forEach((post) => {
|
2025-10-16 17:39:09 -05:00
|
|
|
const article = document.createElement("article");
|
|
|
|
|
article.innerHTML = `
|
|
|
|
|
<div class="thumb"></div>
|
|
|
|
|
<h3>${post.title}</h3>
|
2025-10-16 18:24:10 -05:00
|
|
|
<p class="date">${new Date(post.date).toLocaleDateString()}</p>
|
2025-10-16 17:39:09 -05:00
|
|
|
<p>${post.excerpt}</p>
|
|
|
|
|
`;
|
2025-10-16 19:45:05 -05:00
|
|
|
article.addEventListener(
|
|
|
|
|
"click",
|
|
|
|
|
() => (location.hash = `/post/${post.slug}`)
|
|
|
|
|
);
|
2025-10-16 17:39:09 -05:00
|
|
|
postsContainer.appendChild(article);
|
|
|
|
|
});
|
2025-10-16 18:24:10 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
async function renderPost(slug) {
|
|
|
|
|
const main = document.querySelector("main");
|
|
|
|
|
const meta = state.bySlug.get(slug);
|
|
|
|
|
|
|
|
|
|
if (!meta) {
|
|
|
|
|
main.innerHTML = `<p class="error">⚠️ Post not found.</p>`;
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
const res = await fetch(`posts/${meta.file}`, { cache: "no-cache" });
|
|
|
|
|
if (!res.ok) throw new Error("Post file missing.");
|
|
|
|
|
const md = await res.text();
|
2025-10-16 19:45:05 -05:00
|
|
|
|
|
|
|
|
// remove front-matter before rendering
|
|
|
|
|
const clean = md.replace(/^---[\s\S]*?---/, "").trim();
|
|
|
|
|
|
|
|
|
|
const html = marked.parse(clean);
|
|
|
|
|
const date = new Date(meta.date).toLocaleDateString();
|
2025-10-16 18:24:10 -05:00
|
|
|
|
|
|
|
|
main.innerHTML = `
|
|
|
|
|
<section class="post">
|
|
|
|
|
<a href="#/" id="back">← Back to Archive</a>
|
2025-10-16 19:45:05 -05:00
|
|
|
<div class="markdown">
|
|
|
|
|
<h1>${meta.title}</h1>
|
|
|
|
|
<p class="date">${date}</p>
|
|
|
|
|
<hr/>
|
|
|
|
|
${html}
|
|
|
|
|
</div>
|
2025-10-16 18:24:10 -05:00
|
|
|
</section>
|
|
|
|
|
`;
|
2025-10-16 17:39:09 -05:00
|
|
|
|
2025-10-16 18:24:10 -05:00
|
|
|
$("#back").addEventListener("click", () => (location.hash = "/"));
|
|
|
|
|
} catch (err) {
|
|
|
|
|
main.innerHTML = `<p class="error">⚠️ ${err.message}</p>`;
|
2025-10-16 17:25:32 -05:00
|
|
|
}
|
2025-10-16 18:24:10 -05:00
|
|
|
}
|