Chore: migrate to oxfmt

This commit is contained in:
Fuma Nama
2025-12-30 16:49:34 +08:00
parent c42268c1c6
commit 78e0cf8945
488 changed files with 2527 additions and 6846 deletions
+4 -4
View File
@@ -6,12 +6,12 @@ Before submitting a pull request, there are some guidelines you should notice.
### Guidelines
This project is a monorepo using Turborepo, pnpm and
[Changesets](https://github.com/changesets/changesets).
[Changesets](https://github.com/changesets/changesets).
#### Before Submitting
- Check if there's other similar PRs.
- Format your code with `pnpm run prettier`.
- Format your code with `pnpm run format`.
- Add changesets with `pnpm changeset`, which documents the changes you've made.
- Run unit tests with `pnpm test` and update snapshots if necessary.
@@ -27,7 +27,7 @@ OR open a bug report and link it in your PR.
#### Docs
Contributing to the docs is relatively easier, make sure to check the typos and grammatical mistakes before submitting.
Contributing to the docs is relatively easier, make sure to check the typos and grammatical mistakes before submitting.
### New to contributing?
@@ -37,4 +37,4 @@ it is located in `/apps/docs/content/docs`.
To run the docs site in dev mode,
build the dependencies with `pnpm run build --filter=./packages/*` and run `pnpm run dev --filter=docs` to start the dev server.
You don't need any extra environment variables to run this project.
You don't need any extra environment variables to run this project.
+1 -1
View File
@@ -23,7 +23,7 @@ jobs:
run: pnpm i --frozen-lockfile
- name: Lint Format
run: pnpm lint:prettier
run: pnpm lint:format
- name: Check Types
run: pnpm build --filter=./packages/* && pnpm i --frozen-lockfile && pnpm types:check
+29
View File
@@ -0,0 +1,29 @@
{
"$schema": "./node_modules/oxfmt/configuration_schema.json",
"singleQuote": true,
"ignorePatterns": [
"CHANGELOG.md",
"pnpm-lock.yaml",
".github/**/*.md",
"packages/create-app/versions.json",
"**/test/out/**/*",
"**/test/fixtures/**/*.output.*",
"**/test/fixtures/**/*.out.*",
"out",
".content-collections",
"examples/obsidian/content/docs/vault/**/*",
"examples/obsidian/obsidian/Obsidian Vault",
".content-collections/**/*",
"packages/mdx-remote/test/fixtures/**/*.js",
"packages/mdx-remote/test/fixtures/**/*.json",
"packages/core/test/fixtures/page-trees/**/*.json",
"routeTree.gen.ts",
],
}
-29
View File
@@ -1,29 +0,0 @@
CHANGELOG.md
pnpm-lock.yaml
.github/**/*.md
packages/create-app/versions.json
**/test/out/**/*
**/test/fixtures/**/*.output.*
**/test/fixtures/**/*.out.*
out
.content-collections
apps/docs/content/docs/ui/museum/*.mdx
!apps/docs/content/docs/ui/museum/index.mdx
examples/openapi/content/docs/(api)/**/*.mdx
!examples/openapi/content/docs/(api)/index.mdx
examples/obsidian/content/docs/vault/**/*
examples/obsidian/obsidian/Obsidian Vault
.content-collections/**/*
packages/mdx-remote/test/fixtures/**/*.js
packages/mdx-remote/test/fixtures/**/*.json
packages/core/test/fixtures/page-trees/**/*.json
routeTree.gen.ts
+1 -2
View File
@@ -11,6 +11,5 @@
"files.associations": {
"**/packages/create-app/template/**/*": "plaintext"
},
"oxc.enable": false,
"editor.defaultFormatter": "esbenp.prettier-vscode"
"editor.defaultFormatter": "oxc.oxc-vscode"
}
@@ -10,11 +10,7 @@ export function ShareButton({ url }: { url: string }) {
});
return (
<button
type="button"
className={cn(buttonVariants({ className: 'gap-2' }))}
onClick={onCopy}
>
<button type="button" className={cn(buttonVariants({ className: 'gap-2' }))} onClick={onCopy}>
{isChecked ? <Check className="size-4" /> : <Share className="size-4" />}
{isChecked ? 'Copied URL' : 'Share Post'}
</button>
+3 -7
View File
@@ -28,8 +28,7 @@ export default async function Page(props: PageProps<'/blog/[slug]'>) {
<p className="mb-1 text-sm text-fd-muted-foreground">At</p>
<p className="font-medium">
{new Date(
page.data.date ??
path.basename(page.path, path.extname(page.path)),
page.data.date ?? path.basename(page.path, path.extname(page.path)),
).toDateString()}
</p>
</div>
@@ -61,9 +60,7 @@ export default async function Page(props: PageProps<'/blog/[slug]'>) {
);
}
export async function generateMetadata(
props: PageProps<'/blog/[slug]'>,
): Promise<Metadata> {
export async function generateMetadata(props: PageProps<'/blog/[slug]'>): Promise<Metadata> {
const params = await props.params;
const page = blog.getPage([params.slug]);
@@ -71,8 +68,7 @@ export async function generateMetadata(
return createMetadata({
title: page.data.title,
description:
page.data.description ?? 'The library for building documentation sites',
description: page.data.description ?? 'The library for building documentation sites',
});
}
+1 -3
View File
@@ -39,9 +39,7 @@ export default function Page() {
className="flex flex-col bg-fd-card rounded-2xl border shadow-sm p-4 transition-colors hover:bg-fd-accent hover:text-fd-accent-foreground"
>
<p className="font-medium">{post.data.title}</p>
<p className="text-sm text-fd-muted-foreground">
{post.data.description}
</p>
<p className="text-sm text-fd-muted-foreground">{post.data.description}</p>
<p className="mt-auto pt-4 text-xs text-brand">
{new Date(post.data.date ?? getName(post.path)).toDateString()}
-69
View File
@@ -1,69 +0,0 @@
import { Building2, Library, Pencil } from 'lucide-react';
import Link, { type LinkProps } from 'next/link';
export default function DocsPage() {
return (
<main className="w-full max-w-page mx-auto flex flex-col flex-1 justify-center items-center px-4 py-16 text-center z-2">
<h1 className="mb-4 text-3xl font-semibold md:text-4xl">
Getting Started
</h1>
<p className="text-fd-muted-foreground">
Portal to different sections of docs.
</p>
<div className="mt-8 grid grid-cols-1 gap-4 text-start md:grid-cols-2">
{[
{
name: 'Fumadocs',
description:
'The full-powered documentation framework with an excellent UI.',
icon: <Building2 className="size-full" />,
href: '/docs/ui',
},
{
name: 'Fumadocs Core',
description: 'The core library of Fumadocs.',
icon: <Library className="size-full" />,
href: '/docs/headless',
},
{
name: 'Fumadocs MDX',
description:
'The library for handling MDX in your React.js framework.',
icon: <Pencil className="size-full" />,
href: '/docs/mdx',
},
{
name: 'Fumadocs CLI',
description: 'The CLI tool for Fumadocs.',
icon: <Pencil className="size-full" />,
href: '/docs/cli',
},
].map((item) => (
<Item key={item.name} href={item.href}>
<Icon>{item.icon}</Icon>
<h2 className="mb-2 font-medium">{item.name}</h2>
<p className="text-sm text-fd-muted-foreground">
{item.description}
</p>
</Item>
))}
</div>
</main>
);
}
function Icon({ children }: { children: React.ReactNode }) {
return (
<div className="mb-2 size-8 rounded-lg border p-1 text-fd-muted-foreground bg-fd-muted shadow-md">
{children}
</div>
);
}
function Item(props: LinkProps & { children: React.ReactNode }) {
return (
<Link {...props} className="bg-fd-card rounded-2xl border p-4 shadow-lg">
{props.children}
</Link>
);
}
+4 -20
View File
@@ -36,13 +36,7 @@ export function NextSVG(props: SVGProps<SVGSVGElement>): React.ReactElement {
d="M149.508 157.52L69.142 54H54V125.97H66.1136V69.3836L139.999 164.845C143.333 162.614 146.509 160.165 149.508 157.52Z"
fill="url(#:R0:paint0_linear_408_134)"
/>
<rect
fill="url(#:R0:paint1_linear_408_134)"
height="72"
width="12"
x="115"
y="54"
/>
<rect fill="url(#:R0:paint1_linear_408_134)" height="72" width="12" x="115" y="54" />
</g>
<defs>
<linearGradient
@@ -90,17 +84,9 @@ export function VercelLogo(props: SVGProps<SVGSVGElement>): React.ReactElement {
);
}
export function NetlifyLogo(
props: SVGProps<SVGSVGElement>,
): React.ReactElement {
export function NetlifyLogo(props: SVGProps<SVGSVGElement>): React.ReactElement {
return (
<svg
aria-label="Netlify"
width="512"
height="209"
viewBox="0 0 512 209"
{...props}
>
<svg aria-label="Netlify" width="512" height="209" viewBox="0 0 512 209" {...props}>
<g>
<path
d="M117.436 207.036V154.604L118.529 153.51H129.452L130.545 154.604V207.036L129.452 208.13H118.529L117.436 207.036Z"
@@ -159,9 +145,7 @@ export function NetlifyLogo(
);
}
export function OpenAPIIcon(
props: SVGProps<SVGSVGElement>,
): React.ReactElement {
export function OpenAPIIcon(props: SVGProps<SVGSVGElement>): React.ReactElement {
return (
<svg fill="currentColor" viewBox="0 0 512 512" {...props}>
<path d="m.2041784 294.3757629 135.0729218-.0019836c1.0510101 10.3673706 3.8841248 20.5690308 8.4522095 30.170166l-115.790062 69.7536621c-15.1902405-26.1945495-24.7731477-59.514801-27.7350693-99.9218445zm131.8023066 196.8029786 51.4067841-124.8407288c-5.2182617-2.7826233-10.2264862-6.0957642-14.9382172-9.9553528l-95.4706802 95.4720459c19.4898223 17.5570679 39.5628662 30.7094116 59.0021133 39.3240357zm-65.6047211-45.6471253 95.2843628-95.2860413c-4.9330902-4.9628906-9.4824219-10.8501282-13.6674042-17.6151123l-115.5066642 69.5827027c10.4910965 17.068634 22.1482162 31.8469848 33.8897056 43.3184509zm313.9973145 6.3275757-95.5295105-95.5253601c-1.0485229.8474121-2.1081848 1.671936-3.184845 2.4666138l69.7098999 115.7191772c9.7306824-6.0542297 19.3898926-13.8226013 29.0044556-22.6604309zm-36.7403565 27.5314331-69.5648804-115.4782104c-25.9638519 14.4993591-53.3182526 17.5528564-82.4063263 6.3365173l-51.3009338 124.5834045c70.200531 28.4663086 142.9616852 20.3123169 203.2721405-15.4417114zm-171.9461059-267.7199554-69.7079392-115.7203217c-10.1640396 6.5237503-19.8186493 14.1064529-29.0061646 22.6601715l95.5275803 95.5278625c1.0482636-.8363342 2.1095887-1.6597595 3.1865235-2.4677123zm-171.712616 73.5650482 134.7621918-.0019531c.291153-23.8374939 8.7095032-45.6818237 26.9275513-65.005722l-95.2863083-95.2865982c-43.9871293 46.6117019-65.8919295 100.1147994-66.4034348 160.2942733zm226.6937714-91.9374695.0058594-134.7585754c-44.7272644.2738724-84.0045013 12.32621-116.959053 32.5391159l69.5626297 115.4798889c13.0764617-7.6767197 28.6641692-12.980278 47.3905639-13.2604294zm186.4042816-37.1177979-99.4490967 99.4510498c2.2626953 6.6661987 3.7337646 13.5315552 4.4528503 20.4605103h135.0907898c-.8839111-40.9807434-14.4047241-80.9386597-40.0945434-119.9115601zm40.2803039 129.0516358h-134.747406c-.4680481 25.240448-9.8990479 48.2441101-26.923645 65.0134888l95.2901917 95.2860413c43.8191833-43.1222535 65.1218261-96.9472352 66.3808593-160.2995301zm-217.5386657-226.4921341-.0058594 135.0865784c7.082489.8026276 13.8835602 2.3424988 20.4658203 4.4556732l99.4153442-99.4150772c-35.131958-24.5753861-76.5397338-38.3618508-119.8753051-40.1271744zm195.1100769-56.1426528c-39.1646423 11.456749-55.5329285 55.1829491-38.7815857 88.6712189l-137.3599701 137.3618682c-32.4068756-16.1573944-74.9024811-1.5699463-87.7949829 36.0340729-15.7162628 45.8401489 24.2427673 91.8019104 71.7535858 82.5325317 42.0110779-8.1963196 62.3093567-54.1882019 44.4657593-90.0109253l137.4894714-137.4859314c34.6590576 17.2256775 79.5329285-1.0651627 89.3959961-41.653656 11.4301758-47.0377617-32.6579895-89.0548125-79.1682739-75.449179z" />
+5 -16
View File
@@ -49,8 +49,7 @@ export default function Layout({ children }: LayoutProps<'/'>) {
alt="Perview"
className="rounded-t-lg object-cover"
style={{
maskImage:
'linear-gradient(to bottom,white 60%,transparent)',
maskImage: 'linear-gradient(to bottom,white 60%,transparent)',
}}
/>
</div>
@@ -60,10 +59,7 @@ export default function Layout({ children }: LayoutProps<'/'>) {
</p>
</NavbarMenuLink>
<NavbarMenuLink
href="/docs/ui/components"
className="lg:col-start-2"
>
<NavbarMenuLink href="/docs/ui/components" className="lg:col-start-2">
<ComponentIcon className="bg-fd-primary text-fd-primary-foreground p-1 mb-2 rounded-md" />
<p className="font-medium">Components</p>
<p className="text-fd-muted-foreground text-sm">
@@ -71,22 +67,15 @@ export default function Layout({ children }: LayoutProps<'/'>) {
</p>
</NavbarMenuLink>
<NavbarMenuLink
href="/docs/ui/openapi"
className="lg:col-start-2"
>
<NavbarMenuLink href="/docs/ui/openapi" className="lg:col-start-2">
<Server className="bg-fd-primary text-fd-primary-foreground p-1 mb-2 rounded-md" />
<p className="font-medium">OpenAPI</p>
<p className="text-fd-muted-foreground text-sm">
Generate interactive playgrounds and docs for your OpenAPI
schema.
Generate interactive playgrounds and docs for your OpenAPI schema.
</p>
</NavbarMenuLink>
<NavbarMenuLink
href="/docs/ui/markdown"
className="lg:col-start-3 lg:row-start-1"
>
<NavbarMenuLink href="/docs/ui/markdown" className="lg:col-start-3 lg:row-start-1">
<Pencil className="bg-fd-primary text-fd-primary-foreground p-1 mb-2 rounded-md" />
<p className="font-medium">Markdown</p>
<p className="text-fd-muted-foreground text-sm">
+1 -3
View File
@@ -57,9 +57,7 @@ export function Marquee({
key={i}
className={cn(
'flex shrink-0 justify-around [gap:var(--gap)]',
vertical
? 'animate-marquee-vertical flex-col'
: 'animate-marquee flex-row',
vertical ? 'animate-marquee-vertical flex-col' : 'animate-marquee flex-row',
pauseOnHover && 'group-hover:[animation-play-state:paused]',
reverse && '[animation-direction:reverse]',
)}
+14 -28
View File
@@ -127,9 +127,7 @@ export function CreateAppAnimation() {
lines.push(
<span key="command_type">
{installCmd.substring(0, tick)}
{tick < timeCommandEnter && (
<div className="inline-block h-3 w-1 animate-pulse bg-white" />
)}
{tick < timeCommandEnter && <div className="inline-block h-3 w-1 animate-pulse bg-white" />}
</span>,
);
@@ -176,8 +174,7 @@ export function CreateAppAnimation() {
)}
<pre className="overflow-hidden rounded-xl border text-sm shadow-lg bg-fd-card">
<div className="flex flex-row items-center gap-2 border-b px-4 py-2">
<TerminalIcon className="size-4" />{' '}
<span className="font-bold">Terminal</span>
<TerminalIcon className="size-4" /> <span className="font-bold">Terminal</span>
<div className="grow" />
<div className="size-2 rounded-full bg-red-400" />
</div>
@@ -206,17 +203,14 @@ function LaunchAppWindow(props: HTMLAttributes<HTMLDivElement>) {
);
}
const previewButtonVariants = cva(
'w-20 h-8 text-sm font-medium transition-colors rounded-full',
{
variants: {
active: {
true: 'text-fd-primary-foreground',
false: 'text-fd-muted-foreground',
},
const previewButtonVariants = cva('w-20 h-8 text-sm font-medium transition-colors rounded-full', {
variants: {
active: {
true: 'text-fd-primary-foreground',
false: 'text-fd-muted-foreground',
},
},
);
});
export function PreviewImages(props: ComponentProps<'div'>) {
const [active, setActive] = useState(0);
const previews = [
@@ -261,9 +255,7 @@ export function PreviewImages(props: ComponentProps<'div'>) {
alt="preview"
className={cn(
'col-start-1 row-start-1 select-none',
active === i
? 'animate-in fade-in slide-in-from-bottom-12 duration-800'
: 'invisible',
active === i ? 'animate-in fade-in slide-in-from-bottom-12 duration-800' : 'invisible',
)}
/>
))}
@@ -291,8 +283,7 @@ export function Writing({
}: {
tabs: Record<(typeof WritingTabs)[number]['value'], ReactNode>;
}) {
const [tab, setTab] =
useState<(typeof WritingTabs)[number]['value']>('writer');
const [tab, setTab] = useState<(typeof WritingTabs)[number]['value']>('writer');
return (
<div className="col-span-full my-20">
@@ -300,8 +291,8 @@ export function Writing({
Anybody can write.
</h2>
<p className="text-center mb-8 mx-auto w-full max-w-[800px]">
Native support for Markdown & MDX, offering intuitive, convenient and
extensive syntax for non-dev writers, developers, and AI agents.
Native support for Markdown & MDX, offering intuitive, convenient and extensive syntax for
non-dev writers, developers, and AI agents.
</p>
<div className="flex justify-center items-center gap-4 text-fd-muted-foreground mb-6">
{WritingTabs.map((item) => (
@@ -355,9 +346,7 @@ export function AgnosticBackground() {
);
}
export function ContentAdoptionBackground(
props: ComponentProps<typeof GrainGradient>,
) {
export function ContentAdoptionBackground(props: ComponentProps<typeof GrainGradient>) {
const { resolvedTheme } = useTheme();
return (
@@ -376,10 +365,7 @@ export function ContentAdoptionBackground(
}
let observer: IntersectionObserver;
const observerTargets = new WeakMap<
Element,
(entry: IntersectionObserverEntry) => void
>();
const observerTargets = new WeakMap<Element, (entry: IntersectionObserverEntry) => void>();
function useIsVisible(ref: RefObject<HTMLElement | null>) {
const [visible, setVisible] = useState(false);
File diff suppressed because one or more lines are too long
+5 -15
View File
@@ -302,12 +302,9 @@ export default function Showcase() {
return (
<main className="px-4 py-12 z-2 w-full max-w-[1400px] mx-auto **:border-neutral-400 dark:**:border-neutral-700">
<div className="relative overflow-hidden border border-dashed p-6">
<h1 className="mb-4 text-xl font-medium">
The docs framework designed with care.
</h1>
<h1 className="mb-4 text-xl font-medium">The docs framework designed with care.</h1>
<p className="text-fd-muted-foreground">
A list of beautiful open-source projects with their docs powered by
Fumadocs.
A list of beautiful open-source projects with their docs powered by Fumadocs.
</p>
<div className="mt-6">
<a
@@ -343,10 +340,7 @@ export default function Showcase() {
viewBox="0 0 74 64"
className="size-6 mt-1"
>
<path
d="M37.5896 0.25L74.5396 64.25H0.639648L37.5896 0.25Z"
fill="currentColor"
/>
<path d="M37.5896 0.25L74.5396 64.25H0.639648L37.5896 0.25Z" fill="currentColor" />
</svg>
<div>
<h2 className="text-sm font-medium mb-2">
@@ -389,9 +383,7 @@ export default function Showcase() {
</Link>
</div>
</div>
<h2 className="text-xl font-medium mt-12 px-4">
Fumadocs can power your blog, too.
</h2>
<h2 className="text-xl font-medium mt-12 px-4">Fumadocs can power your blog, too.</h2>
<div className="mt-6 grid gap-2.5 grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4">
{blogs.map((showcase) => (
<ShowcaseItem key={showcase.url} {...showcase} />
@@ -431,9 +423,7 @@ function ShowcaseItem({ name, url, image }: ShowcaseObject) {
rel="noreferrer noopener"
className="flex aspect-[1.91/1] flex-col border border-dashed p-4 transition-all hover:bg-fd-accent"
>
<p className="font-mono text-xs mb-2 text-fd-muted-foreground">
{new URL(url).hostname}
</p>
<p className="font-mono text-xs mb-2 text-fd-muted-foreground">{new URL(url).hostname}</p>
<p className="text-xl font-medium">{name}</p>
</a>
);
+1 -5
View File
@@ -47,11 +47,7 @@ export const organizationAsUserSponsors = [
asUser: 'marclave',
logo: (
<>
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 593 593"
className="size-9.5"
>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 593 593" className="size-9.5">
<path
fill="currentColor"
fillRule="evenodd"
+9 -43
View File
@@ -30,9 +30,7 @@ export default async function Page() {
const result = await getSponsors(owner);
const sponsors = result.map((v) => {
const entity = organizationAsUserSponsors.find(
(entity) => entity.asUser === v.login,
);
const entity = organizationAsUserSponsors.find((entity) => entity.asUser === v.login);
if (entity) {
return {
login: entity.github,
@@ -62,8 +60,7 @@ export default async function Page() {
/>
<h1 className="text-4xl font-semibold">Support Fumadocs</h1>
<p className="mt-4 text-sm">
Your sponsorship means a lot to open-source projects, including
Fumadocs.
Your sponsorship means a lot to open-source projects, including Fumadocs.
</p>
<a
href="https://github.com/sponsors/fuma-nama"
@@ -77,19 +74,9 @@ export default async function Page() {
>
Sponsor
</a>
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 600 200"
className="max-w-[600px]"
>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 600 200" className="max-w-[600px]">
<defs>
<linearGradient
id="circuit-1-fill"
x1="0"
y1="0"
x2="0.939104"
y2="0.786487"
>
<linearGradient id="circuit-1-fill" x1="0" y1="0" x2="0.939104" y2="0.786487">
<stop offset="0%" stopColor="rgb(50,50,60)" />
<stop offset="100%" stopColor="rgb(30,30,38)" />
</linearGradient>
@@ -158,13 +145,7 @@ export default async function Page() {
animationDelay: `${(Math.pow(2, i) * 360).toString()}ms`,
}}
/>
<rect
x="360"
y={120 + i * 20}
width="8"
height="5"
fill="rgb(100,100,120)"
/>
<rect x="360" y={120 + i * 20} width="8" height="5" fill="rgb(100,100,120)" />
</Fragment>
))}
@@ -177,13 +158,7 @@ export default async function Page() {
filter: 'drop-shadow(2px 0px 8px rgb(255, 100, 255))',
}}
/>
<text
dx="256"
dy="147.793041"
fontSize="18"
fontWeight="400"
fill="rgba(70,70,86,0.9)"
>
<text dx="256" dy="147.793041" fontSize="18" fontWeight="400" fill="rgba(70,70,86,0.9)">
Fumadocs
</text>
</svg>
@@ -192,9 +167,7 @@ export default async function Page() {
{sponsors
.filter((sponsor) => sponsor.__typename === 'Organization')
.map((sponsor) => {
const tier = tiers.find(
(tier) => sponsor.tier.monthlyPriceInDollars >= tier.min,
);
const tier = tiers.find((tier) => sponsor.tier.monthlyPriceInDollars >= tier.min);
return (
<a
@@ -219,21 +192,14 @@ export default async function Page() {
</>
)}
</div>
{tier && (
<p className={cn('text-xs', tier.color)}>{tier.name}</p>
)}
{tier && <p className={cn('text-xs', tier.color)}>{tier.name}</p>}
</a>
);
})}
</div>
<h2 className="mt-12 font-mono text-xs mb-7">Hosting Sponsor</h2>
<a href="https://vercel.com" rel="noreferrer noopener">
<svg
aria-label="Vercel logotype"
role="img"
viewBox="0 0 283 64"
className="w-32"
>
<svg aria-label="Vercel logotype" role="img" viewBox="0 0 283 64" className="w-32">
<path
d="M141.68 16.25c-11.04 0-19 7.2-19 18s8.96 18 20 18c6.67 0 12.55-2.64 16.19-7.09l-7.65-4.42c-2.02 2.21-5.09 3.5-8.54 3.5-4.79 0-8.86-2.5-10.37-6.5h28.02c.22-1.12.35-2.28.35-3.5 0-10.79-7.96-17.99-19-17.99zm-9.46 14.5c1.25-3.99 4.67-6.5 9.45-6.5 4.79 0 8.21 2.51 9.45 6.5h-18.9zm117.14-14.5c-11.04 0-19 7.2-19 18s8.96 18 20 18c6.67 0 12.55-2.64 16.19-7.09l-7.65-4.42c-2.02 2.21-5.09 3.5-8.54 3.5-4.79 0-8.86-2.5-10.37-6.5h28.02c.22-1.12.35-2.28.35-3.5 0-10.79-7.96-17.99-19-17.99zm-9.45 14.5c1.25-3.99 4.67-6.5 9.45-6.5 4.79 0 8.21 2.51 9.45 6.5h-18.9zm-39.03 3.5c0 6 3.92 10 10 10 4.12 0 7.21-1.87 8.8-4.92l7.68 4.43c-3.18 5.3-9.14 8.49-16.48 8.49-11.05 0-19-7.2-19-18s7.96-18 19-18c7.34 0 13.29 3.19 16.48 8.49l-7.68 4.43c-1.59-3.05-4.68-4.92-8.8-4.92-6.07 0-10 4-10 10zm82.48-29v46h-9v-46h9zM37.59.25l36.95 64H.64l36.95-64zm92.38 5l-27.71 48-27.71-48h10.39l17.32 30 17.32-30h10.39zm58.91 12v9.69c-1-.29-2.06-.49-3.2-.49-5.81 0-10 4-10 10v14.8h-9v-34h9v9.2c0-5.08 5.91-9.2 13.2-9.2z"
fill="currentColor"
+4 -13
View File
@@ -7,26 +7,17 @@ import Logo from '@/public/logo.png';
export function UwuHero() {
return (
<div className="z-2 hidden flex-col items-center pb-8 text-center bg-fd-background border-x border-t [.uwu_&]:flex">
<Image
alt="logo"
src={Logo}
className="mb-6 w-full max-w-[400px] px-4"
priority
/>
<Image alt="logo" src={Logo} className="mb-6 w-full max-w-[400px] px-4" priority />
<p className="mb-6 h-fit p-2 text-lg text-fd-muted-foreground md:max-w-[80%] md:text-xl">
Fumadocs is the framework for building documentation with{' '}
<b className="font-medium text-fd-foreground">
anime and fuwa fuwa power
</b>
. Using the power of weebs and waifus.
<b className="font-medium text-fd-foreground">anime and fuwa fuwa power</b>. Using the power
of weebs and waifus.
</p>
<div className="inline-flex items-center gap-3">
<Link
href="/docs"
className={cn(
buttonVariants({ size: 'lg', className: 'rounded-full' }),
)}
className={cn(buttonVariants({ size: 'lg', className: 'rounded-full' }))}
>
Getting Started
</Link>
@@ -10,11 +10,7 @@ import { Wrapper } from '@/components/preview/wrapper';
import { Mermaid } from '@/components/mdx/mermaid';
import { Feedback } from '@/components/feedback';
import { onRateAction, owner, repo } from '@/lib/github';
import {
HoverCard,
HoverCardContent,
HoverCardTrigger,
} from '@/components/ui/hover-card';
import { HoverCard, HoverCardContent, HoverCardTrigger } from '@/components/ui/hover-card';
import Link from 'fumadocs-core/link';
import { getPageTreePeers } from 'fumadocs-core/page-tree';
import { Card, Cards } from 'fumadocs-ui/components/card';
@@ -23,13 +19,9 @@ import { LLMCopyButton, ViewOptions } from '@/components/ai/page-actions';
import { Banner } from 'fumadocs-ui/components/banner';
import { Installation } from '@/components/preview/installation';
import { Customisation } from '@/components/preview/customisation';
import {
DocsBody,
DocsPage,
PageLastUpdate,
} from 'fumadocs-ui/layouts/docs/page';
import { DocsBody, DocsPage, PageLastUpdate } from 'fumadocs-ui/layouts/docs/page';
import { NotFound } from '@/components/not-found';
import { getSuggestions } from '@/app/docs/[...slug]/suggestions';
import { getSuggestions } from './suggestions';
import { PathUtils } from 'fumadocs-core/source';
function PreviewRenderer({ preview }: { preview: string }): ReactNode {
@@ -43,13 +35,15 @@ function PreviewRenderer({ preview }: { preview: string }): ReactNode {
export const revalidate = false;
export default async function Page(props: PageProps<'/docs/[...slug]'>) {
export default async function Page(props: PageProps<'/docs/[[...slug]]'>) {
const params = await props.params;
const page = source.getPage(params.slug);
if (!page)
return (
<NotFound getSuggestions={() => getSuggestions(params.slug.join(' '))} />
<NotFound
getSuggestions={async () => (params.slug ? getSuggestions(params.slug.join(' ')) : [])}
/>
);
if (page.data.type === 'openapi') {
@@ -75,9 +69,7 @@ export default async function Page(props: PageProps<'/docs/[...slug]'>) {
}}
>
<h1 className="text-[1.75em] font-semibold">{page.data.title}</h1>
<p className="text-lg text-fd-muted-foreground mb-2">
{page.data.description}
</p>
<p className="text-lg text-fd-muted-foreground mb-2">{page.data.description}</p>
<div className="flex flex-row flex-wrap gap-2 items-center border-b pb-6">
<LLMCopyButton markdownUrl={`${page.url}.mdx`} />
<ViewOptions
@@ -100,20 +92,14 @@ export default async function Page(props: PageProps<'/docs/[...slug]'>) {
return (
<HoverCard>
<HoverCardTrigger
href={
found.hash
? `${found.page.url}#${found.hash}`
: found.page.url
}
href={found.hash ? `${found.page.url}#${found.hash}` : found.page.url}
{...props}
>
{props.children}
</HoverCardTrigger>
<HoverCardContent className="text-sm">
<p className="font-medium">{found.page.data.title}</p>
<p className="text-fd-muted-foreground">
{found.page.data.description}
</p>
<p className="text-fd-muted-foreground">{found.page.data.description}</p>
</HoverCardContent>
</HoverCard>
);
@@ -150,9 +136,7 @@ function DocsCategory({ url }: { url: string }) {
);
}
export async function generateMetadata(
props: PageProps<'/docs/[...slug]'>,
): Promise<Metadata> {
export async function generateMetadata(props: PageProps<'/docs/[[...slug]]'>): Promise<Metadata> {
const { slug = [] } = await props.params;
const page = source.getPage(slug);
if (!page)
@@ -160,8 +144,7 @@ export async function generateMetadata(
title: 'Not Found',
});
const description =
page.data.description ?? 'The library for building documentation sites';
const description = page.data.description ?? 'The library for building documentation sites';
const image = {
url: getPageImage(page).url,
+4 -10
View File
@@ -1,12 +1,9 @@
import { DocsLayout } from 'fumadocs-ui/layouts/docs';
import { baseOptions, linkItems, logo } from '@/lib/layout.shared';
import { source } from '@/lib/source';
import {
AISearch,
AISearchPanel,
AISearchTrigger,
} from '@/components/ai/search';
import { AISearch, AISearchPanel, AISearchTrigger } from '@/components/ai/search';
import 'katex/dist/katex.min.css';
import { getSection } from '@/lib/source/navigation';
export default function Layout({ children }: LayoutProps<'/docs'>) {
const base = baseOptions();
@@ -22,9 +19,7 @@ export default function Layout({ children }: LayoutProps<'/docs'>) {
title: (
<>
{logo}
<span className="font-medium in-[.uwu]:hidden max-md:hidden">
Fumadocs
</span>
<span className="font-medium in-[.uwu]:hidden max-md:hidden">Fumadocs</span>
</>
),
}}
@@ -33,8 +28,7 @@ export default function Layout({ children }: LayoutProps<'/docs'>) {
transform(option, node) {
const meta = source.getNodeMeta(node);
if (!meta || !node.icon) return option;
const color = `var(--${meta.path.split('/')[0]}-color, var(--color-fd-foreground))`;
const color = `var(--${getSection(meta.path)}-color, var(--color-fd-foreground))`;
return {
...option,
+9 -6
View File
@@ -64,10 +64,7 @@
--animate-stroke: stroke 5s linear infinite;
--color-gradient-radial: radial-gradient(circle, var(--tw-gradient-stops));
--color-repeat-gradient-to-r: repeating-linear-gradient(
to right,
var(--tw-gradient-stops)
);
--color-repeat-gradient-to-r: repeating-linear-gradient(to right, var(--tw-gradient-stops));
--color-repeat-gradient-to-br: repeating-linear-gradient(
to bottom right,
var(--tw-gradient-stops)
@@ -82,9 +79,14 @@
--color-fd-primary: var(--headless-color);
}
.framework {
--color-fd-primary: var(--framework-color);
}
:root {
--headless-color: hsl(250, 80%, 54%);
--ui-color: hsl(41, 100%, 40%);
--framework-color: hsl(41, 100%, 40%);
--ui-color: hsl(217, 100%, 58%);
}
html {
@@ -101,7 +103,8 @@ body {
.dark {
--headless-color: hsl(250 100% 80%);
--ui-color: #fff383;
--framework-color: #fff383;
--ui-color: #a9ceff;
}
@keyframes circuit_1 {
+6 -16
View File
@@ -3,24 +3,17 @@
import { useParams } from 'next/navigation';
import { type ReactNode, useId } from 'react';
import { cn } from '@/lib/cn';
import { getSection } from '@/lib/source/navigation';
export function Body({
children,
}: {
children: ReactNode;
}): React.ReactElement {
export function Body({ children }: { children: ReactNode }): React.ReactElement {
const mode = useMode();
return (
<body className={cn(mode, 'relative flex min-h-screen flex-col')}>
{children}
</body>
);
return <body className={cn(mode, 'relative flex min-h-screen flex-col')}>{children}</body>;
}
export function useMode(): string | undefined {
const { slug } = useParams();
return Array.isArray(slug) && slug.length > 0 ? slug[0] : undefined;
const { slug = [] } = useParams();
if (Array.isArray(slug)) return getSection(slug[0]);
}
export function FumadocsIcon(props: React.SVGProps<SVGSVGElement>) {
@@ -36,10 +29,7 @@ export function FumadocsIcon(props: React.SVGProps<SVGSVGElement>) {
strokeWidth="1"
/>
<defs>
<linearGradient
id={`${id}-iconGradient`}
gradientTransform="rotate(45)"
>
<linearGradient id={`${id}-iconGradient`} gradientTransform="rotate(45)">
<stop offset="45%" stopColor="var(--color-fd-background)" />
<stop offset="100%" stopColor="var(--color-fd-primary)" />
</linearGradient>
+1 -5
View File
@@ -37,11 +37,7 @@ export const viewport: Viewport = {
export default function RootLayout({ children }: { children: ReactNode }) {
return (
<html
lang="en"
className={`${geist.variable} ${mono.variable}`}
suppressHydrationWarning
>
<html lang="en" className={`${geist.variable} ${mono.variable}`} suppressHydrationWarning>
<Body>
<NextProvider>
<TreeContextProvider tree={source.pageTree}>
+1 -4
View File
@@ -5,10 +5,7 @@ import { notFound } from 'next/navigation';
export const revalidate = false;
export async function GET(
_req: NextRequest,
{ params }: RouteContext<'/llms.mdx/[...slug]'>,
) {
export async function GET(_req: NextRequest, { params }: RouteContext<'/llms.mdx/[...slug]'>) {
const slug = (await params).slug;
const page = source.getPage(slug);
if (!page) notFound();
+2 -13
View File
@@ -31,22 +31,11 @@ export function generate({ title, description }: GenerateProps) {
const siteName = 'Fumadocs';
const primaryTextColor = 'rgb(240,240,240)';
const logo = (
<svg
width="60"
height="60"
viewBox="0 0 180 180"
filter="url(#logo-shadow)"
>
<svg width="60" height="60" viewBox="0 0 180 180" filter="url(#logo-shadow)">
<circle cx="90" cy="90" r="86" fill="url(#logo-iconGradient)" />
<defs>
<filter id="logo-shadow" colorInterpolationFilters="sRGB">
<feDropShadow
dx="0"
dy="0"
stdDeviation="4"
floodColor="white"
floodOpacity="1"
/>
<feDropShadow dx="0" dy="0" stdDeviation="4" floodColor="white" floodOpacity="1" />
</filter>
<linearGradient id="logo-iconGradient" gradientTransform="rotate(45)">
<stop offset="45%" stopColor="black" />
+2 -8
View File
@@ -6,19 +6,13 @@ import { getPageImage } from '@/lib/metadata';
export const revalidate = false;
export async function GET(
_req: Request,
{ params }: RouteContext<'/og/[...slug]'>,
) {
export async function GET(_req: Request, { params }: RouteContext<'/og/[...slug]'>) {
const { slug } = await params;
const page = source.getPage(slug.slice(0, -1));
if (!page) notFound();
return new ImageResponse(
<MetadataImage
title={page.data.title}
description={page.data.description}
/>,
<MetadataImage title={page.data.title} description={page.data.description} />,
await getImageResponseOptions(),
);
}
+1 -4
View File
@@ -32,10 +32,7 @@ export function Provider({ children }: { children: ReactNode }) {
}}
>
<TooltipProvider>
<script
suppressHydrationWarning
dangerouslySetInnerHTML={{ __html: inject }}
/>
<script suppressHydrationWarning dangerouslySetInnerHTML={{ __html: inject }} />
{children}
</TooltipProvider>
</RootProvider>
+1 -3
View File
@@ -28,8 +28,6 @@ export async function GET(): Promise<Response> {
});
return Response.json(
(await Promise.all(promises)).filter(
(v) => v !== undefined,
) as OramaDocument[],
(await Promise.all(promises)).filter((v) => v !== undefined) as OramaDocument[],
);
}
+1 -4
View File
@@ -55,10 +55,7 @@ export function rehypeWrapWords() {
}
function createProcessor(): Processor {
const processor = remark()
.use(remarkGfm)
.use(remarkRehype)
.use(rehypeWrapWords);
const processor = remark().use(remarkGfm).use(remarkRehype).use(rehypeWrapWords);
return {
async process(content) {
+3 -15
View File
@@ -1,20 +1,10 @@
'use client';
import { useMemo, useState } from 'react';
import {
Check,
ChevronDown,
Copy,
ExternalLinkIcon,
MessageCircleIcon,
} from 'lucide-react';
import { Check, ChevronDown, Copy, ExternalLinkIcon, MessageCircleIcon } from 'lucide-react';
import { cn } from '@/lib/cn';
import { useCopyButton } from 'fumadocs-ui/utils/use-copy-button';
import { buttonVariants } from 'fumadocs-ui/components/ui/button';
import {
Popover,
PopoverContent,
PopoverTrigger,
} from 'fumadocs-ui/components/ui/popover';
import { Popover, PopoverContent, PopoverTrigger } from 'fumadocs-ui/components/ui/popover';
import { cva } from 'class-variance-authority';
const cache = new Map<string, string>();
@@ -88,9 +78,7 @@ export function ViewOptions({
}) {
const items = useMemo(() => {
const fullMarkdownUrl =
typeof window !== 'undefined'
? new URL(markdownUrl, window.location.origin)
: 'loading';
typeof window !== 'undefined' ? new URL(markdownUrl, window.location.origin) : 'loading';
const q = `Read ${fullMarkdownUrl}, I want to ask questions about it.`;
return [
+6 -24
View File
@@ -41,11 +41,7 @@ function Header() {
<p className="text-sm font-medium mb-2">Ask AI</p>
<p className="text-xs text-fd-muted-foreground">
Powered by{' '}
<a
href="https://inkeep.com"
target="_blank"
rel="noreferrer noopener"
>
<a href="https://inkeep.com" target="_blank" rel="noreferrer noopener">
Inkeep AI
</a>
</p>
@@ -112,9 +108,7 @@ function SearchAIActions() {
const StorageKeyInput = '__ai_search_input';
function SearchAIInput(props: ComponentProps<'form'>) {
const { status, sendMessage, stop } = useChatContext();
const [input, setInput] = useState(
() => localStorage.getItem(StorageKeyInput) ?? '',
);
const [input, setInput] = useState(() => localStorage.getItem(StorageKeyInput) ?? '');
const isLoading = status === 'streaming' || status === 'submitted';
const onStart = (e?: SyntheticEvent) => {
e?.preventDefault();
@@ -129,11 +123,7 @@ function SearchAIInput(props: ComponentProps<'form'>) {
}, [isLoading]);
return (
<form
{...props}
className={cn('flex items-start pe-2', props.className)}
onSubmit={onStart}
>
<form {...props} className={cn('flex items-start pe-2', props.className)} onSubmit={onStart}>
<Input
value={input}
placeholder={isLoading ? 'AI is answering...' : 'Ask a question'}
@@ -216,10 +206,7 @@ function List(props: Omit<ComponentProps<'div'>, 'dir'>) {
<div
ref={containerRef}
{...props}
className={cn(
'fd-scroll-container overflow-y-auto min-w-0 flex flex-col',
props.className,
)}
className={cn('fd-scroll-container overflow-y-auto min-w-0 flex flex-col', props.className)}
>
{props.children}
</div>
@@ -252,10 +239,7 @@ const roleName: Record<string, string> = {
assistant: 'fumadocs',
};
function Message({
message,
...props
}: { message: UIMessage } & ComponentProps<'div'>) {
function Message({ message, ...props }: { message: UIMessage } & ComponentProps<'div'>) {
let markdown = '';
let links: z.infer<typeof ProvideLinksToolSchema>['links'] = [];
@@ -311,9 +295,7 @@ export function AISearch({ children }: { children: ReactNode }) {
});
return (
<Context value={useMemo(() => ({ chat, open, setOpen }), [chat, open])}>
{children}
</Context>
<Context value={useMemo(() => ({ chat, open, setOpen }), [chat, open])}>{children}</Context>
);
}
+1 -4
View File
@@ -21,10 +21,7 @@ export default async function ContributorCounter({
.slice(0, displayCount);
return (
<div
{...props}
className={cn('flex flex-col items-center gap-4', props.className)}
>
<div {...props} className={cn('flex flex-col items-center gap-4', props.className)}>
<div className="flex flex-row flex-wrap items-center justify-center md:pe-4">
{topContributors.map((contributor, i) => (
<a
+1 -4
View File
@@ -3,10 +3,7 @@ import { cn } from '@/lib/cn';
import { buttonVariants } from 'fumadocs-ui/components/ui/button';
import { ThumbsDown, ThumbsUp } from 'lucide-react';
import { type SyntheticEvent, useEffect, useState, useTransition } from 'react';
import {
Collapsible,
CollapsibleContent,
} from 'fumadocs-ui/components/ui/collapsible';
import { Collapsible, CollapsibleContent } from 'fumadocs-ui/components/ui/collapsible';
import { cva } from 'class-variance-authority';
import { usePathname } from 'next/navigation';
+2 -11
View File
@@ -1,12 +1,5 @@
'use client';
import {
lazy,
type RefObject,
useEffect,
useMemo,
useRef,
useState,
} from 'react';
import { lazy, type RefObject, useEffect, useMemo, useRef, useState } from 'react';
import type {
ForceGraphMethods,
ForceGraphProps,
@@ -101,9 +94,7 @@ function ClientOnly({
ctx.arc(node.x!, node.y!, radius, 0, 2 * Math.PI, false);
const hoverNode = hoveredRef.current;
const isActive =
hoverNode?.id === node.id ||
hoverNode?.neighbors?.includes(node.id as string);
const isActive = hoverNode?.id === node.id || hoverNode?.neighbors?.includes(node.id as string);
ctx.fillStyle = isActive
? style.getPropertyValue('--color-fd-primary')
+2 -7
View File
@@ -16,10 +16,7 @@ export function Mermaid({ chart }: { chart: string }) {
const cache = new Map<string, Promise<unknown>>();
function cachePromise<T>(
key: string,
setPromise: () => Promise<T>,
): Promise<T> {
function cachePromise<T>(key: string, setPromise: () => Promise<T>): Promise<T> {
const cached = cache.get(key);
if (cached) return cached as Promise<T>;
@@ -31,9 +28,7 @@ function cachePromise<T>(
function MermaidContent({ chart }: { chart: string }) {
const id = useId();
const { resolvedTheme } = useTheme();
const { default: mermaid } = use(
cachePromise('mermaid', () => import('mermaid')),
);
const { default: mermaid } = use(cachePromise('mermaid', () => import('mermaid')));
mermaid.initialize({
startOnLoad: false,
+1 -5
View File
@@ -19,11 +19,7 @@ export function NotFound(props: NotFoundProps) {
<h1 className="text-4xl font-bold font-mono">Not Found</h1>
<div className="w-full border border-fd-foreground/50 border-dashed p-4 max-w-[400px]">
<Suspense
fallback={
<p className="text-sm text-fd-muted-foreground">
Finding Alternatives...
</p>
}
fallback={<p className="text-sm text-fd-muted-foreground">Finding Alternatives...</p>}
>
<Alternative {...props} />
</Suspense>
@@ -16,11 +16,7 @@ export default function Example() {
className="w-fit bg-transparent px-4 py-2 text-sm focus-visible:outline-none"
>
{Object.keys(bundledLanguages).map((lang) => (
<option
value={lang}
key={lang}
className="bg-fd-card text-fd-card-foreground"
>
<option value={lang} key={lang} className="bg-fd-card text-fd-card-foreground">
{lang}
</option>
))}
+4 -16
View File
@@ -55,11 +55,7 @@ export function tabs(): ReactNode {
return (
<Wrapper>
<div className="prose-no-margin">
<Tabs
groupId="language"
persist
items={['Javascript', 'Rust', 'Typescript']}
>
<Tabs groupId="language" persist items={['Javascript', 'Rust', 'Typescript']}>
<Tab value="Javascript">Hello World in Javascript</Tab>
<Tab value="Rust">Hello World in Rust</Tab>
<Tab value="Typescript">Also works if items are not the same</Tab>
@@ -69,9 +65,7 @@ export function tabs(): ReactNode {
<Tab value="Javascript">
Value is shared! Try refresh and see if the value is persisted
</Tab>
<Tab value="Rust">
Value is shared! Try refresh and see if the value is persisted
</Tab>
<Tab value="Rust">Value is shared! Try refresh and see if the value is persisted</Tab>
</Tabs>
</div>
</Wrapper>
@@ -85,8 +79,7 @@ export function typeTable(): ReactNode {
<TypeTable
type={{
percentage: {
description:
'The percentage of scroll position to display the roll button',
description: 'The percentage of scroll position to display the roll button',
type: 'number',
default: '0.2',
},
@@ -215,12 +208,7 @@ export function banner(): ReactNode {
Be careful, Fumadocs v99 has released
</Banner>
<Banner
id="test-rainbow"
className="z-0"
variant="rainbow"
changeLayout={false}
>
<Banner id="test-rainbow" className="z-0" variant="rainbow" changeLayout={false}>
Using the <code>rainbow</code> variant
</Banner>
@@ -1,9 +1,4 @@
import {
Tabs,
TabsContent,
TabsList,
TabsTrigger,
} from 'fumadocs-ui/components/ui/tabs';
import { Tabs, TabsContent, TabsList, TabsTrigger } from 'fumadocs-ui/components/ui/tabs';
import { CodeBlock } from '@/components/code-block';
export function Installation({ name }: { name: string }) {
@@ -14,9 +9,7 @@ export function Installation({ name }: { name: string }) {
<TabsList className="flex flex-col gap-3 text-sm items-start p-3 mb-2 bg-fd-card text-fd-card-foreground rounded-xl border not-prose sm:flex-row">
<div className="me-auto">
<p className="font-medium">Install to your codebase</p>
<p className="mt-1 text-fd-muted-foreground">
Easier customisation & control.
</p>
<p className="mt-1 text-fd-muted-foreground">Easier customisation & control.</p>
</div>
{tabs.map((tab) => (
<TabsTrigger
+2 -6
View File
@@ -9,12 +9,8 @@ export const InlineTOC = dynamic(() =>
import('fumadocs-ui/components/inline-toc').then((res) => res.InlineTOC),
);
export const File = dynamic(() =>
import('fumadocs-ui/components/files').then((res) => res.File),
);
export const Files = dynamic(() =>
import('fumadocs-ui/components/files').then((res) => res.Files),
);
export const File = dynamic(() => import('fumadocs-ui/components/files').then((res) => res.File));
export const Files = dynamic(() => import('fumadocs-ui/components/files').then((res) => res.Files));
export const Folder = dynamic(() =>
import('fumadocs-ui/components/files').then((res) => res.Folder),
);
+3 -11
View File
@@ -23,18 +23,11 @@ export const registry: Registry = {
const filePath = path.relative(baseDir, ref.file);
if (filePath === 'lib/cn.ts') {
return resolveFromRemote(
ui.registry,
'cn',
(file) => file.path === 'cn.ts',
)!;
return resolveFromRemote(ui.registry, 'cn', (file) => file.path === 'cn.ts')!;
}
}
if (
ref.type === 'dependency' &&
ref.specifier === 'fumadocs-ui/components/ui/button'
) {
if (ref.type === 'dependency' && ref.specifier === 'fumadocs-ui/components/ui/button') {
return resolveFromRemote(
radixUi.registry,
'button',
@@ -74,8 +67,7 @@ export const registry: Registry = {
{
name: 'ai/search',
title: 'AI Search (Next.js Only)',
description:
'Ask AI dialog for your docs, you need to configure Inkeep first',
description: 'Ask AI dialog for your docs, you need to configure Inkeep first',
files: [
{
type: 'components',
+3 -15
View File
@@ -15,11 +15,7 @@ import {
} from 'fumadocs-ui/components/dialog/search';
import { useDocsSearch } from 'fumadocs-core/search/client';
import { useMemo, useState } from 'react';
import {
Popover,
PopoverContent,
PopoverTrigger,
} from 'fumadocs-ui/components/ui/popover';
import { Popover, PopoverContent, PopoverTrigger } from 'fumadocs-ui/components/ui/popover';
import { ArrowRight, ChevronDown } from 'lucide-react';
import { buttonVariants } from 'fumadocs-ui/components/ui/button';
import { cn } from '@/lib/cn';
@@ -94,10 +90,7 @@ export default function CustomSearchDialog(props: SharedProps) {
<div className="inline-flex items-center gap-2 text-fd-muted-foreground">
<ArrowRight className="size-4" />
<p>
Jump to{' '}
<span className="font-medium text-fd-foreground">
{page.name}
</span>
Jump to <span className="font-medium text-fd-foreground">{page.name}</span>
</p>
</div>
),
@@ -107,12 +100,7 @@ export default function CustomSearchDialog(props: SharedProps) {
}, [router, search, searchMap]);
return (
<SearchDialog
search={search}
onSearchChange={setSearch}
isLoading={query.isLoading}
{...props}
>
<SearchDialog search={search} onSearchChange={setSearch} isLoading={query.isLoading} {...props}>
<SearchDialogOverlay />
<SearchDialogContent>
<SearchDialogHeader>
+1 -3
View File
@@ -8,9 +8,7 @@ import Link from 'fumadocs-core/link';
const HoverCard = HoverCardPrimitive.Root;
function HoverCardTrigger(
props: ComponentProps<typeof HoverCardPrimitive.Trigger>,
) {
function HoverCardTrigger(props: ComponentProps<typeof HoverCardPrimitive.Trigger>) {
return (
<HoverCardPrimitive.Trigger asChild>
<Link {...props} />
+1 -6
View File
@@ -29,12 +29,7 @@ export default function CustomSearchDialog(props: SharedProps) {
});
return (
<SearchDialog
search={search}
onSearchChange={setSearch}
isLoading={query.isLoading}
{...props}
>
<SearchDialog search={search} onSearchChange={setSearch} isLoading={query.isLoading} {...props}>
<SearchDialogOverlay />
<SearchDialogContent>
<SearchDialogHeader>
+1 -6
View File
@@ -21,12 +21,7 @@ export default function DefaultSearchDialog(props: SharedProps) {
});
return (
<SearchDialog
search={search}
onSearchChange={setSearch}
isLoading={query.isLoading}
{...props}
>
<SearchDialog search={search} onSearchChange={setSearch} isLoading={query.isLoading} {...props}>
<SearchDialogOverlay />
<SearchDialogContent>
<SearchDialogHeader>
@@ -31,12 +31,7 @@ export default function CustomSearchDialog(props: SharedProps) {
});
return (
<SearchDialog
search={search}
onSearchChange={setSearch}
isLoading={query.isLoading}
{...props}
>
<SearchDialog search={search} onSearchChange={setSearch} isLoading={query.isLoading} {...props}>
<SearchDialogOverlay />
<SearchDialogContent>
<SearchDialogHeader>
@@ -30,12 +30,7 @@ export default function CustomSearchDialog(props: SharedProps) {
});
return (
<SearchDialog
search={search}
onSearchChange={setSearch}
isLoading={query.isLoading}
{...props}
>
<SearchDialog search={search} onSearchChange={setSearch} isLoading={query.isLoading} {...props}>
<SearchDialogOverlay />
<SearchDialogContent>
<SearchDialogHeader>
+1 -6
View File
@@ -31,12 +31,7 @@ export default function DefaultSearchDialog(props: SharedProps) {
});
return (
<SearchDialog
search={search}
onSearchChange={setSearch}
isLoading={query.isLoading}
{...props}
>
<SearchDialog search={search} onSearchChange={setSearch} isLoading={query.isLoading} {...props}>
<SearchDialogOverlay />
<SearchDialogContent>
<SearchDialogHeader>
+1 -2
View File
@@ -10,8 +10,7 @@ export async function fetchContributors(
baseUrl: string = 'https://api.github.com',
): Promise<Contributor[]> {
const headers = new Headers();
if (process.env.GITHUB_TOKEN)
headers.set('Authorization', `Bearer ${process.env.GITHUB_TOKEN}`);
if (process.env.GITHUB_TOKEN) headers.set('Authorization', `Bearer ${process.env.GITHUB_TOKEN}`);
const response = await fetch(
`${baseUrl}/repos/${repoOwner}/${repoName}/contributors?per_page=50`,
+1 -4
View File
@@ -23,10 +23,7 @@ export async function getLLMText(page: Page) {
const tasks: Promise<void>[] = [];
visit(tree, (node) => {
if (
node.type === 'mdxJsxFlowElement' ||
node.type === 'mdxFlowExpression'
) {
if (node.type === 'mdxJsxFlowElement' || node.type === 'mdxFlowExpression') {
const context = {
React,
...getMDXComponents(),
+7 -13
View File
@@ -21,9 +21,7 @@ export const revalidate = 60 * 30;
export async function getSponsors(owner: string): Promise<Sponsor[]> {
if (!process.env.GITHUB_TOKEN) {
console.warn(
'GITHUB_TOKEN environment variable is required for fetching sponsors.',
);
console.warn('GITHUB_TOKEN environment variable is required for fetching sponsors.');
return [];
}
@@ -71,18 +69,14 @@ export async function getSponsors(owner: string): Promise<Sponsor[]> {
}
`);
const sponsors = response.user.sponsorshipsAsMaintainer.nodes.map(
(node) => ({
...node.sponsorEntity,
name: node.sponsorEntity.name || node.sponsorEntity.login,
tier: node.tier,
}),
);
const sponsors = response.user.sponsorshipsAsMaintainer.nodes.map((node) => ({
...node.sponsorEntity,
name: node.sponsorEntity.name || node.sponsorEntity.login,
tier: node.tier,
}));
// Sort sponsors by tier price in descending order
return sponsors.sort(
(a, b) => b.tier.monthlyPriceInDollars - a.tier.monthlyPriceInDollars,
);
return sponsors.sort((a, b) => b.tier.monthlyPriceInDollars - a.tier.monthlyPriceInDollars);
} catch (error) {
console.error('Error fetching sponsors:', error);
throw error;
+10 -22
View File
@@ -13,9 +13,7 @@ async function getOctokit(): Promise<Octokit> {
const privateKey = process.env.GITHUB_APP_PRIVATE_KEY;
if (!appId || !privateKey) {
throw new Error(
'No GitHub keys provided for Github app, docs feedback feature will not work.',
);
throw new Error('No GitHub keys provided for Github app, docs feedback feature will not work.');
}
const app = new App({
@@ -23,16 +21,13 @@ async function getOctokit(): Promise<Octokit> {
privateKey,
});
const { data } = await app.octokit.request(
'GET /repos/{owner}/{repo}/installation',
{
owner,
repo,
headers: {
'X-GitHub-Api-Version': '2022-11-28',
},
const { data } = await app.octokit.request('GET /repos/{owner}/{repo}/installation', {
owner,
repo,
headers: {
'X-GitHub-Api-Version': '2022-11-28',
},
);
});
instance = await app.getInstallationOctokit(data.id);
return instance;
@@ -71,24 +66,17 @@ async function getFeedbackDestination() {
return (cachedDestination = repository);
}
export async function onRateAction(
url: string,
feedback: Feedback,
): Promise<ActionResponse> {
export async function onRateAction(url: string, feedback: Feedback): Promise<ActionResponse> {
'use server';
const octokit = await getOctokit();
const destination = await getFeedbackDestination();
if (!octokit || !destination)
throw new Error('GitHub comment integration is not configured.');
if (!octokit || !destination) throw new Error('GitHub comment integration is not configured.');
const category = destination.discussionCategories.nodes.find(
(category) => category.name === DocsCategory,
);
if (!category)
throw new Error(
`Please create a "${DocsCategory}" category in GitHub Discussion`,
);
if (!category) throw new Error(`Please create a "${DocsCategory}" category in GitHub Discussion`);
const title = `Feedback for ${url}`;
const body = `[${feedback.opinion}] ${feedback.message}\n\n> Forwarded from user feedback.`;
+1 -2
View File
@@ -44,7 +44,6 @@ export function getPageImage(page: Page) {
}
export const baseUrl =
process.env.NODE_ENV === 'development' ||
!process.env.VERCEL_PROJECT_PRODUCTION_URL
process.env.NODE_ENV === 'development' || !process.env.VERCEL_PROJECT_PRODUCTION_URL
? new URL('http://localhost:3000')
: new URL(`https://${process.env.VERCEL_PROJECT_PRODUCTION_URL}`);
+1 -6
View File
@@ -31,12 +31,7 @@ export async function getImageResponseOptions(): Promise<ImageResponseOptions> {
};
}
export function generate({
title,
description,
logo,
site = 'My App',
}: GenerateProps) {
export function generate({ title, description, logo, site = 'My App' }: GenerateProps) {
const primaryTextColor = 'rgb(240,240,240)';
return (
+2 -5
View File
@@ -1,13 +1,10 @@
import { OramaCloud } from '@orama/core';
export const DataSourceId = process.env
.NEXT_PUBLIC_ORAMA_DATASOURCE_ID as string;
export const DataSourceId = process.env.NEXT_PUBLIC_ORAMA_DATASOURCE_ID as string;
export const isAdmin = process.env.ORAMA_PRIVATE_API_KEY !== undefined;
export const orama = new OramaCloud({
projectId: process.env.NEXT_PUBLIC_ORAMA_PROJECT_ID as string,
apiKey:
process.env.ORAMA_PRIVATE_API_KEY ??
(process.env.NEXT_PUBLIC_ORAMA_API_KEY as string),
apiKey: process.env.ORAMA_PRIVATE_API_KEY ?? (process.env.NEXT_PUBLIC_ORAMA_API_KEY as string),
});
+13
View File
@@ -0,0 +1,13 @@
export function getSection(path: string | undefined) {
if (!path) return 'framework';
const [dir] = path.split('/', 1);
if (!dir) return 'framework';
return (
{
ui: 'ui',
mdx: 'mdx',
cli: 'cli',
headless: 'headless',
}[dir] ?? 'framework'
);
}
+4 -8
View File
@@ -80,8 +80,7 @@ security:
- openIdConnect: []
tags:
- name: Authentication
description:
Some endpoints are public, but some require authentication. We provide
description: Some endpoints are public, but some require authentication. We provide
all the required endpoints to create an account and authorize yourself.
- name: Planets
description: Everything about planets
@@ -252,8 +251,7 @@ paths:
- Planets
summary: Delete a planet
operationId: deletePlanet
description:
This endpoint was used to delete planets. Unfortunately, that caused
description: This endpoint was used to delete planets. Unfortunately, that caused
a lot of trouble for planets with life. So, this endpoint is now deprecated
and should not be used anymore.
x-scalar-stability: experimental
@@ -438,8 +436,7 @@ webhooks:
$ref: '#/components/schemas/Planet'
responses:
'200':
description:
Return a 200 status to indicate that the data was received
description: Return a 200 status to indicate that the data was received
successfully
components:
securitySchemes:
@@ -523,8 +520,7 @@ components:
default: 10
offset:
name: offset
description:
The number of items to skip before starting to collect the result
description: The number of items to skip before starting to collect the result
set
in: query
required: false
+1 -5
View File
@@ -1,8 +1,4 @@
import {
combineRegistry,
RegistryCompiler,
writeFumadocsRegistry,
} from '@fumadocs/cli/build';
import { combineRegistry, RegistryCompiler, writeFumadocsRegistry } from '@fumadocs/cli/build';
import { registry } from '@/components/registry.js';
import * as ui from '../../../packages/ui/src/_registry';
import * as radixUi from '../../../packages/radix-ui/src/_registry';
+10 -22
View File
@@ -1,9 +1,4 @@
import {
type FileObject,
printErrors,
scanURLs,
validateFiles,
} from 'next-validate-link';
import { type FileObject, printErrors, scanURLs, validateFiles } from 'next-validate-link';
import { InferPageType } from 'fumadocs-core/source';
import { blog, source } from '@/lib/source';
@@ -33,30 +28,23 @@ async function checkLinks() {
},
});
console.log(
`collected ${scanned.urls.size} URLs, ${scanned.fallbackUrls.length} fallbacks`,
);
console.log(`collected ${scanned.urls.size} URLs, ${scanned.fallbackUrls.length} fallbacks`);
printErrors(
await validateFiles(
[...(await getFiles(source)), ...(await getFiles(blog))],
{
scanned,
markdown: {
components: {
Card: { attributes: ['href'] },
},
await validateFiles([...(await getFiles(source)), ...(await getFiles(blog))], {
scanned,
markdown: {
components: {
Card: { attributes: ['href'] },
},
checkRelativePaths: 'as-url',
},
),
checkRelativePaths: 'as-url',
}),
true,
);
}
async function getHeadings({
data,
}: InferPageType<AnySource>): Promise<string[]> {
async function getHeadings({ data }: InferPageType<AnySource>): Promise<string[]> {
if ('type' in data && data.type === 'openapi') return [];
const { _exports, toc } = await data.load();
const headings = toc.map((item) => item.url.slice(1));
+6 -13
View File
@@ -72,24 +72,17 @@ export default defineConfig({
lastModified(),
],
mdxOptions: async () => {
const { rehypeCodeDefaultOptions } =
await import('fumadocs-core/mdx-plugins/rehype-code');
const { rehypeCodeDefaultOptions } = await import('fumadocs-core/mdx-plugins/rehype-code');
const { remarkStructureDefaultOptions } =
await import('fumadocs-core/mdx-plugins/remark-structure');
const { remarkSteps } =
await import('fumadocs-core/mdx-plugins/remark-steps');
const { remarkSteps } = await import('fumadocs-core/mdx-plugins/remark-steps');
const { transformerTwoslash } = await import('fumadocs-twoslash');
const { createFileSystemTypesCache } =
await import('fumadocs-twoslash/cache-fs');
const { createFileSystemTypesCache } = await import('fumadocs-twoslash/cache-fs');
const { default: remarkMath } = await import('remark-math');
const { remarkTypeScriptToJavaScript } =
await import('fumadocs-docgen/remark-ts2js');
const { remarkTypeScriptToJavaScript } = await import('fumadocs-docgen/remark-ts2js');
const { default: rehypeKatex } = await import('rehype-katex');
const {
remarkAutoTypeTable,
createGenerator,
createFileSystemGeneratorCache,
} = await import('fumadocs-typescript');
const { remarkAutoTypeTable, createGenerator, createFileSystemGeneratorCache } =
await import('fumadocs-typescript');
const generator = createGenerator({
cache: createFileSystemGeneratorCache('.next/fumadocs-typescript'),
+1 -7
View File
@@ -7,12 +7,6 @@
"fumadocs-mdx:collections/*": [".source/*"]
}
},
"include": [
"next-env.d.ts",
"**/*.ts",
"content/**/*.mdx",
"**/*.tsx",
".next/types/**/*.ts"
],
"include": ["next-env.d.ts", "**/*.ts", "content/**/*.mdx", "**/*.tsx", ".next/types/**/*.ts"],
"exclude": ["node_modules", "eslint.config.mjs", "components/registry/**/*"]
}
+1 -1
View File
@@ -1,8 +1,8 @@
{
"name": "example-astro",
"type": "module",
"version": "0.0.1",
"private": true,
"type": "module",
"scripts": {
"dev": "astro dev",
"start": "astro dev",
+1 -6
View File
@@ -31,12 +31,7 @@ export default function DefaultSearchDialog(props: SharedProps) {
});
return (
<SearchDialog
search={search}
onSearchChange={setSearch}
isLoading={query.isLoading}
{...props}
>
<SearchDialog search={search} onSearchChange={setSearch} isLoading={query.isLoading} {...props}>
<SearchDialogOverlay />
<SearchDialogContent>
<SearchDialogHeader>
+1 -3
View File
@@ -13,9 +13,7 @@ const server = createFromSource(source, {
description: page.data.description,
structuredData,
url: page.url,
breadcrumbs: getBreadcrumbItems(page.url, source.pageTree).map((item) =>
String(item.name),
),
breadcrumbs: getBreadcrumbItems(page.url, source.pageTree).map((item) => String(item.name)),
};
},
});
@@ -1,18 +1,11 @@
import { source } from '@/lib/source';
import type { Metadata } from 'next';
import {
DocsPage,
DocsBody,
DocsTitle,
DocsDescription,
} from 'fumadocs-ui/layouts/docs/page';
import { DocsPage, DocsBody, DocsTitle, DocsDescription } from 'fumadocs-ui/layouts/docs/page';
import { notFound } from 'next/navigation';
import { MDXContent } from '@content-collections/mdx/react';
import { getMDXComponents } from '@/mdx-components';
export default async function Page(props: {
params: Promise<{ slug?: string[] }>;
}) {
export default async function Page(props: { params: Promise<{ slug?: string[] }> }) {
const params = await props.params;
const page = source.getPage(params.slug);
@@ -35,9 +28,7 @@ export async function generateStaticParams() {
return source.generateParams();
}
export async function generateMetadata(props: {
params: Promise<{ slug?: string[] }>;
}) {
export async function generateMetadata(props: { params: Promise<{ slug?: string[] }> }) {
const params = await props.params;
const page = source.getPage(params.slug);
+1 -4
View File
@@ -1,10 +1,7 @@
import { HomeLayout } from 'fumadocs-ui/layouts/home';
import { baseOptions } from '@/lib/layout.shared';
export default async function Layout({
params,
children,
}: LayoutProps<'/[lang]'>) {
export default async function Layout({ params, children }: LayoutProps<'/[lang]'>) {
const { lang } = await params;
return <HomeLayout {...baseOptions(lang)}>{children}</HomeLayout>;
@@ -1,17 +1,10 @@
import { source } from '@/lib/source';
import {
DocsBody,
DocsDescription,
DocsPage,
DocsTitle,
} from 'fumadocs-ui/layouts/docs/page';
import { DocsBody, DocsDescription, DocsPage, DocsTitle } from 'fumadocs-ui/layouts/docs/page';
import { notFound } from 'next/navigation';
import { getMDXComponents } from '@/mdx-components';
import type { Metadata } from 'next';
export default async function Page(
props: PageProps<'/[lang]/docs/[[...slug]]'>,
) {
export default async function Page(props: PageProps<'/[lang]/docs/[[...slug]]'>) {
const params = await props.params;
const page = source.getPage(params.slug, params.lang);
if (!page) notFound();
+1 -4
View File
@@ -2,10 +2,7 @@ import { source } from '@/lib/source';
import { DocsLayout } from 'fumadocs-ui/layouts/docs';
import { baseOptions } from '@/lib/layout.shared';
export default async function Layout({
params,
children,
}: LayoutProps<'/[lang]/docs'>) {
export default async function Layout({ params, children }: LayoutProps<'/[lang]/docs'>) {
const { lang } = await params;
return (
+1 -4
View File
@@ -26,10 +26,7 @@ const { provider } = defineI18nUI(i18n, {
},
});
export default async function Layout({
params,
children,
}: LayoutProps<'/[lang]'>) {
export default async function Layout({ params, children }: LayoutProps<'/[lang]'>) {
const { lang } = await params;
return (
<html lang={lang} className={inter.className} suppressHydrationWarning>
+1 -4
View File
@@ -6,10 +6,7 @@ export default function HomePage() {
<h1 className="mb-4 text-2xl font-bold">Hello World</h1>
<p className="text-fd-muted-foreground">
You can open{' '}
<Link
href="/docs"
className="text-fd-foreground font-semibold underline"
>
<Link href="/docs" className="text-fd-foreground font-semibold underline">
/docs
</Link>{' '}
and see the documentation.
@@ -24,12 +24,7 @@ export default async function Page(props: PageProps<'/docs/[[...slug]]'>) {
<DocsTitle>{page.data.title}</DocsTitle>
<DocsDescription>{page.data.description}</DocsDescription>
{page.data.coverImage && (
<img
alt="cover"
src={page.data.coverImage}
fetchPriority="high"
className="rounded-xl"
/>
<img alt="cover" src={page.data.coverImage} fetchPriority="high" className="rounded-xl" />
)}
<DocsBody>{node}</DocsBody>
{page.data.updatedAt && <PageLastUpdate date={page.data.updatedAt} />}
@@ -41,9 +36,7 @@ export async function generateStaticParams() {
return source.generateParams();
}
export async function generateMetadata(
props: PageProps<'/docs/[[...slug]]'>,
): Promise<Metadata> {
export async function generateMetadata(props: PageProps<'/docs/[[...slug]]'>): Promise<Metadata> {
const params = await props.params;
const page = source.getPage(params.slug);
if (!page) notFound();
+1 -7
View File
@@ -12,13 +12,7 @@ export function baseOptions(): BaseLayoutProps {
nav: {
title: (
<>
<svg
width="32"
height="32"
viewBox="0 0 32 32"
fill="none"
className="rounded-full"
>
<svg width="32" height="32" viewBox="0 0 32 32" fill="none" className="rounded-full">
<rect width="32" height="32" fill="#1E1E1E" />
<path
d="M14.116 11.5C14.2333 11.5213 14.4947 11.932 14.9 12.732C15.3053 13.5213 15.8013 14.5453 16.388 15.804L16.9 16.876C17.124 16.6307 17.332 16.348 17.524 16.028C17.716 15.6973 17.956 15.2547 18.244 14.7C18.5747 14.0707 18.804 13.6653 18.932 13.484C19.444 12.7053 19.9987 11.8787 20.596 11.004C20.6707 10.8867 20.804 10.8067 20.996 10.764C21.028 10.7533 21.0813 10.748 21.156 10.748C21.412 10.748 21.6947 10.8547 22.004 11.068C22.324 11.2813 22.532 11.5 22.628 11.724C22.6493 11.7667 22.66 11.8467 22.66 11.964C22.66 12.0707 22.6547 12.1507 22.644 12.204C22.6227 12.3 22.596 12.4227 22.564 12.572C22.5427 12.7107 22.5213 12.876 22.5 13.068C22.1587 15.6707 21.9133 17.6227 21.764 18.924C21.7213 19.2227 21.7 19.5587 21.7 19.932L21.668 20.444C21.6573 20.5507 21.652 20.6733 21.652 20.812C21.652 20.9507 21.636 21.0467 21.604 21.1C21.572 21.1533 21.5133 21.18 21.428 21.18C21.364 21.18 21.268 21.164 21.14 21.132C20.7987 21.0573 20.5373 20.924 20.356 20.732C20.1747 20.5293 20.0733 20.252 20.052 19.9C20.0413 19.7187 20.036 19.436 20.036 19.052C20.036 18.6573 20.0467 18.0547 20.068 17.244L20.084 16.364L20.116 15.1C20.02 15.1853 19.7747 15.5213 19.38 16.108C18.996 16.684 18.6173 17.2867 18.244 17.916C17.8707 18.5453 17.6253 19.0307 17.508 19.372C17.4013 19.6387 17.2947 19.836 17.188 19.964C17.092 20.092 16.9747 20.156 16.836 20.156C16.708 20.156 16.5427 20.0973 16.34 19.98C15.764 19.628 15.412 19.1427 15.284 18.524C15.0813 17.5747 14.8307 16.588 14.532 15.564C14.4787 15.372 14.372 15.1107 14.212 14.78C14.0627 14.4493 13.9667 14.2413 13.924 14.156L13.716 13.74C12.1587 15.8093 11.1027 17.708 10.548 19.436C10.4413 19.7987 10.34 20.06 10.244 20.22C10.148 20.3693 10.036 20.444 9.908 20.444C9.70533 20.444 9.41733 20.2627 9.044 19.9C8.756 19.644 8.612 19.356 8.612 19.036C8.612 18.8653 8.644 18.6947 8.708 18.524C9.156 17.2013 9.82267 15.948 10.708 14.764L11.204 14.108C11.876 13.2227 12.3827 12.5293 12.724 12.028C12.98 11.6547 13.3213 11.468 13.748 11.468C13.8227 11.468 13.9453 11.4787 14.116 11.5Z"
+1 -6
View File
@@ -1,9 +1,4 @@
import {
loader,
type MetaData,
type Source,
type VirtualFile,
} from 'fumadocs-core/source';
import { loader, type MetaData, type Source, type VirtualFile } from 'fumadocs-core/source';
import { getPosts } from '@/lib/query';
import type { Post } from '@/lib/types';
import type { StructuredData } from 'fumadocs-core/mdx-plugins';
@@ -1,10 +1,5 @@
import { getPageImage, source } from '@/lib/source';
import {
DocsBody,
DocsDescription,
DocsPage,
DocsTitle,
} from 'fumadocs-ui/layouts/docs/page';
import { DocsBody, DocsDescription, DocsPage, DocsTitle } from 'fumadocs-ui/layouts/docs/page';
import { notFound } from 'next/navigation';
import { getMDXComponents } from '@/mdx-components';
import type { Metadata } from 'next';
@@ -37,9 +32,7 @@ export async function generateStaticParams() {
return source.generateParams();
}
export async function generateMetadata(
props: PageProps<'/docs/[[...slug]]'>,
): Promise<Metadata> {
export async function generateMetadata(props: PageProps<'/docs/[[...slug]]'>): Promise<Metadata> {
const params = await props.params;
const page = source.getPage(params.slug);
if (!page) notFound();
@@ -5,20 +5,13 @@ import { generate as DefaultImage } from 'fumadocs-ui/og';
export const revalidate = false;
export async function GET(
_req: Request,
{ params }: RouteContext<'/og/docs/[...slug]'>,
) {
export async function GET(_req: Request, { params }: RouteContext<'/og/docs/[...slug]'>) {
const { slug } = await params;
const page = source.getPage(slug.slice(0, -1));
if (!page) notFound();
return new ImageResponse(
<DefaultImage
title={page.data.title}
description={page.data.description}
site="My App"
/>,
<DefaultImage title={page.data.title} description={page.data.description} site="My App" />,
{
width: 1200,
height: 630,
+1 -6
View File
@@ -1,9 +1,4 @@
import {
defineConfig,
defineDocs,
frontmatterSchema,
metaSchema,
} from 'fumadocs-mdx/config';
import { defineConfig, defineDocs, frontmatterSchema, metaSchema } from 'fumadocs-mdx/config';
// You can customise Zod schemas for frontmatter and `meta.json` here
// see https://fumadocs.dev/docs/mdx/collections
@@ -1,10 +1,5 @@
import { source } from '@/lib/source';
import {
DocsBody,
DocsDescription,
DocsPage,
DocsTitle,
} from 'fumadocs-ui/layouts/docs/page';
import { DocsBody, DocsDescription, DocsPage, DocsTitle } from 'fumadocs-ui/layouts/docs/page';
import { notFound } from 'next/navigation';
import { getMDXComponents } from '@/mdx-components';
import type { Metadata } from 'next';
@@ -37,9 +32,7 @@ export async function generateStaticParams() {
return source.generateParams();
}
export async function generateMetadata(
props: PageProps<'/docs/[[...slug]]'>,
): Promise<Metadata> {
export async function generateMetadata(props: PageProps<'/docs/[[...slug]]'>): Promise<Metadata> {
const params = await props.params;
const page = source.getPage(params.slug);
if (!page) notFound();
@@ -1,10 +1,5 @@
import { source } from '@/lib/source';
import {
DocsBody,
DocsDescription,
DocsPage,
DocsTitle,
} from 'fumadocs-ui/layouts/docs/page';
import { DocsBody, DocsDescription, DocsPage, DocsTitle } from 'fumadocs-ui/layouts/docs/page';
import { notFound } from 'next/navigation';
import type { Metadata } from 'next';
import defaultMdxComponents, { createRelativeLink } from 'fumadocs-ui/mdx';
@@ -38,9 +33,7 @@ export async function generateStaticParams() {
return source.generateParams();
}
export async function generateMetadata(
props: PageProps<'/docs/[[...slug]]'>,
): Promise<Metadata> {
export async function generateMetadata(props: PageProps<'/docs/[[...slug]]'>): Promise<Metadata> {
const params = await props.params;
const page = source.getPage(params.slug);
if (!page) notFound();
+1 -5
View File
@@ -1,8 +1,4 @@
import {
defineConfig,
defineDocs,
frontmatterSchema,
} from 'fumadocs-mdx/config';
import { defineConfig, defineDocs, frontmatterSchema } from 'fumadocs-mdx/config';
import { remarkObsidian, RemarkObsidianOptions } from 'fumadocs-obsidian/mdx';
import { readVaultFiles } from 'fumadocs-obsidian';
@@ -2,10 +2,7 @@ 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[] }> },
) {
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();
+3 -12
View File
@@ -1,17 +1,10 @@
import { source } from '@/lib/source';
import {
DocsBody,
DocsDescription,
DocsPage,
DocsTitle,
} from 'fumadocs-ui/layouts/docs/page';
import { DocsBody, DocsDescription, DocsPage, DocsTitle } from 'fumadocs-ui/layouts/docs/page';
import { notFound } from 'next/navigation';
import { getMDXComponents } from '@/mdx-components';
import type { Metadata } from 'next';
export default async function Page(props: {
params: Promise<{ slug?: string[] }>;
}) {
export default async function Page(props: { params: Promise<{ slug?: string[] }> }) {
const params = await props.params;
const page = source.getPage(params.slug);
@@ -36,9 +29,7 @@ export async function generateStaticParams() {
return source.generateParams();
}
export async function generateMetadata(props: {
params: Promise<{ slug?: string[] }>;
}) {
export async function generateMetadata(props: { params: Promise<{ slug?: string[] }> }) {
const params = await props.params;
const page = source.getPage(params.slug);
if (!page) notFound();
+1 -5
View File
@@ -14,11 +14,7 @@ const mono = JetBrains_Mono({
export default function Layout({ children }: { children: ReactNode }) {
return (
<html
lang="en"
className={`${inter.className} ${mono.variable}`}
suppressHydrationWarning
>
<html lang="en" className={`${inter.className} ${mono.variable}`} suppressHydrationWarning>
<body className="flex flex-col min-h-screen">
<RootProvider>{children}</RootProvider>
</body>
+1 -7
View File
@@ -1,9 +1,3 @@
{
"pages": [
"---API Documentation---",
"index",
"...",
"---References---",
"...(api)"
]
"pages": ["---API Documentation---", "index", "...", "---References---", "...(api)"]
}
+12 -34
View File
@@ -1844,8 +1844,7 @@ paths:
structure and behaviour. See also: NodeUpdatePropertySchema
tags: [nodes]
parameters: [$ref: '#/components/parameters/NodeSlugParam']
requestBody:
{ $ref: '#/components/requestBodies/NodeUpdatePropertySchema' }
requestBody: { $ref: '#/components/requestBodies/NodeUpdatePropertySchema' }
responses:
default: { $ref: '#/components/responses/InternalServerError' }
'404': { $ref: '#/components/responses/NotFound' }
@@ -1868,8 +1867,7 @@ paths:
quick schema changes without the need to remove or update values before.
tags: [nodes]
parameters: [$ref: '#/components/parameters/NodeSlugParam']
requestBody:
{ $ref: '#/components/requestBodies/NodeUpdatePropertySchema' }
requestBody: { $ref: '#/components/requestBodies/NodeUpdatePropertySchema' }
responses:
default: { $ref: '#/components/responses/InternalServerError' }
'404': { $ref: '#/components/responses/NotFound' }
@@ -2975,8 +2973,7 @@ components:
type: string
content:
application/json:
schema:
{ $ref: '#/components/schemas/WebAuthnPublicKeyCreationOptions' }
schema: { $ref: '#/components/schemas/WebAuthnPublicKeyCreationOptions' }
WebAuthnGetAssertionOK:
description: OK
@@ -4112,12 +4109,9 @@ components:
excludeCredentials:
type: array
items: { $ref: '#/components/schemas/PublicKeyCredentialDescriptor' }
authenticatorSelection:
{ $ref: '#/components/schemas/AuthenticatorSelectionCriteria' }
attestation:
{ $ref: '#/components/schemas/AttestationConveyancePreference' }
extensions:
{ $ref: '#/components/schemas/AuthenticationExtensionsClientInputs' }
authenticatorSelection: { $ref: '#/components/schemas/AuthenticatorSelectionCriteria' }
attestation: { $ref: '#/components/schemas/AttestationConveyancePreference' }
extensions: { $ref: '#/components/schemas/AuthenticationExtensionsClientInputs' }
PublicKeyCredentialRpEntity:
description: |
@@ -4437,18 +4431,7 @@ components:
AccountCommonProps:
required:
[
joined,
handle,
name,
roles,
bio,
links,
meta,
verified_status,
email_addresses,
admin,
]
[joined, handle, name, roles, bio, links, meta, verified_status, email_addresses, admin]
properties:
joined: { $ref: '#/components/schemas/MemberJoinedDate' }
suspended: { $ref: '#/components/schemas/MemberSuspendedDate' }
@@ -5336,8 +5319,7 @@ components:
parent: { $ref: '#/components/schemas/Asset' }
AssetSourceURL:
description:
An asset source URL holds the address of an off-platform media asset
description: An asset source URL holds the address of an off-platform media asset
which is not hosted on a Storyden instance. It may represent a source
URL for an intended download or an asset which is stored elsewhere.
type: string
@@ -5685,8 +5667,7 @@ components:
format: url
nullable: true
description: { $ref: '#/components/schemas/NodeDescription' }
primary_image_asset_id:
{ $ref: '#/components/schemas/NullableIdentifier' }
primary_image_asset_id: { $ref: '#/components/schemas/NullableIdentifier' }
content: { $ref: '#/components/schemas/PostContent' }
parent: { $ref: '#/components/schemas/NodeSlug' }
hide_child_tree:
@@ -6110,8 +6091,7 @@ components:
time_range: { $ref: '#/components/schemas/EventTimeRange' }
primary_image: { $ref: '#/components/schemas/Asset' }
participants: { $ref: '#/components/schemas/EventParticipantList' }
participation_policy:
{ $ref: '#/components/schemas/EventParticipationPolicy' }
participation_policy: { $ref: '#/components/schemas/EventParticipationPolicy' }
visibility: { $ref: '#/components/schemas/Visibility' }
location: { $ref: '#/components/schemas/EventLocation' }
capacity: { $ref: '#/components/schemas/EventCapacity' }
@@ -6133,8 +6113,7 @@ components:
content: { $ref: '#/components/schemas/PostContent' }
time_range: { $ref: '#/components/schemas/EventTimeRange' }
primary_image_asset_id: { $ref: '#/components/schemas/AssetID' }
participation_policy:
{ $ref: '#/components/schemas/EventParticipationPolicy' }
participation_policy: { $ref: '#/components/schemas/EventParticipationPolicy' }
visibility: { $ref: '#/components/schemas/Visibility' }
location: { $ref: '#/components/schemas/EventLocation' }
capacity: { $ref: '#/components/schemas/EventCapacity' }
@@ -6150,8 +6129,7 @@ components:
content: { $ref: '#/components/schemas/PostContent' }
time_range: { $ref: '#/components/schemas/EventTimeRange' }
primary_image_asset_id: { $ref: '#/components/schemas/AssetID' }
participation_policy:
{ $ref: '#/components/schemas/EventParticipationPolicy' }
participation_policy: { $ref: '#/components/schemas/EventParticipationPolicy' }
visibility: { $ref: '#/components/schemas/Visibility' }
location: { $ref: '#/components/schemas/EventLocation' }
capacity: { $ref: '#/components/schemas/EventCapacity' }
+1 -4
View File
@@ -6,10 +6,7 @@ export default function HomePage() {
<h1 className="mb-4 text-2xl font-bold">Hello World</h1>
<p className="text-fd-muted-foreground">
You can open{' '}
<Link
href="/docs"
className="text-fd-foreground font-semibold underline"
>
<Link href="/docs" className="text-fd-foreground font-semibold underline">
/docs
</Link>{' '}
and see the documentation.
+3 -12
View File
@@ -1,17 +1,10 @@
import { source } from '@/lib/source';
import {
DocsPage,
DocsBody,
DocsDescription,
DocsTitle,
} from 'fumadocs-ui/layouts/docs/page';
import { DocsPage, DocsBody, DocsDescription, DocsTitle } from 'fumadocs-ui/layouts/docs/page';
import { notFound } from 'next/navigation';
import { createRelativeLink } from 'fumadocs-ui/mdx';
import { getMDXComponents } from '@/mdx-components';
export default async function Page(props: {
params: Promise<{ slug?: string[] }>;
}) {
export default async function Page(props: { params: Promise<{ slug?: string[] }> }) {
const params = await props.params;
const page = source.getPage(params.slug);
if (!page) notFound();
@@ -38,9 +31,7 @@ export async function generateStaticParams() {
return source.generateParams();
}
export async function generateMetadata(props: {
params: Promise<{ slug?: string[] }>;
}) {
export async function generateMetadata(props: { params: Promise<{ slug?: string[] }> }) {
const params = await props.params;
const page = source.getPage(params.slug);
if (!page) notFound();
+1 -6
View File
@@ -1,11 +1,6 @@
import type { Route } from './+types/page';
import { DocsLayout } from 'fumadocs-ui/layouts/docs';
import {
DocsBody,
DocsDescription,
DocsPage,
DocsTitle,
} from 'fumadocs-ui/layouts/docs/page';
import { DocsBody, DocsDescription, DocsPage, DocsTitle } from 'fumadocs-ui/layouts/docs/page';
import { source } from '@/lib/source';
import defaultMdxComponents from 'fumadocs-ui/mdx';
import browserCollections from 'fumadocs-mdx:collections/browser';
+1 -3
View File
@@ -81,9 +81,7 @@ export function ErrorBoundary({ error }: Route.ErrorBoundaryProps) {
if (isRouteErrorResponse(error)) {
message = error.status === 404 ? '404' : 'Error';
details =
error.status === 404
? 'The requested page could not be found.'
: error.statusText || details;
error.status === 404 ? 'The requested page could not be found.' : error.statusText || details;
} else if (import.meta.env.DEV && error && error instanceof Error) {
details = error.message;
stack = error.stack;
+1 -1
View File
@@ -1 +1 @@
preload = ["./scripts/preload.ts"]
preload = ["./scripts/preload.ts"]
@@ -5,9 +5,7 @@ import type { Info } from './scripts/generate-info';
export default {
ssr: true,
async prerender({ getStaticPaths }) {
const info: Info = JSON.parse(
(await fs.readFile('.react-router/_info')).toString(),
);
const info: Info = JSON.parse((await fs.readFile('.react-router/_info')).toString());
const paths: string[] = [];
for (const path of getStaticPaths()) {
+1 -6
View File
@@ -1,10 +1,5 @@
{
"include": [
"**/*",
"**/.server/**/*",
"**/.client/**/*",
".react-router/types/**/*"
],
"include": ["**/*", "**/.server/**/*", "**/.client/**/*", ".react-router/types/**/*"],
"compilerOptions": {
"lib": ["DOM", "DOM.Iterable", "ES2022"],
"types": ["node", "vite/client"],
+1 -6
View File
@@ -1,11 +1,6 @@
import type { Route } from './+types/page';
import { DocsLayout } from 'fumadocs-ui/layouts/docs';
import {
DocsBody,
DocsDescription,
DocsPage,
DocsTitle,
} from 'fumadocs-ui/layouts/docs/page';
import { DocsBody, DocsDescription, DocsPage, DocsTitle } from 'fumadocs-ui/layouts/docs/page';
import { source } from '@/lib/source';
import defaultMdxComponents from 'fumadocs-ui/mdx';
import { baseOptions } from '@/lib/layout.shared';
+1 -3
View File
@@ -53,9 +53,7 @@ export function ErrorBoundary({ error }: Route.ErrorBoundaryProps) {
if (isRouteErrorResponse(error)) {
message = error.status === 404 ? '404' : 'Error';
details =
error.status === 404
? 'The requested page could not be found.'
: error.statusText || details;
error.status === 404 ? 'The requested page could not be found.' : error.statusText || details;
} else if (import.meta.env.DEV && error && error instanceof Error) {
details = error.message;
stack = error.stack;
+1 -6
View File
@@ -1,10 +1,5 @@
{
"include": [
"**/*",
"**/.server/**/*",
"**/.client/**/*",
".react-router/types/**/*"
],
"include": ["**/*", "**/.server/**/*", "**/.client/**/*", ".react-router/types/**/*"],
"compilerOptions": {
"lib": ["DOM", "DOM.Iterable", "ES2022"],
"types": ["node", "vite/client"],
@@ -30,12 +30,7 @@ export default function DefaultSearchDialog(props: SharedProps) {
});
return (
<SearchDialog
search={search}
onSearchChange={setSearch}
isLoading={query.isLoading}
{...props}
>
<SearchDialog search={search} onSearchChange={setSearch} isLoading={query.isLoading} {...props}>
<SearchDialogOverlay />
<SearchDialogContent>
<SearchDialogHeader>
+1 -6
View File
@@ -1,11 +1,6 @@
import type { Route } from './+types/page';
import { DocsLayout } from 'fumadocs-ui/layouts/docs';
import {
DocsBody,
DocsDescription,
DocsPage,
DocsTitle,
} from 'fumadocs-ui/layouts/docs/page';
import { DocsBody, DocsDescription, DocsPage, DocsTitle } from 'fumadocs-ui/layouts/docs/page';
import { source } from '@/lib/source';
import defaultMdxComponents from 'fumadocs-ui/mdx';
import browserCollections from 'fumadocs-mdx:collections/browser';
+1 -3
View File
@@ -54,9 +54,7 @@ export function ErrorBoundary({ error }: Route.ErrorBoundaryProps) {
if (isRouteErrorResponse(error)) {
message = error.status === 404 ? '404' : 'Error';
details =
error.status === 404
? 'The requested page could not be found.'
: error.statusText || details;
error.status === 404 ? 'The requested page could not be found.' : error.statusText || details;
} else if (import.meta.env.DEV && error && error instanceof Error) {
details = error.message;
stack = error.stack;
+1 -6
View File
@@ -1,10 +1,5 @@
{
"include": [
"**/*",
"**/.server/**/*",
"**/.client/**/*",
".react-router/types/**/*"
],
"include": ["**/*", "**/.server/**/*", "**/.client/**/*", ".react-router/types/**/*"],
"compilerOptions": {
"lib": ["DOM", "DOM.Iterable", "ES2022"],
"types": ["node", "vite/client"],
+1 -6
View File
@@ -1,11 +1,6 @@
import type { Route } from './+types/page';
import { DocsLayout } from 'fumadocs-ui/layouts/docs';
import {
DocsBody,
DocsDescription,
DocsPage,
DocsTitle,
} from 'fumadocs-ui/layouts/docs/page';
import { DocsBody, DocsDescription, DocsPage, DocsTitle } from 'fumadocs-ui/layouts/docs/page';
import { source } from '@/lib/source';
import defaultMdxComponents from 'fumadocs-ui/mdx';
import browserCollections from 'fumadocs-mdx:collections/browser';

Some files were not shown because too many files have changed in this diff Show More