Skip to content

Commit 07c4086

Browse files
authored
Merge pull request #75 from BeyteFlow/copilot/optimize-seo-meta-descriptions
SEO: Full Bing/Copilot compliance — metadata, JSON-LD, sitemap, and heading fixes for all 5 pages
2 parents c4d175f + b263af5 commit 07c4086

8 files changed

Lines changed: 500 additions & 250 deletions

File tree

next-sitemap.config.js

Lines changed: 37 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,40 @@
11
// next-sitemap.config.js
2+
/** @type {import('next-sitemap').IConfig} */
23
module.exports = {
3-
siteUrl: "https://readmegen-ai.vercel.app", // your site URL
4-
generateRobotsTxt: true, // optional: creates robots.txt
4+
siteUrl: "https://readmegen-ai.vercel.app",
5+
generateRobotsTxt: true,
6+
changefreq: "weekly",
7+
priority: 0.7,
8+
additionalPaths: async (config) => [
9+
{
10+
loc: "/",
11+
changefreq: "weekly",
12+
priority: 1.0,
13+
lastmod: new Date().toISOString(),
14+
},
15+
{
16+
loc: "/features",
17+
changefreq: "monthly",
18+
priority: 0.8,
19+
lastmod: new Date().toISOString(),
20+
},
21+
{
22+
loc: "/examples",
23+
changefreq: "monthly",
24+
priority: 0.8,
25+
lastmod: new Date().toISOString(),
26+
},
27+
{
28+
loc: "/docs",
29+
changefreq: "monthly",
30+
priority: 0.8,
31+
lastmod: new Date().toISOString(),
32+
},
33+
{
34+
loc: "/generate",
35+
changefreq: "weekly",
36+
priority: 0.9,
37+
lastmod: new Date().toISOString(),
38+
},
39+
],
540
};

src/app/docs/page.tsx

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,76 @@
1+
import type { Metadata } from "next";
12
import { Navbar } from "@/components/layout/Navbar";
23
import { Footer } from "@/components/layout/Footer";
34
import { QuickStart } from "@/components/docs/QuickStart";
45
import { DocSections } from "@/components/docs/DocSections";
56
import { FAQ } from "@/components/docs/FAQ";
67
import { navLinks } from "@/constants/navLinks";
78

9+
export const metadata: Metadata = {
10+
title: "Documentation | ReadmeGenAI",
11+
description:
12+
"Learn how to use ReadmeGenAI, the AI README generator for GitHub. Quick-start guides, API docs, FAQs, and tips for perfect GitHub README files.",
13+
openGraph: {
14+
title: "Documentation | ReadmeGenAI",
15+
description:
16+
"Learn how to use ReadmeGenAI, the AI README generator for GitHub. Quick-start guides, API docs, FAQs, and tips for perfect GitHub README files.",
17+
url: "/docs",
18+
},
19+
};
20+
21+
const faqJsonLd = {
22+
"@context": "https://schema.org",
23+
"@type": "FAQPage",
24+
mainEntity: [
25+
{
26+
"@type": "Question",
27+
name: "How does the AI analyze my code?",
28+
acceptedAnswer: {
29+
"@type": "Answer",
30+
text: "We use AST parsing and heuristic analysis to identify entry points and dependencies without storing your actual source code.",
31+
},
32+
},
33+
{
34+
"@type": "Question",
35+
name: "Can I use this for private repos?",
36+
acceptedAnswer: {
37+
"@type": "Answer",
38+
text: "Yes, by connecting your GitHub account, we can securely analyze private repositories with your permission.",
39+
},
40+
},
41+
],
42+
};
43+
44+
const breadcrumbJsonLd = {
45+
"@context": "https://schema.org",
46+
"@type": "BreadcrumbList",
47+
itemListElement: [
48+
{
49+
"@type": "ListItem",
50+
position: 1,
51+
name: "Home",
52+
item: "https://readmegen-ai.vercel.app/",
53+
},
54+
{
55+
"@type": "ListItem",
56+
position: 2,
57+
name: "Documentation",
58+
item: "https://readmegen-ai.vercel.app/docs",
59+
},
60+
],
61+
};
62+
863
export default function DocsPage() {
964
return (
1065
<div className="min-h-screen bg-black text-white relative">
66+
<script
67+
type="application/ld+json"
68+
dangerouslySetInnerHTML={{ __html: JSON.stringify(faqJsonLd) }}
69+
/>
70+
<script
71+
type="application/ld+json"
72+
dangerouslySetInnerHTML={{ __html: JSON.stringify(breadcrumbJsonLd) }}
73+
/>
1174
<div className="absolute inset-0 bg-[linear-gradient(to_right,#80808012_1px,transparent_1px),linear-gradient(to_bottom,#80808012_1px,transparent_1px)] bg-[size:40px_40px] [mask-image:radial-gradient(ellipse_60%_50%_at_50%_0%,#000_70%,transparent_100%)] pointer-events-none" />
1275
<Navbar links={navLinks} />
1376

Lines changed: 235 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,235 @@
1+
"use client";
2+
import React, { useState, useRef, useEffect } from "react";
3+
import { Navbar } from "@/components/layout/Navbar";
4+
import { Footer } from "@/components/layout/Footer";
5+
import { Button } from "@/components/ui/Button";
6+
import {
7+
ExternalLink,
8+
Box,
9+
Cpu,
10+
Globe,
11+
Eye,
12+
X,
13+
Copy,
14+
Check,
15+
} from "lucide-react";
16+
import Link from "next/link";
17+
import { navLinks } from "@/constants/navLinks";
18+
19+
const examples = [
20+
{
21+
title: "Modern Web App",
22+
repo: "nextjs-saas-template",
23+
icon: <Globe className="text-blue-400" />,
24+
tags: ["Next.js 16", "Tailwind", "Prisma"],
25+
stars: "1.2k",
26+
description:
27+
"A comprehensive README featuring deployment guides, environment variable tables, and architecture diagrams.",
28+
markdown: `# 🚀 Next.js SaaS Foundation\n\nA professional starter kit for high-performance web applications.\n\n## 🛠 Features\n- **Authentication:** NextAuth.js with multi-provider support.\n- **Database:** Prisma ORM with automated migrations.\n- **UI:** Server-side rendered components via Radix UI.\n\n## 📦 Getting Started\n1. Clone the repo\n2. Run \`npm install\`\n3. Setup \`.env\` file\n4. \`npm run dev\``,
29+
},
30+
{
31+
title: "Utility Library",
32+
repo: "ts-utils-core",
33+
icon: <Box className="text-emerald-400" />,
34+
tags: ["TypeScript", "Rollup", "Vitest"],
35+
stars: "850",
36+
description:
37+
"Technical documentation with API references, installation via multiple package managers, and usage snippets.",
38+
markdown: `# 📦 TS-Utils Core\n\nHigh-performance TypeScript utilities for modern engines.\n\n## 📥 Installation\n\`\`\`bash\nnpm install ts-utils-core\n\`\`\`\n\n## 📖 Quick Usage\n\`\`\`typescript\nimport { formatDate } from 'ts-utils-core';\n\nconst date = formatDate(new Date());\n\`\`\``,
39+
},
40+
{
41+
title: "Backend Engine",
42+
repo: "go-stream-processor",
43+
icon: <Cpu className="text-purple-400" />,
44+
tags: ["Go", "Docker", "Redis"],
45+
stars: "2.4k",
46+
description:
47+
"Performance oriented README focusing on benchmark results, configuration flags, and scaling.",
48+
markdown: `# ⚡ Go Stream Processor\n\nUltra-low latency data streaming engine.\n\n## 📊 Performance Benchmarks\n| Case | Latency | Throughput |\n|------|---------|------------|\n| Sync | 1.2ms | 50k ops/s |\n| Async| 0.4ms | 250k ops/s |\n\n## 🐳 Deployment\n\`\`\`bash\ndocker-compose up -d\n\`\`\``,
49+
},
50+
];
51+
52+
export default function ExamplesClient() {
53+
const [previewContent, setPreviewContent] = useState<string | null>(null);
54+
const [copied, setCopied] = useState(false);
55+
56+
// Refs for timeout and accessibility management
57+
const timeoutRef = useRef<NodeJS.Timeout | null>(null);
58+
const closeButtonRef = useRef<HTMLButtonElement>(null);
59+
60+
// Handle Modal Side Effects: Body Scroll, Escape Key, and Initial Focus
61+
useEffect(() => {
62+
if (previewContent) {
63+
// 1. Lock body scroll
64+
document.body.style.overflow = "hidden";
65+
66+
// 2. Focus the close button for keyboard accessibility
67+
closeButtonRef.current?.focus();
68+
69+
// 3. Close on Escape key press
70+
const handleKeyDown = (e: KeyboardEvent) => {
71+
if (e.key === "Escape") setPreviewContent(null);
72+
};
73+
74+
window.addEventListener("keydown", handleKeyDown);
75+
76+
return () => {
77+
document.body.style.overflow = "unset";
78+
window.removeEventListener("keydown", handleKeyDown);
79+
};
80+
}
81+
}, [previewContent]);
82+
83+
const handleCopy = async (text: string) => {
84+
try {
85+
await navigator.clipboard.writeText(text);
86+
87+
// Clear previous timeout if user clicks rapidly
88+
if (timeoutRef.current) clearTimeout(timeoutRef.current);
89+
90+
setCopied(true);
91+
92+
// Success path: reset icon after 2 seconds
93+
timeoutRef.current = setTimeout(() => {
94+
setCopied(false);
95+
}, 2000);
96+
} catch (err) {
97+
// Error path: log rejection and prevent UI from showing "Checked"
98+
console.error("Failed to copy text: ", err);
99+
}
100+
};
101+
102+
return (
103+
<div className="min-h-screen bg-black text-white selection:bg-blue-500/30">
104+
<Navbar links={navLinks} />
105+
106+
<main className="pt-32 pb-20 px-4">
107+
{/* Header Section */}
108+
<div className="max-w-5xl mx-auto text-center mb-20">
109+
<h1 className="text-4xl md:text-6xl font-extrabold tracking-tighter mb-6">
110+
Trusted by developers <br />
111+
<span className="bg-clip-text text-transparent bg-linear-to-b from-white to-white/40">
112+
to tell their story.
113+
</span>
114+
</h1>
115+
<p className="text-gray-400 text-lg max-w-2xl mx-auto">
116+
Explore how ReadmeGenAI adapts to different tech stacks and project
117+
scales.
118+
</p>
119+
</div>
120+
121+
{/* Examples Grid */}
122+
<div className="max-w-7xl mx-auto grid grid-cols-1 md:grid-cols-3 gap-8">
123+
{examples.map((example, idx) => (
124+
<div
125+
key={idx}
126+
className="group relative p-8 rounded-3xl bg-zinc-950 border border-white/5 hover:border-white/20 transition-all"
127+
>
128+
<div className="flex justify-between items-start mb-6">
129+
<div className="p-3 rounded-2xl bg-white/5 group-hover:bg-white/10 transition-colors">
130+
{example.icon}
131+
</div>
132+
<button
133+
onClick={() => setPreviewContent(example.markdown)}
134+
className="flex items-center gap-1.5 text-xs font-medium text-blue-400 hover:text-blue-300 transition-colors"
135+
>
136+
<Eye size={14} /> View Sample
137+
</button>
138+
</div>
139+
140+
<h3 className="text-xl font-bold mb-2 group-hover:text-blue-400 transition-colors">
141+
{example.title}
142+
</h3>
143+
<p className="text-sm text-gray-500 font-mono mb-4">
144+
{example.repo}
145+
</p>
146+
<p className="text-gray-400 text-sm leading-relaxed mb-6">
147+
{example.description}
148+
</p>
149+
150+
<div className="flex flex-wrap gap-2 mb-8">
151+
{example.tags.map((tag) => (
152+
<span
153+
key={tag}
154+
className="px-2 py-1 rounded-md bg-white/5 text-[10px] font-semibold tracking-wider uppercase text-gray-400"
155+
>
156+
{tag}
157+
</span>
158+
))}
159+
</div>
160+
161+
<Button variant="outline" className="w-full text-sm py-2" asChild>
162+
<Link href="/generate">
163+
Try this style
164+
<ExternalLink size={14} />
165+
</Link>
166+
</Button>
167+
</div>
168+
))}
169+
</div>
170+
171+
{/* --- README PREVIEW MODAL --- */}
172+
{previewContent && (
173+
<div
174+
className="fixed inset-0 z-50 flex items-center justify-center p-4 bg-black/80 backdrop-blur-md animate-in fade-in duration-300"
175+
onClick={() => setPreviewContent(null)} // Close on backdrop click
176+
>
177+
<div
178+
className="bg-zinc-900 border border-white/10 w-full max-w-2xl rounded-2xl overflow-hidden shadow-2xl"
179+
onClick={(e) => e.stopPropagation()} // Prevent closing when clicking inside
180+
role="dialog"
181+
aria-modal="true"
182+
>
183+
<div className="flex items-center justify-between p-4 border-b border-white/5 bg-white/5">
184+
<span className="text-xs font-mono text-gray-400">
185+
README_PREVIEW.md
186+
</span>
187+
<div className="flex items-center gap-2">
188+
<button
189+
onClick={() => handleCopy(previewContent)}
190+
title="Copy to clipboard"
191+
className="p-2 hover:bg-white/10 rounded-lg transition-colors text-gray-400 hover:text-white"
192+
>
193+
{copied ? (
194+
<Check size={16} className="text-emerald-400" />
195+
) : (
196+
<Copy size={16} />
197+
)}
198+
</button>
199+
<button
200+
ref={closeButtonRef}
201+
aria-label="Close preview"
202+
onClick={() => {
203+
setPreviewContent(null);
204+
setCopied(false);
205+
}}
206+
className="p-2 hover:bg-white/10 rounded-lg transition-colors text-gray-400 hover:text-white"
207+
>
208+
<X size={16} />
209+
</button>
210+
</div>
211+
</div>
212+
<div className="p-8 max-h-[60vh] overflow-y-auto">
213+
<pre className="text-sm text-zinc-300 font-mono whitespace-pre-wrap leading-relaxed">
214+
{previewContent}
215+
</pre>
216+
</div>
217+
</div>
218+
</div>
219+
)}
220+
221+
{/* Bottom CTA */}
222+
<div className="mt-32 max-w-4xl mx-auto p-12 rounded-[3rem] bg-linear-to-b from-zinc-900 to-black border border-white/5 text-center">
223+
<h2 className="text-3xl font-bold mb-6">
224+
Ready to document your project?
225+
</h2>
226+
<Button className="px-12 py-6 text-lg" asChild>
227+
<Link href="/generate">Start Generating for Free</Link>
228+
</Button>
229+
</div>
230+
</main>
231+
232+
<Footer />
233+
</div>
234+
);
235+
}

0 commit comments

Comments
 (0)