Compare commits
3 Commits
cloudflare
...
dev/fumado
Author | SHA1 | Date | |
---|---|---|---|
|
0297c3cd88 | ||
|
f3fb9824f4 | ||
|
b577847b4b |
10
package.json
10
package.json
@ -20,9 +20,9 @@
|
|||||||
"@ai-sdk/openai": "^1.1.13",
|
"@ai-sdk/openai": "^1.1.13",
|
||||||
"@aws-sdk/client-s3": "^3.758.0",
|
"@aws-sdk/client-s3": "^3.758.0",
|
||||||
"@aws-sdk/s3-request-presigner": "^3.758.0",
|
"@aws-sdk/s3-request-presigner": "^3.758.0",
|
||||||
"@content-collections/core": "^0.8.0",
|
"@content-collections/core": "^0.8.2",
|
||||||
"@content-collections/mdx": "^0.2.0",
|
"@content-collections/mdx": "^0.2.2",
|
||||||
"@content-collections/next": "^0.2.4",
|
"@content-collections/next": "^0.2.6",
|
||||||
"@dnd-kit/core": "^6.3.1",
|
"@dnd-kit/core": "^6.3.1",
|
||||||
"@dnd-kit/modifiers": "^9.0.0",
|
"@dnd-kit/modifiers": "^9.0.0",
|
||||||
"@dnd-kit/sortable": "^10.0.0",
|
"@dnd-kit/sortable": "^10.0.0",
|
||||||
@ -83,8 +83,8 @@
|
|||||||
"drizzle-orm": "^0.39.3",
|
"drizzle-orm": "^0.39.3",
|
||||||
"embla-carousel-react": "^8.5.2",
|
"embla-carousel-react": "^8.5.2",
|
||||||
"framer-motion": "^12.4.7",
|
"framer-motion": "^12.4.7",
|
||||||
"fumadocs-core": "^15.1.2",
|
"fumadocs-core": "^15.2.8",
|
||||||
"fumadocs-ui": "^15.1.2",
|
"fumadocs-ui": "^15.2.8",
|
||||||
"geist": "^1.3.1",
|
"geist": "^1.3.1",
|
||||||
"input-otp": "^1.4.2",
|
"input-otp": "^1.4.2",
|
||||||
"lucide-react": "^0.483.0",
|
"lucide-react": "^0.483.0",
|
||||||
|
1277
pnpm-lock.yaml
generated
1277
pnpm-lock.yaml
generated
File diff suppressed because it is too large
Load Diff
@ -5,25 +5,15 @@ import { websiteConfig } from '@/config/website';
|
|||||||
import { docsI18nConfig } from '@/lib/docs/i18n';
|
import { docsI18nConfig } from '@/lib/docs/i18n';
|
||||||
import { source } from '@/lib/docs/source';
|
import { source } from '@/lib/docs/source';
|
||||||
import { getUrlWithLocale } from '@/lib/urls/urls';
|
import { getUrlWithLocale } from '@/lib/urls/urls';
|
||||||
import { I18nProvider, type Translations } from 'fumadocs-ui/i18n';
|
|
||||||
import { DocsLayout } from 'fumadocs-ui/layouts/docs';
|
import { DocsLayout } from 'fumadocs-ui/layouts/docs';
|
||||||
import type { BaseLayoutProps } from 'fumadocs-ui/layouts/shared';
|
import type { BaseLayoutProps } from 'fumadocs-ui/layouts/shared';
|
||||||
import { BookIcon, HomeIcon } from 'lucide-react';
|
import { HomeIcon } from 'lucide-react';
|
||||||
import type { Locale } from 'next-intl';
|
import type { Locale } from 'next-intl';
|
||||||
import { getTranslations } from 'next-intl/server';
|
import { getTranslations } from 'next-intl/server';
|
||||||
import type { ReactNode } from 'react';
|
import type { ReactNode } from 'react';
|
||||||
|
|
||||||
import '@/styles/mdx.css';
|
import '@/styles/mdx.css';
|
||||||
|
|
||||||
// available languages that will be displayed on UI
|
|
||||||
// make sure `locale` is consistent with your i18n config
|
|
||||||
const locales = Object.entries(websiteConfig.i18n.locales).map(
|
|
||||||
([locale, data]) => ({
|
|
||||||
name: data.name,
|
|
||||||
locale,
|
|
||||||
})
|
|
||||||
);
|
|
||||||
|
|
||||||
interface DocsLayoutProps {
|
interface DocsLayoutProps {
|
||||||
children: ReactNode;
|
children: ReactNode;
|
||||||
params: Promise<{ locale: Locale }>;
|
params: Promise<{ locale: Locale }>;
|
||||||
@ -50,17 +40,6 @@ export default async function DocsRootLayout({
|
|||||||
const { locale } = await params;
|
const { locale } = await params;
|
||||||
const t = await getTranslations({ locale, namespace: 'DocsPage' });
|
const t = await getTranslations({ locale, namespace: 'DocsPage' });
|
||||||
|
|
||||||
// Create translations object for fumadocs-ui from our message files
|
|
||||||
const translations: Partial<Translations> = {
|
|
||||||
toc: t('toc'),
|
|
||||||
search: t('search'),
|
|
||||||
lastUpdate: t('lastUpdate'),
|
|
||||||
searchNoResult: t('searchNoResult'),
|
|
||||||
previousPage: t('previousPage'),
|
|
||||||
nextPage: t('nextPage'),
|
|
||||||
chooseLanguage: t('chooseLanguage'),
|
|
||||||
};
|
|
||||||
|
|
||||||
// Docs layout configurations
|
// Docs layout configurations
|
||||||
const showLocaleSwitch = Object.keys(websiteConfig.i18n.locales).length > 1;
|
const showLocaleSwitch = Object.keys(websiteConfig.i18n.locales).length > 1;
|
||||||
const docsOptions: BaseLayoutProps = {
|
const docsOptions: BaseLayoutProps = {
|
||||||
@ -85,14 +64,14 @@ export default async function DocsRootLayout({
|
|||||||
},
|
},
|
||||||
...(websiteConfig.metadata.social?.twitter
|
...(websiteConfig.metadata.social?.twitter
|
||||||
? [
|
? [
|
||||||
{
|
{
|
||||||
type: 'icon' as const,
|
type: 'icon' as const,
|
||||||
icon: <XTwitterIcon />,
|
icon: <XTwitterIcon />,
|
||||||
text: 'X',
|
text: 'X',
|
||||||
url: websiteConfig.metadata.social.twitter,
|
url: websiteConfig.metadata.social.twitter,
|
||||||
secondary: true,
|
secondary: true,
|
||||||
},
|
},
|
||||||
]
|
]
|
||||||
: []),
|
: []),
|
||||||
],
|
],
|
||||||
themeSwitch: {
|
themeSwitch: {
|
||||||
@ -103,10 +82,8 @@ export default async function DocsRootLayout({
|
|||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<I18nProvider locales={locales} locale={locale} translations={translations}>
|
<DocsLayout tree={source.pageTree[locale]} {...docsOptions}>
|
||||||
<DocsLayout tree={source.pageTree[locale]} {...docsOptions}>
|
{children}
|
||||||
{children}
|
</DocsLayout>
|
||||||
</DocsLayout>
|
|
||||||
</I18nProvider>
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -52,7 +52,7 @@ export default async function LocaleLayout({
|
|||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
<NextIntlClientProvider>
|
<NextIntlClientProvider>
|
||||||
<Providers>
|
<Providers locale={locale}>
|
||||||
{children}
|
{children}
|
||||||
|
|
||||||
<Toaster richColors position="top-right" offset={64} />
|
<Toaster richColors position="top-right" offset={64} />
|
||||||
|
@ -4,9 +4,16 @@ import { ActiveThemeProvider } from '@/components/layout/active-theme-provider';
|
|||||||
import { PaymentProvider } from '@/components/layout/payment-provider';
|
import { PaymentProvider } from '@/components/layout/payment-provider';
|
||||||
import { TooltipProvider } from '@/components/ui/tooltip';
|
import { TooltipProvider } from '@/components/ui/tooltip';
|
||||||
import { websiteConfig } from '@/config/website';
|
import { websiteConfig } from '@/config/website';
|
||||||
|
import { Translations } from 'fumadocs-ui/i18n';
|
||||||
import { RootProvider } from 'fumadocs-ui/provider';
|
import { RootProvider } from 'fumadocs-ui/provider';
|
||||||
|
import { useTranslations } from 'next-intl';
|
||||||
import { ThemeProvider, useTheme } from 'next-themes';
|
import { ThemeProvider, useTheme } from 'next-themes';
|
||||||
import type { PropsWithChildren } from 'react';
|
import type { ReactNode } from 'react';
|
||||||
|
|
||||||
|
interface ProvidersProps {
|
||||||
|
children: ReactNode;
|
||||||
|
locale: string;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Providers
|
* Providers
|
||||||
@ -19,10 +26,31 @@ import type { PropsWithChildren } from 'react';
|
|||||||
* - TooltipProvider: Provides the tooltip to the app.
|
* - TooltipProvider: Provides the tooltip to the app.
|
||||||
* - PaymentProvider: Provides the payment state to the app.
|
* - PaymentProvider: Provides the payment state to the app.
|
||||||
*/
|
*/
|
||||||
export function Providers({ children }: PropsWithChildren) {
|
export function Providers({ children, locale }: ProvidersProps) {
|
||||||
const theme = useTheme();
|
const theme = useTheme();
|
||||||
const defaultMode = websiteConfig.metadata.mode?.defaultMode ?? 'system';
|
const defaultMode = websiteConfig.metadata.mode?.defaultMode ?? 'system';
|
||||||
|
|
||||||
|
// available languages that will be displayed in the docs UI
|
||||||
|
// make sure `locale` is consistent with your i18n config
|
||||||
|
const locales = Object.entries(websiteConfig.i18n.locales).map(
|
||||||
|
([locale, data]) => ({
|
||||||
|
name: data.name,
|
||||||
|
locale,
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
// translations object for fumadocs-ui from our message files
|
||||||
|
const t = useTranslations('DocsPage');
|
||||||
|
const translations: Partial<Translations> = {
|
||||||
|
toc: t('toc'),
|
||||||
|
search: t('search'),
|
||||||
|
lastUpdate: t('lastUpdate'),
|
||||||
|
searchNoResult: t('searchNoResult'),
|
||||||
|
previousPage: t('previousPage'),
|
||||||
|
nextPage: t('nextPage'),
|
||||||
|
chooseLanguage: t('chooseLanguage'),
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ThemeProvider
|
<ThemeProvider
|
||||||
attribute="class"
|
attribute="class"
|
||||||
@ -31,7 +59,7 @@ export function Providers({ children }: PropsWithChildren) {
|
|||||||
disableTransitionOnChange
|
disableTransitionOnChange
|
||||||
>
|
>
|
||||||
<ActiveThemeProvider>
|
<ActiveThemeProvider>
|
||||||
<RootProvider theme={theme}>
|
<RootProvider theme={theme} i18n={{ locale, locales, translations }}>
|
||||||
<TooltipProvider>
|
<TooltipProvider>
|
||||||
<PaymentProvider>{children}</PaymentProvider>
|
<PaymentProvider>{children}</PaymentProvider>
|
||||||
</TooltipProvider>
|
</TooltipProvider>
|
||||||
|
@ -55,57 +55,19 @@ const searchAPI = createI18nSearchAPI('advanced', {
|
|||||||
});
|
});
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* Fumadocs 15.2.8 fixed the bug that the `locale` is not passed to the search API
|
||||||
|
*
|
||||||
|
* ref:
|
||||||
|
* https://x.com/indie_maker_fox/status/1913457083997192589
|
||||||
|
*
|
||||||
|
* NOTICE:
|
||||||
|
* Fumadocs 15.1.2 has a bug that the `locale` is not passed to the search API
|
||||||
* 1. Wrap the GET handler for debugging docs search
|
* 1. Wrap the GET handler for debugging docs search
|
||||||
* 2. Detect locale from referer header, and add the locale parameter to the search API
|
* 2. Detect locale from referer header, and add the locale parameter to the search API
|
||||||
* 3. Fumadocs core searchAPI get `locale` from searchParams, and pass it to the search API
|
* 3. Fumadocs core searchAPI get `locale` from searchParams, and pass it to the search API
|
||||||
* https://github.com/fuma-nama/fumadocs/blob/dev/packages/core/src/search/orama/create-endpoint.ts#L19
|
* https://github.com/fuma-nama/fumadocs/blob/dev/packages/core/src/search/orama/create-endpoint.ts#L19
|
||||||
*/
|
*/
|
||||||
export const GET = async (request: Request) => {
|
export const GET = async (request: Request) => {
|
||||||
const url = new URL(request.url);
|
const response = await searchAPI.GET(request);
|
||||||
const query = url.searchParams.get('query') || '';
|
|
||||||
let locale = url.searchParams.get('locale') || docsI18nConfig.defaultLanguage;
|
|
||||||
|
|
||||||
// detect locale from referer header
|
|
||||||
const referer = request.headers.get('referer');
|
|
||||||
if (referer) {
|
|
||||||
try {
|
|
||||||
const refererUrl = new URL(referer);
|
|
||||||
console.log('search, referer pathname:', refererUrl.pathname);
|
|
||||||
const refererPathParts = refererUrl.pathname.split('/').filter(Boolean);
|
|
||||||
console.log('search, referer path parts:', refererPathParts);
|
|
||||||
if (
|
|
||||||
refererPathParts.length > 0 &&
|
|
||||||
docsI18nConfig.languages.includes(refererPathParts[0])
|
|
||||||
) {
|
|
||||||
locale = refererPathParts[0];
|
|
||||||
console.log(`search, detected locale from referer: ${locale}`);
|
|
||||||
}
|
|
||||||
} catch (e) {
|
|
||||||
console.error('search, error parsing referer:', e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
console.log(`search, request: query="${query}", detected locale="${locale}"`);
|
|
||||||
|
|
||||||
// ensure locale parameter is passed to search API
|
|
||||||
const searchUrl = new URL(url);
|
|
||||||
searchUrl.searchParams.set('locale', locale);
|
|
||||||
|
|
||||||
const modifiedRequest = new Request(searchUrl, {
|
|
||||||
headers: request.headers,
|
|
||||||
method: request.method,
|
|
||||||
body: request.body,
|
|
||||||
cache: request.cache,
|
|
||||||
credentials: request.credentials,
|
|
||||||
integrity: request.integrity,
|
|
||||||
keepalive: request.keepalive,
|
|
||||||
mode: request.mode,
|
|
||||||
redirect: request.redirect,
|
|
||||||
referrer: request.referrer,
|
|
||||||
referrerPolicy: request.referrerPolicy,
|
|
||||||
signal: request.signal,
|
|
||||||
});
|
|
||||||
|
|
||||||
const response = await searchAPI.GET(modifiedRequest);
|
|
||||||
return response;
|
return response;
|
||||||
};
|
};
|
||||||
|
@ -28,7 +28,16 @@ export default function Example() {
|
|||||||
className="bg-transparent px-4 py-2 text-sm focus-visible:outline-none"
|
className="bg-transparent px-4 py-2 text-sm focus-visible:outline-none"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<DynamicCodeBlock lang={lang} code={code} options={{}} />
|
<DynamicCodeBlock
|
||||||
|
lang={lang}
|
||||||
|
code={code}
|
||||||
|
options={{
|
||||||
|
themes: {
|
||||||
|
light: 'github-light',
|
||||||
|
dark: 'github-dark'
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user