Skip to content

Commit 2621cd9

Browse files
fuma-namamkeskin
andauthored
Sync (#2696)
* Core: Support client-side loader * Fix: handle absolute paths in fetchDocs function (#2692) * Fix: handle absolute paths in fetchDocs function * Core: Support absolute URLs in search fetch client --------- Co-authored-by: Fuma Nama <[email protected]> * Bump deps * UI: Align menu behaviour on notebook layout with home layout * Core: make serialization API lower level * migrate examples * docgen: fix async transform * downgrade tanstack start see TanStack/router#5967 * Bump deps * UI: refactor internals * Docs: experiment mcp server * Docs: introduce OpenAPI loader integration * Docs: introduce client-side loader * fix registry * Chore: try OIDC * CLI: fix recursive build * Version Packages (#2690) --------- Co-authored-by: Mustafa Keskin <[email protected]>
2 parents f4ce093 + 5d4a726 commit 2621cd9

File tree

91 files changed

+3341
-3254
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

91 files changed

+3341
-3254
lines changed

.github/workflows/release.yml

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,9 @@ on:
66
- dev
77

88
concurrency: ${{ github.workflow }}-${{ github.ref }}
9+
permissions:
10+
id-token: write
11+
contents: read
912

1013
jobs:
1114
release:
@@ -35,4 +38,3 @@ jobs:
3538
version: pnpm run version
3639
env:
3740
GITHUB_TOKEN: ${{ secrets.UPDATE_TEMPLATE_SSH_KEY }}
38-
NPM_TOKEN: ${{ secrets.NPM_TOKEN }}

.prettierignore

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,5 +29,5 @@ apps/docs/content/docs/ui/typescript.mdx
2929
packages/mdx-remote/test/fixtures/**/*.js
3030
packages/mdx-remote/test/fixtures/**/*.json
3131
packages/core/test/fixtures/page-trees/**/*.json
32-
build/
32+
3333
routeTree.gen.ts
Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
import { DataSourceId, orama } from '@/lib/orama/client';
2+
import { createMcpHandler } from 'mcp-handler';
3+
import { z } from 'zod';
4+
import { ProvideLinksToolSchema } from '@/lib/chat/inkeep-qa-schema';
5+
import { createOpenAICompatible } from '@ai-sdk/openai-compatible';
6+
import { generateText } from 'ai';
7+
8+
const openai = createOpenAICompatible({
9+
name: 'inkeep',
10+
apiKey: process.env.INKEEP_API_KEY,
11+
baseURL: 'https://api.inkeep.com/v1',
12+
});
13+
14+
const handler = createMcpHandler(
15+
(server) => {
16+
server.registerTool(
17+
'search',
18+
{
19+
title: 'Search Docs',
20+
description: 'Search docs pages with a query',
21+
inputSchema: z.object({
22+
query: z.string('the search query'),
23+
}),
24+
},
25+
async ({ query }) => {
26+
const result = await orama.search({
27+
term: query,
28+
datasources: [DataSourceId],
29+
limit: 50,
30+
});
31+
32+
return {
33+
content: result.hits.map((hit) => ({
34+
type: 'text',
35+
text: JSON.stringify(hit.document),
36+
})),
37+
};
38+
},
39+
);
40+
41+
server.registerTool(
42+
'ask-ai',
43+
{
44+
title: 'Ask AI',
45+
description: 'Ask another specialized AI a question for more info',
46+
inputSchema: z.object({
47+
message: z.string(),
48+
}),
49+
},
50+
async ({ message }) => {
51+
const result = await generateText({
52+
model: openai('inkeep-qa-sonnet-4'),
53+
tools: {
54+
provideLinks: {
55+
inputSchema: ProvideLinksToolSchema,
56+
},
57+
},
58+
messages: [
59+
{
60+
role: 'user',
61+
content: message,
62+
},
63+
],
64+
});
65+
66+
return {
67+
content: [
68+
{
69+
type: 'text',
70+
text: result.text,
71+
},
72+
],
73+
};
74+
},
75+
);
76+
},
77+
{},
78+
{
79+
basePath: '/api',
80+
},
81+
);
82+
83+
export { handler as GET, handler as POST };

apps/docs/app/og/[...slug]/route.tsx

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -15,12 +15,10 @@ export async function GET(
1515
if (!page) notFound();
1616

1717
return new ImageResponse(
18-
(
19-
<MetadataImage
20-
title={page.data.title}
21-
description={page.data.description}
22-
/>
23-
),
18+
<MetadataImage
19+
title={page.data.title}
20+
description={page.data.description}
21+
/>,
2422
await getImageResponseOptions(),
2523
);
2624
}

apps/docs/components/contributor-count.tsx

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,7 @@ import Image from 'next/image';
33
import { cn } from '@/lib/cn';
44
import { fetchContributors } from '@/lib/get-contributors';
55

6-
export interface ContributorCounterProps
7-
extends HTMLAttributes<HTMLDivElement> {
6+
export interface ContributorCounterProps extends HTMLAttributes<HTMLDivElement> {
87
repoOwner: string;
98
repoName: string;
109
displayCount?: number;

apps/docs/content/docs/headless/source-api/index.mdx

Lines changed: 33 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ description: Turn a content source into a unified interface
1111
- Assign URL to each page.
1212
- Output useful utilities to interact with content.
1313

14-
It doesn't rely on the real file system (zero `node:fs` usage), a virtual storage is also allowed.
14+
It is a server-side API, but doesn't rely on the real file system (zero `node:fs` usage), a virtual storage is also allowed.
1515

1616
## Usage
1717

@@ -160,3 +160,35 @@ import { source } from '@/lib/source';
160160
// language -> pages
161161
const entries = source.getLanguages();
162162
```
163+
164+
## Client API
165+
166+
Loader API is best when used in RSC environments, which allows passing JSX nodes across the server-client boundary.
167+
168+
For non-RSC environments, it provides a tiny serialization layer.
169+
170+
```ts tab="Server"
171+
import { source } from '@/lib/source';
172+
173+
// where you pass loader data
174+
async function loader() {
175+
const pageTree = source.getPageTree();
176+
177+
return {
178+
pageTree: await source.serializePageTree(pageTree),
179+
};
180+
}
181+
```
182+
183+
```tsx tab="Client"
184+
import { useFumadocsLoader } from 'fumadocs-core/source/client';
185+
186+
function MyComponent() {
187+
// receives loader data
188+
const data = useLoaderData();
189+
const { pageTree } = useFumadocsLoader(data);
190+
191+
// now render it (e.g. via Fumadocs UI)
192+
return <DocsLayout tree={pageTree}>...</DocsLayout>;
193+
}
194+
```

apps/docs/content/docs/ui/internationalization/next.mdx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -162,7 +162,7 @@ export default async function Layout({
162162

163163
return (
164164
// [!code highlight]
165-
<DocsLayout {...baseOptions(lang)} tree={source.pageTree[lang]}>
165+
<DocsLayout {...baseOptions(lang)} tree={source.getPageTree(lang)}>
166166
{children}
167167
</DocsLayout>
168168
);

apps/docs/content/docs/ui/openapi/index.mdx

Lines changed: 79 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,12 @@ title: OpenAPI
33
description: Generating docs for OpenAPI schema
44
---
55

6-
<Callout type="warning" title="Server Component Only">
7-
It only works under RSC environments.
6+
<Callout type="warning" title="Before Reading">
7+
8+
- Only OpenAPI 3.0 and 3.1 are supported.
9+
- It only works under RSC
10+
environments.
11+
812
</Callout>
913

1014
## Manual Setup
@@ -32,7 +36,7 @@ Add the following line:
3236
@import 'fumadocs-openapi/css/preset.css';
3337
```
3438

35-
### Configure Pages
39+
### Configure Plugin
3640

3741
Create an OpenAPI instance on the server.
3842

@@ -76,6 +80,34 @@ export default defineClientConfig({
7680
});
7781
```
7882

83+
### Generate Pages
84+
85+
<Tabs items={["MDX Files", "Virtual Files"]}>
86+
<Tab>
87+
88+
You can generate MDX files directly from your OpenAPI schema.
89+
90+
Create a script:
91+
92+
```js title="scripts/generate-docs.ts"
93+
import { generateFiles } from 'fumadocs-openapi';
94+
import { openapi } from '@/lib/openapi';
95+
96+
void generateFiles({
97+
input: openapi,
98+
output: './content/docs',
99+
// we recommend to enable it
100+
// make sure your endpoint description doesn't break MDX syntax.
101+
includeDescription: true,
102+
});
103+
```
104+
105+
Generate docs with the script:
106+
107+
```bash
108+
bun ./scripts/generate-docs.ts
109+
```
110+
79111
Add the `APIPage` component to your MDX Components.
80112

81113
```tsx title="mdx-components.tsx"
@@ -93,33 +125,60 @@ export function getMDXComponents(components?: MDXComponents): MDXComponents {
93125
}
94126
```
95127

96-
### Generate Files
128+
</Tab>
129+
<Tab>
97130

98-
You can generate MDX files directly from your OpenAPI schema.
99-
100-
Create a script:
131+
You can also use it without generating real files by integrating into [Loader API](/docs/headless/source-api/source).
101132

102-
```js title="scripts/generate-docs.ts"
103-
import { generateFiles } from 'fumadocs-openapi';
133+
```ts title="lib/source.ts"
134+
import { loader, multiple } from 'fumadocs-core/source';
135+
import { openapiPlugin, openapiSource } from 'fumadocs-openapi/server';
136+
import { docs } from 'fumadocs-mdx:collections/server';
104137
import { openapi } from '@/lib/openapi';
105138

106-
void generateFiles({
107-
input: openapi,
108-
output: './content/docs',
109-
// we recommend to enable it
110-
// make sure your endpoint description doesn't break MDX syntax.
111-
includeDescription: true,
112-
});
139+
export const source = loader(
140+
// [!code ++:6]
141+
multiple({
142+
docs: docs.toFumadocsSource(),
143+
openapi: await openapiSource(openapi, {
144+
baseDir: 'openapi',
145+
}),
146+
}),
147+
{
148+
baseUrl: '/docs',
149+
plugins: [openapiPlugin()],
150+
// ...
151+
},
152+
);
113153
```
114154

115-
> Only OpenAPI 3.0 and 3.1 are supported.
155+
It shares a different type from your original source, explicit handling of OpenAPI pages might be necessary (e.g. in your page component).
116156

117-
Generate docs with the script:
157+
```tsx title="docs/[[...slug]]/page.tsx"
158+
import { APIPage } from '@/components/api-page';
118159

119-
```bash
120-
bun ./scripts/generate-docs.ts
160+
function Page() {
161+
const page = source.getPage('...');
162+
163+
if (page.data.type === 'openapi') {
164+
return (
165+
<DocsPage full>
166+
<h1 className="text-[1.75em] font-semibold">{page.data.title}</h1>
167+
168+
<DocsBody>
169+
<APIPage {...page.data.getAPIPageProps()} />
170+
</DocsBody>
171+
</DocsPage>
172+
);
173+
}
174+
175+
// your original flow below...
176+
}
121177
```
122178

179+
</Tab>
180+
</Tabs>
181+
123182
## Features
124183

125184
The official OpenAPI integration supports:

0 commit comments

Comments
 (0)