feat: SEO optimization, favicons, and dark mode download button fix
- Add comprehensive metadata: Open Graph, Twitter Card, keywords, robots - Add per-page metadata with i18n alternates for homepage (en/zh) - Add robots.ts, sitemap.ts (dynamic from docs), manifest.ts - Add favicon (icon.png 32px), apple-icon.png (180px), PWA icons (192/512px) - Add og-image.png (1200x630) for social sharing - Update site.config.ts with canonical URL and improved description - Fix DownloadButton dark mode: white-on-white text → dark:bg-white dark:text-black Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
1 parent
3f56edc4c8
commit
6e666b1297
13 files changed
+141
-6
No files matched your search
Binary file not shown.
|
After Width: | Height: | Size: 1.4 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 20 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 105 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 82 KiB |
@@ -1,3 +1,4 @@
|
||||
import type { Metadata } from 'next';
|
||||
import { getMarketingContent } from '../../../../content/marketing';
|
||||
import { ScrollNav } from '@/components/marketing/ScrollNav';
|
||||
import { HeroSection } from '@/components/marketing/HeroSection';
|
||||
@@ -6,6 +7,31 @@ import { IntegrationsSection } from '@/components/marketing/IntegrationsSection'
|
||||
import { FAQSection } from '@/components/marketing/FAQAccordion';
|
||||
import { FinalCTA } from '@/components/marketing/FinalCTA';
|
||||
import { SiteFooter } from '@/components/marketing/SiteFooter';
|
||||
import { siteConfig } from '@/lib/site.config';
|
||||
|
||||
export async function generateMetadata({
|
||||
params,
|
||||
}: {
|
||||
params: Promise<{ lang: string }>;
|
||||
}): Promise<Metadata> {
|
||||
const { lang } = await params;
|
||||
const isZh = lang === 'zh';
|
||||
return {
|
||||
title: isZh
|
||||
? 'CodePilot — Claude Code 的桌面工作空间'
|
||||
: 'CodePilot — Desktop Workspace for Claude Code',
|
||||
description: isZh
|
||||
? '将对话、Provider、MCP、Skills 和项目上下文整合到一个桌面界面中。'
|
||||
: siteConfig.description,
|
||||
alternates: {
|
||||
canonical: isZh ? `${siteConfig.url}/zh` : siteConfig.url,
|
||||
languages: {
|
||||
en: siteConfig.url,
|
||||
zh: `${siteConfig.url}/zh`,
|
||||
},
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
export default async function HomePage({
|
||||
params,
|
||||
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 18 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 1.3 KiB |
@@ -1,13 +1,64 @@
|
||||
import './global.css';
|
||||
import type { ReactNode } from 'react';
|
||||
import type { Metadata } from 'next';
|
||||
import { siteConfig } from '@/lib/site.config';
|
||||
|
||||
const title = {
|
||||
default: 'CodePilot — Desktop Workspace for Claude Code',
|
||||
template: '%s | CodePilot',
|
||||
};
|
||||
|
||||
const description = siteConfig.description;
|
||||
|
||||
export const metadata: Metadata = {
|
||||
title: {
|
||||
default: 'CodePilot',
|
||||
template: '%s | CodePilot',
|
||||
title,
|
||||
description,
|
||||
metadataBase: new URL(siteConfig.url),
|
||||
keywords: [
|
||||
'Claude Code',
|
||||
'AI coding',
|
||||
'desktop app',
|
||||
'MCP',
|
||||
'Claude',
|
||||
'Anthropic',
|
||||
'code assistant',
|
||||
'AI agent',
|
||||
],
|
||||
authors: [{ name: 'CodePilot' }],
|
||||
creator: 'CodePilot',
|
||||
openGraph: {
|
||||
type: 'website',
|
||||
locale: 'en_US',
|
||||
url: siteConfig.url,
|
||||
siteName: siteConfig.name,
|
||||
title: title.default,
|
||||
description,
|
||||
images: [
|
||||
{
|
||||
url: '/og-image.png',
|
||||
width: 1200,
|
||||
height: 630,
|
||||
alt: 'CodePilot — Desktop Workspace for Claude Code',
|
||||
},
|
||||
],
|
||||
},
|
||||
twitter: {
|
||||
card: 'summary_large_image',
|
||||
title: title.default,
|
||||
description,
|
||||
images: ['/og-image.png'],
|
||||
},
|
||||
robots: {
|
||||
index: true,
|
||||
follow: true,
|
||||
googleBot: {
|
||||
index: true,
|
||||
follow: true,
|
||||
'max-video-preview': -1,
|
||||
'max-image-preview': 'large',
|
||||
'max-snippet': -1,
|
||||
},
|
||||
},
|
||||
description: 'A native desktop GUI client for Claude Code',
|
||||
};
|
||||
|
||||
export default function RootLayout({ children }: { children: ReactNode }) {
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
import type { MetadataRoute } from 'next';
|
||||
|
||||
export default function manifest(): MetadataRoute.Manifest {
|
||||
return {
|
||||
name: 'CodePilot',
|
||||
short_name: 'CodePilot',
|
||||
description: 'Desktop workspace for Claude Code',
|
||||
start_url: '/',
|
||||
display: 'browser',
|
||||
background_color: '#ffffff',
|
||||
theme_color: '#2563eb',
|
||||
icons: [
|
||||
{ src: '/icon-192.png', sizes: '192x192', type: 'image/png' },
|
||||
{ src: '/icon-512.png', sizes: '512x512', type: 'image/png' },
|
||||
],
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
import type { MetadataRoute } from 'next';
|
||||
import { siteConfig } from '@/lib/site.config';
|
||||
|
||||
export default function robots(): MetadataRoute.Robots {
|
||||
return {
|
||||
rules: [
|
||||
{
|
||||
userAgent: '*',
|
||||
allow: '/',
|
||||
},
|
||||
],
|
||||
sitemap: `${siteConfig.url}/sitemap.xml`,
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
import type { MetadataRoute } from 'next';
|
||||
import { source } from '@/lib/source';
|
||||
import { siteConfig } from '@/lib/site.config';
|
||||
|
||||
export default function sitemap(): MetadataRoute.Sitemap {
|
||||
const url = siteConfig.url;
|
||||
|
||||
const staticPages: MetadataRoute.Sitemap = [
|
||||
{ url, lastModified: new Date(), changeFrequency: 'weekly', priority: 1 },
|
||||
{ url: `${url}/zh`, lastModified: new Date(), changeFrequency: 'weekly', priority: 0.9 },
|
||||
];
|
||||
|
||||
const docPages: MetadataRoute.Sitemap = source.getPages().map((page) => {
|
||||
const lang = page.locale ?? 'en';
|
||||
const slugPath = page.slugs.join('/');
|
||||
const prefix = lang === 'en' ? '' : `/${lang}`;
|
||||
return {
|
||||
url: `${url}${prefix}/docs/${slugPath}`,
|
||||
lastModified: new Date(),
|
||||
changeFrequency: 'weekly' as const,
|
||||
priority: 0.7,
|
||||
};
|
||||
});
|
||||
|
||||
return [...staticPages, ...docPages];
|
||||
}
|
||||
@@ -6,7 +6,7 @@ export function DownloadButton({ label = 'Download Latest Release' }: { label?:
|
||||
href={`${siteConfig.repo.releases}/latest`}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="mt-4 mb-6 inline-flex items-center gap-2 rounded-full bg-foreground px-6 py-3 text-sm font-semibold text-background transition-opacity hover:opacity-80"
|
||||
className="mt-4 mb-6 inline-flex items-center gap-2 rounded-full bg-foreground px-6 py-3 text-sm font-semibold text-background transition-opacity hover:opacity-80 dark:bg-white dark:text-black"
|
||||
>
|
||||
{label}
|
||||
<span aria-hidden="true">→</span>
|
||||
|
||||
@@ -5,7 +5,8 @@
|
||||
*/
|
||||
export const siteConfig = {
|
||||
name: 'CodePilot',
|
||||
description: 'A native desktop GUI client for Claude Code',
|
||||
description: 'Desktop workspace for Claude Code — conversations, providers, MCP, and project context in one place.',
|
||||
url: 'https://www.codepilot.sh',
|
||||
|
||||
// Canonical repository
|
||||
repo: {
|
||||
|
||||
Reference in new issue
Block a user