import { defineConfig } from "blume"; import type { StandardSchema } from "blume/core/standard-schema.ts"; import { filesystem } from "blume/sources"; import { APPENDIX } from "./appendix"; import redirects from "./redirects"; // Blume's defaults (`inter`, `ibm-plex-mono`) are fetched from fonts.gstatic.com // on every build, which has timed out on the Pages runner and, fully offline, // silently ships no fonts at all. The same two families from Fontsource, as // local files, keep the build network-free. Blume resolves `src` from this // directory; the workspace links these packages under ./node_modules. const inter = { name: "Inter", variants: [ { src: "./node_modules/@fontsource-variable/inter/files/inter-latin-wght-normal.woff2", weight: "100..900", }, ], }; const plexMono = { name: "IBM Plex Mono", variants: [ { src: "./node_modules/@fontsource/ibm-plex-mono/files/ibm-plex-mono-latin-400-normal.woff2", weight: 400, }, { src: "./node_modules/@fontsource/ibm-plex-mono/files/ibm-plex-mono-latin-500-normal.woff2", weight: 500, }, ], }; /** * Custom frontmatter, rendered as the badge row by * components/ProblemBadges.astro. Problem pages: `topics:` is LeetCode's topic * tags and `issue:` the problem's GitHub issue URL (both written by * apps/cli/sync.ts), `video:` the NeetCode walkthrough's * YouTube id (apps/cli/videos.ts). Blume rejects undeclared keys and takes * any Standard Schema; these are hand-rolled so the docs workspace needs no * zod. All optional — every other page carries none. */ const optional = ( message: string, valid: (value: unknown) => value is T, ): StandardSchema => ({ "~standard": { version: 1, vendor: "prdlk", validate: (value) => value === undefined || valid(value) ? { value } : { issues: [{ message }] }, }, }); const topicsSchema = optional( "expected a list of { name, slug } LeetCode topic tags", (value): value is { name: string; slug: string }[] => Array.isArray(value) && value.every( (t) => typeof t?.name === "string" && typeof t?.slug === "string", ), ); const videoSchema = optional( "expected an 11-character YouTube video id", (value): value is string => typeof value === "string" && /^[\w-]{11}$/.test(value), ); const issueSchema = optional( "expected a https://github.com///issues/ URL", (value): value is string => typeof value === "string" && /^https:\/\/github\.com\/[\w.-]+\/[\w.-]+\/issues\/\d+$/.test(value), ); export default defineConfig({ title: "prdlk", description: "Documentation for my LeetCode solutions", logo: "./public/logo.svg", deployment: { site: "https://lc.prd.lk", }, github: { owner: "prdlk", repo: "leetcode", }, lastModified: "git", // Sync's `/` short links, then the Appendix's former per-term pages // (`/appendix/`, published until the glossary became one page) onto // their cards, so old links and search results still land. redirects: [ ...redirects, ...Object.keys(APPENDIX).map((slug) => ({ from: `/appendix/${slug}`, to: `/appendix#${slug}`, })), ], frontmatter: { extend: { topics: topicsSchema, video: videoSchema, issue: issueSchema, }, }, seo: { og: { enabled: true }, // No `rss`: the changelog is one custom page (pages/changelog.astro), not // `type: changelog` entries, so a changelog feed would have nothing in it. sitemap: true, robots: true, structuredData: true, x: { handle: "@prdlk" }, }, // Blume's own assistant route is server-rendered (it reads the provider key // per request), which a static Pages build cannot ship. The SRS Worker // answers instead (apps/api/src/ask.ts: OpenRouter, grounded on this site's // llms-full.txt, which `agents.llmsTxt` publishes). ai: { assistant: { enabled: true, endpoint: "https://srs-api.prdlk.workers.dev/ask", }, }, agents: { llmsTxt: true, mcp: { enabled: false }, }, // The only content source. `/changelog` and `/appendix` are custom pages // (pages/*.astro): `/changelog` reads the GitHub releases itself — one // timeline, no per-release pages — and `/appendix` is one glossary page // built from appendix.ts, so neither enters the sidebar tree or prev/next. content: { sources: [filesystem({ root: "content" })], }, theme: { fonts: { display: inter, body: inter, mono: plexMono }, }, navigation: { repo: "https://github.com/prdlk/leetcode", sidebar: { display: "flat", // "flat" | "group" | "page" }, // The two custom pages (pages/*.astro) as header tabs. Neither is a // content folder, so the tabs scope nothing out of the sidebar tree. tabs: [ { label: "Changelog", path: "/changelog" }, { label: "Appendix", path: "/appendix" }, ], }, });