'use client'; import { useState, useEffect, useRef } from 'react'; import { apiClient } from '@/lib/api'; import { initializeOAuth, loginWithHuggingFace, loginDevMode, logout, getStoredUserInfo, isAuthenticated as checkIsAuthenticated, isDevelopmentMode } from '@/lib/auth'; import type { Model, Language } from '@/types'; import type { OAuthUserInfo } from '@/lib/auth'; interface LandingPageProps { onStart: (prompt: string, language: Language, modelId: string) => void; onImport?: (code: string, language: Language, importUrl?: string) => void; isAuthenticated: boolean; initialLanguage?: Language; initialModel?: string; onAuthChange?: () => void; } export default function LandingPage({ onStart, onImport, isAuthenticated, initialLanguage = 'html', initialModel = 'deepseek-ai/DeepSeek-V3.2-Exp', onAuthChange }: LandingPageProps) { const [prompt, setPrompt] = useState(''); const [selectedLanguage, setSelectedLanguage] = useState(initialLanguage); const [selectedModel, setSelectedModel] = useState(initialModel); const [models, setModels] = useState([]); const [languages, setLanguages] = useState([]); const [isLoading, setIsLoading] = useState(true); // Auth states const [userInfo, setUserInfo] = useState(null); const [isAuthLoading, setIsAuthLoading] = useState(true); const [showDevLogin, setShowDevLogin] = useState(false); const [devUsername, setDevUsername] = useState(''); const isDevMode = isDevelopmentMode(); // Dropdown states const [showLanguageDropdown, setShowLanguageDropdown] = useState(false); const [showModelDropdown, setShowModelDropdown] = useState(false); const [showImportDialog, setShowImportDialog] = useState(false); const languageDropdownRef = useRef(null); const modelDropdownRef = useRef(null); const importDialogRef = useRef(null); // Trending apps state const [trendingApps, setTrendingApps] = useState([]); // Import project state const [importUrl, setImportUrl] = useState(''); const [isImporting, setIsImporting] = useState(false); const [importError, setImportError] = useState(''); // Debug effect for dropdown state useEffect(() => { console.log('showModelDropdown state changed to:', showModelDropdown); }, [showModelDropdown]); // Debug effect for models state useEffect(() => { console.log('models state changed, length:', models.length, 'models:', models); }, [models]); useEffect(() => { console.log('Component mounted, initial load starting...'); loadData(); handleOAuthInit(); loadTrendingApps(); // Check auth status periodically to catch OAuth redirects const interval = setInterval(() => { const authenticated = checkIsAuthenticated(); if (authenticated && !userInfo) { handleOAuthInit(); } }, 1000); return () => clearInterval(interval); }, []); const handleOAuthInit = async () => { setIsAuthLoading(true); try { const oauthResult = await initializeOAuth(); if (oauthResult) { setUserInfo(oauthResult.userInfo); apiClient.setToken(oauthResult.accessToken); if (onAuthChange) onAuthChange(); } else { const storedUserInfo = getStoredUserInfo(); if (storedUserInfo) { setUserInfo(storedUserInfo); } } } catch (error) { console.error('OAuth initialization error:', error); } finally { setIsAuthLoading(false); } }; const handleLogin = async () => { try { await loginWithHuggingFace(); } catch (error) { console.error('Login failed:', error); alert('Failed to start login process. Please try again.'); } }; const handleLogout = () => { logout(); apiClient.logout(); setUserInfo(null); if (onAuthChange) onAuthChange(); window.location.reload(); }; const handleDevLogin = () => { if (!devUsername.trim()) { alert('Please enter a username'); return; } try { const result = loginDevMode(devUsername); setUserInfo(result.userInfo); apiClient.setToken(result.accessToken); setShowDevLogin(false); setDevUsername(''); if (onAuthChange) onAuthChange(); } catch (error) { console.error('Dev login failed:', error); alert('Failed to login in dev mode'); } }; // Close dropdowns when clicking outside useEffect(() => { const handleClickOutside = (event: MouseEvent) => { if (languageDropdownRef.current && !languageDropdownRef.current.contains(event.target as Node)) { setShowLanguageDropdown(false); } if (modelDropdownRef.current && !modelDropdownRef.current.contains(event.target as Node)) { setShowModelDropdown(false); } if (importDialogRef.current && !importDialogRef.current.contains(event.target as Node)) { setShowImportDialog(false); } }; document.addEventListener('mousedown', handleClickOutside); return () => { document.removeEventListener('mousedown', handleClickOutside); }; }, []); const loadData = async () => { console.log('loadData called'); setIsLoading(true); await Promise.all([loadModels(), loadLanguages()]); setIsLoading(false); console.log('loadData completed'); }; const loadModels = async () => { try { console.log('Loading models...'); const modelsList = await apiClient.getModels(); console.log('Models loaded successfully:', modelsList); console.log('Number of models:', modelsList.length); setModels(modelsList); console.log('Models state updated'); } catch (error) { console.error('Failed to load models:', error); setModels([]); // Set empty array on error } }; const loadLanguages = async () => { try { const { languages: languagesList } = await apiClient.getLanguages(); setLanguages(languagesList); } catch (error) { console.error('Failed to load languages:', error); } }; const loadTrendingApps = async () => { try { const apps = await apiClient.getTrendingAnycoderApps(); setTrendingApps(apps); } catch (error) { console.error('Failed to load trending apps:', error); } }; const handleSubmit = (e: React.FormEvent) => { e.preventDefault(); if (prompt.trim() && isAuthenticated) { onStart(prompt.trim(), selectedLanguage, selectedModel); } else if (!isAuthenticated) { alert('Please sign in with HuggingFace first!'); } }; const formatLanguageName = (lang: Language) => { if (lang === 'html') return 'HTML'; if (lang === 'transformers.js') return 'Transformers.js'; if (lang === 'comfyui') return 'ComfyUI'; return lang.charAt(0).toUpperCase() + lang.slice(1); }; const handleImportProject = async () => { if (!importUrl.trim()) { setImportError('Please enter a valid URL'); return; } if (!isAuthenticated) { alert('Please sign in with HuggingFace first!'); return; } setIsImporting(true); setImportError(''); try { const result = await apiClient.importProject(importUrl); if (result.status === 'success') { // Use onImport if available (better UX - directly loads code) // Otherwise fall back to onStart (sends message to generate) if (onImport && result.code) { onImport(result.code, result.language || 'html', importUrl); } else { // Fallback: trigger code generation with import context const importMessage = `Imported from ${importUrl}`; onStart(importMessage, result.language || 'html', selectedModel); } setShowImportDialog(false); setImportUrl(''); } else { setImportError(result.message || 'Failed to import project'); } } catch (error: any) { console.error('Import error:', error); setImportError(error.response?.data?.message || error.message || 'Failed to import project'); } finally { setIsImporting(false); } }; return (
{/* Header - Apple style */}
AnyCoder {/* Auth Section */}
{isAuthLoading ? ( Loading... ) : userInfo ? (
{userInfo.avatarUrl && ( {userInfo.name} )} {userInfo.preferredUsername || userInfo.name}
) : (
{/* Dev Mode Login (only on localhost) */} {isDevMode && ( <> {showDevLogin ? (
setDevUsername(e.target.value)} onKeyPress={(e) => e.key === 'Enter' && handleDevLogin()} placeholder="username" className="px-3 py-1.5 rounded-lg text-sm bg-[#1d1d1f] text-[#f5f5f7] border border-[#424245] focus:outline-none focus:border-white/50 w-32 font-medium" autoFocus />
) : ( )} or )} {/* OAuth Login */}
)}
{/* Main Content - Apple-style centered layout */}
{/* Apple-style Headline */}

Build with AnyCoder

Create apps with AI

{/* Simple prompt form */}
{/* Textarea */}