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:
7418andClaude Opus 4.6 committed 2026-03-10 22:03:52 +08:00
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

+55 -4
View File
@@ -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 }) {
+17
View File
@@ -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' },
],
};
}
+14
View File
@@ -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`,
};
}
+26
View File
@@ -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>
+2 -1
View File
@@ -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: {