--- import { env } from "@lib/environment"; import "../styles/global.css"; import { ClientRouter } from "astro:transitions"; export interface Props { title: string; description?: string; image?: string; keywords?: string[]; } const { PUBLIC_SITE_TITLE, PUBLIC_SITE_DESCRIPTION, PUBLIC_SITE_AUTHOR, PUBLIC_TOR_URL, } = env; const isOnion = Astro.url.origin.endsWith(".onion"); const alternate = !isOnion ? PUBLIC_TOR_URL : Astro.site; const canonicalURL = new URL(Astro.url.pathname, Astro.site); const { title, description = PUBLIC_SITE_DESCRIPTION, image = new URL("favicon.svg", Astro.site), keywords = [], } = Astro.props; --- <meta name="title" content={title} itemprop="name" /> <meta name="description" content={description} itemprop="description" /> <meta name="author" content={PUBLIC_SITE_AUTHOR} /> { keywords.length > 0 && ( <meta name="keywords" content={keywords.join(",")} itemprop="keywords" /> ) } <meta name="theme-color" content="oklch(0.4564 0.1835 20.81)" /> <meta name="theme-color" content="oklch(0.6923 0.1759 37.7)" media="(prefers-color-scheme: dark)" /> <!-- 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={image} /> <!-- 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={image} /> <ClientRouter /> <script> import { Workbox, type WorkboxLifecycleWaitingEvent, } from "workbox-window"; if ("serviceWorker" in navigator) { const wb = new Workbox("/sw.js", { type: "module" }); wb.addEventListener("activated", (_event) => { if (!location.pathname.startsWith("/blog/read/")) { return; } const urlsToCache = [ location.href, location.href + ".md", location.href + ".md.sig", ///...performance.getEntriesByType("resource").map((r) => r.name), ]; wb.messageSW({ type: "CACHE_URLS", payload: { urlsToCache }, }); }); const showSkipWaitingPrompt = async ( _event: WorkboxLifecycleWaitingEvent, ) => { wb.addEventListener("controlling", () => { window.location.reload(); }); const updateAccepted = await promptForUpdate(); if (updateAccepted) { wb.messageSkipWaiting(); } }; wb.addEventListener("waiting", (event) => { showSkipWaitingPrompt(event); }); wb.register(); } async function promptForUpdate(): Promise<boolean> { return await Promise.resolve(confirm("update?")); } </script>