diff --git a/frontend/src/App.tsx b/frontend/src/App.tsx
index 4621226..228b448 100644
--- a/frontend/src/App.tsx
+++ b/frontend/src/App.tsx
@@ -1,8 +1,8 @@
-import { BrowserRouter, Routes, Route, Navigate, useNavigate } from 'react-router-dom';
+import { BrowserRouter, Routes, Route, Navigate } from 'react-router-dom';
import { AuthProvider, useAuth } from './contexts/AuthContext';
import { LoginPage } from './pages/LoginPage';
import { SignupPage } from './pages/SignupPage';
-import { Playground } from './pages/Playground';
+import { LandingPage } from './pages/LandingPage';
import { Dashboard } from './components/Dashboard';
function ProtectedRoute({ children }: { children: React.ReactNode }) {
@@ -10,8 +10,8 @@ function ProtectedRoute({ children }: { children: React.ReactNode }) {
if (loading) {
return (
-
-
Loading...
+
);
}
@@ -23,8 +23,7 @@ function ProtectedRoute({ children }: { children: React.ReactNode }) {
return <>{children}>;
}
-function PlaygroundWrapper() {
- const navigate = useNavigate();
+function LandingWrapper() {
const { user } = useAuth();
// If user is logged in, redirect to dashboard
@@ -32,7 +31,7 @@ function PlaygroundWrapper() {
return
;
}
- return
navigate('/signup')} />;
+ return ;
}
function AppRoutes() {
@@ -40,8 +39,8 @@ function AppRoutes() {
return (
- {/* Playground is the new landing page */}
- } />
+ {/* New Landing Page */}
+ } />
- {/* Legacy route redirect */}
+ {/* Fallback */}
} />
);
diff --git a/frontend/src/components/ui/badge.tsx b/frontend/src/components/ui/badge.tsx
new file mode 100644
index 0000000..2d3fbf7
--- /dev/null
+++ b/frontend/src/components/ui/badge.tsx
@@ -0,0 +1,40 @@
+import * as React from "react"
+import { cva, type VariantProps } from "class-variance-authority"
+
+import { cn } from "@/lib/utils"
+
+const badgeVariants = cva(
+ "inline-flex items-center rounded-full border px-2.5 py-0.5 text-xs font-semibold transition-colors focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2",
+ {
+ variants: {
+ variant: {
+ default:
+ "border-transparent bg-primary text-primary-foreground hover:bg-primary/80",
+ secondary:
+ "border-transparent bg-secondary text-secondary-foreground hover:bg-secondary/80",
+ destructive:
+ "border-transparent bg-destructive text-destructive-foreground hover:bg-destructive/80",
+ outline: "text-foreground",
+ success:
+ "border-transparent bg-green-500/10 text-green-500 border-green-500/20",
+ glow:
+ "border-transparent bg-blue-500/10 text-blue-400 border-blue-500/20",
+ },
+ },
+ defaultVariants: {
+ variant: "default",
+ },
+ }
+)
+
+export interface BadgeProps
+ extends React.HTMLAttributes,
+ VariantProps {}
+
+function Badge({ className, variant, ...props }: BadgeProps) {
+ return (
+
+ )
+}
+
+export { Badge, badgeVariants }
diff --git a/frontend/src/pages/LandingPage.tsx b/frontend/src/pages/LandingPage.tsx
new file mode 100644
index 0000000..7d21000
--- /dev/null
+++ b/frontend/src/pages/LandingPage.tsx
@@ -0,0 +1,563 @@
+import { useState, useEffect, useRef } from 'react'
+import { useNavigate } from 'react-router-dom'
+import { Button } from '@/components/ui/button'
+import { Badge } from '@/components/ui/badge'
+import { Card } from '@/components/ui/card'
+import { Prism as SyntaxHighlighter } from 'react-syntax-highlighter'
+import { oneDark } from 'react-syntax-highlighter/dist/esm/styles/prism'
+import { API_URL } from '../config/api'
+import type { SearchResult } from '../types'
+
+// Icons
+const SearchIcon = () => (
+
+)
+
+const ZapIcon = () => (
+
+)
+
+const GitHubIcon = () => (
+
+)
+
+const SparklesIcon = () => (
+
+)
+
+const ArrowRightIcon = () => (
+
+)
+
+const CheckIcon = () => (
+
+)
+
+// Demo repos
+const DEMO_REPOS = [
+ { id: 'flask', name: 'Flask', icon: '🐍', color: 'from-green-500/20 to-green-600/20 border-green-500/30' },
+ { id: 'fastapi', name: 'FastAPI', icon: '⚡', color: 'from-teal-500/20 to-teal-600/20 border-teal-500/30' },
+ { id: 'express', name: 'Express', icon: '🟢', color: 'from-yellow-500/20 to-yellow-600/20 border-yellow-500/30' },
+]
+
+const EXAMPLE_QUERIES = [
+ 'authentication middleware',
+ 'error handling',
+ 'database connection',
+]
+
+// Scroll animation hook
+function useScrollAnimation() {
+ const ref = useRef(null)
+ const [isVisible, setIsVisible] = useState(false)
+
+ useEffect(() => {
+ const observer = new IntersectionObserver(
+ ([entry]) => {
+ if (entry.isIntersecting) {
+ setIsVisible(true)
+ }
+ },
+ { threshold: 0.1, rootMargin: '0px 0px -50px 0px' }
+ )
+
+ if (ref.current) {
+ observer.observe(ref.current)
+ }
+
+ return () => observer.disconnect()
+ }, [])
+
+ return { ref, isVisible }
+}
+
+// Animated section wrapper
+function AnimatedSection({ children, className = '' }: { children: React.ReactNode; className?: string }) {
+ const { ref, isVisible } = useScrollAnimation()
+
+ return (
+
+ {children}
+
+ )
+}
+
+export function LandingPage() {
+ const navigate = useNavigate()
+ const [query, setQuery] = useState('')
+ const [selectedRepo, setSelectedRepo] = useState(DEMO_REPOS[0].id)
+ const [results, setResults] = useState([])
+ const [loading, setLoading] = useState(false)
+ const [searchTime, setSearchTime] = useState(null)
+ const [searchCount, setSearchCount] = useState(0)
+ const [hasSearched, setHasSearched] = useState(false)
+ const [availableRepos, setAvailableRepos] = useState([])
+
+ const FREE_LIMIT = 5
+ const remaining = FREE_LIMIT - searchCount
+
+ useEffect(() => {
+ fetch(`${API_URL}/api/playground/repos`)
+ .then(res => res.json())
+ .then(data => {
+ const available = data.repos?.filter((r: any) => r.available).map((r: any) => r.id) || []
+ setAvailableRepos(available)
+ })
+ .catch(console.error)
+ }, [])
+
+ const handleSearch = async (searchQuery?: string) => {
+ const q = searchQuery || query
+ if (!q.trim() || loading || searchCount >= FREE_LIMIT) return
+
+ setLoading(true)
+ setHasSearched(true)
+ const startTime = Date.now()
+
+ try {
+ const response = await fetch(`${API_URL}/api/playground/search`, {
+ method: 'POST',
+ headers: { 'Content-Type': 'application/json' },
+ body: JSON.stringify({ query: q, demo_repo: selectedRepo, max_results: 10 })
+ })
+ const data = await response.json()
+ if (response.ok) {
+ setResults(data.results || [])
+ setSearchTime(Date.now() - startTime)
+ setSearchCount(prev => prev + 1)
+ }
+ } catch (error) {
+ console.error('Search error:', error)
+ } finally {
+ setLoading(false)
+ }
+ }
+
+ return (
+
+ {/* Navigation */}
+
+
+ {/* ============ HERO SECTION ============ */}
+
+
+
+
+ AI-powered code search
+
+
+
+ grep returned
+ 847 results.
+
+
+ Find the one that matters.
+
+
+
+
+ Search any codebase by meaning, not keywords.
+
+
+ {/* Repo Selector */}
+
+ {DEMO_REPOS.map(repo => {
+ const isAvailable = availableRepos.includes(repo.id)
+ const isSelected = selectedRepo === repo.id
+ return (
+
+ )
+ })}
+
+
+ {/* Search Box */}
+
+
+ {/* Trust Indicators */}
+
+
~100ms
+
+
No signup required
+
+
{remaining} free searches
+
+
+ {/* Example Queries */}
+ {!hasSearched && (
+
+ Try:
+ {EXAMPLE_QUERIES.map(q => (
+
+ ))}
+
+ )}
+
+
+
+ {/* ============ RESULTS SECTION (if searched) ============ */}
+ {hasSearched && (
+
+
+
+
+ {results.length} results
+ {searchTime && <>•{searchTime}ms>}
+
+ {remaining > 0 && remaining < FREE_LIMIT && (
+
{remaining} remaining
+ )}
+
+
+ {searchCount >= FREE_LIMIT && (
+
+ You've used all free searches
+ Sign up to get unlimited searches and index your own repos.
+
+
+ )}
+
+
+ {results.map((result, idx) => (
+
+
+
+
+
{result.name}
+ {result.type.replace('_', ' ')}
+
+
{result.file_path.split('/').slice(-2).join('/')}
+
+
+
{(result.score * 100).toFixed(0)}%
+
match
+
+
+
+ {result.code}
+
+
+ ))}
+
+
+ {results.length === 0 && !loading && (
+
+
🔍
+
No results found
+
Try a different query
+
+ )}
+
+
+ )}
+
+ {/* ============ STORY SECTIONS (only before search) ============ */}
+ {!hasSearched && (
+ <>
+ {/* THE PROBLEM */}
+
+
+
+
+
The Problem
+
You've been here before
+
+ New codebase. 50,000 lines. You need to find where authentication happens.
+
+
+
+ {/* Terminal visualization */}
+
+
+
+
$ grep -r "auth" ./src
+
+
src/components/AuthButton.tsx: // auth button component
+
src/utils/auth.ts: export const authConfig = ...
+
src/pages/auth/login.tsx: function AuthLogin() ...
+
src/middleware/auth.ts: // TODO: add auth
+
src/api/auth/callback.ts: const authCallback = ...
+
... 842 more results
+
+
+ 847 results. Which one handles the actual authentication logic?
+
+
+
+
+
+
+
+ {/* THE SOLUTION */}
+
+
+
+
+
The Solution
+
Search by meaning, not keywords
+
+ Ask for "authentication logic" and get the function that actually handles it.
+
+
+
+ {/* CodeIntel visualization */}
+
+
+
+
+
+
+ authenticate_user
+ function
+
+
src/auth/handlers.py
+
+
+
+
{`def authenticate_user(credentials: dict) -> User:
+ """Main authentication logic - validates credentials
+ and returns authenticated user or raises AuthError."""
+ user = db.get_user(credentials['email'])
+ if not verify_password(credentials['password'], user.hash):
+ raise AuthError("Invalid credentials")
+ return create_session(user)`}
+
+
+
+
+
+
+ {/* HOW IT WORKS */}
+
+
+
+
+ How It Works
+
Three steps to code clarity
+
+
+
+
+
+ 1
+
+
Index your repo
+
Connect your GitHub repo. We analyze and embed every function, class, and module.
+
+
+
+
+ 2
+
+
Search by meaning
+
Ask natural questions. "Where is payment handled?" "Show me error boundaries."
+
+
+
+
+ 3
+
+
Get precise results
+
Not 847 matches. The exact functions you need, ranked by relevance.
+
+
+
+
+
+
+ {/* FEATURES */}
+
+
+
+
+
Built for developers
+
+
+
+
+
+
+ 🔌
+
+
+
MCP Integration
+
Works with Claude, Cursor, and any MCP-compatible AI. Search code directly from your assistant.
+
+
+
+
+
+
+
+ ⚡
+
+
+
Lightning Fast
+
Sub-100ms responses with Redis caching. Semantic search shouldn't slow you down.
+
+
+
+
+
+
+
+ 📊
+
+
+
Code Intelligence
+
Understand dependencies, analyze coding patterns, and see impact before you change.
+
+
+
+
+
+
+
+ 🔓
+
+
+
Open Source
+
Self-host for private repos. Inspect the code. Contribute improvements. No vendor lock-in.
+
+
+
+
+
+
+
+
+ {/* FINAL CTA */}
+
+
+
+
Ready to understand your codebase?
+
Start searching for free. No credit card required.
+
+
+
+
+ Star on GitHub
+
+
+
+
+
+ >
+ )}
+
+ {/* FOOTER */}
+
+
+ )
+}