Files
skillhub/apps/web/app/[locale]/layout.tsx
airano-ir 16ee9e3beb sync: malware page, security alerts, review diagnostics, skill detail improvements
- Add malware security advisory page and SecurityAlertBanner component
- Add review diagnostics API and reviewed stats page
- Improve skill detail page with better scoring display and metadata
- Update review API endpoints with enhanced filtering and stats
- Add skill file serving improvements and cache enhancements
- Remove legacy curation scripts (moved to internal tooling)
- Update CLI search with score filtering support

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-31 16:08:12 +02:00

84 lines
2.3 KiB
TypeScript

import type { Metadata } from 'next';
import { NextIntlClientProvider } from 'next-intl';
import { getMessages, getTranslations, setRequestLocale } from 'next-intl/server';
import { notFound } from 'next/navigation';
import { locales, localeDirection, type Locale } from '@/i18n';
import { Providers } from '../providers';
import { Suspense } from 'react';
import { SecurityAlertBanner } from '@/components/SecurityAlertBanner';
import { QueryNotification } from '@/components/QueryNotification';
import { ProgressBar } from '@/components/ProgressBar';
import '../globals.css';
export async function generateMetadata({
params,
}: {
params: Promise<{ locale: string }>;
}): Promise<Metadata> {
const { locale } = await params;
const t = await getTranslations({ locale, namespace: 'metadata' });
const primaryDomain = process.env.NEXT_PUBLIC_APP_URL || 'https://skills.palebluedot.live';
return {
title: {
default: t('title'),
template: `%s | SkillHub`,
},
description: t('description'),
keywords: ['AI', 'Agent', 'Skills', 'Claude', 'Codex', 'Copilot', 'Marketplace'],
authors: [{ name: 'SkillHub' }],
icons: {
icon: '/logo.svg',
shortcut: '/logo.svg',
apple: '/logo.svg',
},
openGraph: {
title: t('title'),
description: t('description'),
type: 'website',
locale: locale === 'fa' ? 'fa_IR' : 'en_US',
url: `${primaryDomain}/${locale}`,
},
};
}
export function generateStaticParams() {
return locales.map((locale) => ({ locale }));
}
export default async function LocaleLayout({
children,
params,
}: {
children: React.ReactNode;
params: Promise<{ locale: string }>;
}) {
const { locale } = await params;
if (!locales.includes(locale as Locale)) {
notFound();
}
setRequestLocale(locale);
const messages = await getMessages();
const dir = localeDirection[locale as Locale];
return (
<html lang={locale} dir={dir} suppressHydrationWarning>
<body className="min-h-screen bg-surface">
<Providers>
<NextIntlClientProvider messages={messages}>
<SecurityAlertBanner />
<Suspense fallback={null}>
<QueryNotification />
</Suspense>
<ProgressBar />
{children}
</NextIntlClientProvider>
</Providers>
</body>
</html>
);
}