chore: init
This commit is contained in:
commit
f6dcc302d4
118 changed files with 13645 additions and 0 deletions
42
src/components/ArrowCard.astro
Normal file
42
src/components/ArrowCard.astro
Normal file
|
@ -0,0 +1,42 @@
|
|||
---
|
||||
import type { CollectionEntry } from "astro:content";
|
||||
|
||||
type Props = {
|
||||
entry: CollectionEntry<"blog">;
|
||||
};
|
||||
|
||||
const { entry } = Astro.props as {
|
||||
entry: CollectionEntry<"blog">;
|
||||
};
|
||||
---
|
||||
|
||||
<a
|
||||
href={`/${entry.collection}/${entry.slug}`}
|
||||
class="not-prose group relative flex flex-nowrap rounded-lg border border-black/15 px-4 py-3 pr-10 transition-colors duration-300 ease-in-out hover:bg-black/5 hover:text-black focus-visible:bg-black/5 focus-visible:text-black dark:border-white/20 dark:hover:bg-white/5 dark:hover:text-white dark:focus-visible:bg-white/5 dark:focus-visible:text-white"
|
||||
>
|
||||
<div class="flex flex-1 flex-col truncate">
|
||||
<div class="font-semibold">
|
||||
{entry.data.title}
|
||||
</div>
|
||||
<div class="text-sm">
|
||||
{entry.data.description}
|
||||
</div>
|
||||
</div>
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
viewBox="0 0 24 24"
|
||||
class="absolute right-2 top-1/2 size-5 -translate-y-1/2 fill-none stroke-current stroke-2"
|
||||
>
|
||||
<line
|
||||
x1="5"
|
||||
y1="12"
|
||||
x2="19"
|
||||
y2="12"
|
||||
class="translate-x-3 scale-x-0 transition-transform duration-300 ease-in-out group-hover:translate-x-0 group-hover:scale-x-100 group-focus-visible:translate-x-0 group-focus-visible:scale-x-100"
|
||||
></line>
|
||||
<polyline
|
||||
points="12 5 19 12 12 19"
|
||||
class="-translate-x-1 transition-transform duration-300 ease-in-out group-hover:translate-x-0 group-focus-visible:translate-x-0"
|
||||
></polyline>
|
||||
</svg>
|
||||
</a>
|
33
src/components/BackToPrevious.astro
Normal file
33
src/components/BackToPrevious.astro
Normal file
|
@ -0,0 +1,33 @@
|
|||
---
|
||||
type Props = {
|
||||
href: string;
|
||||
};
|
||||
|
||||
const { href } = Astro.props;
|
||||
---
|
||||
|
||||
<a
|
||||
href={href}
|
||||
class="not-prose group relative flex w-fit flex-nowrap rounded border border-black/15 py-1.5 pl-7 pr-3 transition-colors duration-300 ease-in-out hover:bg-black/5 hover:text-black focus-visible:bg-black/5 focus-visible:text-black dark:border-white/20 dark:hover:bg-white/5 dark:hover:text-white dark:focus-visible:bg-white/5 dark:focus-visible:text-white"
|
||||
>
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
viewBox="0 0 24 24"
|
||||
class="absolute left-2 top-1/2 size-4 -translate-y-1/2 fill-none stroke-current stroke-2"
|
||||
>
|
||||
<line
|
||||
x1="5"
|
||||
y1="12"
|
||||
x2="19"
|
||||
y2="12"
|
||||
class="translate-x-2 scale-x-0 transition-transform duration-300 ease-in-out group-hover:translate-x-0 group-hover:scale-x-100 group-focus-visible:translate-x-0 group-focus-visible:scale-x-100"
|
||||
></line>
|
||||
<polyline
|
||||
points="12 5 5 12 12 19"
|
||||
class="translate-x-1 transition-transform duration-300 ease-in-out group-hover:translate-x-0 group-focus-visible:translate-x-0"
|
||||
></polyline>
|
||||
</svg>
|
||||
<div class="text-sm">
|
||||
<slot />
|
||||
</div>
|
||||
</a>
|
27
src/components/BackToTop.astro
Normal file
27
src/components/BackToTop.astro
Normal file
|
@ -0,0 +1,27 @@
|
|||
---
|
||||
|
||||
---
|
||||
|
||||
<button
|
||||
id="back-to-top"
|
||||
class="group relative flex w-fit flex-nowrap rounded border border-black/15 py-1.5 pl-8 pr-3 transition-colors duration-300 ease-in-out hover:bg-black/5 hover:text-black focus-visible:bg-black/5 focus-visible:text-black dark:border-white/20 dark:hover:bg-white/5 dark:hover:text-white dark:focus-visible:bg-white/5 dark:focus-visible:text-white"
|
||||
>
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
viewBox="0 0 24 24"
|
||||
class="absolute left-2 top-1/2 size-4 -translate-y-1/2 rotate-90 fill-none stroke-current stroke-2"
|
||||
>
|
||||
<line
|
||||
x1="5"
|
||||
y1="12"
|
||||
x2="19"
|
||||
y2="12"
|
||||
class="translate-x-2 scale-x-0 transition-transform duration-300 ease-in-out group-hover:translate-x-0 group-hover:scale-x-100 group-focus-visible:translate-x-0 group-focus-visible:scale-x-100"
|
||||
></line>
|
||||
<polyline
|
||||
points="12 5 5 12 12 19"
|
||||
class="translate-x-1 transition-transform duration-300 ease-in-out group-hover:translate-x-0 group-focus-visible:translate-x-0"
|
||||
></polyline>
|
||||
</svg>
|
||||
<div class="text-sm">Back to top</div>
|
||||
</button>
|
23
src/components/CVCard.astro
Normal file
23
src/components/CVCard.astro
Normal file
|
@ -0,0 +1,23 @@
|
|||
---
|
||||
export interface Props {
|
||||
institution: string;
|
||||
time: string;
|
||||
job_title: string;
|
||||
location: string;
|
||||
description: string
|
||||
}
|
||||
|
||||
const { institution, time, job_title, location, description } = Astro.props;
|
||||
---
|
||||
|
||||
<div class="py-5 px-8">
|
||||
<div class="flex justify-between text-sm font-bold md:text-lg">
|
||||
<div>{institution}</div>
|
||||
<div>{location}</div>
|
||||
</div>
|
||||
<div class="flex justify-between text-xs md:text-base">
|
||||
<div class="italic">{job_title}</div>
|
||||
<div>{time}</div>
|
||||
</div>
|
||||
<p class="text-xs mt-2">{description}</p>
|
||||
</div>
|
44
src/components/Callout.astro
Normal file
44
src/components/Callout.astro
Normal file
|
@ -0,0 +1,44 @@
|
|||
---
|
||||
interface Component {
|
||||
type: "default" | "info" | "warning" | "error";
|
||||
}
|
||||
|
||||
const { type = "default" } = Astro.props;
|
||||
|
||||
let emoji = "💡";
|
||||
|
||||
if (type === "info") {
|
||||
emoji = "ℹ️";
|
||||
} else if (type === "warning") {
|
||||
emoji = "⚠️";
|
||||
} else if (type === "error") {
|
||||
emoji = "🚨";
|
||||
}
|
||||
---
|
||||
|
||||
<div class={`not-prose callout callout-${type}`}>
|
||||
<span class="emoji pointer-events-none select-none">{emoji}</span>
|
||||
<slot />
|
||||
</div>
|
||||
|
||||
<style>
|
||||
.callout {
|
||||
@apply relative my-4 flex rounded border border-orange-800 bg-orange-100 p-3 text-orange-950 dark:border-orange-200/20 dark:bg-orange-950/20 dark:text-orange-200;
|
||||
}
|
||||
|
||||
.emoji {
|
||||
@apply pr-3 text-xl;
|
||||
}
|
||||
|
||||
.callout-info {
|
||||
@apply border-blue-800 bg-blue-100 text-blue-950 dark:border-blue-200/20 dark:bg-blue-950/20 dark:text-blue-200;
|
||||
}
|
||||
|
||||
.callout-warning {
|
||||
@apply border-yellow-800 bg-yellow-100 text-yellow-950 dark:border-yellow-200/20 dark:bg-yellow-950/20 dark:text-yellow-200;
|
||||
}
|
||||
|
||||
.callout-error {
|
||||
@apply border-red-800 bg-red-100 text-red-950 dark:border-red-200/20 dark:bg-red-950/20 dark:text-red-200;
|
||||
}
|
||||
</style>
|
5
src/components/Container.astro
Normal file
5
src/components/Container.astro
Normal file
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
|
||||
---
|
||||
|
||||
<div class="mx-auto max-w-screen-md px-3"><slot /></div>
|
92
src/components/Footer.astro
Normal file
92
src/components/Footer.astro
Normal file
|
@ -0,0 +1,92 @@
|
|||
---
|
||||
import Container from "@components/Container.astro";
|
||||
import { SITE } from "@consts";
|
||||
import BackToTop from "@components/BackToTop.astro";
|
||||
import SocialIcons from "./SocialIcons.astro";
|
||||
---
|
||||
|
||||
<footer class="animate">
|
||||
<Container>
|
||||
<div class="flex justify-between my-2">
|
||||
<SocialIcons icon_size={'text-xl'} />
|
||||
<BackToTop />
|
||||
</div>
|
||||
<div class="flex items-center justify-between">
|
||||
<div>© {new Date().getFullYear()} • {SITE.TITLE} 👀<br>
|
||||
Built with Astro
|
||||
</div>
|
||||
<div class="flex flex-wrap items-center gap-1.5">
|
||||
<button
|
||||
id="light-theme-button"
|
||||
aria-label="Light theme"
|
||||
class="group flex size-9 items-center justify-center rounded border border-black/15 hover:bg-black/5 focus-visible:bg-black/5 dark:border-white/20 dark:hover:bg-white/5 dark:focus-visible:bg-white/5"
|
||||
>
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="18"
|
||||
height="18"
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
stroke-width="1.5"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
class="transition-colors duration-300 ease-in-out group-hover:animate-pulse group-hover:stroke-black group-focus-visible:animate-pulse group-focus-visible:stroke-black group-hover:dark:stroke-white dark:group-focus-visible:stroke-white"
|
||||
>
|
||||
<circle cx="12" cy="12" r="5"></circle>
|
||||
<line x1="12" y1="1" x2="12" y2="3"></line>
|
||||
<line x1="12" y1="21" x2="12" y2="23"></line>
|
||||
<line x1="4.22" y1="4.22" x2="5.64" y2="5.64"></line>
|
||||
<line x1="18.36" y1="18.36" x2="19.78" y2="19.78"></line>
|
||||
<line x1="1" y1="12" x2="3" y2="12"></line>
|
||||
<line x1="21" y1="12" x2="23" y2="12"></line>
|
||||
<line x1="4.22" y1="19.78" x2="5.64" y2="18.36"></line>
|
||||
<line x1="18.36" y1="5.64" x2="19.78" y2="4.22"></line>
|
||||
</svg>
|
||||
</button>
|
||||
<button
|
||||
id="dark-theme-button"
|
||||
aria-label="Dark theme"
|
||||
class="group flex size-9 items-center justify-center rounded border border-black/15 hover:bg-black/5 focus-visible:bg-black/5 dark:border-white/20 dark:hover:bg-white/5 dark:focus-visible:bg-white/5"
|
||||
>
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="18"
|
||||
height="18"
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
stroke-width="1.5"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
class="transition-colors duration-300 ease-in-out group-hover:animate-pulse group-hover:stroke-black group-focus-visible:animate-pulse group-focus-visible:stroke-black group-hover:dark:stroke-white dark:group-focus-visible:stroke-white"
|
||||
>
|
||||
<path d="M21 12.79A9 9 0 1 1 11.21 3 7 7 0 0 0 21 12.79z"></path>
|
||||
</svg>
|
||||
</button>
|
||||
<button
|
||||
id="system-theme-button"
|
||||
aria-label="System theme"
|
||||
class="group flex size-9 items-center justify-center rounded border border-black/15 hover:bg-black/5 focus-visible:bg-black/5 dark:border-white/20 dark:hover:bg-white/5 dark:focus-visible:bg-white/5"
|
||||
>
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="18"
|
||||
height="18"
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
stroke-width="1.5"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
class="transition-colors duration-300 ease-in-out group-hover:animate-pulse group-hover:stroke-black group-focus-visible:animate-pulse group-focus-visible:stroke-black group-hover:dark:stroke-white dark:group-focus-visible:stroke-white"
|
||||
>
|
||||
<rect x="2" y="3" width="20" height="14" rx="2" ry="2"></rect>
|
||||
<line x1="8" y1="21" x2="16" y2="21"></line>
|
||||
<line x1="12" y1="17" x2="12" y2="21"></line>
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</Container>
|
||||
</footer>
|
17
src/components/FormattedDate.astro
Normal file
17
src/components/FormattedDate.astro
Normal file
|
@ -0,0 +1,17 @@
|
|||
---
|
||||
interface Props {
|
||||
date: Date;
|
||||
}
|
||||
|
||||
const { date } = Astro.props;
|
||||
---
|
||||
|
||||
<time datetime={date.toISOString()}>
|
||||
{
|
||||
date.toLocaleDateString("en-US", {
|
||||
month: "long",
|
||||
day: "2-digit",
|
||||
year: "numeric",
|
||||
})
|
||||
}
|
||||
</time>
|
20
src/components/Giscus.astro
Normal file
20
src/components/Giscus.astro
Normal file
|
@ -0,0 +1,20 @@
|
|||
<div class="giscus"></div>
|
||||
|
||||
<script
|
||||
is:inline
|
||||
data-astro-rerun
|
||||
src="https://giscus.app/client.js"
|
||||
data-repo="trevortylerlee/astro-micro"
|
||||
data-repo-id="R_kgDOL_6l9Q"
|
||||
data-category="Announcements"
|
||||
data-category-id="DIC_kwDOL_6l9c4Cfk55"
|
||||
data-mapping="pathname"
|
||||
data-strict="0"
|
||||
data-reactions-enabled="1"
|
||||
data-emit-metadata="0"
|
||||
data-input-position="top"
|
||||
data-theme="preferred_color_scheme"
|
||||
data-lang="en"
|
||||
data-loading="lazy"
|
||||
crossorigin="anonymous"
|
||||
async></script>
|
251
src/components/Head.astro
Normal file
251
src/components/Head.astro
Normal file
|
@ -0,0 +1,251 @@
|
|||
---
|
||||
import "../styles/global.css";
|
||||
import { ViewTransitions } from "astro:transitions";
|
||||
|
||||
import "@fontsource/geist-sans";
|
||||
import "@fontsource/geist-mono";
|
||||
|
||||
interface Props {
|
||||
title: string;
|
||||
description: string;
|
||||
image?: string;
|
||||
}
|
||||
|
||||
const canonicalURL = new URL(Astro.url.pathname, Astro.site);
|
||||
|
||||
const { title, description, image = "/blog-placeholder-1.jpg" } = Astro.props;
|
||||
---
|
||||
|
||||
<!-- Global Metadata -->
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width,initial-scale=1" />
|
||||
<link rel="icon" type="image/svg+xml" href="/favicon.svg" />
|
||||
<link
|
||||
rel="icon"
|
||||
href="data:image/svg+xml,<svg xmlns=%22http://www.w3.org/2000/svg%22 viewBox=%220 0 100 100%22><text y=%22.9em%22 font-size=%2290%22>🍄</text></svg>"
|
||||
/>
|
||||
<meta name="generator" content={Astro.generator} />
|
||||
|
||||
<!-- Canonical URL -->
|
||||
<link rel="canonical" href={canonicalURL} />
|
||||
|
||||
<!-- Primary Meta Tags -->
|
||||
<title>{title}</title>
|
||||
<meta name="title" content={title} />
|
||||
<meta name="description" content={description} />
|
||||
|
||||
<!-- Open Graph / Facebook -->
|
||||
<meta property="og:type" content="website" />
|
||||
<meta property="og:url" content={Astro.url} />
|
||||
<meta property="og:title" content={title} />
|
||||
<meta property="og:description" content={description} />
|
||||
<meta property="og:image" content={new URL(image, Astro.url)} />
|
||||
|
||||
<!-- Twitter -->
|
||||
<meta property="twitter:card" content="summary_large_image" />
|
||||
<meta property="twitter:url" content={Astro.url} />
|
||||
<meta property="twitter:title" content={title} />
|
||||
<meta property="twitter:description" content={description} />
|
||||
<meta property="twitter:image" content={new URL(image, Astro.url)} />
|
||||
|
||||
<!-- PageFind -->
|
||||
<link href="/pagefind/pagefind-ui.css" rel="stylesheet" />
|
||||
<script is:inline src="/pagefind/pagefind-ui.js"></script>
|
||||
|
||||
<ViewTransitions />
|
||||
|
||||
<script is:inline>
|
||||
function init() {
|
||||
preloadTheme();
|
||||
onScroll();
|
||||
animate();
|
||||
updateThemeButtons();
|
||||
addCopyCodeButtons();
|
||||
setGiscusTheme();
|
||||
|
||||
const backToTop = document.getElementById("back-to-top");
|
||||
backToTop?.addEventListener("click", (event) => scrollToTop(event));
|
||||
|
||||
const backToPrev = document.getElementById("back-to-prev");
|
||||
backToPrev?.addEventListener("click", () => window.history.back());
|
||||
|
||||
const lightThemeButton = document.getElementById("light-theme-button");
|
||||
lightThemeButton?.addEventListener("click", () => {
|
||||
localStorage.setItem("theme", "light");
|
||||
toggleTheme(false);
|
||||
updateThemeButtons();
|
||||
});
|
||||
|
||||
const darkThemeButton = document.getElementById("dark-theme-button");
|
||||
darkThemeButton?.addEventListener("click", () => {
|
||||
localStorage.setItem("theme", "dark");
|
||||
toggleTheme(true);
|
||||
updateThemeButtons();
|
||||
});
|
||||
|
||||
const systemThemeButton = document.getElementById("system-theme-button");
|
||||
systemThemeButton?.addEventListener("click", () => {
|
||||
localStorage.setItem("theme", "system");
|
||||
toggleTheme(window.matchMedia("(prefers-color-scheme: dark)").matches);
|
||||
updateThemeButtons();
|
||||
});
|
||||
|
||||
window
|
||||
.matchMedia("(prefers-color-scheme: dark)")
|
||||
.addEventListener("change", (event) => {
|
||||
if (localStorage.theme === "system") {
|
||||
toggleTheme(event.matches);
|
||||
}
|
||||
});
|
||||
|
||||
document.addEventListener("scroll", onScroll);
|
||||
}
|
||||
|
||||
function updateThemeButtons() {
|
||||
const theme = localStorage.getItem("theme");
|
||||
const lightThemeButton = document.getElementById("light-theme-button");
|
||||
const darkThemeButton = document.getElementById("dark-theme-button");
|
||||
const systemThemeButton = document.getElementById("system-theme-button");
|
||||
|
||||
function removeActiveButtonTheme(button) {
|
||||
button?.classList.remove("bg-black/5");
|
||||
button?.classList.remove("dark:bg-white/5");
|
||||
}
|
||||
|
||||
function addActiveButtonTheme(button) {
|
||||
button?.classList.add("bg-black/5");
|
||||
button?.classList.add("dark:bg-white/5");
|
||||
}
|
||||
|
||||
removeActiveButtonTheme(lightThemeButton);
|
||||
removeActiveButtonTheme(darkThemeButton);
|
||||
removeActiveButtonTheme(systemThemeButton);
|
||||
|
||||
if (theme === "light") {
|
||||
addActiveButtonTheme(lightThemeButton);
|
||||
} else if (theme === "dark") {
|
||||
addActiveButtonTheme(darkThemeButton);
|
||||
} else {
|
||||
addActiveButtonTheme(systemThemeButton);
|
||||
}
|
||||
}
|
||||
|
||||
function animate() {
|
||||
const animateElements = document.querySelectorAll(".animate");
|
||||
|
||||
animateElements.forEach((element, index) => {
|
||||
setTimeout(() => {
|
||||
element.classList.add("show");
|
||||
}, index * 100);
|
||||
});
|
||||
}
|
||||
|
||||
function onScroll() {
|
||||
if (window.scrollY > 0) {
|
||||
document.documentElement.classList.add("scrolled");
|
||||
} else {
|
||||
document.documentElement.classList.remove("scrolled");
|
||||
}
|
||||
}
|
||||
|
||||
function scrollToTop(event) {
|
||||
event.preventDefault();
|
||||
window.scrollTo({
|
||||
top: 0,
|
||||
behavior: "smooth",
|
||||
});
|
||||
}
|
||||
|
||||
function toggleTheme(dark) {
|
||||
const css = document.createElement("style");
|
||||
|
||||
css.appendChild(
|
||||
document.createTextNode(
|
||||
`* {
|
||||
-webkit-transition: none !important;
|
||||
-moz-transition: none !important;
|
||||
-o-transition: none !important;
|
||||
-ms-transition: none !important;
|
||||
transition: none !important;
|
||||
}
|
||||
`
|
||||
)
|
||||
);
|
||||
|
||||
document.head.appendChild(css);
|
||||
|
||||
if (dark) {
|
||||
document.documentElement.classList.add("dark");
|
||||
} else {
|
||||
document.documentElement.classList.remove("dark");
|
||||
}
|
||||
|
||||
window.getComputedStyle(css).opacity;
|
||||
document.head.removeChild(css);
|
||||
|
||||
setGiscusTheme();
|
||||
}
|
||||
|
||||
function preloadTheme() {
|
||||
const userTheme = localStorage.theme;
|
||||
|
||||
if (userTheme === "light" || userTheme === "dark") {
|
||||
toggleTheme(true); // set default to dark theme
|
||||
} else {
|
||||
toggleTheme(window.matchMedia("(prefers-color-scheme: dark)").matches);
|
||||
}
|
||||
}
|
||||
|
||||
function addCopyCodeButtons() {
|
||||
let copyButtonLabel = "📋";
|
||||
let codeBlocks = Array.from(document.querySelectorAll("pre"));
|
||||
|
||||
async function copyCode(codeBlock, copyButton) {
|
||||
const codeText = codeBlock.innerText;
|
||||
const buttonText = copyButton.innerText;
|
||||
const textToCopy = codeText.replace(buttonText, "");
|
||||
|
||||
await navigator.clipboard.writeText(textToCopy);
|
||||
copyButton.innerText = "✅";
|
||||
|
||||
setTimeout(() => {
|
||||
copyButton.innerText = copyButtonLabel;
|
||||
}, 2000);
|
||||
}
|
||||
|
||||
for (let codeBlock of codeBlocks) {
|
||||
const wrapper = document.createElement("div");
|
||||
wrapper.style.position = "relative";
|
||||
|
||||
const copyButton = document.createElement("button");
|
||||
copyButton.innerText = copyButtonLabel;
|
||||
copyButton.classList = "copy-code";
|
||||
|
||||
codeBlock.setAttribute("tabindex", "0");
|
||||
codeBlock.appendChild(copyButton);
|
||||
|
||||
codeBlock.parentNode.insertBefore(wrapper, codeBlock);
|
||||
wrapper.appendChild(codeBlock);
|
||||
|
||||
copyButton?.addEventListener("click", async () => {
|
||||
await copyCode(codeBlock, copyButton);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
const setGiscusTheme = () => {
|
||||
const giscus = document.querySelector(".giscus-frame");
|
||||
|
||||
const isDark = document.documentElement.classList.contains("dark");
|
||||
|
||||
if (giscus) {
|
||||
const url = new URL(giscus.src);
|
||||
url.searchParams.set("theme", isDark ? "dark" : "light");
|
||||
giscus.src = url.toString();
|
||||
}
|
||||
};
|
||||
|
||||
document.addEventListener("DOMContentLoaded", () => init());
|
||||
document.addEventListener("astro:after-swap", () => init());
|
||||
preloadTheme();
|
||||
</script>
|
59
src/components/Header.astro
Normal file
59
src/components/Header.astro
Normal file
|
@ -0,0 +1,59 @@
|
|||
---
|
||||
import Container from "@components/Container.astro";
|
||||
import Link from "@components/Link.astro";
|
||||
import { SITE } from "@consts";
|
||||
import ProgressBar from "./ProgressBar.astro";
|
||||
---
|
||||
|
||||
<header transition:persist>
|
||||
<Container>
|
||||
<div class="flex flex-wrap justify-between gap-y-2">
|
||||
<Link href="/" underline={false}>
|
||||
<div class="font-semibold">
|
||||
{SITE.TITLE} 🍄
|
||||
</div>
|
||||
</Link>
|
||||
<nav class="flex items-center gap-1 text-sm">
|
||||
<Link href="/blog">blog</Link>
|
||||
<span>
|
||||
{`/`}
|
||||
</span>
|
||||
<Link href="/publications">research</Link>
|
||||
<span>
|
||||
{`/`}
|
||||
</span>
|
||||
<Link href="/cv">cv</Link>
|
||||
<span>
|
||||
{`/`}
|
||||
</span>
|
||||
<Link href="/about">about</Link>
|
||||
<span>
|
||||
{`/`}
|
||||
</span>
|
||||
<Link href="/tags">tags</Link>
|
||||
<span>
|
||||
{`/`}
|
||||
</span>
|
||||
<button
|
||||
id="magnifying-glass"
|
||||
aria-label="Search"
|
||||
class="flex items-center rounded border border-black/15 bg-neutral-100 px-2 py-1 text-xs transition-colors duration-300 ease-in-out hover:bg-black/5 hover:text-black focus-visible:bg-black/5 focus-visible:text-black dark:border-white/20 dark:bg-neutral-900 dark:hover:bg-white/5 dark:hover:text-white dark:focus-visible:bg-white/5 dark:focus-visible:text-white"
|
||||
>
|
||||
<svg
|
||||
height="16"
|
||||
stroke-linejoin="round"
|
||||
viewBox="0 0 16 16"
|
||||
width="16"
|
||||
style="color: currentcolor;"
|
||||
><path
|
||||
fill-rule="evenodd"
|
||||
clip-rule="evenodd"
|
||||
d="M3.5 7C3.5 5.067 5.067 3.5 7 3.5C8.933 3.5 10.5 5.067 10.5 7C10.5 7.88461 10.1718 8.69256 9.63058 9.30876L9.30876 9.63058C8.69256 10.1718 7.88461 10.5 7 10.5C5.067 10.5 3.5 8.933 3.5 7ZM9.96544 11.0261C9.13578 11.6382 8.11014 12 7 12C4.23858 12 2 9.76142 2 7C2 4.23858 4.23858 2 7 2C9.76142 2 12 4.23858 12 7C12 8.11014 11.6382 9.13578 11.0261 9.96544L14.0303 12.9697L14.5607 13.5L13.5 14.5607L12.9697 14.0303L9.96544 11.0261Z"
|
||||
fill="currentColor"></path></svg
|
||||
>
|
||||
Search
|
||||
</button>
|
||||
</nav>
|
||||
</div>
|
||||
</Container>
|
||||
</header>
|
31
src/components/Link.astro
Normal file
31
src/components/Link.astro
Normal file
|
@ -0,0 +1,31 @@
|
|||
---
|
||||
import { cn } from "@lib/utils";
|
||||
|
||||
type Props = {
|
||||
href: string;
|
||||
external?: boolean;
|
||||
underline?: boolean;
|
||||
group?: boolean;
|
||||
};
|
||||
|
||||
const {
|
||||
href,
|
||||
external,
|
||||
underline = true,
|
||||
group = false,
|
||||
...rest
|
||||
} = Astro.props;
|
||||
---
|
||||
|
||||
<a
|
||||
href={href}
|
||||
target={external ? "_blank" : "_self"}
|
||||
class={cn(
|
||||
"inline-block decoration-black/30 hover:decoration-black/50 focus-visible:decoration-black/50 dark:decoration-white/30 dark:hover:decoration-white/50 dark:focus-visible:decoration-white/50 hover:text-cyan-500 focus-visible:text-black dark:hover:text-orange-500 dark:focus-visible:text-white transition-colors duration-300 ease-in-out",
|
||||
underline && "underline underline-offset-[3px]",
|
||||
group && "group"
|
||||
)}
|
||||
{...rest}
|
||||
>
|
||||
<slot />
|
||||
</a>
|
132
src/components/PageFind.astro
Normal file
132
src/components/PageFind.astro
Normal file
|
@ -0,0 +1,132 @@
|
|||
---
|
||||
import Search from "astro-pagefind/components/Search";
|
||||
---
|
||||
|
||||
<aside data-pagefind-ignore>
|
||||
<div
|
||||
transition:persist
|
||||
id="backdrop"
|
||||
class="bg-[rgba(0, 0, 0, 0.5] invisible fixed left-0 top-0 z-50 flex h-screen w-full justify-center p-6 backdrop-blur-sm"
|
||||
>
|
||||
<div
|
||||
id="pagefind-container"
|
||||
class="m-0 flex h-fit max-h-[80%] w-full max-w-screen-sm flex-col overflow-auto rounded border border-black/15 bg-neutral-100 p-2 px-4 py-3 shadow-lg dark:border-white/20 dark:bg-neutral-900"
|
||||
>
|
||||
<Search
|
||||
id="search"
|
||||
className="pagefind-ui"
|
||||
uiOptions={{
|
||||
showImages: false,
|
||||
excerptLength: 15,
|
||||
resetStyles: false,
|
||||
}}
|
||||
/>
|
||||
<div class="mr-2 pb-1 pt-4 text-right text-xs dark:prose-invert">
|
||||
Press <span class="prose text-xs dark:prose-invert"
|
||||
><kbd class="">Esc</kbd></span
|
||||
> or click anywhere to close
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</aside>
|
||||
|
||||
<script is:inline>
|
||||
const magnifyingGlass = document.getElementById("magnifying-glass");
|
||||
const backdrop = document.getElementById("backdrop");
|
||||
|
||||
function openPagefind() {
|
||||
const searchDiv = document.getElementById("search");
|
||||
const search = searchDiv.querySelector("input");
|
||||
setTimeout(() => {
|
||||
search.focus();
|
||||
}, 0);
|
||||
backdrop?.classList.remove("invisible");
|
||||
backdrop?.classList.add("visible");
|
||||
}
|
||||
|
||||
function closePagefind() {
|
||||
const search = document.getElementById("search");
|
||||
search.value = "";
|
||||
backdrop?.classList.remove("visible");
|
||||
backdrop?.classList.add("invisible");
|
||||
}
|
||||
|
||||
// open pagefind
|
||||
magnifyingGlass?.addEventListener("click", () => {
|
||||
openPagefind();
|
||||
});
|
||||
|
||||
document.addEventListener("keydown", (e) => {
|
||||
if (e.key === "/") {
|
||||
e.preventDefault();
|
||||
openPagefind();
|
||||
} else if ((e.metaKey || e.ctrlKey) && e.key === "k") {
|
||||
e.preventDefault();
|
||||
openPagefind();
|
||||
}
|
||||
});
|
||||
|
||||
// close pagefind
|
||||
document.addEventListener("keydown", (e) => {
|
||||
if (e.key === "Escape" || e.keyCode === 27) {
|
||||
closePagefind();
|
||||
}
|
||||
});
|
||||
|
||||
// close pagefind when searched result(link) clicked
|
||||
document.addEventListener("click", (event) => {
|
||||
if (event.target.classList.contains("pagefind-ui__result-link")) {
|
||||
closePagefind();
|
||||
}
|
||||
});
|
||||
|
||||
backdrop?.addEventListener("click", (event) => {
|
||||
if (!event.target.closest("#pagefind-container")) {
|
||||
closePagefind();
|
||||
}
|
||||
});
|
||||
|
||||
// prevent form submission
|
||||
const form = document.getElementById("form");
|
||||
form?.addEventListener("submit", (event) => {
|
||||
event.preventDefault();
|
||||
});
|
||||
</script>
|
||||
|
||||
<style is:global>
|
||||
:root {
|
||||
--pagefind-ui-scale: 0.75;
|
||||
--pagefind-ui-border-width: 1px;
|
||||
--pagefind-ui-border-radius: 3px;
|
||||
--pagefind-ui-font: "Geist", sans-serif;
|
||||
--pagefind-ui-primary: #3d3d3d;
|
||||
--pagefind-ui-text: #3d3d3d;
|
||||
--pagefind-ui-background: #ffffff;
|
||||
--pagefind-ui-border: #d0d0d0;
|
||||
--pagefind-ui-tag: #f5f5f5;
|
||||
}
|
||||
|
||||
.dark {
|
||||
--pagefind-ui-primary: #d4d4d4;
|
||||
--pagefind-ui-text: #d4d4d4;
|
||||
--pagefind-ui-background: #171717;
|
||||
--pagefind-ui-border: #404040;
|
||||
}
|
||||
|
||||
#search input {
|
||||
font-weight: normal;
|
||||
}
|
||||
|
||||
#search p {
|
||||
font-weight: normal;
|
||||
}
|
||||
|
||||
#search .pagefind-ui__result-title {
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
#search .pagefind-ui__message {
|
||||
padding: 0;
|
||||
padding-bottom: 0.75rem;
|
||||
}
|
||||
</style>
|
65
src/components/PostNavigation.astro
Normal file
65
src/components/PostNavigation.astro
Normal file
|
@ -0,0 +1,65 @@
|
|||
---
|
||||
const { prevPost, nextPost } = Astro.props;
|
||||
---
|
||||
|
||||
<div class="grid grid-cols-2 gap-1.5 sm:gap-3">
|
||||
{
|
||||
prevPost?.slug ? (
|
||||
<a
|
||||
href={`/blog/${prevPost?.slug}`}
|
||||
class="group relative flex flex-nowrap rounded-lg border border-black/15 px-4 py-3 pl-10 no-underline transition-colors duration-300 ease-in-out hover:bg-black/5 hover:text-black focus-visible:bg-black/5 focus-visible:text-black dark:border-white/20 dark:hover:bg-white/5 dark:hover:text-white dark:focus-visible:bg-white/5 dark:focus-visible:text-white"
|
||||
>
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
viewBox="0 0 24 24"
|
||||
class="absolute left-2 top-1/2 size-5 -translate-y-1/2 fill-none stroke-current stroke-2"
|
||||
>
|
||||
<line
|
||||
x1="5"
|
||||
y1="12"
|
||||
x2="19"
|
||||
y2="12"
|
||||
class="translate-x-3 scale-x-0 transition-transform duration-300 ease-in-out group-hover:translate-x-0 group-hover:scale-x-100 group-focus-visible:translate-x-0 group-focus-visible:scale-x-100"
|
||||
/>
|
||||
<polyline
|
||||
points="12 5 5 12 12 19"
|
||||
class="translate-x-1 transition-transform duration-300 ease-in-out group-hover:translate-x-0 group-focus-visible:translate-x-0"
|
||||
/>
|
||||
</svg>
|
||||
<div class="flex items-center text-sm">{prevPost?.data.title}</div>
|
||||
</a>
|
||||
) : (
|
||||
<div class="invisible" />
|
||||
)
|
||||
}
|
||||
|
||||
{
|
||||
nextPost?.slug ? (
|
||||
<a
|
||||
href={`/blog/${nextPost?.slug}`}
|
||||
class="group relative flex flex-grow flex-row-reverse flex-nowrap rounded-lg border border-black/15 px-4 py-4 pr-10 no-underline transition-colors duration-300 ease-in-out hover:bg-black/5 hover:text-black focus-visible:bg-black/5 focus-visible:text-black dark:border-white/20 dark:hover:bg-white/5 dark:hover:text-white dark:focus-visible:bg-white/5 dark:focus-visible:text-white"
|
||||
>
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
viewBox="0 0 24 24"
|
||||
class="absolute right-2 top-1/2 size-5 -translate-y-1/2 fill-none stroke-current stroke-2"
|
||||
>
|
||||
<line
|
||||
x1="5"
|
||||
y1="12"
|
||||
x2="19"
|
||||
y2="12"
|
||||
class="translate-x-3 scale-x-0 transition-transform duration-300 ease-in-out group-hover:translate-x-0 group-hover:scale-x-100 group-focus-visible:translate-x-0 group-focus-visible:scale-x-100"
|
||||
/>
|
||||
<polyline
|
||||
points="12 5 19 12 12 19"
|
||||
class="-translate-x-1 transition-transform duration-300 ease-in-out group-hover:translate-x-0 group-focus-visible:translate-x-0"
|
||||
/>
|
||||
</svg>
|
||||
<div class="flex items-center text-sm">{nextPost?.data.title}</div>
|
||||
</a>
|
||||
) : (
|
||||
<div class="invisible" />
|
||||
)
|
||||
}
|
||||
</div>
|
9
src/components/ProgressBar.astro
Normal file
9
src/components/ProgressBar.astro
Normal file
|
@ -0,0 +1,9 @@
|
|||
<div class="fixed w-full h-[2px] bg-neutral-300 dark:bg-slate-900 z-50">
|
||||
<div class="bg-cyan-500 dark:bg-orange-500 h-full"></div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
import { initializeProgressBar } from '../scripts/progress-bar.js';
|
||||
initializeProgressBar();
|
||||
</script>
|
||||
|
74
src/components/PublicationCard.astro
Normal file
74
src/components/PublicationCard.astro
Normal file
|
@ -0,0 +1,74 @@
|
|||
---
|
||||
import type { CollectionEntry } from "astro:content";
|
||||
import { Image } from "astro:assets";
|
||||
import { HIGHLIGHTAUTHOR } from "@consts";
|
||||
|
||||
type Props = {
|
||||
entry: CollectionEntry<"publications">;
|
||||
};
|
||||
|
||||
const { entry } = Astro.props as {
|
||||
entry: CollectionEntry<"publications">;
|
||||
};
|
||||
|
||||
const splitStr = (authors: string | undefined, targetAuthor: string) => {
|
||||
if (!authors) return [];
|
||||
const parts = authors.split(new RegExp(`(${targetAuthor})`, 'g'));
|
||||
return parts;
|
||||
};
|
||||
const decomposeURL = (URL: string | undefined) => {
|
||||
if (!URL) return { text: '', url: '' };
|
||||
const parts = URL.split(": ");
|
||||
return { text: parts[0], url: parts[1] };
|
||||
};
|
||||
|
||||
const dataLink = decomposeURL(entry.data.dataURL);
|
||||
const paperLink = decomposeURL(entry.data.paperURL);
|
||||
const codeLink = decomposeURL(entry.data.codeURL);
|
||||
const webLink = decomposeURL(entry.data.webURL);
|
||||
const authorsParts = splitStr(entry.data.authors, HIGHLIGHTAUTHOR);
|
||||
|
||||
---
|
||||
|
||||
<div
|
||||
class="w-full not-prose group relative grid grid-cols-auto md:grid-cols-[204px_auto] gap-4 rounded-lg items-center border border-black/15 px-4 py-3 transition-colors duration-300 ease-in-out hover:bg-black/5 hover:text-black focus-visible:bg-black/5 focus-visible:text-black dark:border-white/20 dark:hover:bg-white/5 dark:hover:text-white dark:focus-visible:bg-white/5 dark:focus-visible:text-white"
|
||||
>
|
||||
<Image
|
||||
src={entry.data.img ?? ''}
|
||||
alt={entry.data.imgAlt ?? ''}
|
||||
width={640}
|
||||
height={480}
|
||||
class="sm:w-[240px] sm:h-[135px] shadow-sm rounded-md sm:mr-6 hover:opacity-80 transition hidden md:flex"
|
||||
loading="eager"
|
||||
/>
|
||||
|
||||
|
||||
<div class="flex items-center h-full">
|
||||
<div class="flex flex-col">
|
||||
<div class="w-full">
|
||||
<div class="text-md font-semibold w-full">
|
||||
{entry.data.title}
|
||||
</div>
|
||||
<div class="text-sm w-full">
|
||||
{authorsParts.map((part:any) =>
|
||||
part === HIGHLIGHTAUTHOR ? <u><strong>{part}</strong></u> : part
|
||||
)}
|
||||
</div>
|
||||
<div class="text-sm w-full">
|
||||
{paperLink.url!="" && <a class="underline hover:text-cyan-500 text-orange-500 dark:hover:text-cyan-500 transition-colors duration-300 ease-in-out visited:text-indigo-400" target="_blank" href={paperLink.url}>{paperLink.text}</a>}
|
||||
{codeLink.url!="" && <a class="underline hover:text-cyan-500 text-orange-500 dark:hover:text-cyan-500 transition-colors duration-300 ease-in-out visited:text-indigo-400" target="_blank" href={codeLink.url}>{codeLink.text}</a>}
|
||||
{webLink.url!="" && <a class="underline hover:text-cyan-500 text-orange-500 dark:hover:text-cyan-500 transition-colors duration-300 ease-in-out visited:text-indigo-400" target="_blank" href={webLink.url}>{webLink.text}</a>}
|
||||
{dataLink.url!="" && <a class="underline hover:text-cyan-500 text-orange-500 dark:hover:text-cyan-500 transition-colors duration-300 ease-in-out visited:text-indigo-400" target="_blank" href={dataLink.url}>{dataLink.text}</a>}
|
||||
</div>
|
||||
<div class="text-sm">
|
||||
In <div class="inline italic">{entry.data.pub}</div>
|
||||
</div>
|
||||
</div>
|
||||
<p class="text-sm mt-2 break-words">
|
||||
{entry.data.description}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
18
src/components/SocialIcon.astro
Normal file
18
src/components/SocialIcon.astro
Normal file
|
@ -0,0 +1,18 @@
|
|||
---
|
||||
import 'bootstrap-icons/font/bootstrap-icons.css';
|
||||
|
||||
export interface Props {
|
||||
URL: string;
|
||||
icon: string;
|
||||
icon_size: string
|
||||
}
|
||||
|
||||
const { URL, icon, icon_size } = Astro.props;
|
||||
---
|
||||
<a
|
||||
href={URL}
|
||||
target={"_blank"}
|
||||
class={
|
||||
`inline-block ${icon_size} decoration-black/30 dark:decoration-white/30 hover:decoration-black/50 focus-visible:decoration-black/50 dark:hover:decoration-white/50 dark:focus-visible:decoration-white/50 text-current hover:text-cyan-500 focus-visible:text-black dark:hover:text-orange-500 dark:focus-visible:text-white transition-colors duration-300 ease-in-out`}>
|
||||
<i class={`bi bi-${icon}`}></i>
|
||||
</a>
|
18
src/components/SocialIcons.astro
Normal file
18
src/components/SocialIcons.astro
Normal file
|
@ -0,0 +1,18 @@
|
|||
---
|
||||
import SocialIcon from "@components/SocialIcon.astro";
|
||||
import { SITE } from "@consts";
|
||||
|
||||
export interface Props {
|
||||
icon_size: string
|
||||
}
|
||||
|
||||
const { icon_size } = Astro.props;
|
||||
---
|
||||
<ul class="not-prose flex flex-wrap gap-2">
|
||||
<SocialIcon icon_size={icon_size} URL="#" icon="twitter-x"/>
|
||||
<SocialIcon icon_size={icon_size} URL="#" icon="github"/>
|
||||
<SocialIcon icon_size={icon_size} URL="#" icon="linkedin"/>
|
||||
<SocialIcon icon_size={icon_size} URL="#" icon="envelope-fill"/>
|
||||
<SocialIcon icon_size={icon_size} URL="#" icon="mortarboard-fill"/>
|
||||
<SocialIcon icon_size={icon_size} URL={`${SITE.SITEURL}/rss.xml`} icon="rss-fill"/>
|
||||
</ul>
|
54
src/components/TableOfContents.astro
Normal file
54
src/components/TableOfContents.astro
Normal file
|
@ -0,0 +1,54 @@
|
|||
---
|
||||
import TableOfContentsHeading from "./TableOfContentsHeading.astro";
|
||||
|
||||
// https://kld.dev/building-table-of-contents/
|
||||
const { headings } = Astro.props;
|
||||
const toc = buildToc(headings);
|
||||
|
||||
export interface Heading {
|
||||
depth: number;
|
||||
slug: string;
|
||||
text: string;
|
||||
}
|
||||
|
||||
function buildToc(headings: Heading[]) {
|
||||
const toc: Heading[] = [];
|
||||
const parentHeadings = new Map();
|
||||
headings.forEach((h) => {
|
||||
const heading = { ...h, subheadings: [] };
|
||||
parentHeadings.set(heading.depth, heading);
|
||||
if (heading.depth === 2) {
|
||||
toc.push(heading);
|
||||
} else {
|
||||
parentHeadings.get(heading.depth - 1).subheadings.push(heading);
|
||||
}
|
||||
});
|
||||
return toc;
|
||||
}
|
||||
---
|
||||
|
||||
<details
|
||||
open
|
||||
class="animate rounded-lg border border-black/15 dark:border-white/20"
|
||||
>
|
||||
<summary>Table of Contents</summary>
|
||||
<nav class="">
|
||||
<ul class="py-3">
|
||||
{toc.map((heading) => <TableOfContentsHeading heading={heading} />)}
|
||||
</ul>
|
||||
</nav>
|
||||
</details>
|
||||
|
||||
<style>
|
||||
summary {
|
||||
@apply cursor-pointer rounded-t-lg px-3 py-1.5 font-medium transition-colors;
|
||||
}
|
||||
|
||||
summary:hover {
|
||||
@apply bg-black/5 dark:bg-white/5;
|
||||
}
|
||||
|
||||
details[open] summary {
|
||||
@apply bg-black/5 dark:bg-white/5;
|
||||
}
|
||||
</style>
|
23
src/components/TableOfContentsHeading.astro
Normal file
23
src/components/TableOfContentsHeading.astro
Normal file
|
@ -0,0 +1,23 @@
|
|||
---
|
||||
import type { Heading } from "./TableOfContents.astro";
|
||||
import Link from "./Link.astro";
|
||||
|
||||
// https://kld.dev/building-table-of-contents/
|
||||
|
||||
const { heading } = Astro.props;
|
||||
---
|
||||
|
||||
<li class="list-inside list-disc px-6 py-1.5 text-sm">
|
||||
<Link href={"#" + heading.slug} underline>
|
||||
{heading.text}
|
||||
</Link>
|
||||
{
|
||||
heading.subheadings.length > 0 && (
|
||||
<ul class="translate-x-3">
|
||||
{heading.subheadings.map((subheading: Heading) => (
|
||||
<Astro.self heading={subheading} />
|
||||
))}
|
||||
</ul>
|
||||
)
|
||||
}
|
||||
</li>
|
46
src/consts.ts
Normal file
46
src/consts.ts
Normal file
|
@ -0,0 +1,46 @@
|
|||
import type { Metadata, Site } from "@types";
|
||||
|
||||
export const SITE: Site = {
|
||||
TITLE: "Astro Micro Academics",
|
||||
DESCRIPTION: "Astro Micro Academics is for academic user.",
|
||||
EMAIL: "youremial@gmail.com",
|
||||
NUM_POSTS_ON_HOMEPAGE: 2,
|
||||
NUM_PUBLICATIONS_ON_HOMEPAGE: 3,
|
||||
SITEURL: 'https://astro-micro-academic.vercel.app' // Update here to link the RSS icon to your website rss
|
||||
};
|
||||
|
||||
export const HIGHLIGHTAUTHOR = "John B"
|
||||
|
||||
export const HOME: Metadata = {
|
||||
TITLE: "Home",
|
||||
DESCRIPTION: "Astro Micro is an accessible theme for Astro.",
|
||||
};
|
||||
|
||||
export const BLOG: Metadata = {
|
||||
TITLE: "Blog",
|
||||
DESCRIPTION: "A collection of articles on topics I am passionate about.",
|
||||
};
|
||||
|
||||
export const RESEARCH: Metadata = {
|
||||
TITLE: "Publications",
|
||||
DESCRIPTION:
|
||||
"A collection of my publications with links to paper, repositories and live demos.",
|
||||
};
|
||||
|
||||
export const CV: Metadata = {
|
||||
TITLE: "CV",
|
||||
DESCRIPTION:
|
||||
"your cv",
|
||||
};
|
||||
|
||||
export const TAGS: Metadata = {
|
||||
TITLE: "TAGS",
|
||||
DESCRIPTION:
|
||||
"blog tag filter",
|
||||
};
|
||||
|
||||
export const ABOUT: Metadata = {
|
||||
TITLE: "ABOUT",
|
||||
DESCRIPTION:
|
||||
"A self-intro",
|
||||
};
|
51
src/content/blog/00-academic-astro.mdx
Normal file
51
src/content/blog/00-academic-astro.mdx
Normal file
|
@ -0,0 +1,51 @@
|
|||
---
|
||||
title: "[Logs] What is new in Astro Micro Academic"
|
||||
description: "Features, enhancements, and changes."
|
||||
date: "2024-07-25"
|
||||
tags: ['log', 'rss-feed']
|
||||
---
|
||||
|
||||
:exclamation: Also refer to [Old] posts to see examples and changes. Some changes are removed in this version, which are marked.
|
||||
|
||||
## What is New
|
||||
|
||||
- Add Math support to the blog
|
||||
- Add News, Research, Publication, CV sections
|
||||
- add icon to connect section
|
||||
- Add tag function to the blog
|
||||
- add RSS Feed icon
|
||||
- add tags to post slug and enable tag based search
|
||||
- add function to RSS feed only posts with rss-feed tags
|
||||
- Change SOCIALS
|
||||
- add highlight author
|
||||
- redo the news section height
|
||||
- support markdown emoji :blush: :exclamation:
|
||||
- Clear and rearrange all the posts
|
||||
- [x] rewrite astro-micro-academics
|
||||
- [x] astro micro
|
||||
- [x] get started with astro micro
|
||||
- [x] blog collection example
|
||||
|
||||
---
|
||||
|
||||
## TODO
|
||||
|
||||
- Add Group Page
|
||||
- Add Opening Section
|
||||
- Add Award Page
|
||||
- Write a complete personal website setup guide
|
||||
- screenshot and submit theme to astro
|
||||
- add progress bar on the top
|
||||
- show tags on allowcard
|
||||
- fix news section scroll bar
|
||||
|
||||
## Math test
|
||||
|
||||
This is an inline formula: $a = b+1$, $a = \frac{1}{x_1}$
|
||||
|
||||
$$
|
||||
a = \sum_{n=1}^{22} \sqrt{a+b_n}
|
||||
$$
|
||||
|
||||
## 中文测试
|
||||
这里是中文测试
|
256
src/content/blog/00-academics-getting-started.md
Normal file
256
src/content/blog/00-academics-getting-started.md
Normal file
|
@ -0,0 +1,256 @@
|
|||
---
|
||||
title: "[Tutorial] Getting started with Astro-Micro-Academics"
|
||||
description: "Hit the ground running."
|
||||
date: "2024-07-26"
|
||||
tags: ['guide', 'tutorial']
|
||||
---
|
||||
|
||||
:exclamation: Also refer to [Old] posts to see examples and changes. Some changes are removed in this version, which are marked.
|
||||
|
||||
## Install Astro-Micro-Academics
|
||||
|
||||
Clone the [repository](https://github.com/jingwu2121/astro-micro-academic).
|
||||
|
||||
```sh
|
||||
git clone https://github.com/jingwu2121/astro-micro-academic.git
|
||||
```
|
||||
|
||||
```sh
|
||||
cd astro-micro-academic
|
||||
```
|
||||
|
||||
```sh
|
||||
npm i
|
||||
```
|
||||
|
||||
Run local server
|
||||
```sh
|
||||
npm run dev
|
||||
```
|
||||
|
||||
## Update the Homepage
|
||||
|
||||
Update your home page in `src/pages/index.astro`.
|
||||
|
||||
|
||||
## CV & About
|
||||
|
||||
Update your CV and About page in `src/pages/cv.astro` and `src/pages/about.astro`.
|
||||
|
||||
```ts
|
||||
const works = [
|
||||
{company:"Company A", time: "2022-Present", job_title: "Research Scientist", location: "London, UK", description: "Your Notes about the job"},
|
||||
{company:"Company A", time: "2022-Present", job_title: "Research Scientist", location: "London, UK", description: "Your Notes about the job"},
|
||||
]
|
||||
const educations = [
|
||||
{school:"University 1", time: "2022-Present", job_title: "BEng in Electronic Information Engineering", location: "London, UK", description: "Your Notes about the study"},
|
||||
]
|
||||
```
|
||||
|
||||
## Social Links
|
||||
|
||||
Update the social links in `src/components/SocialIcons.astro`, simply replace the `URL`.
|
||||
|
||||
## Publications metadata
|
||||
|
||||
Metadata is required for each post. Add a new `publication.md` to automartically add a publication on the website. Publications are sorted by date.
|
||||
|
||||
```astro
|
||||
---
|
||||
title: "Diffusion Models Beat GANs on Image Synthesis"
|
||||
description: "Lorem ipsum dolor sit amet consectetur adipisicing elit. Molestias earum quod quo repellat blanditiis est iste eos dolorem! Voluptatibus corporis totam sed unde est iusto neque iure natus adipisci omnis."
|
||||
date: "2024-07-26"
|
||||
authors: "John B*, Jon A*, Frank C, John B, Jon A, Frank C"
|
||||
paperURL: "Paper: https://astro-sphere-demo.vercel.app"
|
||||
codeURL: "Code: "
|
||||
webURL: "Web: https://github.com/markhorn-dev/astro-sphere"
|
||||
dataURL: "Data: https://github.com/markhorn-dev/astro-sphere"
|
||||
img: "/rupert-cat.gif"
|
||||
imgAlt: "Paper Teaser"
|
||||
pub: "ECCV2024"
|
||||
---
|
||||
```
|
||||
|
||||
| Field | Req | Type | Remarks |
|
||||
| :---------- | :-- | :------ | :----------------------------------------------- |
|
||||
| title | Yes | string | Title of the content. Used in SEO and RSS. |
|
||||
| description | Yes | string | Description of the content. Used in SEO and RSS. |
|
||||
| date | Yes | string | Must be a valid date string (able to be parsed). |
|
||||
| authors | Yes | string | A string seperated by comma. |
|
||||
| paperURL, codeURL, webURL, dataURL | Yes | string | A string seperated by ": ". If you don't have a link to add, leave the link part blank, e.g. `codeURL: "Code: "` |
|
||||
| img | Yes | string | Path to teaser image. |
|
||||
| imgAlt | Yes | string | Description of the image. |
|
||||
| pub | Yes | string | The conference or journal |
|
||||
|
||||
## Blog metadata
|
||||
|
||||
Metadata is required for each post.
|
||||
|
||||
```astro
|
||||
---
|
||||
title: "Blog Collection";
|
||||
description: "How to add posts to the blog.";
|
||||
date: "2024-03-21";
|
||||
tags: ['guide', 'tutorial']
|
||||
draft: false;
|
||||
---
|
||||
```
|
||||
|
||||
| Field | Req | Type | Remarks |
|
||||
| :---------- | :-- | :------ | :----------------------------------------------- |
|
||||
| title | Yes | string | Title of the content. Used in SEO and RSS. |
|
||||
| description | Yes | string | Description of the content. Used in SEO and RSS. |
|
||||
| date | Yes | string | Must be a valid date string (able to be parsed). |
|
||||
| tags | Yes | list | A list of strings |
|
||||
| draft | No | boolean | If draft: true, content will not be published. |
|
||||
|
||||
## Customize the website metadata and set up RSS
|
||||
|
||||
To change the website metadata, edit `src/consts.ts`.
|
||||
|
||||
```ts
|
||||
// src/consts.ts
|
||||
|
||||
export const SITE: Site = {
|
||||
TITLE: "Astro Micro Academics",
|
||||
DESCRIPTION: "Astro Micro Academics is for academic user.",
|
||||
EMAIL: "youremial@gmail.com",
|
||||
NUM_POSTS_ON_HOMEPAGE: 2,
|
||||
NUM_PUBLICATIONS_ON_HOMEPAGE: 3,
|
||||
SITEURL: 'https://astro-micro-academic.vercel.app' // Update here to link the RSS icon to your website RSS
|
||||
};
|
||||
```
|
||||
|
||||
| Field | Req | Description |
|
||||
| :----------- | :-- | :--------------------------------------------------- |
|
||||
| TITLE | Yes | Displayed in header and footer. Used in SEO and RSS. |
|
||||
| DESCRIPTION | Yes | Used in SEO and RSS. |
|
||||
| EMAIL | Yes | Displayed in contact section. |
|
||||
| NUM_POSTS | Yes | Limit number of posts on home page. |
|
||||
| NUM_PUBLICATIONS | Yes | Limit number of research on home page. |
|
||||
| SITEURL | Yes | Your website URL |
|
||||
|
||||
### RSS Post
|
||||
|
||||
Please tag the post of RSS feed with tag `"rss-feed"`, other posts are not included in the RSS.
|
||||
|
||||
---
|
||||
|
||||
## Custom metadata for highlighted author in your paper
|
||||
|
||||
```ts
|
||||
// src/consts.ts
|
||||
|
||||
export const HIGHLIGHTAUTHOR = "John B"
|
||||
```
|
||||
|
||||
## Customize metadata for individual pages
|
||||
|
||||
```ts
|
||||
// src/consts.ts
|
||||
|
||||
export const HOME: Metadata = {
|
||||
TITLE: "Home",
|
||||
DESCRIPTION: "Astro Micro is an accessible theme for Astro.",
|
||||
};
|
||||
|
||||
export const BLOG: Metadata = {
|
||||
TITLE: "Blog",
|
||||
DESCRIPTION: "A collection of articles on topics I am passionate about.",
|
||||
};
|
||||
|
||||
export const RESEARCH: Metadata = {
|
||||
TITLE: "Publications",
|
||||
DESCRIPTION:
|
||||
"A collection of my publications with links to paper, repositories and live demos.",
|
||||
};
|
||||
|
||||
export const CV: Metadata = {
|
||||
TITLE: "CV",
|
||||
DESCRIPTION:
|
||||
"your cv",
|
||||
};
|
||||
|
||||
export const TAGS: Metadata = {
|
||||
TITLE: "TAGS",
|
||||
DESCRIPTION:
|
||||
"blog tag filter",
|
||||
};
|
||||
|
||||
export const ABOUT: Metadata = {
|
||||
TITLE: "ABOUT",
|
||||
DESCRIPTION:
|
||||
"A self-intro",
|
||||
};
|
||||
```
|
||||
|
||||
| Field | Req | Description |
|
||||
| :---------- | :-- | :--------------------------------------------- |
|
||||
| TITLE | Yes | Displayed in browser tab. Used in SEO and RSS. |
|
||||
| DESCRIPTION | Yes | Used in SEO and RSS. |
|
||||
|
||||
---
|
||||
|
||||
## Deploy the site
|
||||
|
||||
To set up RSS and Giscus, it's easier if the site is deployed and has a URL for you to use. Instantly deploy to Vercel or Netlify by clicking the buttons below.
|
||||
|
||||
<div class="flex gap-2">
|
||||
<a target="_blank" aria-label="Deploy with Vercel" href="https://vercel.com/new/clone?repository-url=https://github.com/trevortylerlee/astro-micro">
|
||||
<img src="/deploy_vercel.svg" />
|
||||
</a>
|
||||
<a target="_blank" aria-label="Deploy with Netlify" href="https://app.netlify.com/start/deploy?repository=https://github.com/trevortylerlee/astro-micro">
|
||||
<img src="/deploy_netlify.svg" />
|
||||
</a>
|
||||
</div>
|
||||
|
||||
To deploy manually see [Astro's docs](https://docs.astro.build/en/guides/deploy/).
|
||||
|
||||
To deploy to Github, see [here](https://docs.astro.build/en/guides/deploy/github/).
|
||||
|
||||
|
||||
## Set up Giscus (from Astro Micro)
|
||||
|
||||
Follow the steps at [giscus.app](https://giscus.app). Once you get your custom Giscus script from that site, go to `Giscus.astro` and replace that script with your own.
|
||||
|
||||
```js
|
||||
// src/components/Giscus.astro
|
||||
|
||||
<script
|
||||
is:inline
|
||||
src="https://giscus.app/client.js"
|
||||
data-repo="trevortylerlee/astro-micro"
|
||||
data-repo-id="R_kgDOL_6l9Q"
|
||||
data-category="Announcements"
|
||||
data-category-id="DIC_kwDOL_6l9c4Cfk55"
|
||||
data-mapping="pathname"
|
||||
data-strict="0"
|
||||
data-reactions-enabled="1"
|
||||
data-emit-metadata="0"
|
||||
data-input-position="top"
|
||||
data-theme="preferred_color_scheme"
|
||||
data-lang="en"
|
||||
data-loading="lazy"
|
||||
crossorigin="anonymous"
|
||||
async></script>
|
||||
|
||||
```
|
||||
|
||||
To change the Giscus themes used, edit the `setGiscusTheme` function in `Head.astro`.
|
||||
|
||||
```js
|
||||
// src/components/Head.astro
|
||||
|
||||
const setGiscusTheme = () => {
|
||||
const giscus = document.querySelector(".giscus-frame");
|
||||
|
||||
const isDark = document.documentElement.classList.contains("dark");
|
||||
|
||||
if (giscus) {
|
||||
const url = new URL(giscus.src);
|
||||
// Change "dark" and "light" to other Giscus themes
|
||||
url.searchParams.set("theme", isDark ? "dark" : "light");
|
||||
giscus.src = url.toString();
|
||||
}
|
||||
};
|
||||
```
|
105
src/content/blog/00-astro-micro-changelog/index.mdx
Normal file
105
src/content/blog/00-astro-micro-changelog/index.mdx
Normal file
|
@ -0,0 +1,105 @@
|
|||
---
|
||||
title: "[Old] Everything new in Astro Micro"
|
||||
description: "Features, enhancements, and changes."
|
||||
date: "2024-05-09"
|
||||
tags: ['astro-micro']
|
||||
---
|
||||
|
||||
import Callout from "@/components/Callout.astro";
|
||||
|
||||
---
|
||||
|
||||
## Pagefind search 🔎
|
||||
|
||||
[Pagefind](https://pagefind.app) is a search library for static websites. Micro uses [Sergey Shishkin's](https://github.com/shishkin) [astro-pagefind](https://github.com/shishkin/astro-pagefind) integration. This integration simplifies development and does not require any changes to the default build script.
|
||||
|
||||
Press <kbd>/</kbd> or <kbd>CTRL</kbd> + <kbd>K</kbd> to open the search dialog. For Mac users, <kbd>⌘</kbd> + <kbd>K</kbd> can also be used. To dismiss the search dialog, press <kbd>Esc</kbd> or click on an area outside the component.
|
||||
|
||||
### Build and develop
|
||||
|
||||
<Callout type="error">
|
||||
The site **must** be built at least once so Pagefind can index the content.
|
||||
</Callout>
|
||||
|
||||
```bash
|
||||
# Pagefind must index the site to function
|
||||
npm run build
|
||||
```
|
||||
|
||||
When developing you can continue to use `npm run dev` and Pagefind will use the index from the last available build.
|
||||
|
||||
---
|
||||
|
||||
## Giscus comments 💬
|
||||
|
||||
[Giscus](https://giscus.app) leverages Github discussions to act as a comments system. To get Giscus working on your own website, see [here](/blog/01-getting-started#deploy-the-site).
|
||||
|
||||
---
|
||||
|
||||
## Callout component 🆕
|
||||
|
||||
<Callout>
|
||||
Adipisicing et officia reprehenderit fugiat occaecat cupidatat exercitation
|
||||
labore consequat ullamco nostrud non.
|
||||
</Callout>
|
||||
|
||||
<Callout type="info">
|
||||
Adipisicing et officia reprehenderit fugiat occaecat cupidatat exercitation
|
||||
labore consequat ullamco nostrud non.
|
||||
</Callout>
|
||||
|
||||
<Callout type="warning">
|
||||
Adipisicing et officia reprehenderit fugiat occaecat cupidatat exercitation
|
||||
labore consequat ullamco nostrud non.
|
||||
</Callout>
|
||||
|
||||
<Callout type="error">
|
||||
Adipisicing et officia reprehenderit fugiat occaecat cupidatat exercitation
|
||||
labore consequat ullamco nostrud non.
|
||||
</Callout>
|
||||
|
||||
---
|
||||
|
||||
## UI enhancements 🎨
|
||||
|
||||
- Elements are styled and animate on focus
|
||||
- Increased contrast in light mode
|
||||
- Active theme is indicated by theme buttons
|
||||
- Separate syntax highlight themes for light and dark mode
|
||||
- Code blocks have a copy button
|
||||
- Add pagination to the bottom of blog posts
|
||||
- Create 404 page
|
||||
- Add ToC component to posts
|
||||
|
||||
---
|
||||
|
||||
## Other changes
|
||||
|
||||
- Change fonts to Geist Sans and Geist Mono
|
||||
- Switch base color from "stone" to "neutral"
|
||||
- Change formatted date to use "long" option for month
|
||||
- Minor spacing changes throughout
|
||||
- Remove "work" collection and components
|
||||
- If desired, you can get the code from [Astro Nano](https://github.com/markhorn-dev/astro-nano)
|
||||
- Slightly increased link decoration offset
|
||||
- Slightly sped-up animations
|
||||
- Reversed animation
|
||||
- Ensure posts use an h1 tag for post titles
|
||||
- Tweaked typography
|
||||
|
||||
---
|
||||
|
||||
## Issues ⚠️
|
||||
|
||||
### Active issues
|
||||
|
||||
- [PostNavigation.astro is broken when deployed with Cloudflare Pages](https://github.com/trevortylerlee/astro-micro/issues/39)
|
||||
|
||||
### Closed issues
|
||||
|
||||
- Fixed by [blopker](https://github.com/blopker): [Pagefind requires a refresh to function when ViewTransitions is enabled](https://github.com/trevortylerlee/astro-micro/issues/7)
|
||||
- Fixed by [blopker](https://github.com/blopker): [ToC links are obscured by Header when scrolled to](https://github.com/trevortylerlee/astro-micro/issues/4)
|
||||
- Fixed by [cgranier](https://github.com/cgranier): [Pagination links advance by slug/folder](https://github.com/trevortylerlee/astro-micro/issues/26)
|
||||
- Fixed by [cgranier](https://github.com/cgranier): [Hides Table of Contents when there are no headings](https://github.com/trevortylerlee/astro-micro/pull/30)
|
||||
- Fixed by [arastoonet](https://github.com/arastoonet): [Fix typo in README](https://github.com/trevortylerlee/astro-micro/pull/19)
|
||||
- Fixed by [luciancah](https://github.com/luciancah): [Prevent Pagefind from double-indexing results](https://github.com/trevortylerlee/astro-micro/issues/40)
|
181
src/content/blog/01-getting-started.md
Normal file
181
src/content/blog/01-getting-started.md
Normal file
|
@ -0,0 +1,181 @@
|
|||
---
|
||||
title: "[Old] Getting started with Astro-Micro"
|
||||
description: "Hit the ground running."
|
||||
date: "2024-03-22"
|
||||
tags: ['guide', 'start']
|
||||
---
|
||||
|
||||
---
|
||||
|
||||
## Install astro-micro
|
||||
|
||||
Clone the [Astro Micro repository](https://github.com/trevortylerlee/astro-micro.git).
|
||||
|
||||
```sh
|
||||
git clone https://github.com/trevortylerlee/astro-micro.git my-astro-micro
|
||||
```
|
||||
|
||||
```sh
|
||||
cd my-astro-micro
|
||||
```
|
||||
|
||||
```sh
|
||||
npm i
|
||||
```
|
||||
|
||||
```sh
|
||||
npm run build
|
||||
```
|
||||
|
||||
```sh
|
||||
npm run dev
|
||||
```
|
||||
|
||||
## Customize the website metadata
|
||||
|
||||
To change the website metadata, edit `src/consts.ts`.
|
||||
|
||||
```ts
|
||||
// src/consts.ts
|
||||
|
||||
export const SITE: Site = {
|
||||
NAME: "Astro Micro",
|
||||
DESCRIPTION: "Astro Micro is an accessible theme for Astro.",
|
||||
EMAIL: "trevortylerlee@gmail.com",
|
||||
NUM_POSTS_ON_HOMEPAGE: 3,
|
||||
NUM_PUBLICATIONS_ON_HOMEPAGE: 3,
|
||||
};
|
||||
```
|
||||
|
||||
| Field | Req | Description |
|
||||
| :----------- | :-- | :--------------------------------------------------- |
|
||||
| TITLE | Yes | Displayed in header and footer. Used in SEO and RSS. |
|
||||
| DESCRIPTION | Yes | Used in SEO and RSS. |
|
||||
| EMAIL | Yes | Displayed in contact section. |
|
||||
| NUM_POSTS | Yes | Limit number of posts on home page. |
|
||||
| NUM_PUBLICATIONS | Yes | Limit number of research on home page. |
|
||||
|
||||
---
|
||||
|
||||
## Customize metadata for individual pages
|
||||
|
||||
```ts
|
||||
// src/consts.ts
|
||||
|
||||
export const ABOUT: Metadata = {
|
||||
TITLE: "About",
|
||||
DESCRIPTION: "Astro Micro is a fork of Astro Nano.",
|
||||
};
|
||||
```
|
||||
|
||||
| Field | Req | Description |
|
||||
| :---------- | :-- | :--------------------------------------------- |
|
||||
| TITLE | Yes | Displayed in browser tab. Used in SEO and RSS. |
|
||||
| DESCRIPTION | Yes | Used in SEO and RSS. |
|
||||
|
||||
---
|
||||
|
||||
## ~~Add your social media links~~ Removed in Micro Academics
|
||||
|
||||
```ts
|
||||
// src/consts.ts
|
||||
|
||||
export const SOCIALS: Socials = [
|
||||
{
|
||||
NAME: "twitter-x",
|
||||
HREF: "https://twitter.com/boogerbuttcheeks",
|
||||
},
|
||||
{
|
||||
NAME: "github",
|
||||
HREF: "https://github.com/trevortylerlee",
|
||||
},
|
||||
{
|
||||
NAME: "linkedin",
|
||||
HREF: "https://www.linkedin.com/in/trevortylerlee",
|
||||
},
|
||||
];
|
||||
```
|
||||
|
||||
| Field | Req | Description |
|
||||
| :---- | :-- | :-------------------------------------- |
|
||||
| NAME | Yes | Displayed in contact section as a link. |
|
||||
| HREF | Yes | External url to social media profile. |
|
||||
|
||||
## Deploy the site
|
||||
|
||||
To set up RSS and Giscus, it's easier if the site is deployed and has a URL for you to use. Instantly deploy to Vercel or Netlify by clicking the buttons below.
|
||||
|
||||
<div class="flex gap-2">
|
||||
<a target="_blank" aria-label="Deploy with Vercel" href="https://vercel.com/new/clone?repository-url=https://github.com/trevortylerlee/astro-micro">
|
||||
<img src="/deploy_vercel.svg" />
|
||||
</a>
|
||||
<a target="_blank" aria-label="Deploy with Netlify" href="https://app.netlify.com/start/deploy?repository=https://github.com/trevortylerlee/astro-micro">
|
||||
<img src="/deploy_netlify.svg" />
|
||||
</a>
|
||||
</div>
|
||||
|
||||
To deploy manually see [Astro's docs](https://docs.astro.build/en/guides/deploy/).
|
||||
|
||||
## ~~Set up RSS~~ New config in Astro Micro Academics
|
||||
|
||||
Change the `site` option to the deployed site's URL.
|
||||
|
||||
```js
|
||||
// astro.config.mjs
|
||||
|
||||
export default defineConfig({
|
||||
site: "https://astro-micro.vercel.app",
|
||||
integrations: [tailwind(), sitemap(), mdx(), pagefind()],
|
||||
markdown: {
|
||||
shikiConfig: {
|
||||
theme: "css-variables",
|
||||
},
|
||||
},
|
||||
});
|
||||
```
|
||||
|
||||
## Set up Giscus
|
||||
|
||||
Follow the steps at [giscus.app](https://giscus.app). Once you get your custom Giscus script from that site, go to `Giscus.astro` and replace that script with your own.
|
||||
|
||||
```js
|
||||
// src/components/Giscus.astro
|
||||
|
||||
<script
|
||||
is:inline
|
||||
src="https://giscus.app/client.js"
|
||||
data-repo="trevortylerlee/astro-micro"
|
||||
data-repo-id="R_kgDOL_6l9Q"
|
||||
data-category="Announcements"
|
||||
data-category-id="DIC_kwDOL_6l9c4Cfk55"
|
||||
data-mapping="pathname"
|
||||
data-strict="0"
|
||||
data-reactions-enabled="1"
|
||||
data-emit-metadata="0"
|
||||
data-input-position="top"
|
||||
data-theme="preferred_color_scheme"
|
||||
data-lang="en"
|
||||
data-loading="lazy"
|
||||
crossorigin="anonymous"
|
||||
async></script>
|
||||
|
||||
```
|
||||
|
||||
To change the Giscus themes used, edit the `setGiscusTheme` function in `Head.astro`.
|
||||
|
||||
```js
|
||||
// src/components/Head.astro
|
||||
|
||||
const setGiscusTheme = () => {
|
||||
const giscus = document.querySelector(".giscus-frame");
|
||||
|
||||
const isDark = document.documentElement.classList.contains("dark");
|
||||
|
||||
if (giscus) {
|
||||
const url = new URL(giscus.src);
|
||||
// Change "dark" and "light" to other Giscus themes
|
||||
url.searchParams.set("theme", isDark ? "dark" : "light");
|
||||
giscus.src = url.toString();
|
||||
}
|
||||
};
|
||||
```
|
63
src/content/blog/02-blog-collection/index.md
Normal file
63
src/content/blog/02-blog-collection/index.md
Normal file
|
@ -0,0 +1,63 @@
|
|||
---
|
||||
title: "[Old] Blog Collection"
|
||||
description: "How to add posts to the blog."
|
||||
date: "2024-03-21"
|
||||
tags: ['aa']
|
||||
---
|
||||
|
||||
---
|
||||
|
||||
## Working with the `blog` collection:
|
||||
|
||||
The `blog` collection is found in `src/content/blog`.
|
||||
|
||||
```
|
||||
📁 /src/content/blog
|
||||
└── 📁 post-1
|
||||
└── 📄 index.md
|
||||
└── 📁 post-2
|
||||
└── 📄 index.mdx
|
||||
```
|
||||
|
||||
In the above example, two blog posts will be generated with the folder name representing the slug.
|
||||
|
||||
- `https://example.com/blog/post-1`
|
||||
- `https://example.com/blog/post-2`
|
||||
|
||||
---
|
||||
|
||||
## Provide metadata
|
||||
|
||||
Metadata is required for each post.
|
||||
|
||||
```astro
|
||||
---
|
||||
title: "Blog Collection";
|
||||
description: "How to add posts to the blog.";
|
||||
date: "2024-03-21";
|
||||
draft: false;
|
||||
---
|
||||
```
|
||||
|
||||
| Field | Req | Type | Remarks |
|
||||
| :---------- | :-- | :------ | :----------------------------------------------- |
|
||||
| title | Yes | string | Title of the content. Used in SEO and RSS. |
|
||||
| description | Yes | string | Description of the content. Used in SEO and RSS. |
|
||||
| date | Yes | string | Must be a valid date string (able to be parsed). |
|
||||
| draft | No | boolean | If draft: true, content will not be published. |
|
||||
|
||||
---
|
||||
|
||||
All that's left to do is write the content under the metadata.
|
||||
|
||||
```astro
|
||||
---
|
||||
title: "Blog Collection";
|
||||
description: "How to add posts to the blog.";
|
||||
date: "2024-03-21";
|
||||
draft: false;
|
||||
---
|
||||
|
||||
## Working with the blog collection
|
||||
<!-- content -->
|
||||
```
|
398
src/content/blog/04-markdown-syntax/index.mdx
Normal file
398
src/content/blog/04-markdown-syntax/index.mdx
Normal file
|
@ -0,0 +1,398 @@
|
|||
---
|
||||
title: "[Old] Markdown syntax guide"
|
||||
description: "Get started writing content in Markdown."
|
||||
date: "2024-03-17"
|
||||
---
|
||||
|
||||
import Callout from "@/components/Callout.astro";
|
||||
|
||||
---
|
||||
|
||||
## Headings
|
||||
|
||||
To create headings, use hash symbols (#) followed by a space. The number of hash symbols indicates the heading level.
|
||||
|
||||
<Callout>
|
||||
Use `h2` tags instead of `h1` tags in the post. Too many `h1` tags on a single
|
||||
page can impact SEO. The post title serves as the `h1`.
|
||||
</Callout>
|
||||
|
||||
```md
|
||||
# Heading 1
|
||||
|
||||
## Heading 2
|
||||
|
||||
### Heading 3
|
||||
|
||||
#### Heading 4
|
||||
|
||||
##### Heading 5
|
||||
|
||||
###### Heading 6
|
||||
```
|
||||
|
||||
<h1>Heading 1</h1>
|
||||
<h2>Heading 2</h2>
|
||||
<h3>Heading 3</h3>
|
||||
<h4>Heading 4</h4>
|
||||
<h5>Heading 5</h5>
|
||||
<h6>Heading 6</h6>
|
||||
|
||||
---
|
||||
|
||||
## Paragraphs
|
||||
|
||||
To create paragraphs, use a blank line to separate one or more lines of text.
|
||||
|
||||
{/* prettier-ignore */}
|
||||
```md
|
||||
<!-- empty line -->
|
||||
I love Star Wars.
|
||||
<!-- empty line -->
|
||||
My favourite is Episode III – Revenge of the Sith.
|
||||
<!-- empty line -->
|
||||
```
|
||||
|
||||
I love Star Wars.
|
||||
|
||||
My favourite is Episode III – Revenge of the Sith.
|
||||
|
||||
---
|
||||
|
||||
## Italic
|
||||
|
||||
Use one asterisk \(\*\) or underscore \(\_\) to italicize text.
|
||||
|
||||
{/* prettier-ignore */}
|
||||
```md
|
||||
I *love* Star Wars.
|
||||
My _favourite_ is Episode III – Revenge of the Sith.
|
||||
```
|
||||
|
||||
I _love_ Star Wars.
|
||||
My _favourite_ is Episode III – Revenge of the Sith.
|
||||
|
||||
---
|
||||
|
||||
## Bold
|
||||
|
||||
Use two asterisks \(\*\) or underscores \(\_\) to bold text.
|
||||
|
||||
{/* prettier-ignore */}
|
||||
```md
|
||||
I **love** Star Wars.
|
||||
My __favourite__ is Episode III – Revenge of the Sith.
|
||||
```
|
||||
|
||||
I **love** Star Wars.
|
||||
My **favourite** is Episode III – Revenge of the Sith.
|
||||
|
||||
---
|
||||
|
||||
## Italic and Bold
|
||||
|
||||
Use three asterisks \(\*\) or underscores \(\_\) to both bold and italicize text.
|
||||
|
||||
{/* prettier-ignore */}
|
||||
```md
|
||||
I ***love*** Star Wars.
|
||||
My ___favourite___ is Episode III – Revenge of the Sith.
|
||||
```
|
||||
|
||||
I **_love_** Star Wars.
|
||||
My **_favourite_** is Episode III – Revenge of the Sith.
|
||||
|
||||
---
|
||||
|
||||
## Horizontal Rule
|
||||
|
||||
Use three hyphens \(\-\), asterisks \(\*\), or underscores \(\_\) to create a horizontal rule.
|
||||
|
||||
{/* prettier-ignore */}
|
||||
```md
|
||||
<!-- empty line -->
|
||||
---
|
||||
<!-- empty line -->
|
||||
***
|
||||
<!-- empty line -->
|
||||
___
|
||||
<!-- empty line -->
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
---
|
||||
|
||||
---
|
||||
|
||||
## Links
|
||||
|
||||
To create a link, the link text in brackets \(\[\]\) and then follow it immediately with the URL in parentheses \(\(\)\).
|
||||
|
||||
```md
|
||||
Micro is a fork of [astro-nano](https://github.com/markhorn-dev/astro-nano).
|
||||
```
|
||||
|
||||
Micro is a fork of [astro-nano](https://github.com/markhorn-dev/astro-nano).
|
||||
|
||||
---
|
||||
|
||||
## Ordered Lists
|
||||
|
||||
To create an ordered list, add line items with numbers followed by periods. Use an indent to create a nested list.
|
||||
|
||||
```md
|
||||
1. Item 1
|
||||
2. Item 2
|
||||
1. Sub item 1
|
||||
2. Sub item 2
|
||||
3. Item 3
|
||||
```
|
||||
|
||||
1. Item 1
|
||||
2. Item 2
|
||||
1. Sub item 1
|
||||
2. Sub item 2
|
||||
3. Item 3
|
||||
|
||||
---
|
||||
|
||||
## Unordered List
|
||||
|
||||
To create an unordered list, add a hyphen \(\-\), an asterisk \(\*\), or a plus sign \(\+\) in front of line items. Don't mix. Use an indent to create a nested list.
|
||||
|
||||
```md
|
||||
- Item 1
|
||||
- Item 2
|
||||
- Sub item 1
|
||||
- Sub item 2
|
||||
- Item 3
|
||||
```
|
||||
|
||||
- Item 1
|
||||
- Item 2
|
||||
- Sub item 1
|
||||
- Sub item 2
|
||||
- Item 3
|
||||
|
||||
---
|
||||
|
||||
## Images
|
||||
|
||||
To add an image, add an exclamation mark (!), followed by alt text in brackets ([]), and the path or URL to the image asset in parentheses.
|
||||
|
||||
```md
|
||||

|
||||
```
|
||||
|
||||
### Relative
|
||||
|
||||
Use the `` pattern relative to the same folder as the markdown file. Notice the period.
|
||||
|
||||
```md
|
||||

|
||||
```
|
||||
|
||||

|
||||
|
||||
### Public Image
|
||||
|
||||
Use the `` pattern relative to the public folder. No period.
|
||||
|
||||
```md
|
||||

|
||||
```
|
||||
|
||||

|
||||
|
||||
### External Image
|
||||
|
||||
Use the `` pattern.
|
||||
|
||||
```md
|
||||

|
||||
```
|
||||
|
||||

|
||||
|
||||
---
|
||||
|
||||
## Blockquotes
|
||||
|
||||
To add a blockquote add the greater-than character \(\>\) before a paragraph. For multi-line blockquotes, add additional greater-than character for each line and include an empty spacer line.
|
||||
|
||||
```md
|
||||
> So this is how liberty dies...
|
||||
>
|
||||
> with thunderous applause.
|
||||
```
|
||||
|
||||
> So this is how liberty dies...
|
||||
>
|
||||
> with thunderous applause.
|
||||
|
||||
---
|
||||
|
||||
## Strikethrough
|
||||
|
||||
Use a tilde \(\~\) symbol to strikethrough text.
|
||||
|
||||
```md
|
||||
~I don't like sand.~ It's coarse and rough and irritating.
|
||||
```
|
||||
|
||||
~I don't like sand.~ It's coarse and rough and irritating.
|
||||
|
||||
---
|
||||
|
||||
## Subscript
|
||||
|
||||
Use the `<sub>` tag to denote subscript.
|
||||
|
||||
```md
|
||||
H<sub>2</sub>O
|
||||
```
|
||||
|
||||
H<sub>2</sub>O
|
||||
|
||||
---
|
||||
|
||||
## Superscript
|
||||
|
||||
Use the `<sup>` tag to denote superscript.
|
||||
|
||||
```md
|
||||
E=mc<sup>2</sup>
|
||||
```
|
||||
|
||||
E=mc<sup>2</sup>
|
||||
|
||||
---
|
||||
|
||||
## Keyboard
|
||||
|
||||
Use the `<kbd>` tag to denote keys on the keyboard.
|
||||
|
||||
```md
|
||||
<kbd>CTRL</kbd> + <kbd>ALT</kbd> + <kbd>Delete</kbd>
|
||||
```
|
||||
|
||||
<kbd>CTRL</kbd> + <kbd>ALT</kbd> + <kbd>Delete</kbd>
|
||||
|
||||
---
|
||||
|
||||
## Abbreviate
|
||||
|
||||
Use the `<abbr>` tag to denote abbreviation.
|
||||
|
||||
```md
|
||||
<abbr title="Graphics Interchange Format">GIF</abbr>
|
||||
```
|
||||
|
||||
<abbr title="Graphics Interchange Format">GIF</abbr>
|
||||
|
||||
---
|
||||
|
||||
### Highlight
|
||||
|
||||
Use the `<mark>` tag to denote highlighted text.
|
||||
|
||||
```md
|
||||
Do or do not. <mark>There is no try.</mark>
|
||||
```
|
||||
|
||||
Do or do not. <mark>There is no try.</mark>
|
||||
|
||||
---
|
||||
|
||||
## Task Lists
|
||||
|
||||
Combine a list with square brackets ([]) representing a checkbox. Typing `x` inside the brackets marks the task as complete.
|
||||
|
||||
```md
|
||||
- [x] Build a lightsaber
|
||||
- [ ] Pass the Jedi Trials
|
||||
- [ ] Train a padawan
|
||||
```
|
||||
|
||||
- [x] Build a lightsaber
|
||||
- [ ] Pass the Jedi Trials
|
||||
- [ ] Train a padawan
|
||||
|
||||
---
|
||||
|
||||
## Tables
|
||||
|
||||
Use three or more hyphens (-) for the column headers and use pipes (|) to separate each column. You can align text in the columns to the left, right, or center by adding a colon (:) to the left, right, or on both side of the hyphens.
|
||||
|
||||
```md
|
||||
| Item | Count |
|
||||
| :----- | ----: |
|
||||
| X-Wing | 1 |
|
||||
| Y-Wing | 2 |
|
||||
| A-Wing | 3 |
|
||||
```
|
||||
|
||||
| Item | Count |
|
||||
| :----- | ----: |
|
||||
| X-Wing | 1 |
|
||||
| Y-Wing | 2 |
|
||||
| A-Wing | 3 |
|
||||
|
||||
---
|
||||
|
||||
## Footnotes
|
||||
|
||||
Add a caret (^) and an identifier inside brackets \(\[\^1\]\). Identifiers can be numbers or words but can't contain spaces or tabs.
|
||||
|
||||
```md
|
||||
Here's a footnote, [^1] and here's another one. [^2]
|
||||
[^1]: This is the first footnote.
|
||||
[^2]: This is the second footnote.
|
||||
```
|
||||
|
||||
Here's a footnote, [^1] and here's another one. [^2]
|
||||
[^1]: This is the first footnote.
|
||||
[^2]: This is the second footnote.
|
||||
|
||||
See the bottom of this page to view the footnotes.
|
||||
|
||||
---
|
||||
|
||||
## Code
|
||||
|
||||
To denote a word or phrase as code, enclose it in backticks (`).
|
||||
|
||||
```md
|
||||
`package.json`
|
||||
```
|
||||
|
||||
`package.json`
|
||||
|
||||
---
|
||||
|
||||
## Code Blocks
|
||||
|
||||
Denote a code block by wrapping a section of valid code in triple backticks (`). To enable syntax highlighting, type the shorthand symbol for the language after the first three backticks. [Reference for shorthand symbols](https://shiki.style/languages).
|
||||
|
||||
````
|
||||
```js
|
||||
function hello() {
|
||||
console.log("hello world");
|
||||
}
|
||||
```
|
||||
````
|
||||
|
||||
```js
|
||||
function hello() {
|
||||
console.log("hello world");
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Conclusion
|
||||
|
||||
Refer to [markdownguide.org](https://www.markdownguide.org/) for best practices as well as extended syntax.
|
||||
|
||||
---
|
BIN
src/content/blog/04-markdown-syntax/x-wing.jpeg
Normal file
BIN
src/content/blog/04-markdown-syntax/x-wing.jpeg
Normal file
Binary file not shown.
After Width: | Height: | Size: 203 KiB |
10
src/content/blog/05-mdx-syntax/component.astro
Normal file
10
src/content/blog/05-mdx-syntax/component.astro
Normal file
|
@ -0,0 +1,10 @@
|
|||
---
|
||||
|
||||
---
|
||||
|
||||
<button
|
||||
onclick="alert(`You clicked the button!`)"
|
||||
class="border border-black/10 p-2 transition-colors duration-300 ease-in-out hover:bg-black/5 dark:border-white/10 dark:hover:bg-white/10"
|
||||
>
|
||||
Relative Button
|
||||
</button>
|
71
src/content/blog/05-mdx-syntax/index.mdx
Normal file
71
src/content/blog/05-mdx-syntax/index.mdx
Normal file
|
@ -0,0 +1,71 @@
|
|||
---
|
||||
title: "[Old] MDX syntax guide"
|
||||
description: "Use interactive components in Markdown."
|
||||
date: "2024-03-16"
|
||||
---
|
||||
|
||||
import Callout from "@/components/Callout.astro";
|
||||
|
||||
---
|
||||
|
||||
MDX is an extension of Markdown with the ability to import `.astro`,
|
||||
`.jsx`, `.tsx` and other framework components you have integrated.
|
||||
|
||||
This guide covers the basics of MDX syntax and how to use it, as well as a few examples.
|
||||
|
||||
## Example 1
|
||||
|
||||
Importing a component from the `/components` directory.
|
||||
|
||||
This component accepts a Javascript date object and format it as a string.
|
||||
|
||||
```astro
|
||||
import DateComp from "../../../components/FormattedDate.astro";
|
||||
|
||||
<DateComp date={new Date()} />
|
||||
```
|
||||
|
||||
import FormattedDate from "../../../components/FormattedDate.astro";
|
||||
|
||||
<FormattedDate date={new Date()} />
|
||||
|
||||
---
|
||||
|
||||
## Example 2
|
||||
|
||||
Importing a component from a relative path to your content.
|
||||
|
||||
This component displays an alert when the button is clicked.
|
||||
|
||||
```astro
|
||||
import RelativeComponent from "./component.astro";
|
||||
|
||||
<RelativeComponent />
|
||||
```
|
||||
|
||||
import RelativeComponent from "./component.astro";
|
||||
|
||||
<RelativeComponent />
|
||||
|
||||
---
|
||||
|
||||
By default Micro has zero frameworks installed. If you install a framework, components of that framework can be used in MDX files.
|
||||
|
||||
<Callout>
|
||||
Don't forget to use [client
|
||||
directives](https://docs.astro.build/en/reference/directives-reference/#client-directives)
|
||||
to make framework components interactive.
|
||||
</Callout>
|
||||
|
||||
```astro
|
||||
<ReactComponent client:load />
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## More Links
|
||||
|
||||
- [MDX Syntax Documentation](https://mdxjs.com/docs/what-is-mdx)
|
||||
- [Astro Framework Integrations](https://docs.astro.build/en/guides/integrations-guide)
|
||||
- [Astro Usage Documentation](https://docs.astro.build/en/guides/markdown-content/#markdown-and-mdx-pages)
|
||||
- [Client Directives](https://docs.astro.build/en/reference/directives-reference/#client-directives)
|
7
src/content/blog/06-year-sorting-example.md
Normal file
7
src/content/blog/06-year-sorting-example.md
Normal file
|
@ -0,0 +1,7 @@
|
|||
---
|
||||
title: "[Old] Year sorting example"
|
||||
description: "Nano groups posts by year."
|
||||
date: "2023-12-31"
|
||||
---
|
||||
|
||||
This post is to demonstrate the year sorting capabilities.
|
10
src/content/blog/07-draft-example.md
Normal file
10
src/content/blog/07-draft-example.md
Normal file
|
@ -0,0 +1,10 @@
|
|||
---
|
||||
title: "[Old] Draft example"
|
||||
description: "Setting draft flag to true to hide this post."
|
||||
date: "2022-12-31"
|
||||
draft: false
|
||||
---
|
||||
|
||||
This post also demonstrates the year sorting capabilities.
|
||||
|
||||
Try setting this file's metadata to `draft: true`.
|
10
src/content/blog/08-prev-next-order-example.md
Normal file
10
src/content/blog/08-prev-next-order-example.md
Normal file
|
@ -0,0 +1,10 @@
|
|||
---
|
||||
title: "[Old] Chronological pagination example"
|
||||
description: "Pagination works regardless of folder name."
|
||||
date: "2024-03-21"
|
||||
draft: false
|
||||
---
|
||||
|
||||
This post should show up in proper chronological order even though its folder comes last in the `content/blog` directory.
|
||||
|
||||
The `Previous Post` and `Next Post` buttons under each blog post should also keep the proper chronological order, based on the frontmatter `date` field.
|
32
src/content/config.ts
Normal file
32
src/content/config.ts
Normal file
|
@ -0,0 +1,32 @@
|
|||
import { defineCollection, z } from "astro:content";
|
||||
|
||||
const blog = defineCollection({
|
||||
type: "content",
|
||||
schema: z.object({
|
||||
title: z.string(),
|
||||
description: z.string(),
|
||||
date: z.coerce.date(),
|
||||
draft: z.boolean().optional(),
|
||||
|
||||
tags: z.array(z.string()).optional(),
|
||||
}),
|
||||
});
|
||||
|
||||
const publications = defineCollection({
|
||||
type: "content",
|
||||
schema: z.object({
|
||||
title: z.string(),
|
||||
description: z.string(),
|
||||
date: z.coerce.date(),
|
||||
paperURL: z.string().optional(),
|
||||
authors: z.string().optional(),
|
||||
codeURL: z.string().optional(),
|
||||
webURL: z.string().optional(),
|
||||
dataURL: z.string().optional(),
|
||||
img: z.string().optional(),
|
||||
imgAlt: z.string().optional(),
|
||||
pub: z.string().optional(),
|
||||
}),
|
||||
});
|
||||
|
||||
export const collections = { blog, publications };
|
14
src/content/publications/papertitle1.md
Normal file
14
src/content/publications/papertitle1.md
Normal file
|
@ -0,0 +1,14 @@
|
|||
---
|
||||
title: "Paper Title1"
|
||||
description: "Your Paper Abstract"
|
||||
date: "2024-03-26"
|
||||
authors: "John B*, Jon A*, Frank C, "
|
||||
paperURL: "Paper: https://astro-sphere-demo.vercel.app"
|
||||
codeURL: "Code: https://astro-sphere-demo.vercel.app"
|
||||
webURL: "Web: https://github.com/markhorn-dev/astro-sphere"
|
||||
img: "/rupert-cat.gif"
|
||||
imgAlt: "Paper Teaser"
|
||||
pub: "ECCV2024"
|
||||
dataURL: "Data: "
|
||||
---
|
||||
|
14
src/content/publications/papertitle2.md
Normal file
14
src/content/publications/papertitle2.md
Normal file
|
@ -0,0 +1,14 @@
|
|||
---
|
||||
title: "Paper Title1"
|
||||
description: "Lorem ipsum dolor sit amet consectetur adipisicing elit. "
|
||||
date: "2024-07-26"
|
||||
authors: "John B*, Jon A*, Frank C, John B, Jon A, Frank C, "
|
||||
paperURL: "Paper: https://astro-sphere-demo.vercel.app"
|
||||
codeURL: "Code: https://astro-sphere-demo.vercel.app"
|
||||
webURL: "Web: https://github.com/markhorn-dev/astro-sphere"
|
||||
img: "/rupert-cat.gif"
|
||||
imgAlt: "Paper Teaser"
|
||||
pub: "ECCV2024"
|
||||
dataURL: "Data: https://github.com/markhorn-dev/astro-sphere"
|
||||
---
|
||||
|
14
src/content/publications/papertitle3.md
Normal file
14
src/content/publications/papertitle3.md
Normal file
|
@ -0,0 +1,14 @@
|
|||
---
|
||||
title: "Paper Title1"
|
||||
description: "Your Paper Abstract"
|
||||
date: "2024-03-26"
|
||||
authors: "John B*, Jon A*, Frank C, "
|
||||
paperURL: "Paper: https://astro-sphere-demo.vercel.app"
|
||||
codeURL: "Code: https://astro-sphere-demo.vercel.app"
|
||||
webURL: "Web: https://github.com/markhorn-dev/astro-sphere"
|
||||
img: "/rupert-cat.gif"
|
||||
imgAlt: "Paper Teaser"
|
||||
pub: "ECCV2024"
|
||||
dataURL: "Data: "
|
||||
---
|
||||
|
2
src/env.d.ts
vendored
Normal file
2
src/env.d.ts
vendored
Normal file
|
@ -0,0 +1,2 @@
|
|||
/// <reference path="../.astro/types.d.ts" />
|
||||
/// <reference types="astro/client" />
|
33
src/layouts/Layout.astro
Normal file
33
src/layouts/Layout.astro
Normal file
|
@ -0,0 +1,33 @@
|
|||
---
|
||||
import Head from "@components/Head.astro";
|
||||
import Header from "@components/Header.astro";
|
||||
import Footer from "@components/Footer.astro";
|
||||
import PageFind from "@components/PageFind.astro";
|
||||
import { SITE } from "@consts";
|
||||
import '../styles/katex.css';
|
||||
import ProgressBar from "@components/ProgressBar.astro";
|
||||
|
||||
type Props = {
|
||||
title: string;
|
||||
description: string;
|
||||
};
|
||||
|
||||
const { title, description } = Astro.props;
|
||||
---
|
||||
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<Head title={`${title} | ${SITE.TITLE}`} description={description} />
|
||||
<link rel="stylesheet" href="/path/to/your/global.css">
|
||||
</head>
|
||||
<body>
|
||||
<Header />
|
||||
<!-- <ProgressBar /> -->
|
||||
<main>
|
||||
<slot />
|
||||
</main>
|
||||
<Footer />
|
||||
<PageFind />
|
||||
</body>
|
||||
</html>
|
21
src/lib/utils.ts
Normal file
21
src/lib/utils.ts
Normal file
|
@ -0,0 +1,21 @@
|
|||
import { clsx, type ClassValue } from "clsx";
|
||||
import { twMerge } from "tailwind-merge";
|
||||
|
||||
export function cn(...inputs: ClassValue[]) {
|
||||
return twMerge(clsx(inputs));
|
||||
}
|
||||
|
||||
export function formatDate(date: Date) {
|
||||
return Intl.DateTimeFormat("en-US", {
|
||||
year: "numeric",
|
||||
month: "2-digit",
|
||||
day: "2-digit",
|
||||
}).format(date);
|
||||
}
|
||||
|
||||
export function readingTime(html: string) {
|
||||
const textOnly = html.replace(/<[^>]+>/g, "");
|
||||
const wordCount = textOnly.split(/\s+/).length;
|
||||
const readingTimeMinutes = (wordCount / 200 + 1).toFixed();
|
||||
return `${readingTimeMinutes} min read`;
|
||||
}
|
20
src/pages/404.astro
Normal file
20
src/pages/404.astro
Normal file
|
@ -0,0 +1,20 @@
|
|||
---
|
||||
import Layout from "@layouts/Layout.astro";
|
||||
import Container from "@components/Container.astro";
|
||||
import Link from "@components/Link.astro";
|
||||
import BackToPrevious from "@components/BackToPrevious.astro";
|
||||
import { SITE } from "@consts";
|
||||
---
|
||||
|
||||
<Layout title="404" description={SITE.DESCRIPTION}>
|
||||
<Container>
|
||||
<div class="mt-16 grid place-items-center gap-3">
|
||||
<h4 class="animate text-2xl font-semibold text-black dark:text-white">
|
||||
404: Page not found
|
||||
</h4>
|
||||
<span class="animate">
|
||||
<BackToPrevious href="/">Go to home page</BackToPrevious>
|
||||
</span>
|
||||
</div>
|
||||
</Container>
|
||||
</Layout>
|
53
src/pages/about.astro
Normal file
53
src/pages/about.astro
Normal file
|
@ -0,0 +1,53 @@
|
|||
---
|
||||
import { getCollection } from "astro:content";
|
||||
import Layout from "@layouts/Layout.astro";
|
||||
import Container from "@components/Container.astro";
|
||||
import { ABOUT } from "@consts";
|
||||
import Image from "astro/components/Image.astro";
|
||||
---
|
||||
|
||||
<Layout title={ABOUT.TITLE} description={ABOUT.DESCRIPTION}>
|
||||
<Container>
|
||||
<aside data-pagefind-ignore>
|
||||
<div class="space-y-10">
|
||||
<div class="animate font-semibold text-black dark:text-white">
|
||||
About
|
||||
</div>
|
||||
<section class="animate not-prose flex flex-col gap-4 text-justify">
|
||||
<p class="text-justify">Lorem ipsum dolor sit amet consectetur adipisicing elit. Dolores porro hic minima incidunt explicabo obcaecati consectetur consequuntur at quisquam commodi.
|
||||
</p>
|
||||
|
||||
<p class="text-justify">Lorem ipsum dolor sit amet consectetur adipisicing elit. Dolores porro hic minima incidunt explicabo obcaecati consectetur consequuntur at quisquam commodi.
|
||||
</p>
|
||||
</section>
|
||||
|
||||
<div class="flex flex-col md:flex-row justify-center">
|
||||
<div class="my-10 text-center">
|
||||
<div class="bg-neutral-300 w-[350px] h-[250px] object-cover rounded-xl -rotate-6 overflow-hidden">
|
||||
<Image
|
||||
src={'/astro-nano.png'}
|
||||
alt={'life2'}
|
||||
width={350}
|
||||
height={250}
|
||||
class="w-[350px] h-[250px] object-cover rounded-xl overflow-hidden"
|
||||
/>
|
||||
</div>
|
||||
<p class="mt-4 text-sm">Lorem ipsum dolor sit amet, consectetur adipisicing elit. </p>
|
||||
</div>
|
||||
<div class="mx-10 my-10 text-center">
|
||||
<div class="bg-neutral-300 w-[150px] h-[250px] object-cover rounded-xl rotate-6 mx-auto sm:ml-auto">
|
||||
<Image
|
||||
src={'/astro-micro.jpg'}
|
||||
alt={'life2'}
|
||||
width={150}
|
||||
height={250}
|
||||
class="w-[150px] h-[250px] object-cover rounded-xl "
|
||||
/>
|
||||
</div>
|
||||
<p class="mt-4 text-sm">Lorem ipsum dolor sit amet, consectetur adipisicing elit.</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</aside>
|
||||
</Container>
|
||||
</Layout>
|
95
src/pages/blog/[...slug].astro
Normal file
95
src/pages/blog/[...slug].astro
Normal file
|
@ -0,0 +1,95 @@
|
|||
---
|
||||
import { type CollectionEntry, getCollection } from "astro:content";
|
||||
import Layout from "@layouts/Layout.astro";
|
||||
import Container from "@components/Container.astro";
|
||||
import FormattedDate from "@components/FormattedDate.astro";
|
||||
import { readingTime } from "@lib/utils";
|
||||
import BackToPrevious from "@components/BackToPrevious.astro";
|
||||
import PostNavigation from "@components/PostNavigation.astro";
|
||||
import TableOfContents from "@components/TableOfContents.astro";
|
||||
import Giscus from "@components/Giscus.astro";
|
||||
|
||||
export async function getStaticPaths() {
|
||||
const posts = (await getCollection("blog"))
|
||||
.filter((post) => !post.data.draft)
|
||||
.sort((a, b) => b.data.date.valueOf() - a.data.date.valueOf());
|
||||
return posts.map((post) => ({
|
||||
params: { slug: post.slug },
|
||||
props: post,
|
||||
}));
|
||||
}
|
||||
type Props = CollectionEntry<"blog">;
|
||||
|
||||
const posts = (await getCollection("blog"))
|
||||
.filter((post) => !post.data.draft)
|
||||
.sort((a, b) => b.data.date.valueOf() - a.data.date.valueOf());
|
||||
|
||||
function getPostIndex(slug: string): number {
|
||||
return posts.findIndex((post) => post.slug === slug);
|
||||
}
|
||||
|
||||
function getNextPost(slug: string): Props | null {
|
||||
const postIndex = getPostIndex(slug);
|
||||
return postIndex !== -1 && postIndex < posts.length - 1 ? posts[postIndex + 1] : null;
|
||||
}
|
||||
|
||||
function getPrevPost(slug: string): Props | null {
|
||||
const postIndex = getPostIndex(slug);
|
||||
return postIndex > 0 ? posts[postIndex - 1] : null;
|
||||
}
|
||||
|
||||
const currentPostSlug = Astro.params.slug;
|
||||
const nextPost = getNextPost(currentPostSlug);
|
||||
const prevPost = getPrevPost(currentPostSlug);
|
||||
|
||||
const post = Astro.props;
|
||||
const { Content, headings } = await post.render();
|
||||
---
|
||||
|
||||
<Layout title={post.data.title} description={post.data.description}>
|
||||
<Container>
|
||||
<div class="animate">
|
||||
<BackToPrevious href="/blog">Back to blog</BackToPrevious>
|
||||
</div>
|
||||
<div class="my-10 space-y-4">
|
||||
<div class="animate flex items-center gap-1.5">
|
||||
<div class="font-base text-sm">
|
||||
<FormattedDate date={post.data.date} />
|
||||
</div>
|
||||
•
|
||||
<div class="font-base text-sm">
|
||||
{readingTime(post.body)}
|
||||
</div>
|
||||
</div>
|
||||
<h1 class="animate text-4xl font-semibold text-black dark:text-white">
|
||||
{post.data.title}
|
||||
</h1>
|
||||
<div class="font-base text-sm ">
|
||||
{post.data.tags && post.data.tags.length > 0 ? (
|
||||
post.data.tags.map((tag) => (
|
||||
<div class="inline-block my-1">
|
||||
<a
|
||||
href={`/tags/${tag}`}
|
||||
class="mx-1 rounded-full px-2 py-1 bg-orange-300 hover:bg-cyan-200 dark:bg-orange-500 dark:hover:bg-cyan-500 transition-colors duration-300 ease-in-out"
|
||||
>
|
||||
#{tag}
|
||||
</a>
|
||||
</div>
|
||||
))
|
||||
) : (
|
||||
<span>No tags available</span>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
{headings.length > 0 && <TableOfContents headings={headings} />}
|
||||
<article class="animate">
|
||||
<Content />
|
||||
<div class="mt-24">
|
||||
<PostNavigation prevPost={prevPost} nextPost={nextPost} />
|
||||
</div>
|
||||
<div class="mt-24">
|
||||
<Giscus />
|
||||
</div>
|
||||
</article>
|
||||
</Container>
|
||||
</Layout>
|
55
src/pages/blog/index.astro
Normal file
55
src/pages/blog/index.astro
Normal file
|
@ -0,0 +1,55 @@
|
|||
---
|
||||
import { type CollectionEntry, getCollection } from "astro:content";
|
||||
import Layout from "@layouts/Layout.astro";
|
||||
import Container from "@components/Container.astro";
|
||||
import ArrowCard from "@components/ArrowCard.astro";
|
||||
import { BLOG } from "@consts";
|
||||
|
||||
const data = (await getCollection("blog"))
|
||||
.filter((post) => !post.data.draft)
|
||||
.sort((a, b) => b.data.date.valueOf() - a.data.date.valueOf());
|
||||
|
||||
type Acc = {
|
||||
[year: string]: CollectionEntry<"blog">[];
|
||||
};
|
||||
|
||||
const posts = data.reduce((acc: Acc, post) => {
|
||||
const year = post.data.date.getFullYear().toString();
|
||||
if (!acc[year]) {
|
||||
acc[year] = [];
|
||||
}
|
||||
acc[year].push(post);
|
||||
return acc;
|
||||
}, {});
|
||||
|
||||
const years = Object.keys(posts).sort((a, b) => parseInt(b) - parseInt(a));
|
||||
---
|
||||
|
||||
<Layout title={BLOG.TITLE} description={BLOG.DESCRIPTION}>
|
||||
<Container>
|
||||
<aside data-pagefind-ignore>
|
||||
<div class="space-y-10">
|
||||
<div class="space-y-4">
|
||||
{
|
||||
years.map((year) => (
|
||||
<section class="animate space-y-4">
|
||||
<div class="font-semibold text-black dark:text-white">
|
||||
{year}
|
||||
</div>
|
||||
<div>
|
||||
<ul class="not-prose flex flex-col gap-4">
|
||||
{posts[year].map((post) => (
|
||||
<li>
|
||||
<ArrowCard entry={post} />
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
</div>
|
||||
</section>
|
||||
))
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
</aside>
|
||||
</Container>
|
||||
</Layout>
|
41
src/pages/cv.astro
Normal file
41
src/pages/cv.astro
Normal file
|
@ -0,0 +1,41 @@
|
|||
---
|
||||
import { getCollection } from "astro:content";
|
||||
import Layout from "@layouts/Layout.astro";
|
||||
import Container from "@components/Container.astro";
|
||||
import { CV } from "@consts";
|
||||
import CVCard from "@components/CVCard.astro";
|
||||
|
||||
// TO Modify
|
||||
const works = [
|
||||
{company:"Company A", time: "2022-Present", job_title: "Research Scientist", location: "London, UK", description: "Your Notes about the job"},
|
||||
{company:"Company A", time: "2022-Present", job_title: "Research Scientist", location: "London, UK", description: "Your Notes about the job"},
|
||||
]
|
||||
const educations = [
|
||||
{school:"University 1", time: "2022-Present", job_title: "BEng in Electronic Information Engineering", location: "London, UK", description: "Your Notes about the study"},
|
||||
]
|
||||
---
|
||||
|
||||
<Layout title={CV.TITLE} description={CV.DESCRIPTION}>
|
||||
<Container>
|
||||
<aside data-pagefind-ignore>
|
||||
<div class="space-y-2 md:space-y-10">
|
||||
<div class="animate font-semibold text-black dark:text-white">
|
||||
Work Experience
|
||||
</div>
|
||||
<ul class="animate not-prose flex flex-col gap-4">
|
||||
{works.map((work) => (
|
||||
<CVCard institution={work.company} time={work.time} job_title={work.job_title} location={work.location} description={work.description} />)
|
||||
)}
|
||||
</ul>
|
||||
<div class="animate font-semibold text-black dark:text-white">
|
||||
Education
|
||||
</div>
|
||||
<ul class="animate not-prose flex flex-col gap-4">
|
||||
{educations.map((education) => (
|
||||
<CVCard institution={education.school} time={education.time} job_title={education.job_title} location={education.location} description={education.description} />)
|
||||
)}
|
||||
</ul>
|
||||
</div>
|
||||
</aside>
|
||||
</Container>
|
||||
</Layout>
|
158
src/pages/index.astro
Normal file
158
src/pages/index.astro
Normal file
|
@ -0,0 +1,158 @@
|
|||
---
|
||||
import Layout from "@layouts/Layout.astro";
|
||||
import Container from "@components/Container.astro";
|
||||
import { SITE, HOME } from "@consts";
|
||||
import ArrowCard from "@components/ArrowCard.astro";
|
||||
import Link from "@components/Link.astro";
|
||||
import { getCollection } from "astro:content";
|
||||
import type { CollectionEntry } from "astro:content";
|
||||
import PublicationCard from "@components/PublicationCard.astro";
|
||||
import SocialIcons from "@components/SocialIcons.astro";
|
||||
|
||||
const blog = (await getCollection("blog"))
|
||||
.filter((post) => !post.data.draft)
|
||||
.sort((a, b) => b.data.date.valueOf() - a.data.date.valueOf())
|
||||
.slice(0, SITE.NUM_POSTS_ON_HOMEPAGE);
|
||||
|
||||
const publications: CollectionEntry<"publications">[] = (
|
||||
await getCollection("publications")
|
||||
)
|
||||
.sort((a, b) => b.data.date.valueOf() - a.data.date.valueOf())
|
||||
.slice(0, SITE.NUM_PUBLICATIONS_ON_HOMEPAGE);
|
||||
---
|
||||
|
||||
<Layout title={HOME.TITLE} description={HOME.DESCRIPTION}>
|
||||
<Container>
|
||||
<aside data-pagefind-ignore>
|
||||
<h1 class="animate font-semibold text-black dark:text-white">
|
||||
Astro Micro Academics 🍄
|
||||
</h1>
|
||||
<div class="space-y-16">
|
||||
<section>
|
||||
<article class="space-y-4">
|
||||
<span class="animate">
|
||||
<p>
|
||||
Astro Micro Academics is a theme for <Link
|
||||
href="https://astro.build/">Astro</Link
|
||||
> and tailored for academic users and researchers. It's built on <Link href="https://astro.build/themes/details/astro-micro/">Astro Micro</Link> and
|
||||
<Link href="https://github.com/markhorn-dev">
|
||||
Mark Horn's
|
||||
</Link> popular theme <Link
|
||||
href="https://astro.build/themes/details/astronano/"
|
||||
>Astro Nano</Link
|
||||
>.
|
||||
</p>
|
||||
<p>
|
||||
Micro Academics adds features like <span class="text-red-500">tags, and blog math support</span> and also inherits <Link href="https://pagefind.app/"
|
||||
>Pagefind</Link
|
||||
> for search, <Link href="https://giscus.app">Giscus</Link> for comments,
|
||||
from Astro Micro. See full changes this <Link
|
||||
href="/blog/00-academic-astro">here</Link
|
||||
>.
|
||||
</p>
|
||||
</span>
|
||||
<span class="animate">
|
||||
<p>
|
||||
Micro Academics still comes with everything great about Micro and Nano — full type
|
||||
safety, a sitemap, an RSS feed, and Markdown + MDX support.
|
||||
Styled with TailwindCSS and preconfigured with system, light,
|
||||
and dark themes.
|
||||
</p>
|
||||
<p>
|
||||
Visit
|
||||
<Link href="https://github.com/jingwu2121/astro-micro-academic">
|
||||
Astro Micro Academics on GitHub
|
||||
</Link>
|
||||
to fork the repository to get started.
|
||||
</p>
|
||||
</span>
|
||||
|
||||
</article>
|
||||
</section>
|
||||
|
||||
<section class="animate space-y-2 border-2 border-red-800 text-red-800 dark:border-red-400 border-dashed p-2 dark:text-red-400">
|
||||
📢📢 Your important Information (Open for job/Recruiting students)
|
||||
</section>
|
||||
|
||||
<section class="animate space-y-6">
|
||||
<span class="animate">
|
||||
<h4 class="font-semibold text-black dark:text-white">
|
||||
Let's Connect
|
||||
</h4>
|
||||
<article>
|
||||
<p>
|
||||
If you want to get in touch with me about something or just to say
|
||||
hi, reach out on social media or send me an email.
|
||||
</p>
|
||||
</article>
|
||||
<SocialIcons icon_size={'text-3xl'} />
|
||||
</span>
|
||||
</section>
|
||||
|
||||
<section class="animate space-y-6">
|
||||
<div class="flex flex-wrap items-center justify-between gap-y-2">
|
||||
<h2 class="font-semibold text-black dark:text-white">
|
||||
Research Interests
|
||||
</h2>
|
||||
</div>
|
||||
<ul class="not-prose flex flex-col gap-4">
|
||||
<li>3D Vision</li>
|
||||
<li>AIGC</li>
|
||||
</ul>
|
||||
</section>
|
||||
|
||||
<section class="animate space-y-6">
|
||||
<div class="flex flex-wrap items-center justify-between gap-y-2">
|
||||
<h2 class="font-semibold text-black dark:text-white">
|
||||
News
|
||||
</h2>
|
||||
</div>
|
||||
<ul class="not-prose flex flex-col gap-4 overflow-y-auto max-h-[150px] scroll_bar">
|
||||
<li>[06/2024]: Your News1</li>
|
||||
<li>[06/2024]: Your <span class="text-red-600">News2</span></li>
|
||||
<li>[06/2024]: Your <span class="text-red-600">News2</span></li>
|
||||
<!-- <li>[06/2024]: Your <span class="text-red-600">News2</span></li>
|
||||
<li>[06/2024]: Your <span class="text-red-600">News2</span></li> -->
|
||||
</ul>
|
||||
</section>
|
||||
|
||||
<section class="animate space-y-6">
|
||||
<div class="flex flex-wrap items-center justify-between gap-y-2">
|
||||
<h2 class="font-semibold text-black dark:text-white">
|
||||
Recent research
|
||||
</h2>
|
||||
<Link href="/publications"> See all research </Link>
|
||||
</div>
|
||||
<ul class="not-prose flex flex-col gap-4">
|
||||
{
|
||||
publications.map((publication) => (
|
||||
<li>
|
||||
<PublicationCard entry={publication} />
|
||||
</li>
|
||||
))
|
||||
}
|
||||
</ul>
|
||||
</section>
|
||||
|
||||
<section class="animate space-y-6">
|
||||
<div class="flex flex-wrap items-center justify-between gap-y-2">
|
||||
<h2 class="font-semibold text-black dark:text-white">
|
||||
Latest posts
|
||||
</h2>
|
||||
<Link href="/blog"> See all posts </Link>
|
||||
</div>
|
||||
<ul class="not-prose flex flex-col gap-4">
|
||||
{
|
||||
blog.map((post) => (
|
||||
<li>
|
||||
<ArrowCard entry={post} />
|
||||
</li>
|
||||
))
|
||||
}
|
||||
</ul>
|
||||
</section>
|
||||
|
||||
</div>
|
||||
</aside>
|
||||
</Container>
|
||||
</Layout>
|
32
src/pages/publications/index.astro
Normal file
32
src/pages/publications/index.astro
Normal file
|
@ -0,0 +1,32 @@
|
|||
---
|
||||
import { getCollection } from "astro:content";
|
||||
import Layout from "@layouts/Layout.astro";
|
||||
import Container from "@components/Container.astro";
|
||||
import { RESEARCH } from "@consts";
|
||||
import PublicationCard from "@components/PublicationCard.astro";
|
||||
// import PublicationCard from "@components/PublicationCard";
|
||||
|
||||
const publications = (await getCollection("publications"))
|
||||
.sort((a, b) => b.data.date.valueOf() - a.data.date.valueOf());
|
||||
---
|
||||
|
||||
<Layout title={RESEARCH.TITLE} description={RESEARCH.DESCRIPTION}>
|
||||
<Container>
|
||||
<aside data-pagefind-ignore>
|
||||
<div class="space-y-10">
|
||||
<div class="animate font-semibold text-black dark:text-white">
|
||||
Research
|
||||
</div>
|
||||
<ul class="animate not-prose flex flex-col gap-4">
|
||||
{
|
||||
publications.map((publication) => (
|
||||
<li>
|
||||
<PublicationCard entry={publication} />
|
||||
</li>
|
||||
))
|
||||
}
|
||||
</ul>
|
||||
</div>
|
||||
</aside>
|
||||
</Container>
|
||||
</Layout>
|
40
src/pages/rss.xml.js
Normal file
40
src/pages/rss.xml.js
Normal file
|
@ -0,0 +1,40 @@
|
|||
import rss from "@astrojs/rss";
|
||||
import { SITE } from "@consts";
|
||||
import { getCollection } from "astro:content";
|
||||
|
||||
export async function GET(context) {
|
||||
// const publications = (await getCollection("publications")).filter(
|
||||
// (publication) => !publication.data.draft,
|
||||
// );
|
||||
|
||||
// const items = [...blog, ...publications].sort(
|
||||
// (a, b) => new Date(b.data.date).valueOf() - new Date(a.data.date).valueOf(),
|
||||
// );
|
||||
try {
|
||||
const blog = (await getCollection("blog")).filter((post) => !post.data.draft);
|
||||
|
||||
// Filter posts by tag 'rss-feed'
|
||||
const filteredBlogs = blog.filter(post => post.data.tags && post.data.tags.includes('rss-feed'));
|
||||
|
||||
// Sort posts by date
|
||||
const items = [...filteredBlogs].sort(
|
||||
(a, b) => new Date(b.data.date).valueOf() - new Date(a.data.date).valueOf()
|
||||
);
|
||||
|
||||
// Return RSS feed
|
||||
return rss({
|
||||
title: SITE.TITLE,
|
||||
description: SITE.DESCRIPTION,
|
||||
site: context.site,
|
||||
items: items.map((item) => ({
|
||||
title: item.data.title,
|
||||
description: item.data.description,
|
||||
pubDate: item.data.date,
|
||||
link: `/${item.collection}/${item.slug}/`,
|
||||
})),
|
||||
});
|
||||
} catch (error) {
|
||||
console.error('Error generating RSS feed:', error);
|
||||
return new Response('Error generating RSS feed', { status: 500 });
|
||||
}
|
||||
}
|
54
src/pages/tags/[...slug].astro
Normal file
54
src/pages/tags/[...slug].astro
Normal file
|
@ -0,0 +1,54 @@
|
|||
---
|
||||
import { type CollectionEntry, getCollection } from "astro:content";
|
||||
import Layout from "@layouts/Layout.astro";
|
||||
import Container from "@components/Container.astro";
|
||||
import ArrowCard from "@components/ArrowCard.astro";
|
||||
import { TAGS } from "@consts";
|
||||
|
||||
type BlogPost = CollectionEntry<'blog'>;
|
||||
|
||||
type Props = {
|
||||
tag: string;
|
||||
posts: BlogPost[];
|
||||
};
|
||||
|
||||
const { tag, posts } = Astro.props;
|
||||
|
||||
export async function getStaticPaths() {
|
||||
const posts = await getCollection('blog');
|
||||
const tags = posts.flatMap((post) => post.data.tags || []);
|
||||
const uniqueTags = Array.from(new Set(tags.filter((tag): tag is string => typeof tag === 'string')));
|
||||
|
||||
return uniqueTags.map((tag) => ({
|
||||
params: { slug: tag },
|
||||
props: { tag, posts: posts.filter((post) => post.data.tags?.includes(tag)) },
|
||||
}));
|
||||
}
|
||||
---
|
||||
|
||||
<Layout title={`Posts tagged with "${tag}"`} description={`A collection of posts tagged with ${tag}.`}>
|
||||
<Container>
|
||||
<aside data-pagefind-ignore>
|
||||
<div class="space-y-10">
|
||||
<div class="animate font-semibold text-black dark:text-white">
|
||||
Tag: <span class="px-3 py-2 rounded-full mx-2 bg-orange-300 hover:bg-cyan-200 dark:bg-orange-500 dark:hover:bg-cyan-500 transition-colors duration-300 ease-in-out">#{tag}</span>
|
||||
</div>
|
||||
<div class="space-y-4">
|
||||
{
|
||||
posts.map((post) => (
|
||||
<section class="animate space-y-4">
|
||||
<div>
|
||||
<ul class="not-prose flex flex-col gap-4">
|
||||
<li>
|
||||
<ArrowCard entry={post} />
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</section>
|
||||
))
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
</aside>
|
||||
</Container>
|
||||
</Layout>
|
33
src/pages/tags/index.astro
Normal file
33
src/pages/tags/index.astro
Normal file
|
@ -0,0 +1,33 @@
|
|||
---
|
||||
import { getCollection } from "astro:content";
|
||||
import Layout from "@layouts/Layout.astro";
|
||||
import Container from "@components/Container.astro";
|
||||
import { TAGS } from "@consts";
|
||||
import Image from "astro/components/Image.astro";
|
||||
|
||||
const blog = (await getCollection("blog"))
|
||||
.filter((post) => !post.data.draft);
|
||||
|
||||
const tags = blog.flatMap(post => post.data.tags).filter((tag, index, self) => self.indexOf(tag) === index);
|
||||
---
|
||||
|
||||
<Layout title={TAGS.TITLE} description={TAGS.DESCRIPTION}>
|
||||
<Container>
|
||||
<aside data-pagefind-ignore>
|
||||
<div class="space-y-10">
|
||||
<div class="animate font-semibold text-black dark:text-white">
|
||||
Tags
|
||||
</div>
|
||||
<ul class="flex flex-wrap ">
|
||||
{tags.map(tag => (
|
||||
<li class="my-3">
|
||||
<a href={`/tags/${tag}`} class="px-3 py-2 rounded-full mx-2 bg-orange-300 hover:bg-cyan-200 dark:bg-orange-500 dark:hover:bg-cyan-500 transition-colors duration-300 ease-in-out">
|
||||
#{tag}
|
||||
</a>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
</div>
|
||||
</aside>
|
||||
</Container>
|
||||
</Layout>
|
18
src/scripts/progress-bar.js
Normal file
18
src/scripts/progress-bar.js
Normal file
|
@ -0,0 +1,18 @@
|
|||
// src/scripts/progress-bar.js
|
||||
|
||||
export function initializeProgressBar() {
|
||||
function updateProgressBar() {
|
||||
const scrollTop = window.scrollY;
|
||||
const docHeight = document.documentElement.scrollHeight;
|
||||
const winHeight = window.innerHeight;
|
||||
const scrollPercent = (scrollTop / (docHeight - winHeight)) * 100;
|
||||
document.getElementById('progress-bar').style.width = `${scrollPercent}%`;
|
||||
}
|
||||
|
||||
window.addEventListener('scroll', updateProgressBar);
|
||||
window.addEventListener('resize', updateProgressBar);
|
||||
|
||||
// Initialize the progress bar on page load
|
||||
updateProgressBar();
|
||||
}
|
||||
|
125
src/styles/global.css
Normal file
125
src/styles/global.css
Normal file
|
@ -0,0 +1,125 @@
|
|||
@tailwind base;
|
||||
@tailwind components;
|
||||
@tailwind utilities;
|
||||
|
||||
html {
|
||||
overflow-y: auto;
|
||||
color-scheme: light;
|
||||
scroll-padding-top: 100px;
|
||||
}
|
||||
|
||||
html.dark {
|
||||
color-scheme: dark;
|
||||
}
|
||||
|
||||
html,
|
||||
body {
|
||||
@apply size-full;
|
||||
}
|
||||
|
||||
body {
|
||||
@apply font-sans antialiased;
|
||||
@apply flex flex-col;
|
||||
@apply bg-neutral-300 dark:bg-slate-900;
|
||||
@apply text-black/75 dark:text-white/75;
|
||||
}
|
||||
|
||||
header {
|
||||
@apply fixed left-0 right-0 top-0 z-50 py-6;
|
||||
@apply bg-neutral-100/75 dark:bg-neutral-900/75;
|
||||
@apply saturate-200 backdrop-blur-sm;
|
||||
}
|
||||
|
||||
main {
|
||||
@apply flex-1 py-32;
|
||||
}
|
||||
|
||||
footer {
|
||||
@apply py-6 text-sm;
|
||||
}
|
||||
|
||||
article {
|
||||
@apply prose prose-neutral max-w-full dark:prose-invert prose-img:mx-auto prose-img:my-auto;
|
||||
@apply prose-headings:font-semibold;
|
||||
@apply prose-headings:text-black prose-headings:dark:text-white;
|
||||
}
|
||||
|
||||
@layer utilities {
|
||||
article a {
|
||||
@apply font-sans text-current underline underline-offset-[3px];
|
||||
@apply decoration-black/30 dark:decoration-white/30;
|
||||
@apply transition-colors duration-300 ease-in-out;
|
||||
}
|
||||
article a:hover {
|
||||
@apply text-black dark:text-white;
|
||||
@apply decoration-black/50 dark:decoration-white/50;
|
||||
}
|
||||
}
|
||||
|
||||
.scroll_bar {
|
||||
scrollbar-width: thin;
|
||||
scrollbar-color: #1e293b #0f172a ;
|
||||
}
|
||||
|
||||
.animate {
|
||||
@apply -translate-y-3 opacity-0;
|
||||
@apply transition-all duration-300 ease-out;
|
||||
}
|
||||
|
||||
.animate.show {
|
||||
@apply translate-y-0 opacity-100;
|
||||
}
|
||||
|
||||
html #back-to-top {
|
||||
@apply pointer-events-none opacity-0;
|
||||
}
|
||||
|
||||
html.scrolled #back-to-top {
|
||||
@apply pointer-events-auto opacity-100;
|
||||
}
|
||||
|
||||
/* shiki config */
|
||||
pre {
|
||||
@apply border border-black/15 py-5 dark:border-white/20;
|
||||
}
|
||||
|
||||
:root {
|
||||
--astro-code-color-text: #09090b;
|
||||
--astro-code-color-background: #fafafa;
|
||||
--astro-code-token-comment: #a19595;
|
||||
--astro-code-token-keyword: #f47067;
|
||||
--astro-code-token-string: #00a99a;
|
||||
--astro-code-token-function: #429996;
|
||||
--astro-code-token-constant: #2b70c5;
|
||||
--astro-code-token-parameter: #4e8fdf;
|
||||
--astro-code-token-string-expression: #ae42a0;
|
||||
--astro-code-token-punctuation: #8996a3;
|
||||
--astro-code-token-link: #8d85ff;
|
||||
}
|
||||
|
||||
.dark {
|
||||
--astro-code-color-text: #fafafa;
|
||||
--astro-code-color-background: #09090b;
|
||||
--astro-code-token-comment: #a19595;
|
||||
--astro-code-token-keyword: #f47067;
|
||||
--astro-code-token-string: #00a99a;
|
||||
--astro-code-token-function: #6eafad;
|
||||
--astro-code-token-constant: #b3cceb;
|
||||
--astro-code-token-parameter: #4e8fdf;
|
||||
--astro-code-token-string-expression: #bf7db6;
|
||||
--astro-code-token-punctuation: #8996a3;
|
||||
--astro-code-token-link: #8d85ff;
|
||||
}
|
||||
|
||||
/* copy code button on codeblocks */
|
||||
.copy-code {
|
||||
@apply absolute right-3 top-3 grid size-9 place-content-center rounded border border-black/15 bg-neutral-100 text-center duration-300 ease-in-out dark:border-white/20 dark:bg-neutral-900;
|
||||
}
|
||||
|
||||
.copy-code:hover {
|
||||
@apply bg-[#E9E9E9] transition-colors dark:bg-[#232323];
|
||||
}
|
||||
|
||||
.copy-code:active {
|
||||
@apply scale-90 transition-transform;
|
||||
}
|
1
src/styles/katex.css
Normal file
1
src/styles/katex.css
Normal file
|
@ -0,0 +1 @@
|
|||
@import 'https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/katex.min.css';
|
13
src/types.ts
Normal file
13
src/types.ts
Normal file
|
@ -0,0 +1,13 @@
|
|||
export type Site = {
|
||||
TITLE: string;
|
||||
DESCRIPTION: string;
|
||||
EMAIL: string;
|
||||
NUM_POSTS_ON_HOMEPAGE: number;
|
||||
NUM_PUBLICATIONS_ON_HOMEPAGE: number;
|
||||
SITEURL: string,
|
||||
};
|
||||
|
||||
export type Metadata = {
|
||||
TITLE: string;
|
||||
DESCRIPTION: string;
|
||||
};
|
Loading…
Add table
Add a link
Reference in a new issue