Some checks failed
Deploy to Production / deploy (push) Failing after 46s
Ported and adapted from the BuildMyDiscord SEO setup: - lib/seo.ts — single source for site constants, the FAQ data (shared by the rendered FAQ and the FAQPage schema so they never drift) and JSON-LD builders. - Rich root metadata: title template, keywords, Open Graph, Twitter card, robots directives, canonical. - JSON-LD: Organization + WebSite + SoftwareApplication sitewide, FAQPage on the landing page. No AggregateRating — there are no real reviews yet. - app/robots.ts — allow all, explicit allow-list for AI answer-engine crawlers (GPTBot, ClaudeBot, PerplexityBot, …), disallow private routes. - app/sitemap.ts — every public marketing + docs route. - app/opengraph-image.tsx — monochrome on-brand 1200x630 share card. - app/manifest.ts + public/llms.txt. - Per-page metadata for pricing, changelog, security, privacy, terms, docs, templates and status. - opengraph-image + apple-icon pinned to the edge runtime — next/og crashes during a Node-runtime prerender. Verified: next build passes; /robots.txt, /sitemap.xml, /manifest.webmanifest and /opengraph-image all generate. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
72 lines
1.6 KiB
TypeScript
72 lines
1.6 KiB
TypeScript
import { JsonLd } from '@/components/json-ld';
|
|
import { SiteBanner } from '@/components/site-banner';
|
|
import {
|
|
SEO_KEYWORDS,
|
|
SITE_DESCRIPTION,
|
|
SITE_NAME,
|
|
SITE_TAGLINE,
|
|
SITE_URL,
|
|
siteJsonLd,
|
|
} from '@/lib/seo';
|
|
import { GeistMono } from 'geist/font/mono';
|
|
import { GeistSans } from 'geist/font/sans';
|
|
import type { Metadata } from 'next';
|
|
import './globals.css';
|
|
|
|
const TITLE = `${SITE_NAME} — ${SITE_TAGLINE}`;
|
|
|
|
export const metadata: Metadata = {
|
|
metadataBase: new URL(SITE_URL),
|
|
title: {
|
|
default: TITLE,
|
|
template: `%s | ${SITE_NAME}`,
|
|
},
|
|
description: SITE_DESCRIPTION,
|
|
applicationName: SITE_NAME,
|
|
keywords: SEO_KEYWORDS,
|
|
authors: [{ name: SITE_NAME }],
|
|
creator: SITE_NAME,
|
|
publisher: SITE_NAME,
|
|
alternates: { canonical: '/' },
|
|
openGraph: {
|
|
type: 'website',
|
|
locale: 'en_US',
|
|
url: SITE_URL,
|
|
siteName: SITE_NAME,
|
|
title: TITLE,
|
|
description: SITE_DESCRIPTION,
|
|
},
|
|
twitter: {
|
|
card: 'summary_large_image',
|
|
title: TITLE,
|
|
description: SITE_DESCRIPTION,
|
|
},
|
|
robots: {
|
|
index: true,
|
|
follow: true,
|
|
googleBot: {
|
|
index: true,
|
|
follow: true,
|
|
'max-image-preview': 'large',
|
|
'max-snippet': -1,
|
|
'max-video-preview': -1,
|
|
},
|
|
},
|
|
};
|
|
|
|
export default function RootLayout({ children }: { children: React.ReactNode }) {
|
|
return (
|
|
<html
|
|
lang="en"
|
|
className={`${GeistSans.variable} ${GeistMono.variable}`}
|
|
suppressHydrationWarning
|
|
>
|
|
<body>
|
|
<JsonLd data={siteJsonLd()} />
|
|
<SiteBanner />
|
|
{children}
|
|
</body>
|
|
</html>
|
|
);
|
|
}
|