"use client"; import Link from "next/link"; import Image from "next/image"; import { useState } from "react"; import { useRouter } from "next/navigation"; import { appConfig } from '@/config/app.config'; import { toast } from "sonner"; // Import shared components import { Connector } from "@/components/shared/layout/curvy-rect"; import HeroFlame from "@/components/shared/effects/flame/hero-flame"; import AsciiExplosion from "@/components/shared/effects/flame/ascii-explosion"; import { HeaderProvider } from "@/components/shared/header/HeaderContext"; // Import hero section components import HomeHeroBackground from "@/components/app/(home)/sections/hero/Background/Background"; import { BackgroundOuterPiece } from "@/components/app/(home)/sections/hero/Background/BackgroundOuterPiece"; import HomeHeroBadge from "@/components/app/(home)/sections/hero/Badge/Badge"; import HomeHeroPixi from "@/components/app/(home)/sections/hero/Pixi/Pixi"; import HomeHeroTitle from "@/components/app/(home)/sections/hero/Title/Title"; import HeroInputSubmitButton from "@/components/app/(home)/sections/hero-input/Button/Button"; // import Globe from "@/components/app/(home)/sections/hero-input/_svg/Globe"; // Import header components import HeaderBrandKit from "@/components/shared/header/BrandKit/BrandKit"; import HeaderWrapper from "@/components/shared/header/Wrapper/Wrapper"; import HeaderDropdownWrapper from "@/components/shared/header/Dropdown/Wrapper/Wrapper"; import GithubIcon from "@/components/shared/header/Github/_svg/GithubIcon"; import ButtonUI from "@/components/ui/shadcn/button" interface SearchResult { url: string; title: string; description: string; screenshot: string | null; markdown: string; } export default function HomePage() { const [mode, setMode] = useState<'website' | 'paper'>('website'); const [url, setUrl] = useState(""); const [selectedStyle, setSelectedStyle] = useState("1"); const [selectedModel, setSelectedModel] = useState(appConfig.ai.defaultModel); const [isValidUrl, setIsValidUrl] = useState(false); const [showSearchTiles, setShowSearchTiles] = useState(false); const [searchResults, setSearchResults] = useState([]); const [isSearching, setIsSearching] = useState(false); const [hasSearched, setHasSearched] = useState(false); const [isFadingOut, setIsFadingOut] = useState(false); const [showSelectMessage, setShowSelectMessage] = useState(false); const [showInstructionsForIndex, setShowInstructionsForIndex] = useState(null); const [additionalInstructions, setAdditionalInstructions] = useState(''); const [extendBrandStyles, setExtendBrandStyles] = useState(false); const router = useRouter(); // Simple URL validation const validateUrl = (urlString: string) => { if (!urlString) return false; // Basic URL pattern - accepts domains with or without protocol const urlPattern = /^(https?:\/\/)?([\da-z\.-]+)\.([a-z\.]{2,6})([\/\w \.-]*)*\/?$/; return urlPattern.test(urlString.toLowerCase()); }; // Check if input is a URL (contains a dot) const isURL = (str: string): boolean => { const urlPattern = /^(https?:\/\/)?([a-zA-Z0-9-]+\.)+[a-zA-Z]{2,}(\/.*)?$/; return urlPattern.test(str.trim()); }; const styles = [ { id: "1", name: "Glassmorphism", description: "Frosted glass effect" }, { id: "2", name: "Neumorphism", description: "Soft 3D shadows" }, { id: "3", name: "Brutalism", description: "Bold and raw" }, { id: "4", name: "Minimalist", description: "Clean and simple" }, { id: "5", name: "Dark Mode", description: "Dark theme design" }, { id: "6", name: "Gradient Rich", description: "Vibrant gradients" }, { id: "7", name: "3D Depth", description: "Dimensional layers" }, { id: "8", name: "Retro Wave", description: "80s inspired" }, ]; const models = appConfig.ai.availableModels.map(model => ({ id: model, name: appConfig.ai.modelDisplayNames[model] || model, })); const handleSubmit = async (selectedResult?: SearchResult) => { const inputValue = url.trim(); if (!inputValue) { toast.error(mode === 'website' ? "Please enter a URL or search term" : "Please enter an Arxiv link or paper query"); return; } if (mode === 'paper') { toast.info("Transforming paper to code..."); try { const response = await fetch('/api/submit-job', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ arxivUrl: inputValue, repo: 'AUXteam/Paper2Code-Jobs', // Default repo for paper jobs paperName: inputValue.split('/').pop() || 'paper' }) }); const data = await response.json(); if (data.success) { toast.success("Job submitted! Code will be pushed to GitHub."); if (data.githubUrl) { window.open(data.githubUrl, '_blank'); } } else { toast.error(data.error || "Failed to submit job"); } } catch (error) { toast.error("An error occurred while submitting the paper"); } return; } // Validate brand extension mode requirements if (extendBrandStyles && isURL(inputValue) && !additionalInstructions.trim()) { toast.error("Please describe what you want to build with this brand's styles"); return; } // If it's a search result being selected, fade out and redirect if (selectedResult) { setIsFadingOut(true); // Wait for fade animation setTimeout(() => { sessionStorage.setItem('targetUrl', selectedResult.url); sessionStorage.setItem('selectedStyle', selectedStyle); sessionStorage.setItem('selectedModel', selectedModel); sessionStorage.setItem('autoStart', 'true'); if (selectedResult.markdown) { sessionStorage.setItem('siteMarkdown', selectedResult.markdown); } router.push('/generation'); }, 500); return; } // If it's a URL, check if we're extending brand styles or cloning if (isURL(inputValue)) { if (extendBrandStyles) { // Brand extension mode - extract brand styles and use them with the prompt sessionStorage.setItem('targetUrl', inputValue); sessionStorage.setItem('selectedModel', selectedModel); sessionStorage.setItem('autoStart', 'true'); sessionStorage.setItem('brandExtensionMode', 'true'); sessionStorage.setItem('brandExtensionPrompt', additionalInstructions || ''); router.push('/generation'); } else { // Normal clone mode sessionStorage.setItem('targetUrl', inputValue); sessionStorage.setItem('selectedStyle', selectedStyle); sessionStorage.setItem('selectedModel', selectedModel); sessionStorage.setItem('autoStart', 'true'); router.push('/generation'); } } else { // It's a search term, fade out if results exist, then search if (hasSearched && searchResults.length > 0) { setIsFadingOut(true); setTimeout(async () => { setSearchResults([]); setIsFadingOut(false); setShowSelectMessage(true); // Perform new search await performSearch(inputValue); setHasSearched(true); setShowSearchTiles(true); setShowSelectMessage(false); // Smooth scroll to carousel setTimeout(() => { const carouselSection = document.querySelector('.carousel-section'); if (carouselSection) { carouselSection.scrollIntoView({ behavior: 'smooth', block: 'center' }); } }, 300); }, 500); } else { // First search, no fade needed setShowSelectMessage(true); setIsSearching(true); setHasSearched(true); setShowSearchTiles(true); // Scroll to carousel area immediately setTimeout(() => { const carouselSection = document.querySelector('.carousel-section'); if (carouselSection) { carouselSection.scrollIntoView({ behavior: 'smooth', block: 'center' }); } }, 100); await performSearch(inputValue); setShowSelectMessage(false); setIsSearching(false); // Smooth scroll to carousel setTimeout(() => { const carouselSection = document.querySelector('.carousel-section'); if (carouselSection) { carouselSection.scrollIntoView({ behavior: 'smooth', block: 'center' }); } }, 300); } } }; // Perform search when user types const performSearch = async (searchQuery: string) => { if (!searchQuery.trim() || isURL(searchQuery)) { setSearchResults([]); setShowSearchTiles(false); return; } setIsSearching(true); setShowSearchTiles(true); try { const response = await fetch('/api/search', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ query: searchQuery }), }); if (response.ok) { const data = await response.json(); setSearchResults(data.results || []); setShowSearchTiles(true); } } catch (error) { console.error('Search error:', error); } finally { setIsSearching(false); } }; return (
{/* Header/Navigation Section */}
{/* Hero Section */}

{mode === 'website' ? "Clone brand format or re-imagine any website, in seconds." : "Transform research papers into functional Python or React code."}

e.preventDefault()} > Powered by Firecrawl.
{/* Mini Playground Input */}
{/* Hero Input Component */}
{/* Show different UI when search results are displayed */} {hasSearched && searchResults.length > 0 && !isFadingOut ? ( <> {/* Selection mode icon */} {/* Selection message */}
Select which site to clone from the results below
{/* Search again button */} ) : ( <> {isURL(url) ? ( // Scrape icon for URLs ) : ( // Search icon for search terms )} { const value = e.target.value; setUrl(value); setIsValidUrl(validateUrl(value)); // Reset search state when input changes if (value.trim() === "") { setShowSearchTiles(false); setHasSearched(false); setSearchResults([]); } }} onKeyDown={(e) => { if (e.key === "Enter" && !isSearching) { e.preventDefault(); handleSubmit(); } }} onFocus={() => { if (url.trim() && !isURL(url) && searchResults.length > 0) { setShowSearchTiles(true); } }} />
{ e.preventDefault(); if (!isSearching) { handleSubmit(); } }} className={isSearching ? 'pointer-events-none' : ''} > 0} buttonText={mode === 'website' ? (isURL(url) ? 'Scrape Site' : 'Search') : 'Submit Paper'} disabled={isSearching} />
)}
{/* Options Section - Only show when valid URL and in website mode */}
{/* Extend Brand Styles Toggle */}
setExtendBrandStyles(!extendBrandStyles)}>
Extend brand styles
{/* Brand Extension Prompt - Show when toggle is enabled */} {extendBrandStyles && (