From 589c2246434cfa12a4cc7e44cf0cc2fd41854c8f Mon Sep 17 00:00:00 2001 From: Devanshu Rajesh Chicholikar Date: Thu, 4 Dec 2025 19:47:31 -0500 Subject: [PATCH 1/2] feat(landing): Phase 1 - Foundation & Hero Section - New LandingPage component with dark theme (#0a0a0b) - Pain-point headline: 'grep returned 847 results' - Semantic search box with gradient glow effect - Repo selector pills (Flask, FastAPI, Express) - Trust indicators (speed, no signup, free searches) - Example queries for quick testing - Search results with syntax highlighting - Rate limit banner with signup CTA - Features preview section (Semantic Search, MCP, Code Intel) - Clean minimal navigation and footer - Badge component with custom variants Part of landing page redesign (#22) --- frontend/src/App.tsx | 19 +- frontend/src/components/ui/badge.tsx | 40 +++ frontend/src/pages/LandingPage.tsx | 440 +++++++++++++++++++++++++++ 3 files changed, 489 insertions(+), 10 deletions(-) create mode 100644 frontend/src/components/ui/badge.tsx create mode 100644 frontend/src/pages/LandingPage.tsx 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...
+
+
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..6fdb0bd --- /dev/null +++ b/frontend/src/pages/LandingPage.tsx @@ -0,0 +1,440 @@ +import { useState, useEffect } from 'react' +import { useNavigate } from 'react-router-dom' +import { Button } from '@/components/ui/button' +import { Input } from '@/components/ui/input' +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 as simple components +const SearchIcon = () => ( + + + +) + +const ZapIcon = () => ( + + + +) + +const GitHubIcon = () => ( + + + +) + +const SparklesIcon = () => ( + + + +) + +// Demo repos configuration +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', + 'route handlers', + 'user validation', +] + +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 + + // Fetch available repos on mount + 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) return + + if (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 */} +
+
+ {/* Eyebrow */} +
+ + AI-powered code search +
+ + {/* Main Headline - Pain Point */} +

+ grep returned + 847 results. +
+ + Find the one that matters. + +

+ + {/* Subheadline */} +

+ Search any codebase by meaning, not keywords. + Powered by semantic embeddings and AI. +

+ + {/* Repo Selector */} +
+ {DEMO_REPOS.map(repo => { + const isAvailable = availableRepos.includes(repo.id) + const isSelected = selectedRepo === repo.id + + return ( + + ) + })} +
+ + {/* Search Box */} +
+
+
+
{ e.preventDefault(); handleSearch(); }} className="flex items-center gap-3"> +
+
+ +
+ setQuery(e.target.value)} + placeholder="Search for authentication, error handling, API routes..." + className="flex-1 bg-transparent border-0 text-white placeholder:text-gray-500 focus:outline-none text-base py-3" + autoFocus + /> +
+ +
+
+
+ + {/* Trust Indicators */} +
+
+ + ~100ms response +
+
+
+ No signup required +
+
+
+ {remaining} free searches +
+
+ + {/* Example Queries */} + {!hasSearched && ( +
+ Try: + {EXAMPLE_QUERIES.map(q => ( + + ))} +
+ )} +
+
+ + {/* Results Section */} + {hasSearched && ( +
+
+ {/* Results Header */} +
+
+ + {results.length} results + + {searchTime && ( + <> + + + {searchTime}ms + + + )} +
+ + {remaining > 0 && remaining < FREE_LIMIT && ( +
+ {remaining} {remaining === 1 ? 'search' : 'searches'} remaining +
+ )} +
+ + {/* Rate Limit Banner */} + {searchCount >= FREE_LIMIT && ( + +

+ You've used all free searches +

+

+ Sign up to get unlimited searches, index your own repos, and use the MCP server. +

+ +
+ )} + + {/* Results List */} +
+ {results.map((result, idx) => ( + + {/* Result Header */} +
+
+
+

{result.name}

+ + {result.type.replace('_', ' ')} + +
+

+ {result.file_path.split('/').slice(-2).join('/')} +

+
+
+
+ {(result.score * 100).toFixed(0)}% +
+
match
+
+
+ + {/* Code Preview */} + + {result.code} + +
+ ))} +
+ + {/* Empty State */} + {results.length === 0 && !loading && ( +
+
🔍
+

No results found

+

Try a different query or select another repository

+
+ )} +
+
+ )} + + {/* Features Preview (shown before search) */} + {!hasSearched && ( +
+
+
+
+
+ 🧠 +
+

Semantic Search

+

+ Find code by meaning, not keywords. Ask for "auth logic" and get authentication functions. +

+
+ +
+
+ 🔌 +
+

MCP Integration

+

+ Connect to Claude, Cursor, or any MCP client. Search code from your AI assistant. +

+
+ +
+
+ 📊 +
+

Code Intelligence

+

+ Understand dependencies, coding patterns, and impact analysis for your codebase. +

+
+
+
+
+ )} + + {/* Simple Footer */} +
+
+
+
+ CI +
+ CodeIntel +
+
+ + GitHub + + + Open Source +
+
+
+
+ ) +} From 075d9fb59a24d4192e26ff215eb9d20bb071f7c6 Mon Sep 17 00:00:00 2001 From: Devanshu Rajesh Chicholikar Date: Thu, 4 Dec 2025 23:50:41 -0500 Subject: [PATCH 2/2] feat(landing): Complete redesign with scroll-based storytelling - Remove bland 'Powered by' section - Add scroll-based story flow with animations - 'The Problem' section: grep chaos terminal visualization - 'The Solution' section: CodeIntel clean result demo - 'How It Works' section: 3-step process - 'Features' section: MCP, Speed, Intelligence, Open Source - Final CTA with GitHub star button - IntersectionObserver for fade-in animations - Cleaner visual hierarchy and spacing Part of landing page redesign (#22) --- frontend/src/pages/LandingPage.tsx | 497 ++++++++++++++++++----------- 1 file changed, 310 insertions(+), 187 deletions(-) diff --git a/frontend/src/pages/LandingPage.tsx b/frontend/src/pages/LandingPage.tsx index 6fdb0bd..7d21000 100644 --- a/frontend/src/pages/LandingPage.tsx +++ b/frontend/src/pages/LandingPage.tsx @@ -1,7 +1,6 @@ -import { useState, useEffect } from 'react' +import { useState, useEffect, useRef } from 'react' import { useNavigate } from 'react-router-dom' import { Button } from '@/components/ui/button' -import { Input } from '@/components/ui/input' import { Badge } from '@/components/ui/badge' import { Card } from '@/components/ui/card' import { Prism as SyntaxHighlighter } from 'react-syntax-highlighter' @@ -9,7 +8,7 @@ import { oneDark } from 'react-syntax-highlighter/dist/esm/styles/prism' import { API_URL } from '../config/api' import type { SearchResult } from '../types' -// Icons as simple components +// Icons const SearchIcon = () => ( @@ -34,7 +33,19 @@ const SparklesIcon = () => ( ) -// Demo repos configuration +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' }, @@ -45,10 +56,49 @@ const EXAMPLE_QUERIES = [ 'authentication middleware', 'error handling', 'database connection', - 'route handlers', - 'user validation', ] +// 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('') @@ -63,7 +113,6 @@ export function LandingPage() { const FREE_LIMIT = 5 const remaining = FREE_LIMIT - searchCount - // Fetch available repos on mount useEffect(() => { fetch(`${API_URL}/api/playground/repos`) .then(res => res.json()) @@ -76,9 +125,7 @@ export function LandingPage() { const handleSearch = async (searchQuery?: string) => { const q = searchQuery || query - if (!q.trim() || loading) return - - if (searchCount >= FREE_LIMIT) return + if (!q.trim() || loading || searchCount >= FREE_LIMIT) return setLoading(true) setHasSearched(true) @@ -88,15 +135,9 @@ export function LandingPage() { 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 - }) + 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) @@ -110,56 +151,39 @@ export function LandingPage() { } return ( -
+
{/* Navigation */} -
{/* Trust Indicators */} -
-
- - ~100ms response -
+
+
~100ms
-
- No signup required -
+ No signup required
-
- {remaining} free searches -
+ {remaining} free searches
{/* Example Queries */} {!hasSearched && ( -
+
Try: {EXAMPLE_QUERIES.map(q => ( - ))} @@ -274,150 +269,280 @@ export function LandingPage() {
- {/* Results Section */} + {/* ============ RESULTS SECTION (if searched) ============ */} {hasSearched && (
- {/* Results Header */}
- - {results.length} results - - {searchTime && ( - <> - - - {searchTime}ms - - - )} + {results.length} results + {searchTime && <>{searchTime}ms}
- {remaining > 0 && remaining < FREE_LIMIT && ( -
- {remaining} {remaining === 1 ? 'search' : 'searches'} remaining -
+
{remaining} remaining
)}
- {/* Rate Limit Banner */} {searchCount >= FREE_LIMIT && ( -

- You've used all free searches -

-

- Sign up to get unlimited searches, index your own repos, and use the MCP server. -

- +

You've used all free searches

+

Sign up to get unlimited searches and index your own repos.

+
)} - {/* Results List */}
{results.map((result, idx) => ( - - {/* Result Header */} +
-

{result.name}

- - {result.type.replace('_', ' ')} - +

{result.name}

+ {result.type.replace('_', ' ')}
-

- {result.file_path.split('/').slice(-2).join('/')} -

+

{result.file_path.split('/').slice(-2).join('/')}

-
- {(result.score * 100).toFixed(0)}% -
+
{(result.score * 100).toFixed(0)}%
match
- - {/* Code Preview */} - + {result.code}
))}
- {/* Empty State */} {results.length === 0 && !loading && (
🔍
-

No results found

-

Try a different query or select another repository

+

No results found

+

Try a different query

)}
)} - {/* Features Preview (shown before search) */} + {/* ============ 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 */} +
+
+
+
+
+ terminal +
+
+
$ 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? +
+
-

Semantic Search

-

- Find code by meaning, not keywords. Ask for "auth logic" and get authentication functions. -

- -
-
- 🔌 + +
+ + {/* THE SOLUTION */} +
+ +
+
+ The Solution +

Search by meaning, not keywords

+

+ Ask for "authentication logic" and get the function that actually handles it. +

+
+ + {/* CodeIntel visualization */} +
+
+
+
+ CI +
+ CodeIntel +
+ 1 result • 89ms +
+
+
+
+
+ authenticate_user + function +
+ src/auth/handlers.py +
+
+
94%
+
match
+
+
+
{`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)`}
+
-

MCP Integration

-

- Connect to Claude, Cursor, or any MCP client. Search code from your AI assistant. -

- -
-
- 📊 + +
+ + {/* 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.

+
-

Code Intelligence

-

- Understand dependencies, coding patterns, and impact analysis for your codebase. -

-
-
-
+ + + + {/* 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 + +
+
+
+
+ )} - {/* Simple Footer */} + {/* FOOTER */}