diff --git a/site/vendor/pages-theme/README.md b/site/vendor/pages-theme/README.md
index bcadd33..9884935 100644
--- a/site/vendor/pages-theme/README.md
+++ b/site/vendor/pages-theme/README.md
@@ -67,7 +67,7 @@ export const collections = { docs: docsCollection() };
| `tagline` | – | A few words for the start page's `
` and the OG image; see „Search engines" |
| `website` | – | The project's landing page (`https://judge.casoon.de`): header, footer, JSON-LD `sameAs` |
| `catalog` | repository name | Slug of the project's entry on casoon.dev (`#projekt-`), linked in the footer; `false` for none |
-| `version` | – | Header badge, "Docs for vX" |
+| `version` | the project's manifest | Header badge, "Docs for vX"; see „Version" |
| `license` | `MIT` | SPDX id for footer and JSON-LD; `false` for proprietary code names no licence |
| `branch` | `main` | "Edit this page" links |
| `accent` | CASOON petrol | `{ light, dark }` project colour; needs 4.5:1 on `#f6f6f5` and `#101010` |
@@ -80,6 +80,18 @@ export const collections = { docs: docsCollection() };
| `lang` | `en` | `en` or `de`: `` and the theme's own UI strings (`lib/i18n.ts`); props passed by the project win |
| `showcaseLead` | – | Lead paragraph of the showcase overview, instead of the theme's generic one |
+#### Version
+
+Left out, `version` comes from the manifest next to `site/`: `version` in `package.json`, else
+`[package]` or `[workspace.package]` in `Cargo.toml`. So the number is maintained in one place,
+and a release updates the site by itself. Pages read it as `config.version` from
+`virtual:casoon-pages/config` — a start page that shows the release in a `Badge` takes it from
+there instead of typing it out.
+
+A wrong number is worse than none, so anything that cannot be read with certainty yields none:
+a workspace whose version sits in a member crate, a `package.json` without `version`, a project
+with neither manifest. Those pass `version` themselves, and `false` shows none at all.
+
### Routes
| Route | Source |
@@ -222,13 +234,19 @@ in `astro.config.mjs`.
`website`, the project's catalog entry on casoon.dev and the catalog itself, all with
descriptive text in the page's `lang`.
-`casoon.github.io` has no root repository, so there is no root `robots.txt` pointing at the
-sitemaps. Register each site in Google Search Console instead, once per project:
+The root `https://casoon.github.io/` comes from the repository `casoon/casoon.github.io`: an
+overview of the project sites, the only `robots.txt` crawlers read for the domain (one `Sitemap:`
+line per project), and the Google Search Console verification. Its URL-prefix property
+`https://casoon.github.io/` covers every project site, so a project needs no property and no
+verification file of its own. For a new site:
+
+1. Deploy it and check that `https://casoon.github.io//sitemap.xml` answers 200.
+2. In `casoon/casoon.github.io`, add `Sitemap: https://casoon.github.io//sitemap.xml` to
+ `robots.txt` and the site to `index.html`, then push.
+3. In the property `https://casoon.github.io/`, *Sitemaps* → submit `/sitemap.xml`.
-1. *Add property* → *URL prefix* → `https://casoon.github.io//`.
-2. Verify ownership with the *HTML file* method: put the downloaded `google.html` into the
- project's `site/public/` and deploy; it is served at `https://casoon.github.io//google.html`.
-3. *Sitemaps* → submit `sitemap.xml` (resolved against the prefix).
+Never set a custom domain on `casoon/casoon.github.io`: GitHub would redirect every project site
+to it as well.
## Rules the theme enforces
diff --git a/site/vendor/pages-theme/SOURCE b/site/vendor/pages-theme/SOURCE
index 8235137..54ba735 100644
--- a/site/vendor/pages-theme/SOURCE
+++ b/site/vendor/pages-theme/SOURCE
@@ -1,6 +1,6 @@
-@casoon/pages-theme 0.7.3
-source: gh-pages-template@884a602
-synced: 2026-10-24
+@casoon/pages-theme 0.8.0
+source: gh-pages-template@a03f9ef
+synced: 2026-10-26
Vendored copy. Do not edit here: change the theme in gh-pages-template,
then run scripts/sync-theme.sh .
diff --git a/site/vendor/pages-theme/index.ts b/site/vendor/pages-theme/index.ts
index d67067f..1791212 100644
--- a/site/vendor/pages-theme/index.ts
+++ b/site/vendor/pages-theme/index.ts
@@ -1,4 +1,4 @@
-import { existsSync } from 'node:fs';
+import { existsSync, readFileSync } from 'node:fs';
import { writeFile } from 'node:fs/promises';
import { resolve } from 'node:path';
import { fileURLToPath } from 'node:url';
@@ -6,6 +6,7 @@ import mdx from '@astrojs/mdx';
import type { AstroIntegration } from 'astro';
import type { Lang } from './lib/i18n';
import { renderOgImage } from './lib/og';
+import { projectVersion } from './lib/version';
export interface PackageLink {
/** Label shown in the footer and as badge, e.g. "crates.io" or "npm". */
@@ -26,8 +27,9 @@ export interface CasoonPagesOptions {
catalog?: string | false;
/** GitHub repository as "owner/name". Omit for a private repository: no GitHub or edit links. */
repo?: string;
- /** Current release, shown in the header and on docs pages. */
- version?: string;
+ /** Current release, shown in the header and on docs pages.
+ * Omitted: read from package.json or Cargo.toml next to site/. `false` shows none. */
+ version?: string | false;
/** SPDX licence identifier, or false for proprietary code (no licence named). Default: "MIT". */
license?: string | false;
/** Default branch, used for "Edit this page" links. Default: "main". */
@@ -108,7 +110,10 @@ export default function casoonPages(options: CasoonPagesOptions): AstroIntegrati
catalog:
options.catalog === false ? '' : (options.catalog ?? options.repo?.split('/')[1] ?? ''),
repo: options.repo ?? '',
- version: options.version ?? '',
+ version:
+ options.version === false
+ ? ''
+ : (options.version ?? projectVersion(resolve(root, '..'))),
license: options.license ?? 'MIT',
branch: options.branch ?? 'main',
accent: options.accent ?? null,
@@ -162,6 +167,17 @@ export default function casoonPages(options: CasoonPagesOptions): AstroIntegrati
},
});
},
+ // Project pages may import the virtual modules too (`config.version` on a start page),
+ // so the declarations go into the project's generated types, not just the theme's.
+ 'astro:config:done': ({ injectTypes }) => {
+ injectTypes({
+ filename: 'virtual.d.ts',
+ content: readFileSync(new URL('./virtual.d.ts', import.meta.url), 'utf8').replace(
+ "'./index'",
+ "'@casoon/pages-theme'"
+ ),
+ });
+ },
// Sitemap and OG image need absolute URLs, so both depend on `site`.
'astro:build:done': async ({ dir, pages }) => {
const base = site;
diff --git a/site/vendor/pages-theme/lib/version.ts b/site/vendor/pages-theme/lib/version.ts
new file mode 100644
index 0000000..9c919d2
--- /dev/null
+++ b/site/vendor/pages-theme/lib/version.ts
@@ -0,0 +1,50 @@
+import { readFileSync } from 'node:fs';
+import { join } from 'node:path';
+
+/**
+ * The project's current release, read from the manifest next to site/ so it is never
+ * maintained twice. npm projects keep it in package.json, Rust projects in Cargo.toml
+ * (also as `[workspace.package]`, which a crate inherits with `version.workspace = true`).
+ *
+ * A wrong number on the page is worse than none: anything this cannot read with certainty
+ * yields '', and the project passes `version` itself.
+ */
+export function projectVersion(repoRoot: string): string {
+ return fromPackageJson(repoRoot) || fromCargoToml(repoRoot) || '';
+}
+
+function read(path: string): string | null {
+ try {
+ return readFileSync(path, 'utf8');
+ } catch {
+ return null;
+ }
+}
+
+function fromPackageJson(repoRoot: string): string {
+ const raw = read(join(repoRoot, 'package.json'));
+ if (raw === null) return '';
+ try {
+ const { version } = JSON.parse(raw) as { version?: unknown };
+ return typeof version === 'string' ? version : '';
+ } catch {
+ return '';
+ }
+}
+
+function fromCargoToml(repoRoot: string): string {
+ const raw = read(join(repoRoot, 'Cargo.toml'));
+ if (raw === null) return '';
+ return section(raw, 'package') || section(raw, 'workspace.package');
+}
+
+/** First `version = "…"` inside [], stopping at the next table header. */
+function section(toml: string, name: string): string {
+ const start = toml.search(new RegExp(`^\\[${name.replaceAll('.', '\\.')}\\]\\s*$`, 'm'));
+ if (start === -1) return '';
+ const rest = toml.slice(start);
+ const next = rest.indexOf('\n[', 1);
+ const body = next === -1 ? rest : rest.slice(0, next);
+ const match = body.match(/^version\s*=\s*"([^"]+)"/m);
+ return match ? match[1] : '';
+}
diff --git a/site/vendor/pages-theme/package.json b/site/vendor/pages-theme/package.json
index aa52ccb..1035e42 100644
--- a/site/vendor/pages-theme/package.json
+++ b/site/vendor/pages-theme/package.json
@@ -1,6 +1,6 @@
{
"name": "@casoon/pages-theme",
- "version": "0.7.3",
+ "version": "0.8.0",
"private": true,
"description": "Shared Astro theme for the GitHub Pages sites of CASOON open-source projects",
"type": "module",