Amp-Thread-ID: https://ampcode.com/threads/T-019c7839-0936-729b-aa87-92b35d3d68fe Co-authored-by: Amp <amp@ampcode.com>
267 lines
7.9 KiB
Text
267 lines
7.9 KiB
Text
---
|
|
import "../styles/global.css";
|
|
import { ClientRouter } from "astro:transitions";
|
|
|
|
import "@fontsource/geist-sans/100.css";
|
|
import "@fontsource/geist-sans/200.css";
|
|
import "@fontsource/geist-sans/300.css";
|
|
import "@fontsource/geist-sans/400.css";
|
|
import "@fontsource/geist-sans/500.css";
|
|
import "@fontsource/geist-sans/600.css";
|
|
import "@fontsource/geist-sans/700.css";
|
|
import "@fontsource/geist-sans/800.css";
|
|
import "@fontsource/geist-sans/900.css";
|
|
import "@fontsource/geist-mono/100.css";
|
|
import "@fontsource/geist-mono/200.css";
|
|
import "@fontsource/geist-mono/300.css";
|
|
import "@fontsource/geist-mono/400.css";
|
|
import "@fontsource/geist-mono/500.css";
|
|
import "@fontsource/geist-mono/600.css";
|
|
import "@fontsource/geist-mono/700.css";
|
|
import "@fontsource/geist-mono/800.css";
|
|
import "@fontsource/geist-mono/900.css";
|
|
|
|
interface Props {
|
|
title: string;
|
|
description: string;
|
|
image?: string;
|
|
}
|
|
|
|
const canonicalURL = new URL(Astro.url.pathname, Astro.site);
|
|
|
|
const { title, description, image = "/astro-micro.jpg" } = Astro.props;
|
|
---
|
|
|
|
<!-- Global Metadata -->
|
|
<meta charset="utf-8" />
|
|
<meta name="viewport" content="width=device-width,initial-scale=1" />
|
|
<link rel="icon" type="image/x-icon" href="/favicon.ico" />
|
|
<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>
|
|
|
|
<ClientRouter />
|
|
|
|
<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(userTheme === "dark");
|
|
} 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>
|