From 0dd781ba422625f0a0bdf447dbd4c38ed79853b3 Mon Sep 17 00:00:00 2001
From: Dania Rifki
Date: Sun, 11 Oct 2026 13:37:28 +0700
Subject: [PATCH 1/2] feat: integrate og-image generation f9or embeds
---
doc/README.md | 8 +
doc/astro.config.mjs | 96 +++
doc/package.json | 3 +
...@alfanjauhari__astro-og-images@0.2.0.patch | 63 ++
doc/pnpm-lock.yaml | 644 ++++++++++++++++++
doc/pnpm-workspace.yaml | 2 +
doc/scripts/check-docs.mjs | 65 +-
doc/src/components/Head.astro | 50 +-
doc/src/styles/custom.css | 14 +-
9 files changed, 933 insertions(+), 12 deletions(-)
create mode 100644 doc/patches/@alfanjauhari__astro-og-images@0.2.0.patch
diff --git a/doc/README.md b/doc/README.md
index e32bf9f..9447b8c 100755
--- a/doc/README.md
+++ b/doc/README.md
@@ -23,6 +23,14 @@ Each section and heading has a unique anchor.
The site uses [Starlight](https://starlight.astro.build/)'s default theme.
Navigation and locale settings are in `astro.config.mjs`. Set `DOCS_BASE` when
deploying beneath a subpath. Production files are written to `dist/`.
+Body text and controls use Fontsource Noto Sans and Noto Sans SC; headings use Noto Serif.
+
+[`@alfanjauhari/astro-og-images`](https://github.com/alfanjauhari/alfanjauhari.com/tree/main/packages/astro-og-images)
+generates a 1200 × 630 PNG preview beside each page during the build. The shared
+head component adds Open Graph and Twitter image metadata. Previews use the page
+title and description, the logo, and Xiaoyu. Titles use Noto Serif, and descriptions
+use Noto Sans. Both families include Chinese fonts.
+The pnpm patch keeps image path checks and 404 image URLs compatible with `DOCS_BASE`.
Run `make wasm` from the repository root to build the browser compiler into
`public/js/`. Without those generated files, the compiler shows the build command.
diff --git a/doc/astro.config.mjs b/doc/astro.config.mjs
index 83f6881..85c2a1f 100644
--- a/doc/astro.config.mjs
+++ b/doc/astro.config.mjs
@@ -4,9 +4,52 @@ import { defineConfig } from "astro/config";
import starlight from "@astrojs/starlight";
import vue from "@astrojs/vue";
+import ogImages, { element, Renderer } from "@alfanjauhari/astro-og-images";
import { locales } from "./src/locales.mjs";
+const ogRenderer = new Renderer();
+for (const [name, font] of [
+ [
+ "Noto Serif",
+ "@fontsource/noto-serif/files/noto-serif-latin-700-normal.woff2",
+ ],
+ [
+ "Noto Serif",
+ "@fontsource/noto-serif/files/noto-serif-latin-ext-700-normal.woff2",
+ ],
+ [
+ "Noto Serif SC",
+ "@fontsource/noto-serif-sc/files/noto-serif-sc-chinese-simplified-700-normal.woff2",
+ ],
+ ["Noto Sans", "@fontsource/noto-sans/files/noto-sans-latin-400-normal.woff2"],
+ [
+ "Noto Sans",
+ "@fontsource/noto-sans/files/noto-sans-latin-ext-400-normal.woff2",
+ ],
+ [
+ "Noto Sans SC",
+ "@fontsource/noto-sans-sc/files/noto-sans-sc-chinese-simplified-400-normal.woff2",
+ ],
+]) {
+ await ogRenderer.registerFont({
+ name,
+ data: readFileSync(new URL(import.meta.resolve(font))),
+ });
+}
+const ogMascot = `data:image/png;base64,${readFileSync(
+ new URL(
+ "./public/image/mascot/electrichearts_20260211A_yuescript_xiaoyu.png",
+ import.meta.url,
+ ),
+).toString("base64")}`;
+const ogLogo = `data:image/svg+xml;base64,${Buffer.from(
+ readFileSync(
+ new URL("./public/image/yuescript.svg", import.meta.url),
+ "utf8",
+ ).replaceAll("fill:#b4ac8f", "fill:#ffffff"),
+).toString("base64")}`;
+
// The sidebar config runs before Astro loads content collections.
const allInOneLabels = Object.fromEntries(
Object.values(locales).map(({ lang }) => [
@@ -44,6 +87,59 @@ export default defineConfig({
trailingSlash: "always",
redirects,
integrations: [
+ ogImages({
+ format: "png",
+ width: 1200,
+ height: 630,
+ renderer: ogRenderer,
+ emoji: "from-font",
+ render: ({ title, description }) =>
+ element(
+ "div",
+ {
+ tw: "flex h-full w-full flex-col bg-[#0b0e14] p-16 text-[#f1f5f9]",
+ style: { fontFamily: "Noto Serif, Noto Serif SC", fontWeight: 700 },
+ },
+ element(
+ "div",
+ {
+ tw: "flex items-center gap-3 text-[32px] leading-[1.2] text-[#d4ceb8]",
+ },
+ element("img", { src: ogLogo, width: 40, height: 34 }),
+ "YueScript",
+ ),
+ element(
+ "div",
+ { tw: "flex flex-1 items-center gap-12" },
+ element(
+ "div",
+ { tw: "flex flex-1 flex-col gap-6" },
+ element(
+ "div",
+ {
+ tw: "leading-[1.2] text-[#d4ceb8]",
+ style: { fontSize: title.length > 60 ? 48 : 64 },
+ },
+ title,
+ ),
+ description &&
+ element(
+ "div",
+ {
+ tw: "text-[26px] leading-[1.4] text-[#e2e8f0]",
+ style: {
+ fontFamily: "Noto Sans, Noto Sans SC",
+ fontWeight: 400,
+ },
+ },
+ description,
+ ),
+ ),
+ element("img", { src: ogMascot, width: 250, height: 350 }),
+ ),
+ element("div", { tw: "text-[22px] text-[#f9c749]" }, "yuescript.org"),
+ ),
+ }),
{
name: "legacy-html-redirects",
hooks: {
diff --git a/doc/package.json b/doc/package.json
index 6249bf6..8e53cf7 100755
--- a/doc/package.json
+++ b/doc/package.json
@@ -23,6 +23,7 @@
"typescript": "^6.0.3"
},
"dependencies": {
+ "@alfanjauhari/astro-og-images": "0.2.0",
"@astrojs/starlight": "^0.42.5",
"@astrojs/vue": "^7.0.3",
"@codemirror/commands": "^6.10.3",
@@ -30,6 +31,8 @@
"@codemirror/legacy-modes": "^6.5.4",
"@codemirror/state": "^6.6.0",
"@codemirror/view": "^6.40.0",
+ "@fontsource/noto-sans": "^5.3.0",
+ "@fontsource/noto-sans-sc": "^5.3.0",
"@fontsource/noto-serif": "^5.3.0",
"@fontsource/noto-serif-sc": "^5.3.0",
"@lezer/highlight": "^1.2.5",
diff --git a/doc/patches/@alfanjauhari__astro-og-images@0.2.0.patch b/doc/patches/@alfanjauhari__astro-og-images@0.2.0.patch
new file mode 100644
index 0000000..55908a3
--- /dev/null
+++ b/doc/patches/@alfanjauhari__astro-og-images@0.2.0.patch
@@ -0,0 +1,63 @@
+diff --git a/dist/index.js b/dist/index.js
+index d282c2db996ea10473714cf63285693e5ad8b0f1..e2a5795fedc3412207a9ccc20456efcbec051db7 100644
+--- a/dist/index.js
++++ b/dist/index.js
+@@ -75,9 +75,13 @@ function getImagePath(image) {
+ return new URL(image, "https://og-image.invalid").pathname.slice(1);
+ }
+ function ogImages(options) {
++ let base = "/";
+ return {
+ name: "og-images",
+ hooks: {
++ "astro:config:done": ({ config }) => {
++ base = config.base;
++ },
+ "astro:build:done": async ({ dir, logger, pages }) => {
+ const outputDir = fileURLToPath(dir);
+ await Promise.all(
+@@ -90,7 +94,7 @@ function ogImages(options) {
+ const imageBuffer = await render(element2, options);
+ const imageFile = htmlFile.replace(/\.html$/, `.${options.format}`);
+ await writeFile(imageFile, imageBuffer);
+- const expectedImagePath = getRelativePath(outputDir, imageFile);
++ const expectedImagePath = path.posix.join(base, getRelativePath(outputDir, imageFile)).replace(/^\/+/, "");
+ const actualImagePath = getImagePath(pageDetails.image);
+ if (actualImagePath !== expectedImagePath) {
+ throw new Error(
+diff --git a/dist/path.d.ts b/dist/path.d.ts
+index 7fb0322f126106415b23b0d2943b4c2b5a44403a..863eb351a03efbe2409e404c7b19b2cee0546eeb 100644
+--- a/dist/path.d.ts
++++ b/dist/path.d.ts
+@@ -1,6 +1,7 @@
+ import { OutputFormat } from 'takumi-js';
+
+-declare function getOgImagePath({ format, site, url, }: {
++declare function getOgImagePath({ base, format, site, url, }: {
++ base?: string;
+ format?: OutputFormat;
+ site: URL | undefined;
+ url: URL;
+diff --git a/dist/path.js b/dist/path.js
+index 736011d96fb43cd28a4c57dcbb7422cf7e2423e8..07c415a779e0b8c7d8bba97ca3ff3ddbf252416b 100644
+--- a/dist/path.js
++++ b/dist/path.js
+@@ -1,5 +1,6 @@
+ // src/path.ts
+ function getOgImagePath({
++ base = "/",
+ format = "webp",
+ site,
+ url
+@@ -15,8 +16,9 @@ function getOgImagePath({
+ } else {
+ target += `.${format}`;
+ }
+- if (target === `/404/index.${format}`) return new URL(`404.${format}`, site).toString();
+- if (target === `/500/index.${format}`) return new URL(`500.${format}`, site).toString();
++ const basePath = base.replace(/\/?$/, "/");
++ if (target === `${basePath}404/index.${format}`) return new URL(`${basePath}404.${format}`, site).toString();
++ if (target === `${basePath}500/index.${format}`) return new URL(`${basePath}500.${format}`, site).toString();
+ return new URL(target.slice(1), site).toString();
+ }
+ export {
diff --git a/doc/pnpm-lock.yaml b/doc/pnpm-lock.yaml
index 68f160d..0a302a3 100644
--- a/doc/pnpm-lock.yaml
+++ b/doc/pnpm-lock.yaml
@@ -4,9 +4,15 @@ settings:
autoInstallPeers: true
excludeLinksFromLockfile: false
+patchedDependencies:
+ "@alfanjauhari/astro-og-images@0.2.0": 21946ac883dbe7a81731461757ca1e71371ce7f22e0565e12a7603e0c717ae07
+
importers:
.:
dependencies:
+ "@alfanjauhari/astro-og-images":
+ specifier: 0.2.0
+ version: 0.2.0(patch_hash=21946ac883dbe7a81731461757ca1e71371ce7f22e0565e12a7603e0c717ae07)(astro@7.3.5(@types/node@24.19.1)(yaml@2.9.1))(csstype@3.2.3)
"@astrojs/starlight":
specifier: ^0.42.5
version: 0.42.5(astro@7.3.5(@types/node@24.19.1)(yaml@2.9.1))(typescript@6.0.3)
@@ -28,6 +34,12 @@ importers:
"@codemirror/view":
specifier: ^6.40.0
version: 6.43.13
+ "@fontsource/noto-sans":
+ specifier: ^5.3.0
+ version: 5.3.0
+ "@fontsource/noto-sans-sc":
+ specifier: ^5.3.0
+ version: 5.3.0
"@fontsource/noto-serif":
specifier: ^5.3.0
version: 5.3.0
@@ -58,6 +70,42 @@ importers:
version: 6.0.3
packages:
+ "@alfanjauhari/astro-og-images@0.2.0":
+ resolution:
+ {
+ integrity: sha512-rqgKPhDLzw5eX/B91JuhFgbw86WkVap6QRLPKpQV/9qkxoT5CiiTgulGaanofj7ksDIaJYS/cBmyyWZ6Pqwtag==,
+ }
+ engines: { node: ">=24.19.0" }
+ peerDependencies:
+ astro: ^7.2.0
+
+ "@asamuzakjp/css-color@5.1.11":
+ resolution:
+ {
+ integrity: sha512-KVw6qIiCTUQhByfTd78h2yD1/00waTmm9uy/R7Ck/ctUyAPj+AEDLkQIdJW0T8+qGgj3j5bpNKK7Q3G+LedJWg==,
+ }
+ engines: { node: ^20.19.0 || ^22.12.0 || >=24.0.0 }
+
+ "@asamuzakjp/dom-selector@7.1.1":
+ resolution:
+ {
+ integrity: sha512-67RZDnYRc8H/8MLDgQCDE//zoqVFwajkepHZgmXrbwybzXOEwOWGPYGmALYl9J2DOLfFPPs6kKCqmbzV895hTQ==,
+ }
+ engines: { node: ^20.19.0 || ^22.12.0 || >=24.0.0 }
+
+ "@asamuzakjp/generational-cache@1.0.1":
+ resolution:
+ {
+ integrity: sha512-wajfB8KqzMCN2KGNFdLkReeHncd0AslUSrvHVvvYWuU8ghncRJoA50kT3zP9MVL0+9g4/67H+cdvBskj9THPzg==,
+ }
+ engines: { node: ^20.19.0 || ^22.12.0 || >=24.0.0 }
+
+ "@asamuzakjp/nwsapi@2.3.9":
+ resolution:
+ {
+ integrity: sha512-n8GuYSrI9bF7FFZ/SjhwevlHc8xaVlb/7HmHelnc/PZXBD2ZR49NnN9sMMuDdEGPeeRQ5d0hqlSlEpgCX3Wl0Q==,
+ }
+
"@astrojs/astro2tsx@0.1.2":
resolution:
{
@@ -607,6 +655,13 @@ packages:
}
engines: { node: ">=6.9.0" }
+ "@bramus/specificity@2.4.3":
+ resolution:
+ {
+ integrity: sha512-0GEz8qJWokkUVYQYRukmkSHIFNoBH8QTreeHrAFbvz1XFYc3Y+puhMs8PQ5x8GNugiC2l5jvDBXKLbBIByeSBw==,
+ }
+ hasBin: true
+
"@bruits/satteri-darwin-arm64@0.10.5":
resolution:
{
@@ -734,6 +789,60 @@ packages:
integrity: sha512-sihaFrUzAsYBQsL9J2t69y8nfMQGwcYmggAZsk+kjPbjYZMyuf2hU8tUNTZ+P+isb6XRr8JE22TZlJxBoVdH1A==,
}
+ "@csstools/color-helpers@6.1.2":
+ resolution:
+ {
+ integrity: sha512-grhRy3OKmniaAEKXMjua5z/EODX0MSqBGjunw8+j/3HQjOnahs2AGhvEOIYVUWcU6ScApbhLhVrQTX8XqrMrow==,
+ }
+ engines: { node: ">=20.19.0" }
+
+ "@csstools/css-calc@3.4.3":
+ resolution:
+ {
+ integrity: sha512-iex20d8CHVkyvg6B7UKV7uHnI2Bqo9g+EFfT9E0y+GvTvhZ/DwONJ+9aKb1dlqm0ZiGsL5RXjp0fCoJYnkeDjA==,
+ }
+ engines: { node: ">=20.19.0" }
+ peerDependencies:
+ "@csstools/css-parser-algorithms": ^4.0.2
+ "@csstools/css-tokenizer": ^4.0.2
+
+ "@csstools/css-color-parser@4.2.6":
+ resolution:
+ {
+ integrity: sha512-iiPQ3iRWwnJkeEn6RIu6SJPr7hYrLz6XZ9s/QZl+2/LI5KQVjpl2fdmDSZKuD4xP6GMmMPgHFFXg6k1Wkz0Trg==,
+ }
+ engines: { node: ">=20.19.0" }
+ peerDependencies:
+ "@csstools/css-parser-algorithms": ^4.0.2
+ "@csstools/css-tokenizer": ^4.0.2
+
+ "@csstools/css-parser-algorithms@4.0.2":
+ resolution:
+ {
+ integrity: sha512-40cSKyMvK+tq4qz6Awrlye2WGuOKt3FwPgtGg6KTfbHOWNw+Rk1rzbAtZnZ6IBhsY491HLRnDXwoyBAijmmILA==,
+ }
+ engines: { node: ">=20.19.0" }
+ peerDependencies:
+ "@csstools/css-tokenizer": ^4.0.2
+
+ "@csstools/css-syntax-patches-for-csstree@1.1.15":
+ resolution:
+ {
+ integrity: sha512-J0u7HkVl2nzSlhsiTOp4AmwcUQ3D+mGEEKfBy/7To5/y7F2OHwyLrXfrhR0SMgr4p5Lo+eaMVSeai24zUcBIxA==,
+ }
+ peerDependencies:
+ css-tree: ^3.2.1
+ peerDependenciesMeta:
+ css-tree:
+ optional: true
+
+ "@csstools/css-tokenizer@4.0.2":
+ resolution:
+ {
+ integrity: sha512-OoKoR0f76dCY666JlcbhmVTs2drYj1GUXZTYTcbUgJjh9Nv41aFfZ21bPQTERm5+L5cBDo466NltB2lplS5GBw==,
+ }
+ engines: { node: ">=20.19.0" }
+
"@ctrl/tinycolor@4.2.1":
resolution:
{
@@ -1053,6 +1162,18 @@ packages:
cpu: [x64]
os: [win32]
+ "@exodus/bytes@1.16.0":
+ resolution:
+ {
+ integrity: sha512-IcpW84uEn3N7ETtNZMlxKhfl6Pec8rUNGOTBtWbK1FKhJxIFAptZyVrvVRVBimAJxJCgc3PxepxkdWWG4DVzfA==,
+ }
+ engines: { node: ^20.19.0 || ^22.12.0 || >=24.0.0 }
+ peerDependencies:
+ "@noble/hashes": ^1.8.0 || ^2.0.0
+ peerDependenciesMeta:
+ "@noble/hashes":
+ optional: true
+
"@expressive-code/core@0.44.2":
resolution:
{
@@ -1077,6 +1198,18 @@ packages:
integrity: sha512-SNI7EgUSiADqbqUzzMbj1ZeC/r2pa5eg3KyFOGAd3a7EsIA4qzjXQLp+ODWQtHVDKFCdGs4J3ovSJUqvKIK96g==,
}
+ "@fontsource/noto-sans-sc@5.3.0":
+ resolution:
+ {
+ integrity: sha512-HeqIlGm0+ohOKxZLuHj1qW6r6avHH0OWdKERAcSDI0RQ+MXrteuLKA+M+5eOA8rYy0MFvOR5AT0fQo2rUkye0Q==,
+ }
+
+ "@fontsource/noto-sans@5.3.0":
+ resolution:
+ {
+ integrity: sha512-fBCog2PY7DiVVTEEqtI/Qdinx/knobHYfoGpjFXdfBX5RoJaBp1Prw2G75p0OIsdlMZg3cLo0c+YbIUYOxnQJw==,
+ }
+
"@fontsource/noto-serif-sc@5.3.0":
resolution:
{
@@ -1691,6 +1824,121 @@ packages:
integrity: sha512-83yeghZ2xxin3Nj8z1NMd/NCuca+gsYXswywDy5bHvwlWL8tpTQmzGeUuHd9FC3E/SBEMvzJRwWEOz5gGes9Qg==,
}
+ "@takumi-rs/core-darwin-arm64@2.14.0":
+ resolution:
+ {
+ integrity: sha512-To8/1EFFVM40V3FlkQIkOl2Ge/xg6bP8KWlxoeBUEusJvjZ3d3PfmKoVmG9erM3oh7ztzw1u3nUXQqLFTNeJBQ==,
+ }
+ engines: { node: ">=18" }
+ cpu: [arm64]
+ os: [darwin]
+
+ "@takumi-rs/core-darwin-x64@2.14.0":
+ resolution:
+ {
+ integrity: sha512-PQ5CiWB7gxap2NElJt9xzoqP0Bzm93SnY//x8OpXuiRQmsW8dOdk5M7mPpJOghebxfRuDc3YIcOEEcsQFpmncQ==,
+ }
+ engines: { node: ">=18" }
+ cpu: [x64]
+ os: [darwin]
+
+ "@takumi-rs/core-linux-arm64-gnu@2.14.0":
+ resolution:
+ {
+ integrity: sha512-2YKku0pJd63ojLKRY8UXerN/NCGhcdyc+HRxbFk27B4enMtxjPPKGgT6xVjBvW1J8vxsOo/eRSGMq2nDq5REBQ==,
+ }
+ engines: { node: ">=18" }
+ cpu: [arm64]
+ os: [linux]
+ libc: [glibc]
+
+ "@takumi-rs/core-linux-arm64-musl@2.14.0":
+ resolution:
+ {
+ integrity: sha512-0XJyEleC6qzxxDOHhobo3TjMddOEvnI3uwSDNq8EH40SUp+Qw71DTBNXqwtwT/NPDj50f8bD3w/M54a86eK3Ow==,
+ }
+ engines: { node: ">=18" }
+ cpu: [arm64]
+ os: [linux]
+ libc: [musl]
+
+ "@takumi-rs/core-linux-x64-gnu@2.14.0":
+ resolution:
+ {
+ integrity: sha512-fINzz8oeamnFqbg75xgOXMrzljhI8BmCf2SZl31dUr+o1oRWUuQyTBm2RjSl2nKjamHd71IoQRo3ilAUqNkadg==,
+ }
+ engines: { node: ">=18" }
+ cpu: [x64]
+ os: [linux]
+ libc: [glibc]
+
+ "@takumi-rs/core-linux-x64-musl@2.14.0":
+ resolution:
+ {
+ integrity: sha512-aMDWTKRSnShorZHx/rFFfk7ovnK8kffmqprwYppNIG25DaTf90GN3CYsFlgGAIRYv0aJ1CbMGjRjzxu2raorYw==,
+ }
+ engines: { node: ">=18" }
+ cpu: [x64]
+ os: [linux]
+ libc: [musl]
+
+ "@takumi-rs/core-win32-arm64-msvc@2.14.0":
+ resolution:
+ {
+ integrity: sha512-apuJ7bT+3tZO6PtZeuuMZ2Qkkxbi+lTniT+iAAX0lGziuTfoguIvDMhbQtQGKUbg7WUE2tqGixOHdUgab7KKCA==,
+ }
+ engines: { node: ">=18" }
+ cpu: [arm64]
+ os: [win32]
+
+ "@takumi-rs/core-win32-x64-msvc@2.14.0":
+ resolution:
+ {
+ integrity: sha512-s5FK8ntDdvIBPCro3OXQEWsHpMYwRLU3JLPdgZK8eILSKjCg+NenAiGDZdU+HmObuqRQn9Q3PYjMc1qwkBzmeQ==,
+ }
+ engines: { node: ">=18" }
+ cpu: [x64]
+ os: [win32]
+
+ "@takumi-rs/core@2.14.0":
+ resolution:
+ {
+ integrity: sha512-PGgLdS+A1xaxPhJtYy/d+k5GYw9MOcwcLMs2zT4RGNkw28FxN+WWHvR1+nnn34rdyD6sG6K2W+50oQZtQV1h/g==,
+ }
+ engines: { node: ">=18" }
+ peerDependencies:
+ csstype: "*"
+ peerDependenciesMeta:
+ csstype:
+ optional: true
+
+ "@takumi-rs/helpers@2.14.0":
+ resolution:
+ {
+ integrity: sha512-jFIpuWfBMAxlULx3tEk2V+FCs1CLOMo2/c7h3I2P+eNzMmxwE9TJ/BtwJVghnLlFW1M88QW/ZOY/E7MqXACWJA==,
+ }
+ engines: { node: ">=18" }
+ peerDependencies:
+ preact: ^10.0.0
+ react: ^18.0.0 || ^19.0.0
+ peerDependenciesMeta:
+ preact:
+ optional: true
+ react:
+ optional: true
+
+ "@takumi-rs/wasm@2.14.0":
+ resolution:
+ {
+ integrity: sha512-ncaNPVl9CL47g5eOyQp1kMPzc0GsUDbQdxRURvNBzMpUC2u6ImeMwJN6ZXiu3tqfSVgYZP1aU0Ntkg+q1S2d4g==,
+ }
+ engines: { node: ">=20.19" }
+ peerDependencies:
+ csstype: "*"
+ peerDependenciesMeta:
+ csstype:
+ optional: true
+
"@tybys/wasm-util@0.10.4":
resolution:
{
@@ -1751,6 +1999,12 @@ packages:
integrity: sha512-aS3/DG0oM05K0RIXXP+hKjinGG5IgSSVGzswZxW3O0sS3pH4/fycXundUC9XsszgKCk4gHXylTEK6hyFxVxnoQ==,
}
+ "@types/react@19.3.0":
+ resolution:
+ {
+ integrity: sha512-N0rFCuH9YoxG9/m61l9MfpJKfmLOVU0em7ipIz6TRgSSkvReLB9vL85GB+yr8Bs5leqpvg96JSwF4ZS1s4viQg==,
+ }
+
"@types/sax@1.2.7":
resolution:
{
@@ -2118,6 +2372,12 @@ packages:
integrity: sha512-KLw+H/gd2p4zly1X7Yh/qziuyae5/w/QFnvTng9eZL5fvszL7Whl3MBoWF8yxL7ksUjBfOD+OxkytiqbBpG+Fw==,
}
+ bidi-js@1.1.0:
+ resolution:
+ {
+ integrity: sha512-fX1Onk0tdVPC7obPWB5EbJ1z7NVhLq4m2xZLq2YXBkxzMXIGRpNMU88n0EPgWseKl12J7zXs7qrDxPK4sRs2fg==,
+ }
+
birpc@2.9.0:
resolution:
{
@@ -2327,6 +2587,13 @@ packages:
integrity: sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==,
}
+ data-urls@7.0.0:
+ resolution:
+ {
+ integrity: sha512-23XHcCF+coGYevirZceTVD7NdJOqVn+49IHyxgszm+JIiHLoB2TkmPtsYkNWT1pvRSGkc35L6NHs0yHkN2SumA==,
+ }
+ engines: { node: ^20.19.0 || ^22.12.0 || >=24.0.0 }
+
debug@4.4.3:
resolution:
{
@@ -2339,6 +2606,12 @@ packages:
supports-color:
optional: true
+ decimal.js@10.6.0:
+ resolution:
+ {
+ integrity: sha512-YpgQiITW3JXGntzdUmyUR1V812Hn8T1YVXhCu+wO3OpS4eU9l4YdD3qjyiKdV6mvV29zapkMeD390UVEf2lkUg==,
+ }
+
decode-named-character-reference@1.3.0:
resolution:
{
@@ -2482,6 +2755,13 @@ packages:
}
engines: { node: ">=0.12" }
+ entities@8.1.0:
+ resolution:
+ {
+ integrity: sha512-kxL7msIffSuh9aaFAMD7rxAIuTRMAHMeBtgHW2yUdWw732ZNh4MehkF2gdjvtdmikkaIP9bFDDJOPlsvm7avrA==,
+ }
+ engines: { node: ">=20.19.0" }
+
error-stack-parser-es@1.0.5:
resolution:
{
@@ -2740,6 +3020,13 @@ packages:
integrity: sha512-Yc+BQe8SvoXH1643Qez1zqLRmbA5rCL+sSmk6TVos0LWVfNIB7PGncdlId77WzLGSIB5KaWgTaNTs2lNVEI6VQ==,
}
+ html-encoding-sniffer@6.0.0:
+ resolution:
+ {
+ integrity: sha512-CV9TW3Y3f8/wT0BRFc1/KAVQ3TUHiXmaAb6VW9vtiMFf7SLoMd1PdAc4W3KFOFETBJUb90KatHqlsZMWV+R9Gg==,
+ }
+ engines: { node: ^20.19.0 || ^22.12.0 || >=24.0.0 }
+
html-escaper@3.0.3:
resolution:
{
@@ -2843,6 +3130,12 @@ packages:
}
engines: { node: ">=12" }
+ is-potential-custom-element-name@1.0.1:
+ resolution:
+ {
+ integrity: sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ==,
+ }
+
is-wsl@3.1.1:
resolution:
{
@@ -2863,6 +3156,18 @@ packages:
}
hasBin: true
+ jsdom@29.1.1:
+ resolution:
+ {
+ integrity: sha512-ECi4Fi2f7BdJtUKTflYRTiaMxIB0O6zfR1fX0GXpUrf6flp8QIYn1UT20YQqdSOfk2dfkCwS8LAFoJDEppNK5Q==,
+ }
+ engines: { node: ^20.19.0 || ^22.13.0 || >=24.0.0 }
+ peerDependencies:
+ canvas: ^3.0.0
+ peerDependenciesMeta:
+ canvas:
+ optional: true
+
jsesc@3.1.0:
resolution:
{
@@ -3407,6 +3712,12 @@ packages:
integrity: sha512-GG2AQYWoLgL877gQIKeRPGO1xF9+eG1ujIb5soS5gPvLQ1y2o8FL90w2QWNdf9I361Mpp7726c+lj3U0qK1uGw==,
}
+ parse5@8.0.1:
+ resolution:
+ {
+ integrity: sha512-z1e/HMG90obSGeidlli3hj7cbocou0/wa5HacvI3ASx34PecNjNQeaHNo5WIZpWofN9kgkqV1q5YvXe3F0FoPw==,
+ }
+
path-browserify@1.0.1:
resolution:
{
@@ -3518,6 +3829,13 @@ packages:
integrity: sha512-TwEZ+X+yCJmYfL7TPUOcvBZ4QfoT5YenQiJuX//0th53DE6w0xxLEtfK3iyryQFddXuvkIk51EEgrJQ0WJkOmQ==,
}
+ punycode@2.3.1:
+ resolution:
+ {
+ integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==,
+ }
+ engines: { node: ">=6" }
+
radix3@1.1.2:
resolution:
{
@@ -3640,6 +3958,13 @@ packages:
}
engines: { node: ">=11.0.0" }
+ saxes@6.0.0:
+ resolution:
+ {
+ integrity: sha512-xAg7SOnEhrm5zI3puOOKyy1OMcMlIJZYNJY7xLBwSze0UjhPLnWfj2GF2EpT0jmzaJKIWKHLsaSSajf35bcYnA==,
+ }
+ engines: { node: ">=v12.22.7" }
+
semver@6.3.1:
resolution:
{
@@ -3762,6 +4087,19 @@ packages:
engines: { node: ">=16" }
hasBin: true
+ symbol-tree@3.2.4:
+ resolution:
+ {
+ integrity: sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==,
+ }
+
+ takumi-js@2.14.0:
+ resolution:
+ {
+ integrity: sha512-dZHMrK9KI2T8uKjFa+tid3KK5HOlk8Nb08Uoq321Y6ISkUkZRFQ69acQUdFGeQjEinenzt1qAkXP/62AjmL7hg==,
+ }
+ engines: { node: ">=20.19" }
+
tiny-inflate@1.0.3:
resolution:
{
@@ -3789,6 +4127,19 @@ packages:
}
engines: { node: ">=12.0.0" }
+ tldts-core@7.4.18:
+ resolution:
+ {
+ integrity: sha512-KI2PKvCmvQI/xD6ypPqkU0i7oxNXR/O58RLRaHR4svsgVUQ2JE/XC8BtMbG6PSehD27I8sZJRcC1ASnCy6lp3g==,
+ }
+
+ tldts@7.4.18:
+ resolution:
+ {
+ integrity: sha512-somxA8xKAUzlAxl0TX/jMSYIbRNFIVGnI96pVGJPeMc5BsHmfVXn6mOJT6U++ELnl04eGfzpfcEuZe8WiASkXg==,
+ }
+ hasBin: true
+
totalist@3.0.1:
resolution:
{
@@ -3796,6 +4147,20 @@ packages:
}
engines: { node: ">=6" }
+ tough-cookie@6.0.2:
+ resolution:
+ {
+ integrity: sha512-exgYmnmL/sJpR3upZfXG5PoatXQii55xAiXGXzY+sROLZ/Y+SLcp9PgJNI9Vz37HpQ74WvDcLT8eqm+kV3FzrA==,
+ }
+ engines: { node: ">=16" }
+
+ tr46@6.0.0:
+ resolution:
+ {
+ integrity: sha512-bLVMLPtstlZ4iMQHpFHTR7GAGj2jxi8Dg0s2h2MafAE4uSWF98FC/3MomU51iQAMf8/qDUbKWf5GxuvvVcXEhw==,
+ }
+ engines: { node: ">=20" }
+
trim-lines@3.0.1:
resolution:
{
@@ -3858,6 +4223,13 @@ packages:
integrity: sha512-WRNW+sJgj5OBN4/0JpHFqtqzhpbnV0GuB+OozA9gCL7a993SmU+1JBZCzLNxYsbMfIeDL+lTsphD5jN5N+n0zg==,
}
+ undici@7.30.0:
+ resolution:
+ {
+ integrity: sha512-dkrQXeHSaoamnItlYbmzG0wFYrM0ZwDxCIg0A7aKjTyyhh9svRzCNFEzV+Vm05/yehjCzjDZ31KXfGEjYSztDQ==,
+ }
+ engines: { node: ">=20.18.1" }
+
undici@8.11.2:
resolution:
{
@@ -4315,6 +4687,34 @@ packages:
integrity: sha512-dpojBhNsCNN7T82Tm7k26A6G9ML3NkhDsnw9n/eoxSRlVBB4CEtIQ/KTCLI2Fwf3ataSXRhYFkQi3SlnFwPvPQ==,
}
+ w3c-xmlserializer@5.0.0:
+ resolution:
+ {
+ integrity: sha512-o8qghlI8NZHU1lLPrpi2+Uq7abh4GGPpYANlalzWxyWteJOCsr/P+oPBA49TOLu5FTZO4d3F9MnWJfiMo4BkmA==,
+ }
+ engines: { node: ">=18" }
+
+ webidl-conversions@8.0.1:
+ resolution:
+ {
+ integrity: sha512-BMhLD/Sw+GbJC21C/UgyaZX41nPt8bUTg+jWyDeg7e7YN4xOM05YPSIXceACnXVtqyEw/LMClUQMtMZ+PGGpqQ==,
+ }
+ engines: { node: ">=20" }
+
+ whatwg-mimetype@5.0.0:
+ resolution:
+ {
+ integrity: sha512-sXcNcHOC51uPGF0P/D4NVtrkjSU2fNsm9iog4ZvZJsL3rjoDAzXZhkm2MWt1y+PUdggKAYVoMAIYcs78wJ51Cw==,
+ }
+ engines: { node: ">=20" }
+
+ whatwg-url@16.0.1:
+ resolution:
+ {
+ integrity: sha512-1to4zXBxmXHV3IiSSEInrreIlu02vUOvrhxJJH5vcxYTBDAx51cqZiKdyTxlecdKNSjj8EcxGBxNf6Vg+945gw==,
+ }
+ engines: { node: ^20.19.0 || ^22.12.0 || >=24.0.0 }
+
wrap-ansi@9.0.2:
resolution:
{
@@ -4329,6 +4729,19 @@ packages:
}
engines: { node: ">=20" }
+ xml-name-validator@5.0.0:
+ resolution:
+ {
+ integrity: sha512-EvGK8EJ3DhaHfbRlETOWAS5pO9MZITeauHKJyb8wyajUfQUenkIg2MvLDTZ4T/TgIcm3HU0TFBgWWboAZ30UHg==,
+ }
+ engines: { node: ">=18" }
+
+ xmlchars@2.2.0:
+ resolution:
+ {
+ integrity: sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==,
+ }
+
xxhash-wasm@1.1.0:
resolution:
{
@@ -4405,6 +4818,40 @@ packages:
}
snapshots:
+ "@alfanjauhari/astro-og-images@0.2.0(patch_hash=21946ac883dbe7a81731461757ca1e71371ce7f22e0565e12a7603e0c717ae07)(astro@7.3.5(@types/node@24.19.1)(yaml@2.9.1))(csstype@3.2.3)":
+ dependencies:
+ "@takumi-rs/core": 2.14.0(csstype@3.2.3)
+ "@types/react": 19.3.0
+ astro: 7.3.5(@types/node@24.19.1)(yaml@2.9.1)
+ jsdom: 29.1.1
+ takumi-js: 2.14.0(csstype@3.2.3)
+ transitivePeerDependencies:
+ - "@noble/hashes"
+ - canvas
+ - csstype
+ - preact
+ - react
+
+ "@asamuzakjp/css-color@5.1.11":
+ dependencies:
+ "@asamuzakjp/generational-cache": 1.0.1
+ "@csstools/css-calc": 3.4.3(@csstools/css-parser-algorithms@4.0.2(@csstools/css-tokenizer@4.0.2))(@csstools/css-tokenizer@4.0.2)
+ "@csstools/css-color-parser": 4.2.6(@csstools/css-parser-algorithms@4.0.2(@csstools/css-tokenizer@4.0.2))(@csstools/css-tokenizer@4.0.2)
+ "@csstools/css-parser-algorithms": 4.0.2(@csstools/css-tokenizer@4.0.2)
+ "@csstools/css-tokenizer": 4.0.2
+
+ "@asamuzakjp/dom-selector@7.1.1":
+ dependencies:
+ "@asamuzakjp/generational-cache": 1.0.1
+ "@asamuzakjp/nwsapi": 2.3.9
+ bidi-js: 1.1.0
+ css-tree: 3.2.1
+ is-potential-custom-element-name: 1.0.1
+
+ "@asamuzakjp/generational-cache@1.0.1": {}
+
+ "@asamuzakjp/nwsapi@2.3.9": {}
+
"@astrojs/astro2tsx@0.1.2(@emnapi/runtime@1.11.3)":
dependencies:
"@emnapi/core": 1.11.3
@@ -4855,6 +5302,10 @@ snapshots:
"@babel/helper-string-parser": 7.29.7
"@babel/helper-validator-identifier": 7.29.7
+ "@bramus/specificity@2.4.3":
+ dependencies:
+ css-tree: 3.2.1
+
"@bruits/satteri-darwin-arm64@0.10.5":
optional: true
@@ -4933,6 +5384,30 @@ snapshots:
style-mod: 4.1.3
w3c-keyname: 2.2.8
+ "@csstools/color-helpers@6.1.2": {}
+
+ "@csstools/css-calc@3.4.3(@csstools/css-parser-algorithms@4.0.2(@csstools/css-tokenizer@4.0.2))(@csstools/css-tokenizer@4.0.2)":
+ dependencies:
+ "@csstools/css-parser-algorithms": 4.0.2(@csstools/css-tokenizer@4.0.2)
+ "@csstools/css-tokenizer": 4.0.2
+
+ "@csstools/css-color-parser@4.2.6(@csstools/css-parser-algorithms@4.0.2(@csstools/css-tokenizer@4.0.2))(@csstools/css-tokenizer@4.0.2)":
+ dependencies:
+ "@csstools/color-helpers": 6.1.2
+ "@csstools/css-calc": 3.4.3(@csstools/css-parser-algorithms@4.0.2(@csstools/css-tokenizer@4.0.2))(@csstools/css-tokenizer@4.0.2)
+ "@csstools/css-parser-algorithms": 4.0.2(@csstools/css-tokenizer@4.0.2)
+ "@csstools/css-tokenizer": 4.0.2
+
+ "@csstools/css-parser-algorithms@4.0.2(@csstools/css-tokenizer@4.0.2)":
+ dependencies:
+ "@csstools/css-tokenizer": 4.0.2
+
+ "@csstools/css-syntax-patches-for-csstree@1.1.15(css-tree@3.2.1)":
+ optionalDependencies:
+ css-tree: 3.2.1
+
+ "@csstools/css-tokenizer@4.0.2": {}
+
"@ctrl/tinycolor@4.2.1": {}
"@emmetio/abbreviation@2.3.3":
@@ -5065,6 +5540,8 @@ snapshots:
"@esbuild/win32-x64@0.28.2":
optional: true
+ "@exodus/bytes@1.16.0": {}
+
"@expressive-code/core@0.44.2":
dependencies:
"@ctrl/tinycolor": 4.2.1
@@ -5090,6 +5567,10 @@ snapshots:
dependencies:
"@expressive-code/core": 0.44.2
+ "@fontsource/noto-sans-sc@5.3.0": {}
+
+ "@fontsource/noto-sans@5.3.0": {}
+
"@fontsource/noto-serif-sc@5.3.0": {}
"@fontsource/noto-serif@5.3.0": {}
@@ -5367,6 +5848,58 @@ snapshots:
"@shikijs/vscode-textmate@10.0.2": {}
+ "@takumi-rs/core-darwin-arm64@2.14.0":
+ optional: true
+
+ "@takumi-rs/core-darwin-x64@2.14.0":
+ optional: true
+
+ "@takumi-rs/core-linux-arm64-gnu@2.14.0":
+ optional: true
+
+ "@takumi-rs/core-linux-arm64-musl@2.14.0":
+ optional: true
+
+ "@takumi-rs/core-linux-x64-gnu@2.14.0":
+ optional: true
+
+ "@takumi-rs/core-linux-x64-musl@2.14.0":
+ optional: true
+
+ "@takumi-rs/core-win32-arm64-msvc@2.14.0":
+ optional: true
+
+ "@takumi-rs/core-win32-x64-msvc@2.14.0":
+ optional: true
+
+ "@takumi-rs/core@2.14.0(csstype@3.2.3)":
+ dependencies:
+ "@takumi-rs/helpers": 2.14.0
+ optionalDependencies:
+ "@takumi-rs/core-darwin-arm64": 2.14.0
+ "@takumi-rs/core-darwin-x64": 2.14.0
+ "@takumi-rs/core-linux-arm64-gnu": 2.14.0
+ "@takumi-rs/core-linux-arm64-musl": 2.14.0
+ "@takumi-rs/core-linux-x64-gnu": 2.14.0
+ "@takumi-rs/core-linux-x64-musl": 2.14.0
+ "@takumi-rs/core-win32-arm64-msvc": 2.14.0
+ "@takumi-rs/core-win32-x64-msvc": 2.14.0
+ csstype: 3.2.3
+ transitivePeerDependencies:
+ - preact
+ - react
+
+ "@takumi-rs/helpers@2.14.0": {}
+
+ "@takumi-rs/wasm@2.14.0(csstype@3.2.3)":
+ dependencies:
+ "@takumi-rs/helpers": 2.14.0
+ optionalDependencies:
+ csstype: 3.2.3
+ transitivePeerDependencies:
+ - preact
+ - react
+
"@tybys/wasm-util@0.10.4":
dependencies:
tslib: 2.8.1
@@ -5401,6 +5934,10 @@ snapshots:
dependencies:
undici-types: 7.24.6
+ "@types/react@19.3.0":
+ dependencies:
+ csstype: 3.2.3
+
"@types/sax@1.2.7":
dependencies:
"@types/node": 24.19.1
@@ -5757,6 +6294,10 @@ snapshots:
is-alphanumerical: 2.0.1
is-decimal: 2.0.1
+ bidi-js@1.1.0:
+ dependencies:
+ require-from-string: 2.0.2
+
birpc@2.9.0: {}
birpc@4.2.0: {}
@@ -5853,10 +6394,19 @@ snapshots:
csstype@3.2.3: {}
+ data-urls@7.0.0:
+ dependencies:
+ whatwg-mimetype: 5.0.0
+ whatwg-url: 16.0.1
+ transitivePeerDependencies:
+ - "@noble/hashes"
+
debug@4.4.3:
dependencies:
ms: 2.1.3
+ decimal.js@10.6.0: {}
+
decode-named-character-reference@1.3.0:
dependencies:
character-entities: 2.0.2
@@ -5921,6 +6471,8 @@ snapshots:
entities@7.0.1: {}
+ entities@8.1.0: {}
+
error-stack-parser-es@1.0.5: {}
es-module-lexer@2.3.2: {}
@@ -6130,6 +6682,12 @@ snapshots:
hookable@5.5.3: {}
+ html-encoding-sniffer@6.0.0:
+ dependencies:
+ "@exodus/bytes": 1.16.0
+ transitivePeerDependencies:
+ - "@noble/hashes"
+
html-escaper@3.0.3: {}
html-void-elements@3.0.0: {}
@@ -6167,6 +6725,8 @@ snapshots:
is-plain-obj@4.1.0: {}
+ is-potential-custom-element-name@1.0.1: {}
+
is-wsl@3.1.1:
dependencies:
is-inside-container: 1.0.0
@@ -6177,6 +6737,32 @@ snapshots:
dependencies:
argparse: 2.0.1
+ jsdom@29.1.1:
+ dependencies:
+ "@asamuzakjp/css-color": 5.1.11
+ "@asamuzakjp/dom-selector": 7.1.1
+ "@bramus/specificity": 2.4.3
+ "@csstools/css-syntax-patches-for-csstree": 1.1.15(css-tree@3.2.1)
+ "@exodus/bytes": 1.16.0
+ css-tree: 3.2.1
+ data-urls: 7.0.0
+ decimal.js: 10.6.0
+ html-encoding-sniffer: 6.0.0
+ is-potential-custom-element-name: 1.0.1
+ lru-cache: 11.5.3
+ parse5: 8.0.1
+ saxes: 6.0.0
+ symbol-tree: 3.2.4
+ tough-cookie: 6.0.2
+ undici: 7.30.0
+ w3c-xmlserializer: 5.0.0
+ webidl-conversions: 8.0.1
+ whatwg-mimetype: 5.0.0
+ whatwg-url: 16.0.1
+ xml-name-validator: 5.0.0
+ transitivePeerDependencies:
+ - "@noble/hashes"
+
jsesc@3.1.0: {}
json-schema-traverse@1.0.0: {}
@@ -6572,6 +7158,10 @@ snapshots:
is-decimal: 2.0.1
is-hexadecimal: 2.0.1
+ parse5@8.0.1:
+ dependencies:
+ entities: 8.1.0
+
path-browserify@1.0.1: {}
pathe@2.0.3: {}
@@ -6622,6 +7212,8 @@ snapshots:
property-information@7.1.0: {}
+ punycode@2.3.1: {}
+
radix3@1.1.2: {}
readdirp@4.1.2: {}
@@ -6713,6 +7305,10 @@ snapshots:
sax@1.6.1: {}
+ saxes@6.0.0:
+ dependencies:
+ xmlchars: 2.2.0
+
semver@6.3.1: {}
semver@7.8.5: {}
@@ -6819,6 +7415,18 @@ snapshots:
picocolors: 1.1.1
sax: 1.6.1
+ symbol-tree@3.2.4: {}
+
+ takumi-js@2.14.0(csstype@3.2.3):
+ dependencies:
+ "@takumi-rs/core": 2.14.0(csstype@3.2.3)
+ "@takumi-rs/helpers": 2.14.0
+ "@takumi-rs/wasm": 2.14.0(csstype@3.2.3)
+ transitivePeerDependencies:
+ - csstype
+ - preact
+ - react
+
tiny-inflate@1.0.3: {}
tinyclip@1.0.3: {}
@@ -6830,8 +7438,22 @@ snapshots:
fdir: 6.5.0(picomatch@4.0.7)
picomatch: 4.0.7
+ tldts-core@7.4.18: {}
+
+ tldts@7.4.18:
+ dependencies:
+ tldts-core: 7.4.18
+
totalist@3.0.1: {}
+ tough-cookie@6.0.2:
+ dependencies:
+ tldts: 7.4.18
+
+ tr46@6.0.0:
+ dependencies:
+ punycode: 2.3.1
+
trim-lines@3.0.1: {}
trough@2.2.0: {}
@@ -6854,6 +7476,8 @@ snapshots:
undici-types@7.24.6: {}
+ undici@7.30.0: {}
+
undici@8.11.2: {}
unified@11.0.5:
@@ -7127,6 +7751,22 @@ snapshots:
w3c-keyname@2.2.8: {}
+ w3c-xmlserializer@5.0.0:
+ dependencies:
+ xml-name-validator: 5.0.0
+
+ webidl-conversions@8.0.1: {}
+
+ whatwg-mimetype@5.0.0: {}
+
+ whatwg-url@16.0.1:
+ dependencies:
+ "@exodus/bytes": 1.16.0
+ tr46: 6.0.0
+ webidl-conversions: 8.0.1
+ transitivePeerDependencies:
+ - "@noble/hashes"
+
wrap-ansi@9.0.2:
dependencies:
ansi-styles: 6.2.3
@@ -7138,6 +7778,10 @@ snapshots:
is-wsl: 3.1.1
powershell-utils: 0.2.1
+ xml-name-validator@5.0.0: {}
+
+ xmlchars@2.2.0: {}
+
xxhash-wasm@1.1.0: {}
y18n@5.0.8: {}
diff --git a/doc/pnpm-workspace.yaml b/doc/pnpm-workspace.yaml
index 5ed0b5a..2589c6b 100644
--- a/doc/pnpm-workspace.yaml
+++ b/doc/pnpm-workspace.yaml
@@ -1,2 +1,4 @@
allowBuilds:
esbuild: true
+patchedDependencies:
+ "@alfanjauhari/astro-og-images@0.2.0": patches/@alfanjauhari__astro-og-images@0.2.0.patch
diff --git a/doc/scripts/check-docs.mjs b/doc/scripts/check-docs.mjs
index 14dbbbf..d40bf9e 100644
--- a/doc/scripts/check-docs.mjs
+++ b/doc/scripts/check-docs.mjs
@@ -1,8 +1,28 @@
import assert from "node:assert/strict";
import { existsSync, readdirSync, readFileSync, statSync } from "node:fs";
+import { getOgImagePath } from "@alfanjauhari/astro-og-images/path";
import { locales } from "../src/locales.mjs";
+for (const [pathname, imagePath] of [
+ ["/docs/404/", "/docs/404.png"],
+ ["/docs/zh/404/", "/docs/zh/404/index.png"],
+ ["/docs/doc/", "/docs/doc/index.png"],
+]) {
+ assert.equal(
+ new URL(
+ getOgImagePath({
+ base: "/docs/",
+ format: "png",
+ site: new URL("https://yuescript.org"),
+ url: new URL(pathname, "https://yuescript.org"),
+ }),
+ ).pathname,
+ imagePath,
+ `Wrong preview path under DOCS_BASE: ${pathname}`,
+ );
+}
+
const translations = Object.fromEntries(
Object.values(locales).map(({ lang }) => [
lang,
@@ -110,6 +130,36 @@ function checkTranslations(html, lang, route) {
}
function checkInterface(html, route) {
+ const previewImages = [
+ ...html.matchAll(/]*>/g)) {
assert.match(
image,
@@ -128,7 +178,18 @@ function checkInterface(html, route) {
);
}
assert.match(html, /class="[^"]*sl-skip-link/, `Missing skip link: ${route}`);
- assert.match(html, /name="theme-color"/, `Missing theme color: ${route}`);
+ for (const [theme, color] of [
+ ["light", "#8c8260"],
+ ["dark", "#b4ac8f"],
+ ]) {
+ assert.match(
+ html,
+ new RegExp(
+ `name="theme-color" content="${color}" media="\\(prefers-color-scheme: ${theme}\\)"`,
+ ),
+ `Missing ${theme} accent theme color: ${route}`,
+ );
+ }
assert.match(
html,
/rel="preload"[^>]*as="font"/,
@@ -301,5 +362,5 @@ assert.ok(
"Standalone yue-*.md manuals must be removed",
);
console.log(
- `Verified ${docs.length} routes, legacy HTML redirects, ${Object.keys(locales).length} language aggregates, localized UI, ordering and anchors, and ${controls} compiler controls.`,
+ `Verified ${docs.length} routes, preview images, legacy HTML redirects, ${Object.keys(locales).length} language aggregates, localized UI, ordering and anchors, and ${controls} compiler controls.`,
);
diff --git a/doc/src/components/Head.astro b/doc/src/components/Head.astro
index d2a4b29..fb1903a 100644
--- a/doc/src/components/Head.astro
+++ b/doc/src/components/Head.astro
@@ -2,9 +2,33 @@
import Head from "@astrojs/starlight/components/Head.astro";
import headingFont from "@fontsource/noto-serif/files/noto-serif-latin-700-normal.woff2?url";
import heroFont from "@fontsource/noto-serif/files/noto-serif-latin-800-normal.woff2?url";
+import bodyFont from "@fontsource/noto-sans/files/noto-sans-latin-400-normal.woff2?url";
+import { getOgImagePath } from "@alfanjauhari/astro-og-images/path";
+
+const image = getOgImagePath({
+ base: import.meta.env.BASE_URL,
+ format: "png",
+ site: Astro.site,
+ url: Astro.url,
+});
+const imageAlt = `${Astro.locals.starlightRoute.entry.data.title} | YueScript`;
---
+
+
+
+
+
+
+
+
)}
-
+
+