diff --git a/docs/next.config.mjs b/docs/next.config.mjs
index ae0a6d2..4a82553 100644
--- a/docs/next.config.mjs
+++ b/docs/next.config.mjs
@@ -4,15 +4,12 @@ const withMDX = createMDX();
/** @type {import('next').NextConfig} */
const config = {
+ output: 'export',
reactStrictMode: true,
- async rewrites() {
- return [
- {
- source: '/:path*.mdx',
- destination: '/llms.mdx/:path*',
- },
- ];
- },
+ // Use trailing slashes to avoid conflicts with route handlers that have file extensions
+ trailingSlash: true,
+ // Note: rewrites are not supported with static export
+ // The /llms.mdx route will be pre-rendered as static files
};
export default withMDX(config);
diff --git a/docs/package.json b/docs/package.json
index 16ba5fb..d3554d4 100644
--- a/docs/package.json
+++ b/docs/package.json
@@ -2,8 +2,9 @@
"name": "docs",
"version": "0.0.0",
"private": true,
+ "type": "module",
"scripts": {
- "build": "next build",
+ "build": "next build && node scripts/post-export.js",
"dev": "next dev",
"start": "next start",
"types:check": "fumadocs-mdx && next typegen && tsc --noEmit",
diff --git a/docs/scripts/post-export.js b/docs/scripts/post-export.js
new file mode 100755
index 0000000..08a761f
--- /dev/null
+++ b/docs/scripts/post-export.js
@@ -0,0 +1,46 @@
+#!/usr/bin/env node
+
+/**
+ * Post-export script to copy English content to root directory
+ * This allows the default language (en) to be accessible without language prefix
+ */
+
+import { cpSync, existsSync, mkdirSync } from 'node:fs';
+import { join } from 'node:path';
+import { fileURLToPath } from 'node:url';
+
+const __dirname = fileURLToPath(new URL('.', import.meta.url));
+const outDir = join(__dirname, '../out');
+const enDir = join(outDir, 'en');
+
+console.log('Copying English content to root directory...');
+
+if (!existsSync(enDir)) {
+ console.error('Error: /en directory not found in output');
+ process.exit(1);
+}
+
+// Get all items in /en directory
+const fs = await import('node:fs/promises');
+const items = await fs.readdir(enDir);
+
+// Copy each item to root, excluding already existing root items
+for (const item of items) {
+ const source = join(enDir, item);
+ const dest = join(outDir, item);
+
+ // Skip if item already exists at root (like _next, api, etc.)
+ if (existsSync(dest)) {
+ console.log(`Skipping ${item} (already exists at root)`);
+ continue;
+ }
+
+ try {
+ cpSync(source, dest, { recursive: true });
+ console.log(`Copied ${item}`);
+ } catch (error) {
+ console.error(`Error copying ${item}:`, error.message);
+ }
+}
+
+console.log('English content copied to root directory successfully!');
diff --git a/docs/src/app/[lang]/(docs)/layout.tsx b/docs/src/app/[lang]/(docs)/layout.tsx
index 1527d11..4ac7c24 100644
--- a/docs/src/app/[lang]/(docs)/layout.tsx
+++ b/docs/src/app/[lang]/(docs)/layout.tsx
@@ -1,3 +1,4 @@
+import type { ReactNode } from 'react';
import { source } from '@/lib/source';
import { DocsLayout } from 'fumadocs-ui/layouts/docs';
import { baseOptions } from '@/lib/layout.shared';
@@ -5,7 +6,10 @@ import { baseOptions } from '@/lib/layout.shared';
export default async function Layout({
children,
params,
-}: LayoutProps<'/[lang]/[[...slug]]'>) {
+}: {
+ children: ReactNode;
+ params: Promise<{ lang: string; slug?: string[] }>;
+}) {
const resolvedParams = await params;
const locale = resolvedParams.lang;
return (
diff --git a/docs/src/app/[lang]/layout.tsx b/docs/src/app/[lang]/layout.tsx
index cb1ec6d..a3a9ff1 100644
--- a/docs/src/app/[lang]/layout.tsx
+++ b/docs/src/app/[lang]/layout.tsx
@@ -24,5 +24,16 @@ export default async function Layout({
const { lang } = await params;
const locale = isLocale(lang) ? lang : i18n.defaultLanguage;
- return {children};
+ return (
+
+ {children}
+
+ );
}
diff --git a/docs/src/app/api/search/route.ts b/docs/src/app/api/search/route.ts
index 7ba7e82..21cbcce 100644
--- a/docs/src/app/api/search/route.ts
+++ b/docs/src/app/api/search/route.ts
@@ -1,7 +1,14 @@
import { source } from '@/lib/source';
import { createFromSource } from 'fumadocs-core/search/server';
-export const { GET } = createFromSource(source, {
- // https://docs.orama.com/docs/orama-js/supported-languages
- language: 'english',
+// statically cached for static export
+export const revalidate = false;
+
+// Configure language support for both English and Chinese
+export const { staticGET: GET } = createFromSource(source, {
+ localeMap: {
+ en: { language: 'english' },
+ // Chinese is not natively supported by Orama, use English tokenizer for zh
+ zh: { language: 'english' },
+ },
});
diff --git a/docs/src/app/llms-full.txt/route.ts b/docs/src/app/llms-full.txt/route.ts
deleted file mode 100644
index d494d2c..0000000
--- a/docs/src/app/llms-full.txt/route.ts
+++ /dev/null
@@ -1,10 +0,0 @@
-import { getLLMText, source } from '@/lib/source';
-
-export const revalidate = false;
-
-export async function GET() {
- const scan = source.getPages().map(getLLMText);
- const scanned = await Promise.all(scan);
-
- return new Response(scanned.join('\n\n'));
-}
diff --git a/docs/src/app/llms.mdx/[[...slug]]/route.ts b/docs/src/app/llms.mdx/[[...slug]]/route.ts
deleted file mode 100644
index 1281482..0000000
--- a/docs/src/app/llms.mdx/[[...slug]]/route.ts
+++ /dev/null
@@ -1,27 +0,0 @@
-import { i18n, resolveLocaleFromSlug, stripLocaleFromSlug } from '@/lib/i18n';
-import { getLLMText, source } from '@/lib/source';
-import { notFound } from 'next/navigation';
-
-export const revalidate = false;
-
-export async function GET(_req: Request, { params }: RouteContext<'/llms.mdx/[[...slug]]'>) {
- const { slug } = await params;
- const locale = resolveLocaleFromSlug(slug);
- const slugs = stripLocaleFromSlug(slug);
- const page = source.getPage(slugs, locale);
- if (!page) notFound();
-
- return new Response(await getLLMText(page), {
- headers: {
- 'Content-Type': 'text/markdown',
- },
- });
-}
-
-export function generateStaticParams() {
- return source.getLanguages().flatMap(({ language, pages }) =>
- pages.map((page) => ({
- slug: language === i18n.defaultLanguage ? page.slugs : [language, ...page.slugs],
- })),
- );
-}
diff --git a/docs/src/app/og/docs/[...slug]/route.tsx b/docs/src/app/og/docs/[...slug]/route.tsx
deleted file mode 100644
index fcf1863..0000000
--- a/docs/src/app/og/docs/[...slug]/route.tsx
+++ /dev/null
@@ -1,30 +0,0 @@
-import { resolveLocaleFromSlug, stripLocaleFromSlug } from '@/lib/i18n';
-import { getPageImage, source } from '@/lib/source';
-import { notFound } from 'next/navigation';
-import { ImageResponse } from 'next/og';
-import { generate as DefaultImage } from 'fumadocs-ui/og';
-
-export const revalidate = false;
-
-export async function GET(_req: Request, { params }: RouteContext<'/og/docs/[...slug]'>) {
- const { slug } = await params;
- const pageSlug = slug.slice(0, -1);
- const locale = resolveLocaleFromSlug(pageSlug);
- const slugs = stripLocaleFromSlug(pageSlug);
- const page = source.getPage(slugs, locale);
- if (!page) notFound();
-
- return new ImageResponse(
- ,
- {
- width: 1200,
- height: 630,
- },
- );
-}
-
-export function generateStaticParams() {
- return source.getPages().map((page) => ({
- slug: getPageImage(page).segments,
- }));
-}
diff --git a/docs/src/lib/i18n.ts b/docs/src/lib/i18n.ts
index 350f748..4b5526f 100644
--- a/docs/src/lib/i18n.ts
+++ b/docs/src/lib/i18n.ts
@@ -3,6 +3,7 @@ import { defineI18n } from 'fumadocs-core/i18n';
export const i18n = defineI18n({
languages: ['en', 'zh'],
defaultLanguage: 'en',
+ // Hide locale prefix for default language (en) so English content appears at root
hideLocale: 'default-locale',
parser: 'dir'
});
@@ -12,7 +13,7 @@ export type Locale = (typeof i18n.languages)[number];
const localeSet = new Set(i18n.languages);
export function isLocale(value?: string): value is Locale {
- return Boolean(value && localeSet.has(value));
+ return Boolean(value && localeSet.has(value as Locale));
}
export function resolveLocaleFromSlug(slug?: string[]): Locale {
diff --git a/docs/src/middleware.ts b/docs/src/middleware.ts
index c8ee8ba..dda3835 100644
--- a/docs/src/middleware.ts
+++ b/docs/src/middleware.ts
@@ -4,5 +4,6 @@ import { i18n } from '@/lib/i18n';
export default createI18nMiddleware(i18n);
export const config = {
- matcher: ['/((?!api|_next/static|_next/image|favicon.ico|og|llms\.mdx|llms-full\.txt).*)'],
+ // Note: Middleware doesn't run in static export, but kept for development
+ matcher: ['/((?!api|_next/static|_next/image|favicon.ico).*)'],
};