mirror of
https://github.com/hanzo-docs/docs.git
synced 2026-07-27 04:31:57 +00:00
Chore: migrate to oxfmt
This commit is contained in:
@@ -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.
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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",
|
||||
],
|
||||
}
|
||||
@@ -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
|
||||
Vendored
+1
-2
@@ -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>
|
||||
|
||||
@@ -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',
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -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()}
|
||||
|
||||
@@ -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>
|
||||
);
|
||||
}
|
||||
@@ -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" />
|
||||
|
||||
@@ -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">
|
||||
|
||||
@@ -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]',
|
||||
)}
|
||||
|
||||
@@ -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);
|
||||
|
||||
+60
-151
File diff suppressed because one or more lines are too long
@@ -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>
|
||||
);
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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,
|
||||
@@ -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,
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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}>
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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" />
|
||||
|
||||
@@ -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(),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -32,10 +32,7 @@ export function Provider({ children }: { children: ReactNode }) {
|
||||
}}
|
||||
>
|
||||
<TooltipProvider>
|
||||
<script
|
||||
suppressHydrationWarning
|
||||
dangerouslySetInnerHTML={{ __html: inject }}
|
||||
/>
|
||||
<script suppressHydrationWarning dangerouslySetInnerHTML={{ __html: inject }} />
|
||||
{children}
|
||||
</TooltipProvider>
|
||||
</RootProvider>
|
||||
|
||||
@@ -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[],
|
||||
);
|
||||
}
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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 [
|
||||
|
||||
@@ -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>
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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';
|
||||
|
||||
|
||||
@@ -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')
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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>
|
||||
))}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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),
|
||||
);
|
||||
|
||||
@@ -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',
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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} />
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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`,
|
||||
|
||||
@@ -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(),
|
||||
|
||||
@@ -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
@@ -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.`;
|
||||
|
||||
@@ -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}`);
|
||||
|
||||
@@ -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 (
|
||||
|
||||
@@ -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),
|
||||
});
|
||||
|
||||
@@ -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'
|
||||
);
|
||||
}
|
||||
@@ -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,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
@@ -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));
|
||||
|
||||
@@ -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'),
|
||||
|
||||
@@ -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,8 +1,8 @@
|
||||
{
|
||||
"name": "example-astro",
|
||||
"type": "module",
|
||||
"version": "0.0.1",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"dev": "astro dev",
|
||||
"start": "astro dev",
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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,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();
|
||||
|
||||
@@ -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 (
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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,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,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,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();
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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,9 +1,3 @@
|
||||
{
|
||||
"pages": [
|
||||
"---API Documentation---",
|
||||
"index",
|
||||
"...",
|
||||
"---References---",
|
||||
"...(api)"
|
||||
]
|
||||
"pages": ["---API Documentation---", "index", "...", "---References---", "...(api)"]
|
||||
}
|
||||
|
||||
@@ -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' }
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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,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';
|
||||
|
||||
@@ -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 @@
|
||||
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,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,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';
|
||||
|
||||
@@ -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,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,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';
|
||||
|
||||
@@ -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,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,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
Reference in New Issue
Block a user