;
+}
diff --git a/docs/src/components/provider.tsx b/docs/src/components/provider.tsx
new file mode 100644
index 0000000..fb30046
--- /dev/null
+++ b/docs/src/components/provider.tsx
@@ -0,0 +1,7 @@
+'use client';
+import type { ReactNode } from 'react';
+import { RootProvider } from 'fumadocs-ui/provider/waku';
+
+export function Provider({ children }: { children: ReactNode }) {
+ return {children};
+}
diff --git a/docs/src/lib/cn.ts b/docs/src/lib/cn.ts
new file mode 100644
index 0000000..241ffb3
--- /dev/null
+++ b/docs/src/lib/cn.ts
@@ -0,0 +1 @@
+export { cn } from 'cnfast';
diff --git a/docs/src/lib/layout.shared.tsx b/docs/src/lib/layout.shared.tsx
new file mode 100644
index 0000000..88b9ca8
--- /dev/null
+++ b/docs/src/lib/layout.shared.tsx
@@ -0,0 +1,12 @@
+import type { BaseLayoutProps } from 'fumadocs-ui/layouts/shared';
+import { appName, gitConfig } from './shared';
+
+export function baseOptions(): BaseLayoutProps {
+ return {
+ nav: {
+ // JSX supported
+ title: appName,
+ },
+ githubUrl: `https://github.com/${gitConfig.user}/${gitConfig.repo}`,
+ };
+}
diff --git a/docs/src/lib/shared.ts b/docs/src/lib/shared.ts
new file mode 100644
index 0000000..4fa88de
--- /dev/null
+++ b/docs/src/lib/shared.ts
@@ -0,0 +1,11 @@
+export const appName = 'Waku';
+export const docsRoute = '/docs';
+export const docsImageRoute = '/og/docs';
+export const docsContentRoute = '/llms.mdx/docs';
+
+// fill this with your actual GitHub info, for example:
+export const gitConfig = {
+ user: 'fuma-nama',
+ repo: 'fumadocs',
+ branch: 'main',
+};
diff --git a/docs/src/lib/source.ts b/docs/src/lib/source.ts
new file mode 100644
index 0000000..2e25e58
--- /dev/null
+++ b/docs/src/lib/source.ts
@@ -0,0 +1,47 @@
+import { loader } from 'fumadocs-core/source';
+import { lucideIconsPlugin } from 'fumadocs-core/source/lucide-icons';
+import { docsContentRoute, docsImageRoute, docsRoute } from './shared';
+import { defineDocs } from 'fumadocs-mdx/macro';
+import { metaSchema, pageSchema } from 'fumadocs-core/source/schema';
+
+const docs = defineDocs({
+ dir: 'content/docs',
+ docs: {
+ schema: pageSchema,
+ postprocess: {
+ includeProcessedMarkdown: true,
+ },
+ },
+ meta: {
+ schema: metaSchema,
+ },
+});
+
+export const source = loader({
+ source: docs.toFumadocsSource(),
+ baseUrl: docsRoute,
+ plugins: [lucideIconsPlugin()],
+});
+
+export function getPageImageUrl(page: (typeof source)['$inferPage']) {
+ const segments = [...page.slugs, 'image.webp'];
+
+ return '/' + [page.locale, ...docsImageRoute.split('/'), ...segments].filter(Boolean).join('/');
+}
+
+export function getPageMarkdownUrl(page: (typeof source)['$inferPage']) {
+ const segments = [...page.slugs, 'content.md'];
+
+ return {
+ segments,
+ url: '/' + [page.locale, ...docsContentRoute.split('/'), ...segments].filter(Boolean).join('/'),
+ };
+}
+
+export async function getLLMText(page: (typeof source)['$inferPage']) {
+ const processed = await page.data.getText('processed');
+
+ return `# ${page.data.title} (${page.url})
+
+${processed}`;
+}
diff --git a/docs/src/pages/(home)/404.tsx b/docs/src/pages/(home)/404.tsx
new file mode 100644
index 0000000..0a9b00d
--- /dev/null
+++ b/docs/src/pages/(home)/404.tsx
@@ -0,0 +1 @@
+export { DefaultNotFound as default } from 'fumadocs-ui/layouts/home/not-found';
diff --git a/docs/src/pages/(home)/_layout.tsx b/docs/src/pages/(home)/_layout.tsx
new file mode 100644
index 0000000..dbecdd5
--- /dev/null
+++ b/docs/src/pages/(home)/_layout.tsx
@@ -0,0 +1,7 @@
+import type { ReactNode } from 'react';
+import { baseOptions } from '@/lib/layout.shared';
+import { HomeLayout } from 'fumadocs-ui/layouts/home';
+
+export default function Layout({ children }: { children: ReactNode }) {
+ return {children};
+}
diff --git a/docs/src/pages/(home)/index.tsx b/docs/src/pages/(home)/index.tsx
new file mode 100644
index 0000000..58a364a
--- /dev/null
+++ b/docs/src/pages/(home)/index.tsx
@@ -0,0 +1,21 @@
+import { Link } from 'waku';
+
+export default function Home() {
+ return (
+
+
Fumadocs on Waku.
+
+ Open Docs
+
+
+ );
+}
+
+export async function getConfig() {
+ return {
+ render: 'static',
+ };
+}
diff --git a/docs/src/pages/_api/api/search.ts b/docs/src/pages/_api/api/search.ts
new file mode 100644
index 0000000..6f73844
--- /dev/null
+++ b/docs/src/pages/_api/api/search.ts
@@ -0,0 +1,4 @@
+import { createFromSource } from 'fumadocs-core/search/server';
+import { source } from '@/lib/source';
+
+export const { GET } = createFromSource(source);
diff --git a/docs/src/pages/_api/llms-full.txt.ts b/docs/src/pages/_api/llms-full.txt.ts
new file mode 100644
index 0000000..4a91c06
--- /dev/null
+++ b/docs/src/pages/_api/llms-full.txt.ts
@@ -0,0 +1,13 @@
+import { getLLMText, source } from '@/lib/source';
+
+export async function GET() {
+ const scan = source.getPages().map(getLLMText);
+ const scanned = await Promise.all(scan);
+ return new Response(scanned.join('\n\n'));
+}
+
+export async function getConfig() {
+ return {
+ render: 'static' as const,
+ } as const;
+}
diff --git a/docs/src/pages/_api/llms.mdx/docs/[...slugs]/content.md.ts b/docs/src/pages/_api/llms.mdx/docs/[...slugs]/content.md.ts
new file mode 100644
index 0000000..9c23617
--- /dev/null
+++ b/docs/src/pages/_api/llms.mdx/docs/[...slugs]/content.md.ts
@@ -0,0 +1,29 @@
+import { getLLMText, source } from '@/lib/source';
+import { ApiContext } from 'waku/router';
+import { unstable_notFound } from 'waku/router/server';
+
+export async function GET(
+ _: Request,
+ { params }: ApiContext<'/llms.mdx/docs/[...slugs]/content.md'>,
+) {
+ const slugs = params.slugs;
+ const page = source.getPage(slugs);
+ if (!page) unstable_notFound();
+
+ return new Response(await getLLMText(page), {
+ headers: {
+ 'Content-Type': 'text/markdown',
+ },
+ });
+}
+
+export async function getConfig() {
+ const pages = source
+ .generateParams()
+ .map((item) => (item.lang ? [item.lang, ...item.slug] : item.slug));
+
+ return {
+ render: 'static' as const,
+ staticPaths: pages,
+ } as const;
+}
diff --git a/docs/src/pages/_api/llms.txt.ts b/docs/src/pages/_api/llms.txt.ts
new file mode 100644
index 0000000..cf8e952
--- /dev/null
+++ b/docs/src/pages/_api/llms.txt.ts
@@ -0,0 +1,12 @@
+import { source } from '@/lib/source';
+import { llms } from 'fumadocs-core/source';
+
+export function GET() {
+ return new Response(llms(source).index());
+}
+
+export async function getConfig() {
+ return {
+ render: 'static' as const,
+ } as const;
+}
diff --git a/docs/src/pages/_api/og/docs/[...slugs]/image.webp.tsx b/docs/src/pages/_api/og/docs/[...slugs]/image.webp.tsx
new file mode 100644
index 0000000..45578a6
--- /dev/null
+++ b/docs/src/pages/_api/og/docs/[...slugs]/image.webp.tsx
@@ -0,0 +1,31 @@
+import { appName } from '@/lib/shared';
+import { source } from '@/lib/source';
+import { ImageResponse } from 'takumi-js/response';
+import { generate as DefaultImage } from 'fumadocs-ui/og/takumi';
+import { ApiContext } from 'waku/router';
+
+export async function GET(_: Request, { params }: ApiContext<'/og/docs/[...slugs]/image.webp'>) {
+ const page = source.getPage(params.slugs);
+
+ if (!page) return new Response(undefined, { status: 404 });
+
+ return new ImageResponse(
+ ,
+ {
+ width: 1200,
+ height: 630,
+ format: 'webp',
+ },
+ );
+}
+
+export async function getConfig() {
+ const pages = source
+ .generateParams()
+ .map((item) => (item.lang ? [item.lang, ...item.slug] : item.slug));
+
+ return {
+ render: 'static' as const,
+ staticPaths: pages,
+ } as const;
+}
diff --git a/docs/src/pages/_root.tsx b/docs/src/pages/_root.tsx
new file mode 100644
index 0000000..9034c08
--- /dev/null
+++ b/docs/src/pages/_root.tsx
@@ -0,0 +1,20 @@
+import type { ReactNode } from 'react';
+import { Provider } from '@/components/provider';
+import '@/styles/globals.css';
+
+export default async function RootElement({ children }: { children: ReactNode }) {
+ return (
+
+
+
+ {children}
+
+
+ );
+}
+
+export async function getConfig() {
+ return {
+ render: 'static',
+ } as const;
+}
diff --git a/docs/src/pages/docs/[...slugs].tsx b/docs/src/pages/docs/[...slugs].tsx
new file mode 100644
index 0000000..bd590cc
--- /dev/null
+++ b/docs/src/pages/docs/[...slugs].tsx
@@ -0,0 +1,55 @@
+import { getPageImageUrl, getPageMarkdownUrl, source } from '@/lib/source';
+import { PageProps } from 'waku/router';
+import { createRelativeLink } from 'fumadocs-ui/mdx';
+import {
+ DocsBody,
+ DocsDescription,
+ DocsPage,
+ DocsTitle,
+ MarkdownCopyButton,
+ ViewOptionsPopover,
+} from 'fumadocs-ui/layouts/docs/page';
+import { unstable_notFound } from 'waku/router/server';
+import { gitConfig } from '@/lib/shared';
+import { getMDXComponents } from '@/components/mdx';
+
+export default function Page({ slugs }: PageProps<'/docs/[...slugs]'>) {
+ const page = source.getPage(slugs);
+ if (!page) unstable_notFound();
+
+ const MDX = page.data.body;
+ const markdownUrl = getPageMarkdownUrl(page).url;
+ return (
+
+
+ {page.data.title}
+ {page.data.description}
+
+
+
+
+
+
+
+
+ );
+}
+
+export async function getConfig() {
+ const pages = source
+ .generateParams()
+ .map((item) => (item.lang ? [item.lang, ...item.slug] : item.slug));
+
+ return {
+ render: 'static' as const,
+ staticPaths: pages,
+ } as const;
+}
diff --git a/docs/src/pages/docs/_layout.tsx b/docs/src/pages/docs/_layout.tsx
new file mode 100644
index 0000000..44b4475
--- /dev/null
+++ b/docs/src/pages/docs/_layout.tsx
@@ -0,0 +1,12 @@
+import type { ReactNode } from 'react';
+import { DocsLayout } from 'fumadocs-ui/layouts/docs';
+import { source } from '@/lib/source';
+import { baseOptions } from '@/lib/layout.shared';
+
+export default function Layout({ children }: { children: ReactNode }) {
+ return (
+
+ {children}
+
+ );
+}
diff --git a/docs/src/styles/globals.css b/docs/src/styles/globals.css
new file mode 100644
index 0000000..dd2ef5f
--- /dev/null
+++ b/docs/src/styles/globals.css
@@ -0,0 +1,16 @@
+@import "tailwindcss";
+@import "fumadocs-ui/css/solar.css";
+@import "fumadocs-ui/css/preset.css";
+
+html {
+ scrollbar-gutter: stable;
+}
+
+html > body[data-scroll-locked] {
+ margin-right: 0px !important;
+ --removed-body-scroll-bar-size: 0px !important;
+}
+
+:root {
+ --fd-layout-width: 1400px;
+}
diff --git a/docs/tsconfig.json b/docs/tsconfig.json
new file mode 100644
index 0000000..343fec6
--- /dev/null
+++ b/docs/tsconfig.json
@@ -0,0 +1,23 @@
+{
+ "compilerOptions": {
+ "target": "ES2022",
+ "lib": ["dom", "dom.iterable", "ES2022"],
+ "types": ["vite", "vite/client"],
+ "jsx": "react-jsx",
+ "module": "ESNext",
+ "moduleResolution": "bundler",
+ "resolveJsonModule": true,
+ "allowJs": true,
+ "checkJs": true,
+ "esModuleInterop": true,
+ "forceConsistentCasingInFileNames": true,
+ "strict": true,
+ "skipLibCheck": true,
+ "noUncheckedIndexedAccess": true,
+ "noEmit": true,
+ "paths": {
+ "@/*": ["./src/*"]
+ }
+ },
+ "exclude": ["node_modules", "dist"]
+}
diff --git a/docs/wrangler.jsonc b/docs/wrangler.jsonc
new file mode 100644
index 0000000..b3e382d
--- /dev/null
+++ b/docs/wrangler.jsonc
@@ -0,0 +1,28 @@
+{
+ "$schema": "node_modules/wrangler/config-schema.json",
+ "name": "leetcode-docs",
+ "main": "./dist/server/index.js",
+ "compatibility_date": "2026-08-13",
+ "compatibility_flags": ["nodejs_als"],
+ "workers_dev": false,
+ "preview_urls": true,
+ "build": {
+ // waku's cloudflare build enhancer writes a .wrangler/deploy redirect to its minimal
+ // generated config (no routes/preview_urls); remove it so this file stays authoritative.
+ "command": "bun run build && rm -rf .wrangler/deploy",
+ "watch_dir": "content"
+ },
+ "routes": [
+ {
+ "pattern": "lc.prad.codes",
+ "custom_domain": true
+ }
+ ],
+ "assets": {
+ "binding": "ASSETS",
+ "directory": "./dist/public",
+ "html_handling": "drop-trailing-slash"
+ },
+ "rules": [{ "type": "ESModule", "globs": ["**/*.js", "**/*.mjs"] }],
+ "no_bundle": true
+}