barrettruth.com/public/scripts/index.js
2025-11-09 14:45:26 -05:00

269 lines
7.5 KiB
JavaScript

(() => {
if (window.__BT_INDEX_INIT) return;
window.__BT_INDEX_INIT = true;
const TERMINAL_PROMPT = "barrett@ruth:~$ ";
let typing = false;
function promptEl() {
return document.querySelector(".terminal-prompt");
}
function promptTail() {
const el = promptEl();
if (!el) return "";
const s = el.textContent || "";
return s.startsWith(TERMINAL_PROMPT) ? s.slice(TERMINAL_PROMPT.length) : s;
}
function setPromptTailImmediate(tail) {
const el = promptEl();
if (!el) return;
el.textContent = TERMINAL_PROMPT + tail;
}
function persistPrompt() {
const el = promptEl();
if (el) sessionStorage.setItem("terminalPromptText", el.textContent);
}
(function restorePrompt() {
const saved = sessionStorage.getItem("terminalPromptText");
const el = promptEl();
if (saved && el) el.textContent = saved;
sessionStorage.removeItem("terminalPromptText");
})();
function normalizeDisplayPath(pathname) {
let p = pathname.replace(/\/index\.html$/, "/").replace(/\.html$/, "");
p = p.replace(/\/{2,}/g, "/");
if (p !== "/" && p.endsWith("/")) p = p.slice(0, -1);
return p === "/" ? "" : p;
}
function displayPathFromHref(href) {
const url = new URL(href, location.origin);
return normalizeDisplayPath(url.pathname);
}
function currentDisplayPath() {
return normalizeDisplayPath(location.pathname);
}
function animateToDisplayPath(displayPath, totalMs, done) {
if (typing) return;
typing = true;
const el = promptEl();
if (!el) {
typing = false;
return;
}
const targetTail = displayPath ? " " + displayPath : "";
const curTail = promptTail();
let i = 0;
const max = Math.min(curTail.length, targetTail.length);
while (i < max && curTail.charAt(i) === targetTail.charAt(i)) i++;
const delSteps = curTail.length - i;
const typeSteps = targetTail.length - i;
const totalSteps = delSteps + typeSteps;
if (totalSteps === 0) {
typing = false;
done && done();
return;
}
const stepMs = totalMs / totalSteps;
let delCount = 0;
function tickDelete() {
if (delCount < delSteps) {
setPromptTailImmediate(curTail.slice(0, curTail.length - delCount - 1));
delCount++;
setTimeout(tickDelete, stepMs);
} else {
let j = 0;
function tickType() {
if (j < typeSteps) {
setPromptTailImmediate(
curTail.slice(0, i) + targetTail.slice(i, i + j + 1),
);
j++;
setTimeout(tickType, stepMs);
} else {
typing = false;
done && done();
}
}
tickType();
}
}
tickDelete();
}
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.slug ||
post.id
?.split("/")
.pop()
?.replace(/\.mdx?$/, "");
a.href = `/${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 path = window.location.pathname;
const isHome = path === "/" || path === "/index.html";
const topic = link.dataset.topic?.toLowerCase() || "";
const href = link.getAttribute("href") || "/";
const delay = 500;
const colorFn = window.getTopicColor || (() => "");
document.querySelectorAll("[data-topic]").forEach((t) => {
t.classList.remove("active");
t.style.color = "";
});
link.classList.add("active");
const c = colorFn(topic);
if (c) link.style.color = c;
const displayPath = isHome ? `/${topic}` : displayPathFromHref(href);
animateToDisplayPath(displayPath, delay, () => {
if (
isHome &&
topic &&
window.postsByCategory &&
window.postsByCategory[topic]
) {
renderPosts(topic);
return;
}
persistPrompt();
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";
const delay = 500;
if (isHome) {
animateToDisplayPath("", delay, () => {
const posts = document.getElementById("posts");
if (posts) posts.innerHTML = "";
document.querySelectorAll("[data-topic].active").forEach((t) => {
t.classList.remove("active");
t.style.color = "";
});
document.title = "";
});
} else {
persistPrompt();
animateToDisplayPath("", delay, () => {
window.location.href = "/";
});
}
}
document.addEventListener("DOMContentLoaded", () => {
const initial = currentDisplayPath();
if (initial) setPromptTailImmediate(" " + initial);
else setPromptTailImmediate("");
document.body.addEventListener("click", (e) => {
if (e.target.closest(".home-link")) return handleHomeClick(e);
if (e.target.closest(".topics [data-topic]"))
return handleDataTopicClick(e);
});
document.body.addEventListener(
"mouseenter",
(e) => {
const link = e.target.closest(".topics [data-topic]");
if (!link) return;
const raw = link.dataset.topic || "";
const key = raw.split("/")[0].toLowerCase();
const color = (window.getTopicColor && window.getTopicColor(key)) || "";
if (color) {
link.style.color = color;
link.style.textDecorationColor = color;
}
},
true,
);
document.body.addEventListener(
"mouseleave",
(e) => {
const link = e.target.closest(".topics [data-topic]");
if (!link) return;
if (!link.classList.contains("active")) {
link.style.color = "";
link.style.textDecorationColor = "";
}
},
true,
);
const themeToggle = document.getElementById("theme-toggle");
if (themeToggle) {
function updateBearVisual() {
const currentTheme =
document.documentElement.getAttribute("data-theme");
if (currentTheme === "dark") {
themeToggle.textContent = "☾⊂ʕ•ᴥ•ʔ";
} else {
themeToggle.textContent = "☼⊂ʕ•ᴥ•ʔ";
}
}
updateBearVisual();
themeToggle.addEventListener("click", () => {
const currentTheme =
document.documentElement.getAttribute("data-theme");
const newTheme = currentTheme === "dark" ? "light" : "dark";
document.documentElement.setAttribute("data-theme", newTheme);
localStorage.setItem("theme", newTheme);
updateBearVisual();
});
}
window.addEventListener("beforeunload", () => {
const el = promptEl();
if (el) el.textContent = TERMINAL_PROMPT;
});
});
})();