diff --git a/.github/workflows/website.yml b/.github/workflows/website.yml
new file mode 100644
index 0000000..9c6e93b
--- /dev/null
+++ b/.github/workflows/website.yml
@@ -0,0 +1,71 @@
+name: Deploy website
+
+on:
+ push:
+ branches: [ main ]
+ paths:
+ - "website/**"
+ - "package.json"
+ - "pnpm-lock.yaml"
+ - "pnpm-workspace.yaml"
+ - ".github/workflows/website.yml"
+ workflow_dispatch:
+
+permissions:
+ contents: read
+ pages: write
+ id-token: write
+
+concurrency:
+ group: github-pages
+ cancel-in-progress: false
+
+jobs:
+ build:
+ name: Build website
+ runs-on: ubuntu-latest
+
+ steps:
+ - name: Checkout
+ uses: actions/checkout@v7
+
+ - name: Setup pnpm
+ uses: pnpm/action-setup@v6
+ with:
+ version: 10.24.0
+
+ - name: Setup Node.js
+ uses: actions/setup-node@v6
+ with:
+ node-version: 22
+ cache: pnpm
+
+ - name: Install dependencies
+ run: pnpm install --frozen-lockfile
+
+ - name: Configure GitHub Pages
+ id: pages
+ uses: actions/configure-pages@v6
+
+ - name: Build website
+ env:
+ SITE_BASE_PATH: ${{ steps.pages.outputs.base_path }}/
+ run: pnpm run website:build
+
+ - name: Upload GitHub Pages artifact
+ uses: actions/upload-pages-artifact@v4
+ with:
+ path: website/dist
+
+ deploy:
+ name: Deploy website
+ needs: build
+ runs-on: ubuntu-latest
+ environment:
+ name: github-pages
+ url: ${{ steps.deployment.outputs.page_url }}
+
+ steps:
+ - name: Deploy to GitHub Pages
+ id: deployment
+ uses: actions/deploy-pages@v4
diff --git a/website/api/index.html b/website/api/index.html
index 8d5d487..d132e2d 100644
--- a/website/api/index.html
+++ b/website/api/index.html
@@ -4,8 +4,8 @@
-
-
+
+
-
+
zig-napi
@@ -207,7 +207,7 @@ function SiteHeader({ route }: { route: SiteRoute }) {
className="flex items-center gap-5 text-sm text-(--color-muted)"
aria-label="Primary navigation"
>
-
+
API
@@ -233,7 +233,7 @@ function Hero() {
@@ -256,7 +256,7 @@ function Hero() {
@@ -439,7 +439,7 @@ function ApiOverviewSection() {
Use the API page when you need exact Zig signatures and wrapper behavior instead of the
overview flow.
-
+
Open API Reference
diff --git a/website/src/site-paths.ts b/website/src/site-paths.ts
new file mode 100644
index 0000000..b2d0b4b
--- /dev/null
+++ b/website/src/site-paths.ts
@@ -0,0 +1,13 @@
+const siteBasePath = import.meta.env.BASE_URL;
+
+export function siteHref(path = "") {
+ return `${siteBasePath}${path.replace(/^\/+/, "")}`;
+}
+
+export function siteRelativePath(pathname = window.location.pathname) {
+ const relativePath = pathname.startsWith(siteBasePath)
+ ? pathname.slice(siteBasePath.length)
+ : pathname;
+
+ return relativePath.replace(/^\/+|\/+$/g, "");
+}
diff --git a/website/vite.config.ts b/website/vite.config.ts
index 87ba637..6dd2ca3 100644
--- a/website/vite.config.ts
+++ b/website/vite.config.ts
@@ -1,5 +1,5 @@
import { execFileSync } from "node:child_process";
-import { statSync } from "node:fs";
+import { copyFileSync, mkdirSync, statSync } from "node:fs";
import { dirname, resolve } from "node:path";
import { fileURLToPath } from "node:url";
import { defineConfig, type Plugin } from "vite";
@@ -8,6 +8,7 @@ import { voidReact } from "@void/react/plugin";
import tailwindcss from "@tailwindcss/vite";
const rootDir = dirname(fileURLToPath(import.meta.url));
+const siteBasePath = normalizeBasePath(process.env.SITE_BASE_PATH);
const API_LAST_UPDATED_ID = "virtual:api-last-updated";
const RESOLVED_API_LAST_UPDATED_ID = `\0${API_LAST_UPDATED_ID}`;
const apiMarkdownFiles = [
@@ -68,6 +69,29 @@ function apiLastUpdated(): Plugin {
};
}
+function apiStaticRoutes(): Plugin {
+ return {
+ name: "api-static-routes",
+ apply: "build",
+ closeBundle() {
+ const apiEntry = resolve(rootDir, "dist/api/index.html");
+
+ for (const [sectionId] of apiMarkdownFiles) {
+ if (sectionId === "overview") continue;
+
+ const routeDir = resolve(rootDir, "dist/api", sectionId);
+ mkdirSync(routeDir, { recursive: true });
+ copyFileSync(apiEntry, resolve(routeDir, "index.html"));
+ }
+ },
+ };
+}
+
+function normalizeBasePath(value: string | undefined) {
+ const path = value?.trim().replace(/^\/+|\/+$/g, "");
+ return path ? `/${path}/` : "/";
+}
+
function lastUpdatedIso(filePath: string) {
try {
const committed = execFileSync("git", ["log", "-1", "--format=%cI", "--", filePath], {
@@ -84,6 +108,7 @@ function lastUpdatedIso(filePath: string) {
}
export default defineConfig({
+ base: siteBasePath,
plugins: [
voidReact(),
voidMarkdown({
@@ -96,6 +121,7 @@ export default defineConfig({
}),
apiFallback(),
apiLastUpdated(),
+ apiStaticRoutes(),
tailwindcss(),
],
build: {