diff --git a/public/scripts/index.js b/public/scripts/index.js index d8cdc30..e176442 100644 --- a/public/scripts/index.js +++ b/public/scripts/index.js @@ -1,126 +1,194 @@ -const TERMINAL_PROMPT = "barrett@ruth:~$ "; -let typing = false; -let clearing = false; +(() => { + if (window.__BT_INDEX_INIT) return; + window.__BT_INDEX_INIT = true; -function clearPrompt(delay, callback) { - if (clearing) return; - clearing = true; + const TERMINAL_PROMPT = "barrett@ruth:~$ "; + let typing = false; + let clearing = false; - const terminalPrompt = document.querySelector(".terminal-prompt"); - if (!terminalPrompt) { - clearing = false; - return; + function promptEl() { + return document.querySelector(".terminal-prompt"); } - const topicLength = - terminalPrompt.innerHTML.length - TERMINAL_PROMPT.length; - let i = 0; + (function restorePrompt() { + const saved = sessionStorage.getItem("terminalPromptText"); + const el = promptEl(); + if (saved && el) el.textContent = saved; + sessionStorage.removeItem("terminalPromptText"); + })(); - function removeChar() { - if (i++ < topicLength) { - terminalPrompt.textContent = terminalPrompt.textContent.slice(0, -1); - setTimeout(removeChar, delay / topicLength); - } else { - i = 0; + function clearPrompt(delay, callback) { + if (clearing) return; + clearing = true; + const el = promptEl(); + if (!el) { clearing = false; - callback && callback(); + return; } - } - - removeChar(); -} - -function typechars(e) { - e.preventDefault(); - - const topicElement = e.target; - if (topicElement.classList.contains("active")) return; - if (typing) return; - typing = true; - - const topic = topicElement.dataset.topic; - const terminalText = ` /${topic.toLowerCase()}`; - const terminalPrompt = document.querySelector(".terminal-prompt"); - const delay = - terminalPrompt.innerHTML.length > TERMINAL_PROMPT.length ? 250 : 500; - - const topics = document.querySelectorAll(".topic a"); - topics.forEach((t) => { - t.classList.remove("active"); - t.style.color = ""; - }); - - topicElement.classList.add("active"); - topicElement.style.color = getTopicColor(topic); - - clearPrompt(delay, () => { + const excess = el.textContent.length - TERMINAL_PROMPT.length; let i = 0; - function typechar() { - if (i < terminalText.length) { - terminalPrompt.innerHTML += terminalText.charAt(i++); - setTimeout(typechar, delay / terminalText.length); + function tick() { + if (i++ < excess) { + el.textContent = el.textContent.slice(0, -1); + setTimeout(tick, delay / Math.max(excess, 1)); } else { - renderPosts(topic); - typing = false; + clearing = false; + callback && callback(); } } - typechar(); - }); -} - -function renderPosts(topic) { - const posts = document.getElementById("posts"); - posts.innerHTML = ""; - - const categoryPosts = postsByCategory[topic]; - - if (!categoryPosts) { - return; + tick(); } - categoryPosts.forEach((post) => { - const postDiv = document.createElement("div"); - postDiv.classList.add("post"); - - const link = document.createElement("a"); - const slug = post.id - .split("/") - .pop() - .replace(/\.mdx?$/, ""); - - link.href = `/posts/${topic}/${slug}.html`; - link.textContent = post.data.title; - link.style.textDecoration = "underline"; - - postDiv.appendChild(link); - posts.appendChild(postDiv); - }); -} - -document.addEventListener("DOMContentLoaded", function () { - const topics = document.querySelectorAll(".topic a"); - - topics.forEach((topic) => { - topic.addEventListener("click", typechars); - - const topicName = topic.dataset.topic; - - topic.addEventListener("mouseenter", () => { - const color = window.getTopicColor(topicName); - topic.style.color = color; - }); - - topic.addEventListener("mouseleave", () => { - if (!topic.classList.contains("active")) { - topic.style.color = ""; + function typeTerminalPath(topic, delay, callback) { + if (typing) return; + typing = true; + const el = promptEl(); + if (!el) return; + const txt = ` /${topic}`; + clearPrompt(delay, () => { + let i = 0; + function step() { + if (i < txt.length) { + el.textContent += txt.charAt(i++); + setTimeout(step, delay / txt.length); + } else { + typing = false; + callback && callback(); + } } + step(); + }); + } + + function persistPrompt() { + const el = promptEl(); + if (el) sessionStorage.setItem("terminalPromptText", el.textContent); + } + + function renderPosts(topic) { + if (!window.postsByCategory) return; + const posts = document.getElementById("posts"); + if (!posts) return; + posts.innerHTML = ""; + const arr = window.postsByCategory[topic]; + if (!arr) return; + for (const post of arr) { + const div = document.createElement("div"); + div.className = "post"; + const a = document.createElement("a"); + const slug = post.id.split("/").pop().replace(/\.mdx?$/, ""); + a.href = `/posts/${topic}/${slug}.html`; + a.textContent = post.data.title; + a.style.textDecoration = "underline"; + div.appendChild(a); + posts.appendChild(div); + } + } + + function handleDataTopicClick(e) { + const link = e.target.closest("[data-topic]"); + if (!link) return; + e.preventDefault(); + if (typing) return; + + const topic = link.dataset.topic.toLowerCase(); + const href = link.getAttribute("href") || "/"; + const delay = 500; + const path = window.location.pathname; + + const colorFn = window.getTopicColor || (() => ""); + const topics = document.querySelectorAll("[data-topic]"); + topics.forEach((t) => { + t.classList.remove("active"); + t.style.color = ""; + }); + link.classList.add("active"); + const c = colorFn(topic); + if (c) link.style.color = c; + + typeTerminalPath(topic, delay, () => { + persistPrompt(); + + if (path === "/" || path === "/index.html") { + if (window.postsByCategory && window.postsByCategory[topic]) { + renderPosts(topic); + return; + } + } + + const isMail = href.startsWith("mailto:"); + if (isMail) { + window.location.href = href; + return; + } + + if (link.target === "_blank") { + window.open(href, "_blank"); + return; + } + + window.location.href = href; + }); + } + + function handleHomeClick(e) { + const home = e.target.closest(".home-link"); + if (!home) return; + e.preventDefault(); + const isHome = + window.location.pathname === "/" || + window.location.pathname === "/index.html"; + if (isHome) { + clearPrompt(500, () => { + const posts = document.getElementById("posts"); + if (posts) posts.innerHTML = ""; + const topics = document.querySelectorAll("[data-topic].active"); + topics.forEach((t) => { + t.classList.remove("active"); + t.style.color = ""; + }); + }); + } else { + persistPrompt(); + clearPrompt(500, () => { + window.location.href = "/"; + }); + } + } + + document.addEventListener("DOMContentLoaded", () => { + document.body.addEventListener("click", (e) => { + if (e.target.closest(".home-link")) return handleHomeClick(e); + if (e.target.closest("[data-topic]")) return handleDataTopicClick(e); + }); + + document.body.addEventListener( + "mouseenter", + (e) => { + const link = e.target.closest("[data-topic]"); + if (!link) return; + const color = + (window.getTopicColor && + window.getTopicColor(link.dataset.topic.toLowerCase())) || + ""; + if (color) link.style.color = color; + }, + true + ); + + document.body.addEventListener( + "mouseleave", + (e) => { + const link = e.target.closest("[data-topic]"); + if (!link) return; + if (!link.classList.contains("active")) link.style.color = ""; + }, + true + ); + + window.addEventListener("beforeunload", () => { + const el = promptEl(); + if (el) el.textContent = TERMINAL_PROMPT; }); }); - - window.addEventListener("beforeunload", () => { - const terminalPrompt = document.querySelector(".terminal-prompt"); - if (terminalPrompt) { - terminalPrompt.innerHTML = TERMINAL_PROMPT; - } - }); -}); +})(); diff --git a/src/components/Footer.astro b/src/components/Footer.astro index bd1df64..2f0d501 100644 --- a/src/components/Footer.astro +++ b/src/components/Footer.astro @@ -1,14 +1,10 @@ @@ -20,23 +16,12 @@ align-items: center; justify-content: space-between; } - .footer-links a { margin-left: 25px; text-decoration: none; + cursor: pointer; } - - if (host.includes("localhost") || host.includes("127.0.0.1")) { - gistLink.href = "http://localhost:4321/gist.html"; - gitLink.href = "http://localhost:4321/git.html"; - } else { - gistLink.href = "https://barrettruth.com/gist.html"; - gitLink.href = "https://barrettruth.com/git.html"; - } - diff --git a/src/components/Header.astro b/src/components/Header.astro index 34cf25e..781d626 100644 --- a/src/components/Header.astro +++ b/src/components/Header.astro @@ -1,11 +1,11 @@ --- const path = Astro.url.pathname; -const isHome = path === "/" || path === "/index.html"; const is404 = path === "/404.html" || path === "/404"; -function getTopic() { +function deriveTopic() { if (is404) return "/not-found"; if (path.startsWith("/about")) return "/about"; + if (path === "/gist" || path.startsWith("/gist/")) return "/gist"; if (path === "/git" || path.startsWith("/git/")) return "/git"; if (path.startsWith("/posts/")) { const parts = path.replace(/\/+$/, "").split("/"); @@ -14,132 +14,35 @@ function getTopic() { return ""; } -const topic = getTopic(); +const topic = deriveTopic(); const promptText = topic ? `barrett@ruth:~$ ${topic}` : "barrett@ruth:~$"; ---
- +
{promptText}
- - + + diff --git a/src/layouts/PostLayout.astro b/src/layouts/PostLayout.astro index f94bf21..c57607c 100644 --- a/src/layouts/PostLayout.astro +++ b/src/layouts/PostLayout.astro @@ -55,6 +55,7 @@ const topicColor = getTopicColor(category); + {frontmatter.scripts?.map(script => ( diff --git a/src/pages/gist.astro b/src/pages/gist.astro index 9d3d4fa..07b1709 100644 --- a/src/pages/gist.astro +++ b/src/pages/gist.astro @@ -10,15 +10,23 @@ gists.sort((a, b) => a.slug.localeCompare(b.slug)); + + diff --git a/src/pages/git.astro b/src/pages/git.astro index 4a92d95..0557bd0 100644 --- a/src/pages/git.astro +++ b/src/pages/git.astro @@ -7,12 +7,11 @@ const title = "Git Repositories"; +
-
    -
  • -
+
    @@ -31,16 +30,16 @@ const title = "Git Repositories"; repos.sort((a, b) => a.localeCompare(b)); for (const name of repos) { - const label = name.replace(/\.git$/,""); - const cls = `repo-${label}`; + const label = name.replace(/\.git$/, ""); const li = document.createElement("li"); - li.className = `topic ${cls}`; + li.className = `topic repo-${label}`; const a = document.createElement("a"); - a.href = `${window.location.origin}/git/${label}.html`; + a.href = `/git/${label}.html`; a.textContent = label; - a.setAttribute("data-topic", name); + a.dataset.topic = "git"; + a.dataset.repo = label; li.appendChild(a); listEl.appendChild(li); @@ -51,3 +50,4 @@ const title = "Git Repositories"; loadRepos();
    +