{message}
+{details}
+ {stack && ( +
+ {stack}
+
+ )}
+ diff --git a/.gitignore b/.gitignore
index b2d6de3..8fa7200 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,20 +1,7 @@
-# Dependencies
-/node_modules
-
-# Production
-/build
-
-# Generated files
-.docusaurus
-.cache-loader
-
-# Misc
.DS_Store
-.env.local
-.env.development.local
-.env.test.local
-.env.production.local
+/node_modules/
-npm-debug.log*
-yarn-debug.log*
-yarn-error.log*
+# React Router
+/.react-router/
+/build/
+.source
diff --git a/README.md b/README.md
index 20288c7..8c96829 100644
--- a/README.md
+++ b/README.md
@@ -5,7 +5,8 @@ and other resources to help you get started with contributing to PoliNetwork's p
## Website
-This website is built using [Docusaurus](https://docusaurus.io/), a modern static website generator.
+This website is built with [Fumadocs](https://fumadocs.dev/) on
+[React Router](https://reactrouter.com/), and exported as a static site.
### Installation
@@ -16,10 +17,10 @@ pnpm install
### Local Development
```sh
-pnpm start
+pnpm dev
```
-This command starts a local development server and opens up a browser window. Most changes are reflected live without having to restart the server.
+This command starts a local development server. Most changes are reflected live without having to restart the server.
### Build
@@ -27,14 +28,31 @@ This command starts a local development server and opens up a browser window. Mo
pnpm build
```
-This command generates static content into the `build` directory and can be served using any static contents hosting service.
+This command prerenders every page into the `build` directory, which can be served using any static contents hosting service.
You can then serve your static build by running:
```sh
-pnpm serve
+pnpm start
```
### Deployment
-This website is deployed using GitHub Pages. The deployment process is automated using GitHub Actions.
+This website is deployed using GitHub Pages. The deployment process is automated using GitHub Actions,
+and every pull request gets a preview deployment.
+
+## Writing docs
+
+Pages live in `content/docs/` as `.mdx` files: the URL follows the file path
+(`content/docs/infrastructure/Guides/backups.mdx` is `/docs/infrastructure/Guides/backups`).
+
+- Every page needs a `title` in its front matter (and optionally a `description`): it is
+ rendered as the page heading, so don't repeat it as a `#` heading.
+- The sidebar order and folder names come from the `meta.json` file in each folder.
+- Link to other pages with their URL, e.g. `[Backups](/docs/infrastructure/Guides/backups)`.
+- Callouts: `
+ {appName}
+ >
+ ),
+ },
+ links: [
+ { text: 'Infrastructure', url: '/docs/infrastructure/Introduction', active: 'nested-url' },
+ { text: 'Archived Projects 🗃️', url: '/docs/archive', active: 'nested-url' },
+ ],
+ githubUrl: `https://github.com/${gitConfig.user}/${gitConfig.repo}`,
+ }
+}
diff --git a/app/lib/shared.ts b/app/lib/shared.ts
new file mode 100644
index 0000000..d22ccfe
--- /dev/null
+++ b/app/lib/shared.ts
@@ -0,0 +1,14 @@
+export const appName = 'PoliNetwork Docs'
+export const docsRoute = '/docs'
+export const docsContentRoute = '/llms.mdx/docs'
+
+export const gitConfig = {
+ user: 'polinetworkorg',
+ repo: 'docs',
+ branch: 'main',
+}
+
+export function getPageMarkdownUrl(page: { slugs: string[] }) {
+ const segments = [...page.slugs, 'content.md']
+ return { segments, url: `${docsContentRoute}/${segments.join('/')}` }
+}
diff --git a/app/lib/source.ts b/app/lib/source.ts
new file mode 100644
index 0000000..0ccf630
--- /dev/null
+++ b/app/lib/source.ts
@@ -0,0 +1,32 @@
+import { createElement } from 'react'
+import { Archive, Server } from 'lucide-react'
+import { llms, loader } from 'fumadocs-core/source'
+import { defineDocs } from 'fumadocs-mdx/macro'
+import { docsRoute } from './shared'
+
+export const docs = defineDocs({
+ dir: 'content/docs',
+ docs: {
+ async: true,
+ postprocess: {
+ includeProcessedMarkdown: true,
+ },
+ },
+})
+
+// icons named in meta.json files
+const icons = { Archive, Server }
+
+export const source = loader({
+ source: docs.toFumadocsSource(),
+ baseUrl: docsRoute,
+ icon(name) {
+ if (name && name in icons) return createElement(icons[name as keyof typeof icons])
+ },
+})
+
+export const docsLlms = llms(source, {
+ renderPage: async (page) => `# ${page.data.title} (${page.url})
+
+${await page.data.getText('processed')}`,
+})
diff --git a/app/llms/full.ts b/app/llms/full.ts
new file mode 100644
index 0000000..542a6b0
--- /dev/null
+++ b/app/llms/full.ts
@@ -0,0 +1,5 @@
+import { docsLlms } from '@/lib/source'
+
+export async function loader() {
+ return new Response(await docsLlms.full())
+}
diff --git a/app/llms/index.ts b/app/llms/index.ts
new file mode 100644
index 0000000..ebc8d24
--- /dev/null
+++ b/app/llms/index.ts
@@ -0,0 +1,5 @@
+import { docsLlms } from '@/lib/source'
+
+export async function loader() {
+ return new Response(await docsLlms.index())
+}
diff --git a/app/llms/mdx.ts b/app/llms/mdx.ts
new file mode 100644
index 0000000..297a17f
--- /dev/null
+++ b/app/llms/mdx.ts
@@ -0,0 +1,17 @@
+import type { Route } from './+types/mdx'
+import { docsLlms, source } from '@/lib/source'
+
+export async function loader({ params }: Route.LoaderArgs) {
+ const slugs = params['*'].split('/').filter((v) => v.length > 0)
+ // remove the appended "content.md"
+ slugs.pop()
+ const page = source.getPage(slugs)
+ if (!page) {
+ return new Response('not found', { status: 404 })
+ }
+ return new Response(await docsLlms.page(page), {
+ headers: {
+ 'Content-Type': 'text/markdown',
+ },
+ })
+}
diff --git a/app/root.tsx b/app/root.tsx
new file mode 100644
index 0000000..37a084b
--- /dev/null
+++ b/app/root.tsx
@@ -0,0 +1,77 @@
+import {
+ isRouteErrorResponse,
+ Links,
+ Meta,
+ Outlet,
+ Scripts,
+ ScrollRestoration,
+} from 'react-router'
+import { RootProvider } from 'fumadocs-ui/provider/react-router'
+import type { Route } from './+types/root'
+import './app.css'
+import SearchDialog from '@/components/search'
+import NotFound from './routes/not-found'
+import { withBase } from '@/lib/base-url'
+
+export const links: Route.LinksFunction = () => [
+ { rel: 'icon', href: withBase('/img/favicon.ico') },
+ { rel: 'preconnect', href: 'https://fonts.googleapis.com' },
+ {
+ rel: 'preconnect',
+ href: 'https://fonts.gstatic.com',
+ crossOrigin: 'anonymous',
+ },
+ {
+ rel: 'stylesheet',
+ href: 'https://fonts.googleapis.com/css2?family=Inter:ital,opsz,wght@0,14..32,100..900;1,14..32,100..900&display=swap',
+ },
+]
+
+export function Layout({ children }: { children: React.ReactNode }) {
+ return (
+
+
+
+
+
+ {details}
+ {stack && ( +
+ {stack}
+
+ )}
+ This page could not be found.
+ + Back to Docs + +{description}
-{siteConfig.tagline}
-