Skip to content

Commit f786b05

Browse files
committed
upgrade to latest deps
1 parent 377eb7e commit f786b05

8 files changed

Lines changed: 2096 additions & 1200 deletions

File tree

‎app/docs/[[...slug]]/page.tsx‎

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,19 @@ import { notFound } from 'next/navigation';
99
import { getMDXComponents } from '@/mdx-components';
1010
import type { Metadata } from 'next';
1111
import { createRelativeLink } from 'fumadocs-ui/mdx';
12+
import type { TOCItemType } from 'fumadocs-core/toc';
13+
import { Children, cloneElement, isValidElement, type ReactNode } from 'react';
14+
15+
// TOC items are rendered as links, so unwrap any links in heading titles to avoid nested <a>
16+
function stripLinks(node: ReactNode): ReactNode {
17+
return Children.map(node, (child) => {
18+
if (!isValidElement<{ href?: string; children?: ReactNode }>(child)) return child;
19+
const children = stripLinks(child.props.children);
20+
return child.props.href != null ? children : cloneElement(child, undefined, children);
21+
});
22+
}
23+
24+
const tocWithoutLinks = (toc: TOCItemType[]) => toc.map((item) => ({ ...item, title: stripLinks(item.title) }));
1225

1326
export default async function Page(props: PageProps<'/docs/[[...slug]]'>) {
1427
const params = await props.params;
@@ -18,7 +31,7 @@ export default async function Page(props: PageProps<'/docs/[[...slug]]'>) {
1831
const MDX = page.data.body;
1932

2033
return (
21-
<DocsPage toc={page.data.toc} full={page.data.full}>
34+
<DocsPage toc={tocWithoutLinks(page.data.toc)} full={page.data.full}>
2235
<DocsTitle>{page.data.title}</DocsTitle>
2336
<DocsDescription>{page.data.description}</DocsDescription>
2437
<DocsBody>

‎app/layout.tsx‎

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,15 @@ export default function Layout({ children }: LayoutProps<'/'>) {
3434
gtag('config', 'G-JV0T1EB3R7');
3535
`}
3636
</Script>
37-
<RootProvider>{children}</RootProvider>
37+
<RootProvider
38+
theme={{
39+
// next-themes' inline script only needs to run from SSR; mark it as a data block on the client
40+
// to avoid React 19's "Encountered a script tag while rendering React component" dev warning
41+
scriptProps: { type: typeof window === 'undefined' ? 'text/javascript' : 'text/plain' },
42+
}}
43+
>
44+
{children}
45+
</RootProvider>
3846
</body>
3947
</html>
4048
);

‎components/template-intro.tsx‎

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,13 @@
11
import Link from 'next/link';
2-
import { ExternalLink, Github } from 'lucide-react';
2+
import { ExternalLink } from 'lucide-react';
3+
4+
function GithubIcon({ className }: { className?: string }) {
5+
return (
6+
<svg className={className} viewBox="0 0 24 24" fill="currentColor" aria-hidden="true">
7+
<path d="M12 .5C5.65.5.5 5.65.5 12c0 5.08 3.29 9.39 7.86 10.91.58.11.79-.25.79-.56v-1.97c-3.2.7-3.87-1.54-3.87-1.54-.52-1.33-1.28-1.69-1.28-1.69-1.04-.71.08-.7.08-.7 1.15.08 1.76 1.18 1.76 1.18 1.03 1.76 2.69 1.25 3.35.96.1-.75.4-1.25.73-1.54-2.55-.29-5.24-1.28-5.24-5.68 0-1.26.45-2.28 1.18-3.09-.12-.29-.51-1.46.11-3.04 0 0 .97-.31 3.17 1.18a11 11 0 0 1 5.77 0c2.2-1.49 3.17-1.18 3.17-1.18.63 1.58.23 2.75.11 3.04.74.81 1.18 1.83 1.18 3.09 0 4.41-2.69 5.38-5.25 5.67.41.36.78 1.06.78 2.14v3.17c0 .31.21.68.8.56A11.5 11.5 0 0 0 23.5 12C23.5 5.65 18.35.5 12 .5Z" />
8+
</svg>
9+
);
10+
}
311

412
interface TemplateIntroProps {
513
image: string;
@@ -45,7 +53,7 @@ export default function TemplateIntro({ image, repo, demo, name }: TemplateIntro
4553
rel="noopener noreferrer"
4654
className="inline-flex items-center gap-2 px-6 py-2.5 bg-white dark:bg-slate-900 border border-slate-200 dark:border-slate-800 hover:bg-slate-50 dark:hover:bg-slate-800 text-slate-700 dark:text-slate-200 rounded-lg font-medium transition-all shadow-sm hover:shadow-md"
4755
>
48-
<Github className="w-4 h-4" />
56+
<GithubIcon className="w-4 h-4" />
4957
View Source
5058
</a>
5159
</div>

‎components/templates.tsx‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
'use client';
2-
import { cn } from 'fumadocs-ui/utils/cn';
2+
import { cn } from '@/app/lib/utils';
33
import React, { useState, useMemo } from 'react';
44

55
export interface Template {

‎mdx-components.tsx‎

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import defaultMdxComponents from 'fumadocs-ui/mdx';
22
import type { MDXComponents } from 'mdx/types';
3+
import { Children, isValidElement, type ComponentProps, type ReactNode } from 'react';
34
import { Info } from '@/components/info';
45
import { Tip } from '@/components/tip';
56
import { YouTube } from '@/components/youtube';
@@ -8,9 +9,45 @@ import { ScreenshotsGallery } from '@/components/screenshots-gallery';
89
import { CopyBlock } from '@/app/components/copy-block';
910
import { Pipeline, PipelineStep } from '@/components/pipeline';
1011

12+
const DefaultImg = defaultMdxComponents.img;
13+
14+
// remark-image can emit fractional SVG dimensions, which next/image rejects
15+
const round = (n?: number | string) => (n == null ? n : Math.round(Number(n)));
16+
17+
const hasLink = (children: ReactNode): boolean =>
18+
Children.toArray(children).some(
19+
(c) => isValidElement<{ href?: string; children?: ReactNode }>(c) && (c.props.href != null || hasLink(c.props.children)),
20+
);
21+
22+
// fumadocs' Heading wraps its children in an anchor link, so headings that are already links
23+
// would render a nested <a> (invalid HTML that breaks hydration); render those as plain headings
24+
function heading(as: 'h1' | 'h2' | 'h3' | 'h4' | 'h5' | 'h6') {
25+
const Default = defaultMdxComponents[as];
26+
return function LinkSafeHeading(props: ComponentProps<typeof as>) {
27+
if (!hasLink(props.children)) return <Default {...props} />;
28+
const As = as;
29+
return <As {...props} className={['scroll-m-28', props.className].filter(Boolean).join(' ')} />;
30+
};
31+
}
32+
33+
const headings = {
34+
h1: heading('h1'),
35+
h2: heading('h2'),
36+
h3: heading('h3'),
37+
h4: heading('h4'),
38+
h5: heading('h5'),
39+
h6: heading('h6'),
40+
};
41+
42+
const Img = (props: ComponentProps<typeof DefaultImg>) => (
43+
<DefaultImg {...props} width={round(props.width)} height={round(props.height)} />
44+
);
45+
1146
export function getMDXComponents(components?: MDXComponents): MDXComponents {
1247
return {
1348
...defaultMdxComponents,
49+
...headings,
50+
img: Img,
1451
Info,
1552
Tip,
1653
YouTube,

0 commit comments

Comments
 (0)