diff --git a/package-lock.json b/package-lock.json index 066734f..fff2cea 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,6 +9,7 @@ "version": "0.1.0", "dependencies": { "@google/generative-ai": "^0.24.1", + "@radix-ui/react-slot": "^1.2.4", "lucide-react": "^0.563.0", "next": "16.1.6", "octokit": "^5.0.5", @@ -1580,6 +1581,39 @@ "node": ">= 20" } }, + "node_modules/@radix-ui/react-compose-refs": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@radix-ui/react-compose-refs/-/react-compose-refs-1.1.2.tgz", + "integrity": "sha512-z4eqJvfiNnFMHIIvXP3CY57y2WJs5g2v3X0zm9mEJkrkNv4rDxu+sg9Jh8EkXyeqBkB7SOcboo9dMVqhyrACIg==", + "license": "MIT", + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-slot": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.2.4.tgz", + "integrity": "sha512-Jl+bCv8HxKnlTLVrcDE8zTMJ09R9/ukw4qBs/oZClOfoQk/cOTbDn+NceXfV7j09YPVQUryJPHurafcSg6EVKA==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-compose-refs": "1.1.2" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, "node_modules/@rtsao/scc": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/@rtsao/scc/-/scc-1.1.0.tgz", diff --git a/package.json b/package.json index f7cf36d..02ef3ed 100644 --- a/package.json +++ b/package.json @@ -10,6 +10,7 @@ }, "dependencies": { "@google/generative-ai": "^0.24.1", + "@radix-ui/react-slot": "^1.2.4", "lucide-react": "^0.563.0", "next": "16.1.6", "octokit": "^5.0.5", diff --git a/public/ReadmeGenAI.png b/public/ReadmeGenAI.png new file mode 100644 index 0000000..b107fc5 Binary files /dev/null and b/public/ReadmeGenAI.png differ diff --git a/public/file.svg b/public/file.svg deleted file mode 100644 index 004145c..0000000 --- a/public/file.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/public/globe.svg b/public/globe.svg deleted file mode 100644 index 567f17b..0000000 --- a/public/globe.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/public/next.svg b/public/next.svg deleted file mode 100644 index 5174b28..0000000 --- a/public/next.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/public/vercel.svg b/public/vercel.svg deleted file mode 100644 index 7705396..0000000 --- a/public/vercel.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/public/window.svg b/public/window.svg deleted file mode 100644 index b2b2a44..0000000 --- a/public/window.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/src/app/docs/page.tsx b/src/app/docs/page.tsx index b5e3da5..1ea60cb 100644 --- a/src/app/docs/page.tsx +++ b/src/app/docs/page.tsx @@ -1,81 +1,38 @@ -"use client"; -import React from 'react'; import { Navbar } from '@/components/layout/Navbar'; import { Footer } from '@/components/layout/Footer'; -import { ChevronRight, Terminal } from 'lucide-react'; +import { QuickStart } from '@/components/docs/QuickStart'; +import { DocSections } from '@/components/docs/DocSections'; +import { FAQ } from '@/components/docs/FAQ'; +import { navLinks } from '@/constants/navLinks'; export default function DocsPage() { - const navLinks = [ - { name: 'Home', href: '/' }, - { name: 'Features', href: '/features' }, - { name: 'Examples', href: '/examples' }, - { name: 'Docs', href: '/docs' }, - ]; - return ( -
+
+
-
- - {/* Sidebar Nav */} - - - {/* Main Content */} -
-

Getting Started

-

- ReadmeGenAI is designed to be plug-and-play. Our goal is to remove the friction - of writing documentation so you can focus on writing code. + +

+
+

+ Documentation
+ + at your fingertips. + +

+

+ Everything you need to know about ReadmeGenAI. From quick start guides to + advanced configuration, we've got you covered.

+
-
-
-

- 1. Provide your URL -

-

- Enter your public GitHub repository URL into the generator. Ensure the repo - contains at least a main entry point (like `index.js` or `main.py`). -

-
- -
-
- - Quick Start Command -
- - npx readmegenai@latest --repo [your-url] - -
- -
-

2. AI Analysis

-

- Our engine uses Octokit to build a virtual file tree. This tree is passed to - Gemini 2.5 Flash with a specific context window optimized for software architecture. -

-
-
+
+ + +
+
); -} \ No newline at end of file +} diff --git a/src/app/examples/page.tsx b/src/app/examples/page.tsx index c4aa742..9ef5935 100644 --- a/src/app/examples/page.tsx +++ b/src/app/examples/page.tsx @@ -1,10 +1,20 @@ "use client"; -import React from 'react'; -import { Navbar } from '@/components/layout/Navbar'; -import { Footer } from '@/components/layout/Footer'; -import { Button } from '@/components/ui/Button'; -import { ExternalLink, Star, Box, Cpu, Globe } from 'lucide-react'; -import Link from 'next/link'; +import React, { useState, useRef, useEffect } from "react"; +import { Navbar } from "@/components/layout/Navbar"; +import { Footer } from "@/components/layout/Footer"; +import { Button } from "@/components/ui/Button"; +import { + ExternalLink, + Box, + Cpu, + Globe, + Eye, + X, + Copy, + Check, +} from "lucide-react"; +import Link from "next/link"; +import { navLinks } from "@/constants/navLinks"; const examples = [ { @@ -13,7 +23,9 @@ const examples = [ icon: , tags: ["Next.js 16", "Tailwind", "Prisma"], stars: "1.2k", - description: "A comprehensive README featuring deployment guides, environment variable tables, and architecture diagrams." + description: + "A comprehensive README featuring deployment guides, environment variable tables, and architecture diagrams.", + 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\``, }, { title: "Utility Library", @@ -21,7 +33,9 @@ const examples = [ icon: , tags: ["TypeScript", "Rollup", "Vitest"], stars: "850", - description: "Technical-heavy documentation with API references, installation via multiple package managers, and usage snippets." + description: + "Technical documentation with API references, installation via multiple package managers, and usage snippets.", + 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\`\`\``, }, { title: "Backend Engine", @@ -29,20 +43,64 @@ const examples = [ icon: , tags: ["Go", "Docker", "Redis"], stars: "2.4k", - description: "High-performance oriented README focusing on benchmark results, configuration flags, and horizontal scaling." - } + description: + "Performance oriented README focusing on benchmark results, configuration flags, and scaling.", + 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\`\`\``, + }, ]; export default function ExamplesPage() { - const navLinks = [ - { name: 'Home', href: '/' }, - { name: 'Features', href: '/#features' }, - { name: 'Examples', href: '/examples' }, - { name: 'Docs', href: '#docs' }, - ]; + const [previewContent, setPreviewContent] = useState(null); + const [copied, setCopied] = useState(false); + + // Refs for timeout and accessibility management + const timeoutRef = useRef(null); + const closeButtonRef = useRef(null); + + // Handle Modal Side Effects: Body Scroll, Escape Key, and Initial Focus + useEffect(() => { + if (previewContent) { + // 1. Lock body scroll + document.body.style.overflow = "hidden"; + + // 2. Focus the close button for keyboard accessibility + closeButtonRef.current?.focus(); + + // 3. Close on Escape key press + const handleKeyDown = (e: KeyboardEvent) => { + if (e.key === "Escape") setPreviewContent(null); + }; + + window.addEventListener("keydown", handleKeyDown); + + return () => { + document.body.style.overflow = "unset"; + window.removeEventListener("keydown", handleKeyDown); + }; + } + }, [previewContent]); + + const handleCopy = async (text: string) => { + try { + await navigator.clipboard.writeText(text); + + // Clear previous timeout if user clicks rapidly + if (timeoutRef.current) clearTimeout(timeoutRef.current); + + setCopied(true); + + // Success path: reset icon after 2 seconds + timeoutRef.current = setTimeout(() => { + setCopied(false); + }, 2000); + } catch (err) { + // Error path: log rejection and prevent UI from showing "Checked" + console.error("Failed to copy text: ", err); + } + }; return ( -
+
@@ -55,25 +113,28 @@ export default function ExamplesPage() {

- Explore how ReadmeGenAI adapts to different tech stacks and project scales to create documentation that converts visitors into users. + Explore how ReadmeGenAI adapts to different tech stacks and project + scales.

{/* Examples Grid */}
{examples.map((example, idx) => ( -
{example.icon}
-
- - {example.stars} -
+

@@ -87,35 +148,88 @@ export default function ExamplesPage() {

- {example.tags.map(tag => ( - + {example.tags.map((tag) => ( + {tag} ))}
- - - + +

))}
+ {/* --- README PREVIEW MODAL --- */} + {previewContent && ( +
setPreviewContent(null)} // Close on backdrop click + > +
e.stopPropagation()} // Prevent closing when clicking inside + role="dialog" + aria-modal="true" + > +
+ + README_PREVIEW.md + +
+ + +
+
+
+
+                  {previewContent}
+                
+
+
+
+ )} + {/* Bottom CTA */}
-

Ready to document your project?

- - - +

+ Ready to document your project? +

+
); -} \ No newline at end of file +} diff --git a/src/app/favicon.ico b/src/app/favicon.ico deleted file mode 100644 index 718d6fe..0000000 Binary files a/src/app/favicon.ico and /dev/null differ diff --git a/src/app/features/page.tsx b/src/app/features/page.tsx index d13383a..e7b9c0a 100644 --- a/src/app/features/page.tsx +++ b/src/app/features/page.tsx @@ -52,7 +52,7 @@ export default function FeaturesPage() {

Documentation
- + reimagined.

diff --git a/src/app/generate/page.tsx b/src/app/generate/page.tsx index a8347bf..74462e3 100644 --- a/src/app/generate/page.tsx +++ b/src/app/generate/page.tsx @@ -4,6 +4,7 @@ import { Navbar } from '@/components/layout/Navbar'; import { Footer } from '@/components/layout/Footer'; import { SearchInput } from '@/components/Generator/SearchInput'; import { MarkdownPreview } from '@/components/Generator/MarkdownPreview'; +import { navLinks } from '@/constants/navLinks'; export default function GeneratePage() { const [markdown, setMarkdown] = useState(''); @@ -11,7 +12,7 @@ export default function GeneratePage() { const handleGenerate = async (githubUrl: string) => { setIsLoading(true); - setMarkdown(''); // Clear previous results + setMarkdown(''); try { const response = await fetch('/api/generate', { method: 'POST', @@ -19,51 +20,43 @@ export default function GeneratePage() { body: JSON.stringify({ url: githubUrl }), }); - const data = await response.json(); - if (data.markdown) { - setMarkdown(data.markdown); - } else { - alert(data.error || "Something went wrong"); + // Updated Error Handling logic + if (!response.ok) { + // First, get the raw text to avoid JSON parsing errors on HTML responses + const errorText = await response.text(); + let errorMessage: string; + + try { + // Attempt to parse the text as JSON + const errorData = JSON.parse(errorText); + errorMessage = errorData.error || errorData.message || errorText; + } catch { + // Fallback to raw text if JSON.parse fails (e.g., 502 Bad Gateway HTML) + errorMessage = errorText || response.statusText; + } + + // Throw an error that includes the HTTP status for better debugging + throw new Error(`[${response.status} ${response.statusText}]: ${errorMessage}`); } - } catch (error) { - console.error("Generation failed:", error); - alert("Failed to connect to the server."); + + const data = await response.json(); + setMarkdown(data.markdown); + } catch (error: unknown) { + console.error("Generation Error:", error); + alert(error instanceof Error ? error.message : "Something went wrong"); } finally { setIsLoading(false); } }; - const navLinks = [ - { name: 'Home', href: '/' }, - { name: 'Features', href: '/features' }, - { name: 'Examples', href: '/examples' }, - { name: 'Docs', href: '/docs' }, - ]; - return (
-
-
-

Generate Documentation

-

Enter your repository URL and let the AI do the heavy lifting.

-
- - - {/* Results will appear here once generated */} - - {/* Empty state helper */} - {!markdown && !isLoading && ( -
-

Waiting for repository URL...

-
- )}
-
); -} \ No newline at end of file +} diff --git a/src/app/layout.tsx b/src/app/layout.tsx index f7fa87e..7a81272 100644 --- a/src/app/layout.tsx +++ b/src/app/layout.tsx @@ -13,8 +13,13 @@ const geistMono = Geist_Mono({ }); export const metadata: Metadata = { - title: "Create Next App", - description: "Generated by create next app", + title: "ReadmeGenAI", + description: "REadmeGenAI is an AI-powered tool that automatically generates professional README files for your GitHub projects.", + icons: { + icon: "/ReadmeGenAI.png", + shortcut: "/ReadmeGenAI.png", + apple: "/ReadmeGenAI.png", + }, }; export default function RootLayout({ diff --git a/src/app/page.tsx b/src/app/page.tsx index 9ffd8ef..502b4e1 100644 --- a/src/app/page.tsx +++ b/src/app/page.tsx @@ -1,31 +1,43 @@ +"use client"; +import React from 'react'; import { Navbar } from '@/components/layout/Navbar'; import { Hero } from '@/components/sections/Hero'; import { Features } from '@/components/sections/Features'; import { Footer } from '@/components/layout/Footer'; import { Code, Layout, FileText } from 'lucide-react'; +import { navLinks } from '@/constants/navLinks'; export default function Home() { - const navLinks = [ - { name: 'Home', href: '/' }, - { name: 'Features', href: '/features' }, - { name: 'Examples', href: '/examples' }, - { name: 'Docs', href: '/docs' }, - ]; - const featureList = [ - { icon: , title: "Context Awareness", desc: "Detects frameworks and dependencies automatically." }, - { icon: , title: "Clean Templates", desc: "Formatted Markdown following GitHub best practices." }, - { icon: , title: "AI Optimization", desc: "Optimized for project clarity and searchability." }, + { + icon: , + title: "Context Awareness", + desc: "Detects frameworks and dependencies automatically using advanced AST parsing." + }, + { + icon: , + title: "Clean Templates", + desc: "Formatted Markdown following GitHub best practices for maximum readability." + }, + { + icon: , + title: "AI Optimization", + desc: "Leverages Gemini 3 Flash to ensure project clarity and SEO-friendly documentation." + }, ]; return ( -
+
+
+ + {/* Removed the wrapper div that had the duplicate id="features" */}
+
); -} \ No newline at end of file +} diff --git a/src/components/Generator/MarkdownPreview.tsx b/src/components/Generator/MarkdownPreview.tsx index 80594e2..a635de6 100644 --- a/src/components/Generator/MarkdownPreview.tsx +++ b/src/components/Generator/MarkdownPreview.tsx @@ -5,10 +5,14 @@ import { Copy, Check, FileCode } from 'lucide-react'; export const MarkdownPreview = ({ content }: { content: string }) => { const [copied, setCopied] = useState(false); - const handleCopy = () => { - navigator.clipboard.writeText(content); - setCopied(true); - setTimeout(() => setCopied(false), 2000); + const handleCopy = async () => { + try { + await navigator.clipboard.writeText(content); + setCopied(true); + setTimeout(() => setCopied(false), 2000); + } catch (err) { + console.error('Failed to copy text: ', err); + } }; if (!content) return null; @@ -21,18 +25,13 @@ export const MarkdownPreview = ({ content }: { content: string }) => { README.md
-
-
-
-            {content}
-          
+
+ {content}
diff --git a/src/components/Generator/SearchInput.tsx b/src/components/Generator/SearchInput.tsx index b14b373..e4c1b92 100644 --- a/src/components/Generator/SearchInput.tsx +++ b/src/components/Generator/SearchInput.tsx @@ -1,6 +1,6 @@ "use client"; import React, { useState } from 'react'; -import { Search, Loader2, Github } from 'lucide-react'; +import { Loader2, Github, AlertCircle } from 'lucide-react'; import { Button } from '../ui/Button'; interface SearchInputProps { @@ -10,13 +10,19 @@ interface SearchInputProps { export const SearchInput = ({ onGenerate, isLoading }: SearchInputProps) => { const [url, setUrl] = useState(''); + const [error, setError] = useState(null); const handleSubmit = (e: React.FormEvent) => { e.preventDefault(); - if (url.includes('github.com')) { - onGenerate(url); + setError(null); + + // Stricter Regex: Matches https://github.com/user/repo + const githubUrlPattern = /^https?:\/\/(www\.)?github\.com\/[\w.-]+\/[\w.-]+\/?$/; + + if (githubUrlPattern.test(url.trim())) { + onGenerate(url.trim()); } else { - alert("Please enter a valid GitHub URL"); + setError("Please enter a valid GitHub repository URL."); } }; @@ -29,28 +35,25 @@ export const SearchInput = ({ onGenerate, isLoading }: SearchInputProps) => { setUrl(e.target.value)} + onChange={(e) => { + setUrl(e.target.value); + if (error) setError(null); + }} placeholder="https://github.com/username/repo" - className="w-full bg-zinc-900/50 border border-white/10 rounded-2xl py-6 pl-14 pr-40 text-white placeholder:text-gray-600 focus:outline-none focus:ring-2 focus:ring-blue-500/50 focus:border-blue-500/50 transition-all backdrop-blur-xl" - required + className={`w-full bg-zinc-900/50 border ${error ? 'border-red-500/50' : 'border-white/10'} rounded-2xl py-6 pl-14 pr-40 text-white placeholder:text-gray-600 focus:outline-none focus:ring-2 focus:ring-blue-500/50 transition-all backdrop-blur-xl`} />
-
+ {error && ( +
+ + {error} +
+ )}
); }; \ No newline at end of file diff --git a/src/components/docs/DocSections.tsx b/src/components/docs/DocSections.tsx new file mode 100644 index 0000000..95f10fb --- /dev/null +++ b/src/components/docs/DocSections.tsx @@ -0,0 +1,86 @@ +"use client"; +import React from "react"; +import { Rocket, Settings, HelpCircle, CheckCircle2 } from "lucide-react"; + +export const DocSections = () => { + const sections = [ + { + title: "Getting Started", + icon: , + accentColor: "text-blue-400", + items: [ + "Sign in via GitHub OAuth", + "Paste repository SSH or HTTPS URL", + "Auto-detect project frameworks", + "Initialize README metadata", + ], + }, + { + title: "Configuration", + icon: , + accentColor: "text-amber-400", + items: [ + "Custom prompt engineering", + "Toggle badge visibility", + "Select documentation language", + "Choose visual layout styles", + ], + }, + { + title: "Troubleshooting", + icon: , + accentColor: "text-purple-400", + items: [ + "API rate limit handling", + "Private repo access guides", + "Markdown rendering fixes", + "Version control integration", + ], + }, + ]; + + return ( +
+
+
+

+ Documentation Sections +

+

+ Everything you need to master your workflow +

+
+ +
+ {sections.map((section, i) => ( +
+
+ {section.icon} +
+

+ {section.title} +

+
    + {section.items.map((item, idx) => ( +
  • + + {item} +
  • + ))} +
+
+ ))} +
+
+
+ ); +}; diff --git a/src/components/docs/FAQ.tsx b/src/components/docs/FAQ.tsx new file mode 100644 index 0000000..83eb84b --- /dev/null +++ b/src/components/docs/FAQ.tsx @@ -0,0 +1,33 @@ +"use client"; +import React from 'react'; +import { HelpCircle } from 'lucide-react'; + +export const FAQ = () => { + const faqs = [ + { q: "How does the AI analyze my code?", a: "We use AST parsing and heuristic analysis to identify entry points and dependencies without storing your actual source code." }, + { q: "Can I use this for private repos?", a: "Yes, by connecting your GitHub account, we can securely analyze private repositories with your permission." } + ]; + + return ( +
+
+

Frequently Asked Questions

+
+ {faqs.map((faq, i) => ( +
+
+
+ +
+
+

{faq.q}

+

{faq.a}

+
+
+
+ ))} +
+
+
+ ); +}; \ No newline at end of file diff --git a/src/components/docs/QuickStart.tsx b/src/components/docs/QuickStart.tsx new file mode 100644 index 0000000..68c1d04 --- /dev/null +++ b/src/components/docs/QuickStart.tsx @@ -0,0 +1,83 @@ +"use client"; +import React from "react"; +import { Github, FileText, Zap, Download } from "lucide-react"; + +export const QuickStart = () => { + const steps = [ + { + icon: , + title: "Connect GitHub", + desc: "Sign in with your GitHub account to access your repositories", + step: 1, + accent: "text-blue-400", + }, + { + icon: , + title: "Select Repository", + desc: "Choose a repository or paste a GitHub URL to analyze", + step: 2, + accent: "text-emerald-400", + }, + { + icon: , + title: "Generate README", + desc: "Let our AI analyze your project and create documentation", + step: 3, + accent: "text-amber-400", + }, + { + icon: , + title: "Download & Use", + desc: "Download your README and add it to your repository", + step: 4, + accent: "text-pink-400", + }, + ]; + + return ( +
+
+
+

+ Quick Start Guide +

+

+ Get up and running in just 4 simple steps +

+
+ +
+ {steps.map((s, i) => ( +
+
+
+
+ {s.icon} +
+
+ STEP {s.step} +
+
+

+ {s.title} +

+

+ {s.desc} +

+
+
+ ))} +
+
+
+ ); +}; diff --git a/src/components/sections/Hero.tsx b/src/components/sections/Hero.tsx index 131a2ba..3fa352d 100644 --- a/src/components/sections/Hero.tsx +++ b/src/components/sections/Hero.tsx @@ -1,22 +1,20 @@ -import React from 'react'; -import Link from 'next/link'; -import { Zap, ArrowRight } from 'lucide-react'; -import { Button } from '../ui/Button'; -import { TerminalMockup } from './TerminalMockup'; +import React from "react"; +import Link from "next/link"; +import { Zap, ArrowRight } from "lucide-react"; +import { Button } from "../ui/Button"; +import { TerminalMockup } from "./TerminalMockup"; export const Hero = () => { return (
- {/* Background Glow - Arbitrary values for Tailwind v4 compatibility */}
- +
- {/* Badge */}
- v2.0: Now with real-time codebase context + v1.0: Now with real-time codebase context
- + {/* Headline */}

Ship documentation
@@ -27,16 +25,20 @@ export const Hero = () => { {/* Sub-headline */}

- ReadmeGenAI scans your repository and crafts professional, - engaging README files automatically. The perfect intro for your next big project. + ReadmeGenAI scans your repository and crafts professional, engaging + README files automatically. The perfect intro for your next big + project.

- {/* CTA Buttons - Now Functional */} + {/* CTA Buttons - Fixed Routing */}
- + {/* Changed from "generate" to "/generate" */} @@ -47,11 +49,8 @@ export const Hero = () => {
- {/* Terminal Section */} -
- -
+

); -}; \ No newline at end of file +}; diff --git a/src/components/ui/Button.tsx b/src/components/ui/Button.tsx index d148411..f575181 100644 --- a/src/components/ui/Button.tsx +++ b/src/components/ui/Button.tsx @@ -1,23 +1,35 @@ import React from 'react'; +import { Slot } from '@radix-ui/react-slot'; interface ButtonProps extends React.ButtonHTMLAttributes { variant?: 'primary' | 'outline' | 'ghost'; children: React.ReactNode; + asChild?: boolean; // Add this prop } -export const Button = ({ children, variant = 'primary', className = '', ...props }: ButtonProps) => { +export const Button = ({ + children, + variant = 'primary', + className = '', + asChild = false, // Default to false + ...props +}: ButtonProps) => { + const variants = { primary: 'bg-white text-black hover:bg-gray-200', outline: 'bg-transparent border border-white/10 hover:bg-white/5 text-white', ghost: 'bg-transparent hover:text-white text-gray-400', }; + // If asChild is true, we render the 'Slot', otherwise we render 'button' + const Component = asChild ? Slot : 'button'; + return ( - + ); }; \ No newline at end of file diff --git a/src/constants/navLinks.ts b/src/constants/navLinks.ts new file mode 100644 index 0000000..a61f452 --- /dev/null +++ b/src/constants/navLinks.ts @@ -0,0 +1,6 @@ +export const navLinks = [ + { name: 'Home', href: '/' }, + { name: 'Features', href: '/features' }, + { name: 'Examples', href: '/examples' }, + { name: 'Docs', href: '/docs' }, +]; \ No newline at end of file