mirror of
https://github.com/hanzo-docs/docs.git
synced 2026-10-25 04:20:02 +00:00
update previous examples & add Tanstack Start i18n example
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
import type { ReactNode } from 'react';
|
||||
import { HomeLayout } from 'fumadocs-ui/layouts/home';
|
||||
import { baseOptions } from '@/app/layout.config';
|
||||
import { baseOptions } from '@/lib/layout.shared';
|
||||
|
||||
export default async function Layout({
|
||||
params,
|
||||
|
||||
@@ -1,13 +1,14 @@
|
||||
import { source } from '@/lib/source';
|
||||
import {
|
||||
DocsPage,
|
||||
DocsBody,
|
||||
DocsTitle,
|
||||
DocsDescription,
|
||||
DocsPage,
|
||||
DocsTitle,
|
||||
} from 'fumadocs-ui/page';
|
||||
import { notFound } from 'next/navigation';
|
||||
import { metadataImage } from '@/lib/metadata';
|
||||
import { getMDXComponents } from '@/mdx-components';
|
||||
import type { Metadata } from 'next';
|
||||
|
||||
export default async function Page(props: {
|
||||
params: Promise<{ lang: string; slug?: string[] }>;
|
||||
@@ -35,7 +36,7 @@ export async function generateStaticParams() {
|
||||
|
||||
export async function generateMetadata(props: {
|
||||
params: Promise<{ lang: string; slug?: string[] }>;
|
||||
}) {
|
||||
}): Promise<Metadata> {
|
||||
const params = await props.params;
|
||||
const page = source.getPage(params.slug, params.lang);
|
||||
if (!page) notFound();
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import type { ReactNode } from 'react';
|
||||
import { source } from '@/lib/source';
|
||||
import { DocsLayout } from 'fumadocs-ui/layouts/docs';
|
||||
import { baseOptions } from '@/app/layout.config';
|
||||
import { baseOptions } from '@/lib/layout.shared';
|
||||
|
||||
export default async function Layout({
|
||||
params,
|
||||
|
||||
@@ -2,11 +2,31 @@ import 'fumadocs-ui/style.css';
|
||||
import { RootProvider } from 'fumadocs-ui/provider';
|
||||
import { Inter } from 'next/font/google';
|
||||
import type { ReactNode } from 'react';
|
||||
import { defineI18nUI } from 'fumadocs-ui/i18n';
|
||||
import { i18n } from '@/lib/i18n';
|
||||
|
||||
const inter = Inter({
|
||||
subsets: ['latin'],
|
||||
});
|
||||
|
||||
const { provider } = defineI18nUI(i18n, {
|
||||
translations: {
|
||||
en: {
|
||||
displayName: 'English',
|
||||
},
|
||||
cn: {
|
||||
displayName: 'Chinese',
|
||||
toc: '目錄',
|
||||
search: '搜尋文檔',
|
||||
lastUpdate: '最後更新於',
|
||||
searchNoResult: '沒有結果',
|
||||
previousPage: '上一頁',
|
||||
nextPage: '下一頁',
|
||||
chooseLanguage: '選擇語言',
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
export default async function Layout({
|
||||
params,
|
||||
children,
|
||||
@@ -24,34 +44,7 @@ export default async function Layout({
|
||||
minHeight: '100vh',
|
||||
}}
|
||||
>
|
||||
<RootProvider
|
||||
i18n={{
|
||||
locale: lang,
|
||||
locales: [
|
||||
{
|
||||
name: 'English',
|
||||
locale: 'en',
|
||||
},
|
||||
{
|
||||
name: 'Chinese',
|
||||
locale: 'cn',
|
||||
},
|
||||
],
|
||||
translations: {
|
||||
cn: {
|
||||
toc: '目錄',
|
||||
search: '搜尋文檔',
|
||||
lastUpdate: '最後更新於',
|
||||
searchNoResult: '沒有結果',
|
||||
previousPage: '上一頁',
|
||||
nextPage: '下一頁',
|
||||
chooseLanguage: '選擇語言',
|
||||
},
|
||||
}[lang],
|
||||
}}
|
||||
>
|
||||
{children}
|
||||
</RootProvider>
|
||||
<RootProvider i18n={provider(lang)}>{children}</RootProvider>
|
||||
</body>
|
||||
</html>
|
||||
);
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import type { I18nConfig } from 'fumadocs-core/i18n';
|
||||
import { defineI18n } from 'fumadocs-core/i18n';
|
||||
|
||||
export const i18n: I18nConfig = {
|
||||
export const i18n = defineI18n({
|
||||
defaultLanguage: 'en',
|
||||
languages: ['en', 'cn'],
|
||||
};
|
||||
});
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
import type { ReactNode } from 'react';
|
||||
import { HomeLayout } from 'fumadocs-ui/layouts/home';
|
||||
import { baseOptions } from '@/app/layout.config';
|
||||
import { baseOptions } from '@/lib/layout.shared';
|
||||
|
||||
export default function Layout({
|
||||
children,
|
||||
}: {
|
||||
children: ReactNode;
|
||||
}): React.ReactElement {
|
||||
return <HomeLayout {...baseOptions}>{children}</HomeLayout>;
|
||||
return <HomeLayout {...baseOptions()}>{children}</HomeLayout>;
|
||||
}
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
import { source } from '@/lib/source';
|
||||
import { DocsLayout } from 'fumadocs-ui/layouts/docs';
|
||||
import type { ReactNode } from 'react';
|
||||
import { baseOptions } from '@/app/layout.config';
|
||||
import { baseOptions } from '@/lib/layout.shared';
|
||||
|
||||
export default function Layout({ children }: { children: ReactNode }) {
|
||||
return (
|
||||
<DocsLayout tree={source.pageTree} {...baseOptions}>
|
||||
<DocsLayout tree={source.pageTree} {...baseOptions()}>
|
||||
{children}
|
||||
</DocsLayout>
|
||||
);
|
||||
|
||||
@@ -1,7 +0,0 @@
|
||||
import { BaseLayoutProps } from 'fumadocs-ui/layouts/shared';
|
||||
|
||||
export const baseOptions: BaseLayoutProps = {
|
||||
nav: {
|
||||
title: 'My App',
|
||||
},
|
||||
};
|
||||
@@ -0,0 +1,9 @@
|
||||
import type { BaseLayoutProps } from 'fumadocs-ui/layouts/shared';
|
||||
|
||||
export function baseOptions(): BaseLayoutProps {
|
||||
return {
|
||||
nav: {
|
||||
title: 'My App',
|
||||
},
|
||||
};
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
import type { ReactNode } from 'react';
|
||||
import { HomeLayout } from 'fumadocs-ui/layouts/home';
|
||||
import { baseOptions } from '@/app/layout.config';
|
||||
import { baseOptions } from '@/lib/layout.shared';
|
||||
|
||||
export default function Layout({ children }: { children: ReactNode }) {
|
||||
return <HomeLayout {...baseOptions}>{children}</HomeLayout>;
|
||||
return <HomeLayout {...baseOptions()}>{children}</HomeLayout>;
|
||||
}
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
import { source } from '@/lib/source';
|
||||
import { DocsLayout } from 'fumadocs-ui/layouts/notebook';
|
||||
import type { ReactNode } from 'react';
|
||||
import { baseOptions } from '@/app/layout.config';
|
||||
import { baseOptions } from '@/lib/layout.shared';
|
||||
|
||||
export default function RootDocsLayout({ children }: { children: ReactNode }) {
|
||||
return (
|
||||
<DocsLayout tree={source.pageTree} {...baseOptions}>
|
||||
<DocsLayout tree={source.pageTree} {...baseOptions()}>
|
||||
{children}
|
||||
</DocsLayout>
|
||||
);
|
||||
|
||||
@@ -1,8 +0,0 @@
|
||||
import type { BaseLayoutProps } from 'fumadocs-ui/layouts/shared';
|
||||
import { Logo } from '@/app/logo';
|
||||
|
||||
export const baseOptions: BaseLayoutProps = {
|
||||
nav: {
|
||||
title: <Logo />,
|
||||
},
|
||||
};
|
||||
@@ -0,0 +1,10 @@
|
||||
import type { BaseLayoutProps } from 'fumadocs-ui/layouts/shared';
|
||||
import { Logo } from '@/app/logo';
|
||||
|
||||
export function baseOptions(): BaseLayoutProps {
|
||||
return {
|
||||
nav: {
|
||||
title: <Logo />,
|
||||
},
|
||||
};
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
import type { ReactNode } from 'react';
|
||||
import { HomeLayout } from 'fumadocs-ui/layouts/home';
|
||||
import { baseOptions } from '@/app/layout.config';
|
||||
import { baseOptions } from '@/lib/layout.shared';
|
||||
|
||||
export default function Layout({ children }: { children: ReactNode }) {
|
||||
return <HomeLayout {...baseOptions}>{children}</HomeLayout>;
|
||||
return <HomeLayout {...baseOptions()}>{children}</HomeLayout>;
|
||||
}
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
import { DocsLayout } from 'fumadocs-ui/layouts/docs';
|
||||
import type { ReactNode } from 'react';
|
||||
import { baseOptions } from '@/app/layout.config';
|
||||
import { baseOptions } from '@/lib/layout.shared';
|
||||
import { source } from '@/lib/source';
|
||||
|
||||
export default function Layout({ children }: { children: ReactNode }) {
|
||||
return (
|
||||
<DocsLayout tree={source.pageTree} {...baseOptions}>
|
||||
<DocsLayout tree={source.pageTree} {...baseOptions()}>
|
||||
{children}
|
||||
</DocsLayout>
|
||||
);
|
||||
|
||||
@@ -1,21 +0,0 @@
|
||||
import type { BaseLayoutProps } from 'fumadocs-ui/layouts/shared';
|
||||
|
||||
/**
|
||||
* Shared layout configurations
|
||||
*
|
||||
* you can customise layouts individually from:
|
||||
* Home Layout: app/(home)/layout.tsx
|
||||
* Docs Layout: app/docs/layout.tsx
|
||||
*/
|
||||
export const baseOptions: BaseLayoutProps = {
|
||||
nav: {
|
||||
title: 'Python Docs',
|
||||
},
|
||||
links: [
|
||||
{
|
||||
text: 'Documentation',
|
||||
url: '/docs',
|
||||
active: 'nested-url',
|
||||
},
|
||||
],
|
||||
};
|
||||
@@ -0,0 +1,16 @@
|
||||
import type { BaseLayoutProps } from 'fumadocs-ui/layouts/shared';
|
||||
|
||||
export function baseOptions(): BaseLayoutProps {
|
||||
return {
|
||||
nav: {
|
||||
title: 'Python Docs',
|
||||
},
|
||||
links: [
|
||||
{
|
||||
text: 'Documentation',
|
||||
url: '/docs',
|
||||
active: 'nested-url',
|
||||
},
|
||||
],
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
node_modules
|
||||
package-lock.json
|
||||
yarn.lock
|
||||
|
||||
.DS_Store
|
||||
.cache
|
||||
.env
|
||||
.vercel
|
||||
.output
|
||||
.nitro
|
||||
/build/
|
||||
/api/
|
||||
/server/build
|
||||
/public/build# Sentry Config File
|
||||
.env.sentry-build-plugin
|
||||
/test-results/
|
||||
/playwright-report/
|
||||
/blob-report/
|
||||
/playwright/.cache/
|
||||
.tanstack
|
||||
|
||||
src/routeTree.gen.ts
|
||||
source.generated.ts
|
||||
@@ -0,0 +1,4 @@
|
||||
**/build
|
||||
**/public
|
||||
pnpm-lock.yaml
|
||||
routeTree.gen.ts
|
||||
@@ -0,0 +1,8 @@
|
||||
---
|
||||
title: 中文
|
||||
description: 您的第一個文檔
|
||||
---
|
||||
|
||||
## Hi 中文
|
||||
|
||||
Fumadocs 對 i18n 有良好的支持
|
||||
@@ -0,0 +1,34 @@
|
||||
---
|
||||
title: Hello World
|
||||
description: Your favourite docs framework.
|
||||
icon: Rocket
|
||||
---
|
||||
|
||||
Hey there! Fumadocs is a docs framework built for Next.js, but do you know it also works on Tanstack Start?
|
||||
|
||||
## Heading
|
||||
|
||||
Hello World!
|
||||
|
||||
<Cards>
|
||||
<Card
|
||||
title="Learn more about Tanstack Start"
|
||||
href="https://tanstack.com/start"
|
||||
/>
|
||||
<Card title="Learn more about Fumadocs" href="https://fumadocs.vercel.app" />
|
||||
</Cards>
|
||||
|
||||
### CodeBlock
|
||||
|
||||
```ts
|
||||
console.log('Hello World');
|
||||
```
|
||||
|
||||
#### Table
|
||||
|
||||
| Head | Description |
|
||||
| ------------------------------- | ----------------------------------- |
|
||||
| `hello` | Hello World |
|
||||
| very **important** | Hey |
|
||||
| _Surprisingly_ | Fumadocs |
|
||||
| very long text that looks weird | hello world hello world hello world |
|
||||
@@ -0,0 +1,12 @@
|
||||
---
|
||||
title: Test
|
||||
description: This is another page
|
||||
---
|
||||
|
||||
Hello World again!
|
||||
|
||||
## Installation
|
||||
|
||||
```npm
|
||||
npm i fumadocs-core fumadocs-ui
|
||||
```
|
||||
@@ -0,0 +1,35 @@
|
||||
{
|
||||
"name": "example-tanstack-start-i18n",
|
||||
"private": true,
|
||||
"sideEffects": false,
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"dev": "vite dev",
|
||||
"build": "vite build && tsc --noEmit",
|
||||
"start": "node .output/server/index.mjs"
|
||||
},
|
||||
"dependencies": {
|
||||
"@orama/tokenizers": "^3.1.11",
|
||||
"@tanstack/react-router": "^1.131.17",
|
||||
"@tanstack/react-router-devtools": "^1.131.17",
|
||||
"@tanstack/react-start": "^1.131.17",
|
||||
"fumadocs-core": "workspace:*",
|
||||
"fumadocs-mdx": "workspace:*",
|
||||
"fumadocs-ui": "workspace:*",
|
||||
"lucide-static": "^0.539.0",
|
||||
"react": "^19.1.1",
|
||||
"react-dom": "^19.1.1",
|
||||
"tailwind-merge": "^3.3.1",
|
||||
"vite": "^7.1.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@tailwindcss/vite": "^4.1.12",
|
||||
"@types/node": "^24.3.0",
|
||||
"@types/react": "^19.1.10",
|
||||
"@types/react-dom": "^19.1.7",
|
||||
"@vitejs/plugin-react": "^5.0.0",
|
||||
"tailwindcss": "^4.1.12",
|
||||
"typescript": "^5.9.2",
|
||||
"vite-tsconfig-paths": "^5.1.4"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
import { defineConfig, defineDocs } from 'fumadocs-mdx/config';
|
||||
|
||||
export const docs = defineDocs({
|
||||
dir: 'content/docs',
|
||||
});
|
||||
|
||||
export default defineConfig();
|
||||
@@ -0,0 +1,31 @@
|
||||
import { Link, useParams } from '@tanstack/react-router';
|
||||
import { HomeLayout } from 'fumadocs-ui/layouts/home';
|
||||
|
||||
export function NotFound() {
|
||||
const { lang } = useParams({ from: '/$lang/' });
|
||||
|
||||
return (
|
||||
<HomeLayout
|
||||
nav={{
|
||||
title: 'Tanstack Start',
|
||||
}}
|
||||
className="text-center py-32 justify-center"
|
||||
>
|
||||
<div className="flex flex-col items-center gap-4">
|
||||
<h1 className="text-6xl font-bold text-fd-muted-foreground">404</h1>
|
||||
<h2 className="text-2xl font-semibold">Page Not Found</h2>
|
||||
<p className="text-fd-muted-foreground max-w-md">
|
||||
The page you are looking for might have been removed, had its name
|
||||
changed, or is temporarily unavailable.
|
||||
</p>
|
||||
<Link
|
||||
to="/$lang"
|
||||
params={{ lang }}
|
||||
className="mt-4 px-4 py-2 rounded-lg bg-fd-primary text-fd-primary-foreground font-medium text-sm hover:opacity-90 transition-opacity"
|
||||
>
|
||||
Back to Home
|
||||
</Link>
|
||||
</div>
|
||||
</HomeLayout>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
import { defineI18n } from 'fumadocs-core/i18n';
|
||||
|
||||
export const i18n = defineI18n({
|
||||
defaultLanguage: 'en',
|
||||
languages: ['en', 'cn'],
|
||||
});
|
||||
@@ -0,0 +1,13 @@
|
||||
import type { BaseLayoutProps } from 'fumadocs-ui/layouts/shared';
|
||||
import { i18n } from '~/lib/i18n';
|
||||
|
||||
export function baseOptions(
|
||||
locale: string = i18n.defaultLanguage,
|
||||
): BaseLayoutProps {
|
||||
return {
|
||||
i18n,
|
||||
nav: {
|
||||
title: `Tanstack Start ${locale}`,
|
||||
},
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
import { loader } from 'fumadocs-core/source';
|
||||
import * as icons from 'lucide-static';
|
||||
import { create, docs } from '../../source.generated';
|
||||
import { i18n } from '~/lib/i18n';
|
||||
|
||||
export const source = loader({
|
||||
source: await create.sourceAsync(docs.doc, docs.meta),
|
||||
baseUrl: '/docs',
|
||||
i18n,
|
||||
// @ts-expect-error -- string
|
||||
icon(icon) {
|
||||
if (!icon) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (icon in icons) return icons[icon as keyof typeof icons];
|
||||
},
|
||||
});
|
||||
@@ -0,0 +1,18 @@
|
||||
import { createRouter as createTanStackRouter } from '@tanstack/react-router';
|
||||
import { routeTree } from './routeTree.gen';
|
||||
import { NotFound } from '~/components/not-found';
|
||||
|
||||
export function createRouter() {
|
||||
return createTanStackRouter({
|
||||
routeTree,
|
||||
defaultPreload: 'intent',
|
||||
scrollRestoration: true,
|
||||
defaultNotFoundComponent: NotFound,
|
||||
});
|
||||
}
|
||||
|
||||
declare module '@tanstack/react-router' {
|
||||
interface Register {
|
||||
router: ReturnType<typeof createRouter>;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,107 @@
|
||||
import { createFileRoute, notFound } from '@tanstack/react-router';
|
||||
import { DocsLayout } from 'fumadocs-ui/layouts/docs';
|
||||
import { createServerFn } from '@tanstack/react-start';
|
||||
import { source } from '~/lib/source';
|
||||
import type { PageTree } from 'fumadocs-core/server';
|
||||
import { useMemo } from 'react';
|
||||
import { docs } from '../../../../source.generated';
|
||||
import {
|
||||
DocsBody,
|
||||
DocsDescription,
|
||||
DocsPage,
|
||||
DocsTitle,
|
||||
} from 'fumadocs-ui/page';
|
||||
import defaultMdxComponents from 'fumadocs-ui/mdx';
|
||||
import { createClientLoader } from 'fumadocs-mdx/runtime/vite';
|
||||
import { baseOptions } from '~/lib/layout.shared';
|
||||
|
||||
export const Route = createFileRoute('/$lang/docs/$')({
|
||||
component: Page,
|
||||
loader: async ({ params }) => {
|
||||
const data = await loader({
|
||||
data: {
|
||||
slugs: params._splat?.split('/') ?? [],
|
||||
lang: params.lang,
|
||||
},
|
||||
});
|
||||
|
||||
await clientLoader.preload(data.path);
|
||||
return data;
|
||||
},
|
||||
});
|
||||
|
||||
const loader = createServerFn({
|
||||
method: 'GET',
|
||||
})
|
||||
.validator((params: { slugs: string[]; lang?: string }) => params)
|
||||
.handler(async ({ data: { slugs, lang } }) => {
|
||||
const page = source.getPage(slugs, lang);
|
||||
if (!page) throw notFound();
|
||||
|
||||
return {
|
||||
tree: source.getPageTree(lang) as object,
|
||||
path: page.path,
|
||||
};
|
||||
});
|
||||
|
||||
const clientLoader = createClientLoader(docs.doc, {
|
||||
id: 'docs',
|
||||
component({ toc, frontmatter, default: MDX }) {
|
||||
return (
|
||||
<DocsPage toc={toc}>
|
||||
<DocsTitle>{frontmatter.title}</DocsTitle>
|
||||
<DocsDescription>{frontmatter.description}</DocsDescription>
|
||||
<DocsBody>
|
||||
<MDX
|
||||
components={{
|
||||
...defaultMdxComponents,
|
||||
}}
|
||||
/>
|
||||
</DocsBody>
|
||||
</DocsPage>
|
||||
);
|
||||
},
|
||||
});
|
||||
|
||||
function Page() {
|
||||
const { lang } = Route.useParams();
|
||||
const data = Route.useLoaderData();
|
||||
const Content = clientLoader.getComponent(data.path);
|
||||
const tree = useMemo(
|
||||
() => transformPageTree(data.tree as PageTree.Folder),
|
||||
[data.tree],
|
||||
);
|
||||
|
||||
return (
|
||||
<DocsLayout {...baseOptions(lang)} tree={tree}>
|
||||
<Content />
|
||||
</DocsLayout>
|
||||
);
|
||||
}
|
||||
|
||||
function transformPageTree(tree: PageTree.Folder): PageTree.Folder {
|
||||
function page(item: PageTree.Item) {
|
||||
if (typeof item.icon !== 'string') return item;
|
||||
|
||||
return {
|
||||
...item,
|
||||
icon: (
|
||||
<span
|
||||
dangerouslySetInnerHTML={{
|
||||
__html: item.icon,
|
||||
}}
|
||||
/>
|
||||
),
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
...tree,
|
||||
index: tree.index ? page(tree.index) : undefined,
|
||||
children: tree.children.map((item) => {
|
||||
if (item.type === 'page') return page(item);
|
||||
if (item.type === 'folder') return transformPageTree(item);
|
||||
return item;
|
||||
}),
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
import { createFileRoute, Link } from '@tanstack/react-router';
|
||||
import { HomeLayout } from 'fumadocs-ui/layouts/home';
|
||||
import { baseOptions } from '~/lib/layout.shared';
|
||||
|
||||
export const Route = createFileRoute('/$lang/')({
|
||||
component: Home,
|
||||
});
|
||||
|
||||
function Home() {
|
||||
const { lang } = Route.useParams();
|
||||
|
||||
return (
|
||||
<HomeLayout
|
||||
{...baseOptions(lang)}
|
||||
className="text-center py-32 justify-center"
|
||||
>
|
||||
<h1 className="font-medium text-xl mb-4">Fumadocs on Tanstack Start.</h1>
|
||||
<Link
|
||||
to="/$lang/docs/$"
|
||||
params={{
|
||||
lang,
|
||||
_splat: '',
|
||||
}}
|
||||
className="px-3 py-2 rounded-lg bg-fd-primary text-fd-primary-foreground font-medium text-sm mx-auto"
|
||||
>
|
||||
Open Docs
|
||||
</Link>
|
||||
</HomeLayout>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,71 @@
|
||||
/// <reference types="vite/client" />
|
||||
import {
|
||||
createRootRoute,
|
||||
HeadContent,
|
||||
Outlet,
|
||||
Scripts,
|
||||
useParams,
|
||||
} from '@tanstack/react-router';
|
||||
import * as React from 'react';
|
||||
import appCss from '~/styles/app.css?url';
|
||||
import { RootProvider } from 'fumadocs-ui/provider/base';
|
||||
import { TanstackProvider } from 'fumadocs-core/framework/tanstack';
|
||||
import { defineI18nUI } from 'fumadocs-ui/i18n';
|
||||
import { i18n } from '~/lib/i18n';
|
||||
|
||||
export const Route = createRootRoute({
|
||||
head: () => ({
|
||||
meta: [
|
||||
{
|
||||
charSet: 'utf-8',
|
||||
},
|
||||
{
|
||||
name: 'viewport',
|
||||
content: 'width=device-width, initial-scale=1',
|
||||
},
|
||||
{
|
||||
title: 'Fumadocs on TanStack Start',
|
||||
},
|
||||
],
|
||||
links: [{ rel: 'stylesheet', href: appCss }],
|
||||
}),
|
||||
component: RootComponent,
|
||||
});
|
||||
|
||||
function RootComponent() {
|
||||
return (
|
||||
<RootDocument>
|
||||
<Outlet />
|
||||
</RootDocument>
|
||||
);
|
||||
}
|
||||
|
||||
const { provider } = defineI18nUI(i18n, {
|
||||
translations: {
|
||||
cn: {
|
||||
displayName: 'Chinese',
|
||||
search: 'Translated Content',
|
||||
},
|
||||
en: {
|
||||
displayName: 'English',
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
function RootDocument({ children }: { children: React.ReactNode }) {
|
||||
const { lang = i18n.defaultLanguage } = useParams({ strict: false });
|
||||
|
||||
return (
|
||||
<html suppressHydrationWarning>
|
||||
<head>
|
||||
<HeadContent />
|
||||
</head>
|
||||
<body className="flex flex-col min-h-screen">
|
||||
<TanstackProvider>
|
||||
<RootProvider i18n={provider(lang)}>{children}</RootProvider>
|
||||
</TanstackProvider>
|
||||
<Scripts />
|
||||
</body>
|
||||
</html>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
import { createServerFileRoute } from '@tanstack/react-start/server';
|
||||
import { source } from '~/lib/source';
|
||||
import { createFromSource } from 'fumadocs-core/search/server';
|
||||
import { createTokenizer } from '@orama/tokenizers/mandarin';
|
||||
|
||||
const server = createFromSource(source, {
|
||||
localeMap: {
|
||||
en: 'english',
|
||||
cn: {
|
||||
tokenizer: createTokenizer(),
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
export const ServerRoute = createServerFileRoute('/api/search').methods({
|
||||
GET: async ({ request }) => server.GET(request),
|
||||
});
|
||||
@@ -0,0 +1,3 @@
|
||||
@import 'tailwindcss';
|
||||
@import 'fumadocs-ui/css/neutral.css';
|
||||
@import 'fumadocs-ui/css/preset.css';
|
||||
@@ -0,0 +1,22 @@
|
||||
{
|
||||
"include": ["**/*.ts", "**/*.tsx"],
|
||||
"compilerOptions": {
|
||||
"strict": true,
|
||||
"esModuleInterop": true,
|
||||
"jsx": "react-jsx",
|
||||
"module": "ESNext",
|
||||
"moduleResolution": "Bundler",
|
||||
"lib": ["DOM", "DOM.Iterable", "ES2022"],
|
||||
"isolatedModules": true,
|
||||
"resolveJsonModule": true,
|
||||
"skipLibCheck": true,
|
||||
"target": "ES2022",
|
||||
"allowJs": true,
|
||||
"forceConsistentCasingInFileNames": true,
|
||||
"baseUrl": ".",
|
||||
"paths": {
|
||||
"~/*": ["./src/*"]
|
||||
},
|
||||
"noEmit": true
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
import react from '@vitejs/plugin-react';
|
||||
import { tanstackStart } from '@tanstack/react-start/plugin/vite';
|
||||
import { defineConfig } from 'vite';
|
||||
import tsConfigPaths from 'vite-tsconfig-paths';
|
||||
import tailwindcss from '@tailwindcss/vite';
|
||||
import mdx from 'fumadocs-mdx/vite';
|
||||
|
||||
export default defineConfig({
|
||||
server: {
|
||||
port: 3000,
|
||||
},
|
||||
plugins: [
|
||||
mdx(await import('./source.config')),
|
||||
tailwindcss(),
|
||||
tsConfigPaths({
|
||||
projects: ['./tsconfig.json'],
|
||||
}),
|
||||
tanstackStart({
|
||||
customViteReactPlugin: true,
|
||||
}),
|
||||
react(),
|
||||
],
|
||||
});
|
||||
@@ -61,8 +61,13 @@
|
||||
"types": "./dist/mdx-plugins/index.d.ts"
|
||||
},
|
||||
"./i18n": {
|
||||
"import": "./dist/i18n/index.js",
|
||||
"types": "./dist/i18n/index.d.ts"
|
||||
"browser": "./dist/i18n/index.js",
|
||||
"import": "./dist/i18n/index.server.js",
|
||||
"types": "./dist/i18n/index.server.d.ts"
|
||||
},
|
||||
"./i18n/middleware": {
|
||||
"import": "./dist/i18n/middleware.js",
|
||||
"types": "./dist/i18n/middleware.d.ts"
|
||||
},
|
||||
"./highlight": {
|
||||
"import": "./dist/highlight/index.js",
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import type { ReactNode } from 'react';
|
||||
import { HomeLayout } from 'fumadocs-ui/layouts/home';
|
||||
import { baseOptions } from '@/app/layout.config';
|
||||
import { baseOptions } from '@/lib/layout.shared';
|
||||
|
||||
export default function Layout({ children }: { children: ReactNode }) {
|
||||
return <HomeLayout {...baseOptions}>{children}</HomeLayout>;
|
||||
return <HomeLayout {...baseOptions()}>{children}</HomeLayout>;
|
||||
}
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
import { DocsLayout } from 'fumadocs-ui/layouts/docs';
|
||||
import type { ReactNode } from 'react';
|
||||
import { baseOptions } from '@/app/layout.config';
|
||||
import { baseOptions } from '@/lib/layout.shared';
|
||||
import { source } from '@/lib/source';
|
||||
|
||||
export default function Layout({ children }: { children: ReactNode }) {
|
||||
return (
|
||||
<DocsLayout tree={source.pageTree} {...baseOptions}>
|
||||
<DocsLayout tree={source.pageTree} {...baseOptions()}>
|
||||
{children}
|
||||
</DocsLayout>
|
||||
);
|
||||
|
||||
@@ -1,28 +0,0 @@
|
||||
import type { BaseLayoutProps } from 'fumadocs-ui/layouts/shared';
|
||||
|
||||
/**
|
||||
* Shared layout configurations
|
||||
*
|
||||
* you can customise layouts individually from:
|
||||
* Home Layout: app/(home)/layout.tsx
|
||||
* Docs Layout: app/docs/layout.tsx
|
||||
*/
|
||||
export const baseOptions: BaseLayoutProps = {
|
||||
nav: {
|
||||
title: (
|
||||
<>
|
||||
<svg
|
||||
width="24"
|
||||
height="24"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
aria-label="Logo"
|
||||
>
|
||||
<circle cx={12} cy={12} r={12} fill="currentColor" />
|
||||
</svg>
|
||||
My App
|
||||
</>
|
||||
),
|
||||
},
|
||||
// see https://fumadocs.dev/docs/ui/navigation/links
|
||||
links: [],
|
||||
};
|
||||
@@ -0,0 +1,30 @@
|
||||
import type { BaseLayoutProps } from 'fumadocs-ui/layouts/shared';
|
||||
|
||||
/**
|
||||
* Shared layout configurations
|
||||
*
|
||||
* you can customise layouts individually from:
|
||||
* Home Layout: app/(home)/layout.tsx
|
||||
* Docs Layout: app/docs/layout.tsx
|
||||
*/
|
||||
export function baseOptions(): BaseLayoutProps {
|
||||
return {
|
||||
nav: {
|
||||
title: (
|
||||
<>
|
||||
<svg
|
||||
width="24"
|
||||
height="24"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
aria-label="Logo"
|
||||
>
|
||||
<circle cx={12} cy={12} r={12} fill="currentColor" />
|
||||
</svg>
|
||||
My App
|
||||
</>
|
||||
),
|
||||
},
|
||||
// see https://fumadocs.dev/docs/ui/navigation/links
|
||||
links: [],
|
||||
};
|
||||
}
|
||||
@@ -6,7 +6,8 @@ import {
|
||||
DocsPage,
|
||||
DocsTitle,
|
||||
} from 'fumadocs-ui/page';
|
||||
import { source } from '@/source';
|
||||
import { source } from '@/lib/source';
|
||||
import { baseOptions } from '@/lib/layout.shared';
|
||||
import { type PageTree } from 'fumadocs-core/server';
|
||||
import defaultMdxComponents from 'fumadocs-ui/mdx';
|
||||
import { docs } from '../../source.generated';
|
||||
@@ -45,12 +46,7 @@ export default function Page(props: Route.ComponentProps) {
|
||||
const Content = renderer[path];
|
||||
|
||||
return (
|
||||
<DocsLayout
|
||||
nav={{
|
||||
title: 'React Router',
|
||||
}}
|
||||
tree={tree as PageTree.Root}
|
||||
>
|
||||
<DocsLayout {...baseOptions()} tree={tree as PageTree.Root}>
|
||||
<Content />
|
||||
</DocsLayout>
|
||||
);
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
import type { BaseLayoutProps } from 'fumadocs-ui/layouts/shared';
|
||||
|
||||
export function baseOptions(): BaseLayoutProps {
|
||||
return {
|
||||
nav: {
|
||||
title: 'React Router',
|
||||
},
|
||||
};
|
||||
}
|
||||
@@ -1,6 +1,7 @@
|
||||
import type { Route } from './+types/home';
|
||||
import { HomeLayout } from 'fumadocs-ui/layouts/home';
|
||||
import { Link } from 'react-router';
|
||||
import { baseOptions } from '@/lib/layout.shared';
|
||||
|
||||
export function meta({}: Route.MetaArgs) {
|
||||
return [
|
||||
@@ -11,11 +12,7 @@ export function meta({}: Route.MetaArgs) {
|
||||
|
||||
export default function Home() {
|
||||
return (
|
||||
<HomeLayout
|
||||
nav={{
|
||||
title: 'React Router',
|
||||
}}
|
||||
>
|
||||
<HomeLayout {...baseOptions()}>
|
||||
<div className="p-4 flex flex-col items-center justify-center text-center flex-1">
|
||||
<h1 className="text-xl font-bold mb-2">Fumadocs on React Router.</h1>
|
||||
<p className="text-fd-muted-foreground mb-4">
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
import type { BaseLayoutProps } from 'fumadocs-ui/layouts/shared';
|
||||
|
||||
export function baseOptions(): BaseLayoutProps {
|
||||
return {
|
||||
nav: {
|
||||
title: 'Tanstack Start',
|
||||
},
|
||||
};
|
||||
}
|
||||
@@ -13,6 +13,7 @@ import {
|
||||
} from 'fumadocs-ui/page';
|
||||
import defaultMdxComponents from 'fumadocs-ui/mdx';
|
||||
import { createClientLoader } from 'fumadocs-mdx/runtime/vite';
|
||||
import { baseOptions } from '~/lib/layout.shared';
|
||||
|
||||
export const Route = createFileRoute('/docs/$')({
|
||||
component: Page,
|
||||
@@ -66,12 +67,7 @@ function Page() {
|
||||
);
|
||||
|
||||
return (
|
||||
<DocsLayout
|
||||
tree={tree}
|
||||
nav={{
|
||||
title: 'Fumadocs Tanstack',
|
||||
}}
|
||||
>
|
||||
<DocsLayout {...baseOptions()} tree={tree}>
|
||||
<Content />
|
||||
</DocsLayout>
|
||||
);
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { createFileRoute, Link } from '@tanstack/react-router';
|
||||
import { HomeLayout } from 'fumadocs-ui/layouts/home';
|
||||
import { baseOptions } from '~/lib/layout.shared';
|
||||
|
||||
export const Route = createFileRoute('/')({
|
||||
component: Home,
|
||||
@@ -7,12 +8,7 @@ export const Route = createFileRoute('/')({
|
||||
|
||||
function Home() {
|
||||
return (
|
||||
<HomeLayout
|
||||
nav={{
|
||||
title: 'Tanstack Start',
|
||||
}}
|
||||
className="text-center py-32 justify-center"
|
||||
>
|
||||
<HomeLayout {...baseOptions()} className="text-center py-32 justify-center">
|
||||
<h1 className="font-medium text-xl mb-4">Fumadocs on Tanstack Start.</h1>
|
||||
<Link
|
||||
to="/docs/$"
|
||||
|
||||
Generated
+64
@@ -776,6 +776,70 @@ importers:
|
||||
specifier: ^5.1.4
|
||||
version: 5.1.4([email protected])([email protected](@types/[email protected])([email protected])([email protected])([email protected])([email protected])([email protected]))
|
||||
|
||||
examples/tanstack-start-i18n:
|
||||
dependencies:
|
||||
'@orama/tokenizers':
|
||||
specifier: ^3.1.11
|
||||
version: 3.1.11
|
||||
'@tanstack/react-router':
|
||||
specifier: ^1.131.17
|
||||
version: 1.131.17([email protected]([email protected]))([email protected])
|
||||
'@tanstack/react-router-devtools':
|
||||
specifier: ^1.131.17
|
||||
version: 1.131.17(@tanstack/[email protected]([email protected]([email protected]))([email protected]))(@tanstack/[email protected])([email protected])([email protected]([email protected]))([email protected])([email protected])([email protected])
|
||||
'@tanstack/react-start':
|
||||
specifier: ^1.131.17
|
||||
version: 1.131.17(@netlify/[email protected])(@tanstack/[email protected]([email protected]([email protected]))([email protected]))(@vitejs/[email protected]([email protected](@types/[email protected])([email protected])([email protected])([email protected])([email protected])([email protected])))([email protected]([email protected]))([email protected])([email protected](@types/[email protected])([email protected])([email protected])([email protected])([email protected])([email protected]))([email protected](@swc/[email protected])([email protected]))
|
||||
fumadocs-core:
|
||||
specifier: workspace:*
|
||||
version: link:../../packages/core
|
||||
fumadocs-mdx:
|
||||
specifier: workspace:*
|
||||
version: link:../../packages/mdx
|
||||
fumadocs-ui:
|
||||
specifier: workspace:*
|
||||
version: link:../../packages/ui
|
||||
lucide-static:
|
||||
specifier: ^0.539.0
|
||||
version: 0.539.0
|
||||
react:
|
||||
specifier: ^19.1.1
|
||||
version: 19.1.1
|
||||
react-dom:
|
||||
specifier: ^19.1.1
|
||||
version: 19.1.1([email protected])
|
||||
tailwind-merge:
|
||||
specifier: ^3.3.1
|
||||
version: 3.3.1
|
||||
vite:
|
||||
specifier: ^7.1.2
|
||||
version: 7.1.2(@types/[email protected])([email protected])([email protected])([email protected])([email protected])([email protected])
|
||||
devDependencies:
|
||||
'@tailwindcss/vite':
|
||||
specifier: ^4.1.12
|
||||
version: 4.1.12([email protected](@types/[email protected])([email protected])([email protected])([email protected])([email protected])([email protected]))
|
||||
'@types/node':
|
||||
specifier: ^24.3.0
|
||||
version: 24.3.0
|
||||
'@types/react':
|
||||
specifier: ^19.1.10
|
||||
version: 19.1.10
|
||||
'@types/react-dom':
|
||||
specifier: ^19.1.7
|
||||
version: 19.1.7(@types/[email protected])
|
||||
'@vitejs/plugin-react':
|
||||
specifier: ^5.0.0
|
||||
version: 5.0.0([email protected](@types/[email protected])([email protected])([email protected])([email protected])([email protected])([email protected]))
|
||||
tailwindcss:
|
||||
specifier: ^4.1.12
|
||||
version: 4.1.12
|
||||
typescript:
|
||||
specifier: ^5.9.2
|
||||
version: 5.9.2
|
||||
vite-tsconfig-paths:
|
||||
specifier: ^5.1.4
|
||||
version: 5.1.4([email protected])([email protected](@types/[email protected])([email protected])([email protected])([email protected])([email protected])([email protected]))
|
||||
|
||||
examples/waku:
|
||||
dependencies:
|
||||
fumadocs-core:
|
||||
|
||||
Reference in New Issue
Block a user