fix: terminal promtp
This commit is contained in:
parent
cefde24774
commit
4076af3592
6 changed files with 212 additions and 246 deletions
|
|
@ -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;
|
||||
}
|
||||
});
|
||||
});
|
||||
})();
|
||||
|
|
|
|||
|
|
@ -1,14 +1,10 @@
|
|||
<footer>
|
||||
<span>ʕ •ᴥ•ʔ</span>
|
||||
<div class="footer-links">
|
||||
<a id="gist-link" target="_blank">gist</a>
|
||||
<a id="git-link" target="_blank">git</a>
|
||||
<a
|
||||
target="_blank"
|
||||
href="https://www.linkedin.com/in/barrett-ruth/"
|
||||
>linkedin</a
|
||||
>
|
||||
<a target="_blank" href="mailto:br.barrettruth@gmail.com">email</a>
|
||||
<a href="/gist" data-topic="gist">gist</a>
|
||||
<a href="/git" data-topic="git">git</a>
|
||||
<a href="https://www.linkedin.com/in/barrett-ruth/" data-topic="linkedin" target="_blank">linkedin</a>
|
||||
<a href="mailto:br.barrettruth@gmail.com" data-topic="mail">email</a>
|
||||
</div>
|
||||
</footer>
|
||||
|
||||
|
|
@ -20,23 +16,12 @@
|
|||
align-items: center;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
.footer-links a {
|
||||
margin-left: 25px;
|
||||
text-decoration: none;
|
||||
cursor: pointer;
|
||||
}
|
||||
</style>
|
||||
|
||||
<script>
|
||||
const host = window.location.hostname;
|
||||
const gitLink = document.getElementById("git-link");
|
||||
const gistLink = document.getElementById("gist-link");
|
||||
<script type="module" src="/scripts/index.js"></script>
|
||||
|
||||
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";
|
||||
}
|
||||
</script>
|
||||
|
|
|
|||
|
|
@ -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:~$";
|
||||
---
|
||||
|
||||
<header>
|
||||
<a href="/" style="text-decoration: none; color: inherit">
|
||||
<a href="/" class="home-link" style="text-decoration: none; color: inherit">
|
||||
<div class="terminal-container">
|
||||
<span class="terminal-prompt">{promptText}</span>
|
||||
<span class="terminal-cursor"></span>
|
||||
</div>
|
||||
</a>
|
||||
<div class="header-links">
|
||||
<a target="_blank" href="/resume.pdf">resume</a>
|
||||
<a target="_blank" href="/transcript.pdf">transcript</a>
|
||||
<a href="/about.html">about</a>
|
||||
<a href="/resume.pdf" data-topic="resume" target="_blank">resume</a>
|
||||
<a href="/transcript.pdf" data-topic="transcript" target="_blank">transcript</a>
|
||||
<a href="/about" data-topic="about">about</a>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<script>
|
||||
const TERMINAL_PROMPT = "barrett@ruth:~$ ";
|
||||
let clearing = false;
|
||||
|
||||
function clearPrompt(delay, callback) {
|
||||
if (clearing) return;
|
||||
clearing = true;
|
||||
|
||||
const terminalPrompt = document.querySelector(".terminal-prompt");
|
||||
if (!terminalPrompt) {
|
||||
clearing = false;
|
||||
return;
|
||||
}
|
||||
|
||||
const topicLength =
|
||||
terminalPrompt.innerHTML.length - TERMINAL_PROMPT.length;
|
||||
let i = 0;
|
||||
|
||||
function removeChar() {
|
||||
if (i++ < topicLength) {
|
||||
terminalPrompt.textContent = terminalPrompt.textContent.slice(0, -1);
|
||||
setTimeout(removeChar, delay / topicLength);
|
||||
} else {
|
||||
i = 0;
|
||||
clearing = false;
|
||||
callback && callback();
|
||||
}
|
||||
}
|
||||
|
||||
removeChar();
|
||||
}
|
||||
|
||||
function goHome(e) {
|
||||
e.preventDefault();
|
||||
clearPrompt(500, () => (window.location.href = "/"));
|
||||
}
|
||||
|
||||
function goToAbout(e) {
|
||||
e.preventDefault();
|
||||
const terminalPrompt = document.querySelector(".terminal-prompt");
|
||||
const terminalText = " /about";
|
||||
const delay = 500;
|
||||
|
||||
clearPrompt(delay, () => {
|
||||
let i = 0;
|
||||
function typechar() {
|
||||
if (i < terminalText.length) {
|
||||
terminalPrompt.innerHTML += terminalText.charAt(i++);
|
||||
setTimeout(typechar, delay / terminalText.length);
|
||||
} else {
|
||||
window.location.href = "/about.html";
|
||||
}
|
||||
}
|
||||
typechar();
|
||||
});
|
||||
}
|
||||
|
||||
document.addEventListener("DOMContentLoaded", () => {
|
||||
window.TERMINAL_PROMPT = TERMINAL_PROMPT;
|
||||
window.clearPrompt = clearPrompt;
|
||||
window.goHome = goHome;
|
||||
|
||||
const homeLink = document.querySelector('header a[href="/"]');
|
||||
if (homeLink) {
|
||||
const path = window.location.pathname;
|
||||
const isHome = path === "/" || path === "/index.html";
|
||||
|
||||
if (isHome) {
|
||||
homeLink.addEventListener("click", (e) => {
|
||||
e.preventDefault();
|
||||
const topics = document.querySelectorAll(".topic a");
|
||||
topics.forEach((topic) => {
|
||||
topic.classList.remove("active");
|
||||
topic.style.color = "";
|
||||
});
|
||||
const posts = document.getElementById("posts");
|
||||
if (posts) posts.innerHTML = "";
|
||||
clearPrompt(500);
|
||||
});
|
||||
} else {
|
||||
homeLink.addEventListener("click", goHome);
|
||||
}
|
||||
}
|
||||
|
||||
const aboutLink = document.querySelector('.header-links a[href="/about.html"]');
|
||||
if (aboutLink) {
|
||||
const path = window.location.pathname;
|
||||
const isHome = path === "/" || path === "/index.html";
|
||||
|
||||
if (isHome) {
|
||||
aboutLink.addEventListener("click", goToAbout);
|
||||
}
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<style>
|
||||
.header-links a {
|
||||
margin-left: 25px;
|
||||
text-decoration: none;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
@media (max-width: 768px) {
|
||||
header {
|
||||
flex-direction: column;
|
||||
align-items: flex-start;
|
||||
}
|
||||
|
||||
.header-links {
|
||||
align-self: flex-end;
|
||||
display: flex;
|
||||
|
|
@ -147,10 +50,11 @@ const promptText = topic ? `barrett@ruth:~$ ${topic}` : "barrett@ruth:~$";
|
|||
text-align: right;
|
||||
gap: 5px;
|
||||
}
|
||||
|
||||
.header-links a {
|
||||
margin-left: 0;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
<script type="module" src="/scripts/index.js"></script>
|
||||
|
||||
|
|
|
|||
|
|
@ -55,6 +55,7 @@ const topicColor = getTopicColor(category);
|
|||
</div>
|
||||
|
||||
<slot name="scripts" slot="scripts">
|
||||
<script src="/scripts/index.js" is:inline></script>
|
||||
<script src="/scripts/centerKatex.js" is:inline></script>
|
||||
{frontmatter.scripts?.map(script => (
|
||||
<script src={script} type="module"></script>
|
||||
|
|
|
|||
|
|
@ -10,15 +10,23 @@ gists.sort((a, b) => a.slug.localeCompare(b.slug));
|
|||
<BaseLayout title={title}>
|
||||
<slot name="head" slot="head">
|
||||
<link rel="stylesheet" href="/styles/index.css" />
|
||||
<script type="module" src="/scripts/index.js"></script>
|
||||
</slot>
|
||||
|
||||
<div class="content">
|
||||
<ul class="topics">
|
||||
{gists.map((gist) => (
|
||||
<li class="topic">
|
||||
<a href={`/gist/${gist.slug}.html`}>{gist.data.title || gist.slug}</a>
|
||||
<a
|
||||
href={`/gist/${gist.slug}.html`}
|
||||
data-topic="gist"
|
||||
data-gist={gist.slug}
|
||||
>
|
||||
{gist.data.title || gist.slug}
|
||||
</a>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
</div>
|
||||
</BaseLayout>
|
||||
|
||||
|
|
|
|||
|
|
@ -7,12 +7,11 @@ const title = "Git Repositories";
|
|||
<BaseLayout title={title}>
|
||||
<slot name="head" slot="head">
|
||||
<link rel="stylesheet" href="/styles/index.css" />
|
||||
<script type="module" src="/scripts/index.js"></script>
|
||||
</slot>
|
||||
|
||||
<div class="content">
|
||||
<ul class="topics" id="repo-list">
|
||||
<li class="topic"></li>
|
||||
</ul>
|
||||
<ul class="topics" id="repo-list"></ul>
|
||||
<div class="posts" id="posts"></div>
|
||||
</div>
|
||||
|
||||
|
|
@ -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();
|
||||
</script>
|
||||
</BaseLayout>
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue