OpenAPI: Support fumadocs-openapi/css/preset.css for Tailwind CSS

This commit is contained in:
Fuma Nama
2025-04-23 15:43:55 +08:00
parent 5590326ca3
commit 540027e8c2
13 changed files with 68 additions and 40 deletions
+15
View File
@@ -0,0 +1,15 @@
---
'fumadocs-openapi': patch
---
**Support `fumadocs-openapi/css/preset.css` for Tailwind CSS**
We highly recommend to use the following instead:
```css
@import 'tailwindcss';
@import 'fumadocs-ui/css/neutral.css';
@import 'fumadocs-ui/css/preset.css';
/* do this */
@import 'fumadocs-openapi/css/preset.css';
```
+1 -1
View File
@@ -2,11 +2,11 @@
@import 'fumadocs-ui/css/neutral.css';
@import 'fumadocs-ui/css/preset.css';
@import 'fumadocs-twoslash/twoslash.css';
@import 'fumadocs-openapi/css/preset.css';
@source ".";
@source "../components";
@source "../content";
@source '../node_modules/fumadocs-openapi/dist/**/*.js';
@plugin 'tailwindcss-animate';
@theme {
@@ -13,18 +13,14 @@ fumadocs-openapi shiki
### Generate Styles
The interactive UI of OpenAPI integration is styled with Tailwind CSS, it doesn't include a pre-built stylesheet.
You must use it with Tailwind CSS configured.
Add the package to `@source` in your Tailwind CSS configuration.
Please note that you must have Tailwind CSS v4 configured.
```css title="Tailwind CSS"
@import 'tailwindcss';
@import 'fumadocs-ui/css/neutral.css';
@import 'fumadocs-ui/css/preset.css';
/* [!code highlight] */
@source '../node_modules/fumadocs-openapi/dist/**/*.js';
@import 'fumadocs-openapi/css/preset.css';
```
### Configure Pages
@@ -1,13 +1,24 @@
import { metadataImage } from '@/lib/metadata';
import { generateOGImage } from 'fumadocs-ui/og';
import { source } from '@/lib/source';
import { notFound } from 'next/navigation';
export async function GET(
_req: Request,
{ params }: { params: Promise<{ slug: string[] }> },
) {
const { slug } = await params;
const page = source.getPage(slug.slice(0, -1));
if (!page) notFound();
export const GET = metadataImage.createAPI((page) => {
return generateOGImage({
title: page.data.title,
description: page.data.description,
});
});
}
export function generateStaticParams() {
return metadataImage.generateParams();
return source.generateParams().map((page) => ({
...page,
slug: [...page.slug, 'image.png'],
}));
}
+13 -6
View File
@@ -1,13 +1,13 @@
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<{ slug?: string[] }>;
@@ -41,11 +41,18 @@ export async function generateMetadata(props: {
}) {
const params = await props.params;
const page = source.getPage(params.slug);
if (!page) notFound();
const image = ['/docs-og', ...(params.slug ?? []), 'image.png'].join('/');
return metadataImage.withImage(page.slugs, {
return {
title: page.data.title,
description: page.data.description,
});
openGraph: {
images: image,
},
twitter: {
card: 'summary_large_image',
images: image,
},
} satisfies Metadata;
}
+1 -2
View File
@@ -1,8 +1,7 @@
@import 'tailwindcss';
@import 'fumadocs-ui/css/neutral.css';
@import 'fumadocs-ui/css/preset.css';
@source '../node_modules/fumadocs-openapi/dist/**/*.js';
@import 'fumadocs-openapi/css/preset.css';
:root {
--fd-layout-width: 1400px;
-7
View File
@@ -1,7 +0,0 @@
import { createMetadataImage } from 'fumadocs-core/server';
import { source } from '@/lib/source';
export const metadataImage = createMetadataImage({
imageRoute: 'docs-og',
source,
});
+1
View File
@@ -1,6 +1,7 @@
export {
highlight,
createStyleTransformer,
getHighlighter,
type HighlightOptions,
type HighlightOptionsCommon,
type HighlightOptionsThemes,
+17 -11
View File
@@ -1,13 +1,13 @@
import {
type BundledLanguage,
type CodeOptionsThemes,
type ShikiTransformer,
type CodeOptionsMeta,
type CodeToHastOptionsCommon,
type RegexEngine,
type Awaitable,
type Highlighter,
type BundledHighlighterOptions,
type BundledLanguage,
type CodeOptionsMeta,
type CodeOptionsThemes,
type CodeToHastOptionsCommon,
type Highlighter,
type RegexEngine,
type ShikiTransformer,
} from 'shiki';
import type { BundledTheme } from 'shiki/themes';
import { type Components, toJsxRuntime } from 'hast-util-to-jsx-runtime';
@@ -86,6 +86,12 @@ export function _renderHighlight(hast: Root, options?: HighlightOptions) {
});
}
/**
* Get Shiki highlighter instance of Fumadocs (mostly for internal use, don't recommend you to use it).
*
* @param engineType - engine type, the engine specified in `options` will only be effective when this is set to `custom`.
* @param options - Shiki options.
*/
export async function getHighlighter(
engineType: 'js' | 'oniguruma' | 'custom',
options: BundledHighlighterOptions<BundledLanguage, BundledTheme>,
@@ -94,18 +100,18 @@ export async function getHighlighter(
let highlighter = highlighters.get(engineType);
if (!highlighter) {
let engine = options.engine;
let engine;
if (engineType === 'js') {
engine = import('shiki/engine/javascript').then((res) =>
res.createJavaScriptRegexEngine(),
);
}
if (engineType === 'oniguruma' || !engine) {
} else if (engineType === 'oniguruma' || !options.engine) {
engine = import('shiki/engine/oniguruma').then((res) =>
res.createOnigurumaEngine(import('shiki/wasm')),
);
} else {
engine = options.engine;
}
highlighter = createHighlighter({
+1
View File
@@ -0,0 +1 @@
@source '../dist/**/*.js';
-2
View File
@@ -1,2 +0,0 @@
@import 'tailwindcss';
@import 'fumadocs-ui/css/style.css';
+1
View File
@@ -16,6 +16,7 @@
"import": "./dist/index.js",
"types": "./dist/index.d.ts"
},
"./css/*": "./css/*",
"./ui": {
"import": "./dist/ui/index.js",
"types": "./dist/ui/index.d.ts"
+1 -1
View File
@@ -599,7 +599,7 @@ function CollapsiblePanel({
<ChevronDown className="ms-auto size-3.5 text-fd-muted-foreground group-data-[state=open]:rotate-180" />
</CollapsibleTrigger>
<CollapsibleContent>
<div className="flex flex-col gap-3 p-3">{children}</div>
<div className="flex flex-col gap-3 p-3 pt-1">{children}</div>
</CollapsibleContent>
</Collapsible>
);