clone

a deno framework for local-first web apps that open instantly, work offline and sync when users choose

main
lofi / website / docusaurus.config.ts
docusaurus.config.ts 11.9K
1import { readFileSync } from "node:fs";2import { fileURLToPath } from "node:url";3import { themes as prismThemes } from "prism-react-renderer";4import type { Config } from "@docusaurus/types";5import type * as Preset from "@docusaurus/preset-classic";67// This runs in Node.js - Don't use client-side code here (browser APIs, JSX...)89const denoJson = JSON.parse(10 readFileSync(new URL("../deno.json", import.meta.url), "utf8"),11) as { version: string; description: string };1213// The /node landing shows the lofi-node version from the same pinned checkout14// the docs render from (LOFI_NODE_DIR, default ../lofi-node); "alpha" when the15// checkout is absent so a docs-only environment still builds.16let lofiNodeVersion = "alpha";17try {18 const nodeDir = process.env.LOFI_NODE_DIR ?? "../lofi-node";19 lofiNodeVersion = (JSON.parse(20 readFileSync(new URL(`../${nodeDir}/deno.json`, import.meta.url), "utf8"),21 ) as { version: string }).version;22} catch {23 // Keep the fallback label.24}2526// The site is served at the root of lofi.host. `forge site deploy` serves it27// under a path instead and says where with FORGE_SITE_URL and FORGE_SITE_BASE.28// That host is a plain file server with no `<path>.html` fallback, so forge29// builds emit every route as a directory with an index.html.30const forgeSite = process.env.FORGE_SITE_URL ? new URL(process.env.FORGE_SITE_URL) : null;31const baseUrl = process.env.FORGE_SITE_BASE || "/";3233// Fonts must ship as files, not base64 in the render-blocking stylesheet —34// webpack's default inline threshold would bloat styles.css by hundreds of KB.35// Rewrites Docusaurus's own font rule in place: appending a second rule makes36// webpack run both, and the asset/resource pass emits the url-loader's JS37// module source as the .woff2 the CSS points at — a 127-byte stub, no font.38function noInlineFontsPlugin() {39 return {40 name: "lofi-no-inline-fonts",41 configureWebpack: (config: { module?: { rules?: unknown[] } }) => {42 for (const rule of config.module?.rules ?? []) {43 if (44 rule && typeof rule === "object" && "test" in rule &&45 rule.test instanceof RegExp && rule.test.test("x.woff2")46 ) {47 const fontRule = rule as Record<string, unknown>;48 delete fontRule.use;49 fontRule.type = "asset/resource";50 fontRule.generator = { filename: "assets/fonts/[name]-[hash][ext]" };51 }52 }53 return {};54 },55 };56}5758const config: Config = {59 title: "lofi",60 tagline: "Put down roots. Skip the spinner.",61 favicon: "img/favicon.svg",6263 future: {64 v4: true,65 },6667 url: forgeSite?.origin || "https://lofi.host",68 baseUrl,69 trailingSlash: forgeSite !== null,7071 onBrokenLinks: "throw",72 onBrokenAnchors: "warn",7374 customFields: {75 lofiVersion: denoJson.version,76 lofiDescription: denoJson.description,77 lofiNodeVersion,78 },7980 markdown: {81 // Parse .md files as CommonMark so existing docs need no MDX escaping;82 // only .mdx files opt into MDX.83 format: "detect",84 mermaid: true,85 hooks: {86 onBrokenMarkdownLinks: "throw",87 },88 },8990 themes: [91 "@docusaurus/theme-mermaid",92 [93 // fully client-side search over an index built at build time — no94 // external search service, which suits a local-first framework95 "@easyops-cn/docusaurus-search-local",96 {97 hashed: true,98 indexBlog: false,99 docsRouteBasePath: ["docs", "api", "node/docs", "node/api"],100 docsDir: ["../docs", "api-gen", "node-docs-gen", "node-api-gen"],101 },102 ],103 ],104105 i18n: {106 defaultLocale: "en",107 locales: ["en"],108 },109110 // Fonts are self-hosted via Fontsource imports in src/css/custom.css, so111 // the docs work offline with their real faces and no third-party requests.112113 plugins: [114 noInlineFontsPlugin,115 [116 // The docs for the offline-first framework install and read offline.117 // Precaching is opt-in (installed PWA / standalone / ?offlineMode) so118 // drive-by visitors are not handed the whole corpus.119 "@docusaurus/plugin-pwa",120 {121 // trailingSlash:false emits routes as <path>.html, which the default122 // worker never checks; see src/sw-custom.js.123 swCustom: fileURLToPath(new URL("./src/sw-custom.js", import.meta.url)),124 offlineModeActivationStrategies: [125 "appInstalled",126 "standalone",127 "queryString",128 ],129 pwaHead: [130 { tagName: "link", rel: "manifest", href: `${baseUrl}manifest.json` },131 { tagName: "meta", name: "theme-color", content: "#18231f" },132 {133 tagName: "meta",134 name: "apple-mobile-web-app-capable",135 content: "yes",136 },137 {138 tagName: "meta",139 name: "apple-mobile-web-app-status-bar-style",140 content: "black-translucent",141 },142 {143 tagName: "link",144 rel: "apple-touch-icon",145 href: `${baseUrl}img/pwa/icon-192.png`,146 },147 ],148 },149 ],150 [151 "@docusaurus/plugin-content-docs",152 {153 id: "api",154 path: "api-gen",155 routeBasePath: "api",156 sidebarPath: "./sidebars-api.ts",157 editUrl: undefined,158 },159 ],160 [161 "@docusaurus/plugin-content-docs",162 {163 // Assembled by tools/node_docs.ts from the pinned lofi-node checkout164 // rendered out of the pinned lofi-node checkout; no edit URL because165 // pages have two possible homes — each page names its source.166 id: "node",167 path: "node-docs-gen",168 routeBasePath: "node/docs",169 sidebarPath: "./sidebars-node.ts",170 editUrl: undefined,171 },172 ],173 [174 "@docusaurus/plugin-content-docs",175 {176 id: "node-api",177 path: "node-api-gen",178 routeBasePath: "node/api",179 sidebarPath: "./sidebars-node-api.ts",180 editUrl: undefined,181 },182 ],183 ],184185 presets: [186 [187 "classic",188 {189 docs: {190 path: "../docs",191 routeBasePath: "docs",192 sidebarPath: "./sidebars.ts",193 // Decision records are date-ordered with numeric prefixes; keep the194 // prefix in the document id so ids match filenames everywhere the195 // manifest is consumed (sidebar, llms corpus, links).196 numberPrefixParser: false,197 // `exclude` replaces the plugin defaults, so the `_`-prefix globs198 // must be restated alongside the repo-only content.199 exclude: [200 "spikes/**",201 "devx-contract.md",202 "seed.md",203 "assets/**",204 // Served under /node/docs by the "node" plugin instance instead.205 "node/**",206 "**/_*.{js,jsx,ts,tsx,md,mdx}",207 "**/_*/**",208 ],209 editUrl: "https://github.com/FelineStateMachine/lofi/edit/main/docs/",210 },211 blog: false,212 theme: {213 customCss: "./src/css/custom.css",214 },215 } satisfies Preset.Options,216 ],217 ],218219 themeConfig: {220 image: "img/lofi-social-card.png",221 mermaid: {222 // one custom brand palette for both color modes: filled shapes carry223 // their own text color (mint/lime with forest ink), and floating text224 // and lines use a mid sage that stays readable on light and dark pages225 theme: { light: "base", dark: "base" },226 options: {227 themeVariables: {228 fontFamily: '"Karla", system-ui, -apple-system, sans-serif',229 primaryColor: "#99e5bd",230 primaryTextColor: "#18231f",231 primaryBorderColor: "#39725c",232 secondaryColor: "#d5f26a",233 secondaryTextColor: "#18231f",234 secondaryBorderColor: "#39725c",235 tertiaryColor: "#c9eeda",236 tertiaryTextColor: "#18231f",237 tertiaryBorderColor: "#39725c",238 lineColor: "#6f9d88",239 // floating labels render on the mint edge-label chip, so they carry240 // forest ink; standalone titles use the mid sage via titleColor241 textColor: "#18231f",242 clusterBkg: "rgba(153, 229, 189, 0.16)",243 clusterBorder: "#39725c",244 titleColor: "#6f9d88",245 clusterTextColor: "#6f9d88",246 edgeLabelBackground: "#99e5bd",247 actorBkg: "#99e5bd",248 actorTextColor: "#18231f",249 actorBorder: "#39725c",250 actorLineColor: "#6f9d88",251 signalColor: "#6f9d88",252 signalTextColor: "#6f9d88",253 labelBoxBkgColor: "#d5f26a",254 labelBoxBorderColor: "#39725c",255 labelTextColor: "#18231f",256 loopTextColor: "#6f9d88",257 activationBkgColor: "#d5f26a",258 activationBorderColor: "#39725c",259 noteBkgColor: "#d5f26a",260 noteTextColor: "#18231f",261 noteBorderColor: "#39725c",262 },263 },264 },265 colorMode: {266 respectPrefersColorScheme: true,267 },268 navbar: {269 // Two trees, no wordmark: the site fronts two products, and each270 // product's own dropdown carries its links (so no top-level GitHub/JSR).271 // The demo stands beside them as a third top-level entry.272 logo: {273 alt: "lofi",274 src: "img/two-trees.svg",275 // square intrinsic dimensions at the navbar's 1.7rem height so the276 // image never contributes layout shift277 width: 30,278 height: 30,279 },280 items: [281 {282 type: "dropdown",283 label: "lofi",284 position: "left",285 to: "/",286 items: [287 { label: "docs", to: "/docs" },288 { label: "api", to: "/api" },289 {290 label: "github",291 href: "https://github.com/FelineStateMachine/lofi",292 },293 { label: "jsr", href: "https://jsr.io/@nzip/lofi" },294 ],295 },296 {297 type: "dropdown",298 label: "lofi-node",299 position: "left",300 to: "/node",301 items: [302 { label: "docs", to: "/node/docs" },303 { label: "api", to: "/node/api" },304 {305 label: "github",306 href: "https://github.com/FelineStateMachine/lofi-node",307 },308 { label: "jsr", href: "https://jsr.io/@nzip/lofi-node" },309 ],310 },311 {312 label: "demo",313 position: "left",314 href: "https://demo.lofi.host",315 // Wears the demo's magma styling (custom.css) instead of the316 // navbar link treatment.317 className: "navbar-demo-link",318 },319 ],320 },321 footer: {322 style: "dark",323 // One column per product, parallel structure, lowercase like the nav.324 links: [325 {326 title: "lofi",327 items: [328 { label: "docs", to: "/docs" },329 { label: "api", to: "/api" },330 { label: "demo", href: "https://demo.lofi.host" },331 {332 label: "github",333 href: "https://github.com/FelineStateMachine/lofi",334 },335 { label: "jsr", href: "https://jsr.io/@nzip/lofi" },336 { label: "llms.txt", to: "/llms.txt", target: "_blank" },337 ],338 },339 {340 title: "lofi-node",341 items: [342 { label: "docs", to: "/node/docs" },343 { label: "api", to: "/node/api" },344 {345 label: "github",346 href: "https://github.com/FelineStateMachine/lofi-node",347 },348 { label: "jsr", href: "https://jsr.io/@nzip/lofi-node" },349 { label: "llms.txt", to: "/node/llms.txt", target: "_blank" },350 ],351 },352 ],353 copyright: `MIT licensed. Built with Docusaurus.`,354 },355 prism: {356 // gruvbox material is the standard theme closest to the brand palette:357 // olive/lime greens and mint aquas on warm dark and cream backgrounds358 theme: prismThemes.gruvboxMaterialLight,359 darkTheme: prismThemes.gruvboxMaterialDark,360 additionalLanguages: ["bash", "json", "diff"],361 },362 } satisfies Preset.ThemeConfig,363};364365export default config;