Spaces:
Sleeping
Sleeping
| <html lang="en"> | |
| <head> | |
| <meta charset="UTF-8"> | |
| <meta name="viewport" content="width=device-width, initial-scale=1.0"> | |
| <title>SpamDex - AI Spam Detection</title> | |
| <!-- React & Tailwind Setup --> | |
| <script crossorigin src="https://unpkg.com/react@18/umd/react.production.min.js"></script> | |
| <script crossorigin src="https://unpkg.com/react-dom@18/umd/react-dom.production.min.js"></script> | |
| <script src="https://unpkg.com/@babel/standalone/babel.min.js"></script> | |
| <script src="https://cdn.tailwindcss.com"></script> | |
| <!-- Google Fonts --> | |
| <style> | |
| @import url('https://fonts.googleapis.com/css2?family=Outfit:wght@300;400;600;700&display=swap'); | |
| body { font-family: 'Outfit', sans-serif; } | |
| /* Animations */ | |
| @keyframes float { | |
| 0%, 100% { transform: translate(0, 0) rotate(0deg); } | |
| 25% { transform: translate(10px, -20px) rotate(5deg); } | |
| 50% { transform: translate(-5px, -30px) rotate(-5deg); } | |
| 75% { transform: translate(8px, -15px) rotate(3deg); } | |
| } | |
| @keyframes glow { | |
| 0%, 100% { opacity: 0.3; transform: scale(1); } | |
| 50% { opacity: 0.6; transform: scale(1.1); } | |
| } | |
| @keyframes slide-up { | |
| from { transform: translateY(30px); opacity: 0; } | |
| to { transform: translateY(0); opacity: 1; } | |
| } | |
| @keyframes pulse-ring { | |
| 0% { transform: scale(0.95); box-shadow: 0 0 0 0 rgba(255, 82, 82, 0.7); } | |
| 70% { transform: scale(1); box-shadow: 0 0 0 10px rgba(255, 82, 82, 0); } | |
| 100% { transform: scale(0.95); box-shadow: 0 0 0 0 rgba(255, 82, 82, 0); } | |
| } | |
| @keyframes pulse-ring-green { | |
| 0% { transform: scale(0.95); box-shadow: 0 0 0 0 rgba(76, 175, 80, 0.7); } | |
| 70% { transform: scale(1); box-shadow: 0 0 0 10px rgba(76, 175, 80, 0); } | |
| 100% { transform: scale(0.95); box-shadow: 0 0 0 0 rgba(76, 175, 80, 0); } | |
| } | |
| /* Glassmorphism Classes */ | |
| .glass { | |
| backdrop-filter: blur(16px) saturate(180%); | |
| -webkit-backdrop-filter: blur(16px) saturate(180%); | |
| } | |
| .glass-dark { | |
| background: rgba(17, 25, 40, 0.6); | |
| border: 1px solid rgba(255, 255, 255, 0.125); | |
| } | |
| .glass-light { | |
| background: rgba(255, 255, 255, 0.7); | |
| border: 1px solid rgba(209, 213, 219, 0.3); | |
| } | |
| /* Text Effects */ | |
| .glow-text-dark { text-shadow: 0 0 20px rgba(168, 85, 247, 0.5); } | |
| .glow-text-light { text-shadow: 0 0 15px rgba(236, 72, 153, 0.3); } | |
| .gradient-text { | |
| background: linear-gradient(135deg, #6366f1 0%, #a855f7 50%, #ec4899 100%); | |
| -webkit-background-clip: text; | |
| -webkit-text-fill-color: transparent; | |
| background-clip: text; | |
| } | |
| /* Utilities */ | |
| .animate-slide-up { animation: slide-up 0.8s cubic-bezier(0.2, 0.8, 0.2, 1) forwards; } | |
| .shimmer { | |
| background: linear-gradient(90deg, transparent, rgba(255,255,255,0.1), transparent); | |
| background-size: 1000px 100%; | |
| animation: shimmer 2s infinite linear; | |
| } | |
| @keyframes shimmer { 0% { background-position: -1000px 0; } 100% { background-position: 1000px 0; } } | |
| .cursive { font-family: 'Brush Script MT', cursive; } | |
| /* Smooth Scrollbar */ | |
| textarea::-webkit-scrollbar { width: 8px; } | |
| textarea::-webkit-scrollbar-track { background: transparent; } | |
| textarea::-webkit-scrollbar-thumb { background: rgba(156, 163, 175, 0.5); border-radius: 4px; } | |
| </style> | |
| </head> | |
| <body> | |
| <div id="root"></div> | |
| <script type="text/babel"> | |
| const { useState, useEffect } = React; | |
| // --- Icons (SVG Components for Browser Compatibility) --- | |
| const Icon = ({ path, className }) => ( | |
| <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" className={className}> | |
| {path} | |
| </svg> | |
| ); | |
| const Icons = { | |
| Shield: (p) => <Icon {...p} path={<path d="M12 22s8-4 8-10V5l-8-3-8 3v7c0 6 8 10 8 10z"/>} />, | |
| Sun: (p) => <Icon {...p} path={<><circle cx="12" cy="12" r="4"/><path d="M12 2v2"/><path d="M12 20v2"/><path d="m4.93 4.93 1.41 1.41"/><path d="m17.66 17.66 1.41 1.41"/><path d="M2 12h2"/><path d="M20 12h2"/><path d="m6.34 17.66-1.41 1.41"/><path d="m19.07 4.93-1.41 1.41"/></>} />, | |
| Moon: (p) => <Icon {...p} path={<path d="M12 3a6 6 0 0 0 9 9 9 9 0 1 1-9-9Z"/>} />, | |
| Zap: (p) => <Icon {...p} path={<polygon points="13 2 3 14 12 14 11 22 21 10 12 10 13 2"/>} />, | |
| Copy: (p) => <Icon {...p} path={<><rect width="14" height="14" x="8" y="8" rx="2" ry="2"/><path d="M4 16c-1.1 0-2-.9-2-2V4c0-1.1.9-2 2-2h10c1.1 0 2 .9 2 2"/></>} />, | |
| Check: (p) => <Icon {...p} path={<polyline points="20 6 9 17 4 12"/>} />, | |
| Sparkles: (p) => <Icon {...p} path={<><path d="m12 3-1.912 5.813a2 2 0 0 1-1.275 1.275L3 12l5.813 1.912a2 2 0 0 1 1.275 1.275L12 21l1.912-5.813a2 2 0 0 1 1.275-1.275L12 3Z"/><path d="M5 3v4"/><path d="M19 17v4"/><path d="M3 5h4"/><path d="M17 19h4"/></>} />, | |
| Brain: (p) => <Icon {...p} path={<><path d="M9.5 2A2.5 2.5 0 0 1 12 4.5v15a2.5 2.5 0 0 1-4.96.44 2.5 2.5 0 0 1-2.96-3.08 3 3 0 0 1-.34-5.58 2.5 2.5 0 0 1 1.32-4.24 2.5 2.5 0 0 1 1.98-3A2.5 2.5 0 0 1 9.5 2Z"/><path d="M14.5 2A2.5 2.5 0 0 0 12 4.5v15a2.5 2.5 0 0 0 4.96.44 2.5 2.5 0 0 0 2.96-3.08 3 3 0 0 0 .34-5.58 2.5 2.5 0 0 0-1.32-4.24 2.5 2.5 0 0 0-1.98-3A2.5 2.5 0 0 0 14.5 2Z"/></>} />, | |
| Lock: (p) => <Icon {...p} path={<><rect width="18" height="11" x="3" y="11" rx="2" ry="2"/><path d="M7 11V7a5 5 0 0 1 10 0v4"/></>} />, | |
| Globe: (p) => <Icon {...p} path={<><circle cx="12" cy="12" r="10"/><path d="M12 2a15.3 15.3 0 0 1 4 10 15.3 15.3 0 0 1-4 10 15.3 15.3 0 0 1-4-10 15.3 15.3 0 0 1 4-10z"/><path d="M2 12h20"/></>} />, | |
| Code: (p) => <Icon {...p} path={<><polyline points="16 18 22 12 16 6"/><polyline points="8 6 2 12 8 18"/></>} />, | |
| AlertCircle: (p) => <Icon {...p} path={<><circle cx="12" cy="12" r="10"/><line x1="12" x2="12" y1="8" y2="12"/><line x1="12" x2="12.01" y1="16" y2="16"/></>} />, | |
| CheckCircle: (p) => <Icon {...p} path={<><path d="M22 11.08V12a10 10 0 1 1-5.93-9.14"/><polyline points="22 4 12 14.01 9 11.01"/></>} />, | |
| Mail: (p) => <Icon {...p} path={<><rect width="20" height="16" x="2" y="4" rx="2"/><path d="m22 7-8.97 5.7a1.94 1.94 0 0 1-2.06 0L2 7"/></>} />, | |
| Send: (p) => <Icon {...p} path={<><line x1="22" x2="11" y1="2" y2="13"/><polygon points="22 2 15 22 11 13 2 9 22 2"/></>} />, | |
| Award: (p) => <Icon {...p} path={<><circle cx="12" cy="8" r="7"/><polyline points="8.21 13.89 7 23 12 20 17 23 15.79 13.88"/></>} />, | |
| TrendingUp: (p) => <Icon {...p} path={<polyline points="23 6 13.5 15.5 8.5 10.5 1 18"/>} /> | |
| }; | |
| // --- Main Application --- | |
| const SpamDexUI = () => { | |
| const [darkMode, setDarkMode] = useState(true); | |
| const [inputText, setInputText] = useState(''); | |
| const [result, setResult] = useState(null); | |
| const [loading, setLoading] = useState(false); | |
| const [copied, setCopied] = useState(false); | |
| const [particles, setParticles] = useState([]); | |
| const [currentUrl, setCurrentUrl] = useState(''); | |
| useEffect(() => { | |
| // Initialize particles | |
| const newParticles = [...Array(25)].map((_, i) => ({ | |
| id: i, | |
| size: Math.random() * 4 + 2, | |
| x: Math.random() * 100, | |
| y: Math.random() * 100, | |
| duration: Math.random() * 20 + 10, | |
| delay: Math.random() * 5 | |
| })); | |
| setParticles(newParticles); | |
| // Set current URL for API display | |
| setCurrentUrl(window.location.origin); | |
| }, []); | |
| const analyzeText = async () => { | |
| if (!inputText.trim()) return; | |
| setLoading(true); | |
| setResult(null); | |
| try { | |
| // Call the Python FastAPI Backend | |
| const response = await fetch('/api/predict', { | |
| method: 'POST', | |
| headers: { | |
| 'Content-Type': 'application/json', | |
| }, | |
| body: JSON.stringify({ text: inputText }) | |
| }); | |
| if (!response.ok) { | |
| const errorData = await response.json(); | |
| throw new Error(errorData.detail || 'Prediction failed'); | |
| } | |
| const data = await response.json(); | |
| setResult({ | |
| type: data.prediction, // 'spam' or 'safe' | |
| confidence: data.confidence, | |
| label: data.label | |
| }); | |
| } catch (err) { | |
| console.error("Error:", err); | |
| alert("Error connecting to server. Is the model loaded?"); | |
| } finally { | |
| setLoading(false); | |
| } | |
| }; | |
| const copyApiEndpoint = () => { | |
| navigator.clipboard.writeText(currentUrl + '/api/predict'); | |
| setCopied(true); | |
| setTimeout(() => setCopied(false), 2000); | |
| }; | |
| const examples = [ | |
| { text: "Congratulations! You won $1000! Click here now!", icon: Icons.AlertCircle, type: "spam" }, | |
| { text: "Hi, can we schedule a meeting tomorrow at 3 PM?", icon: Icons.CheckCircle, type: "safe" }, | |
| { text: "FREE FREE FREE!!! Win iPhone now!!!", icon: Icons.AlertCircle, type: "spam" }, | |
| { text: "Your package has been delivered successfully.", icon: Icons.CheckCircle, type: "safe" } | |
| ]; | |
| const features = [ | |
| { icon: Icons.Zap, title: "Lightning Fast", desc: "Sub-second spam detection", color: "from-yellow-400 to-orange-500" }, | |
| { icon: Icons.Brain, title: "AI Powered", desc: "Naive Bayes + TF-IDF", color: "from-purple-400 to-pink-500" }, | |
| { icon: Icons.Lock, title: "High Accuracy", desc: "98%+ precision rate", color: "from-green-400 to-emerald-500" }, | |
| { icon: Icons.Globe, title: "API Ready", desc: "Easy integration", color: "from-blue-400 to-cyan-500" } | |
| ]; | |
| const stats = [ | |
| { label: "Accuracy", value: "98.5%", icon: Icons.TrendingUp }, | |
| { label: "Models Trained", value: "10K+", icon: Icons.Brain }, | |
| { label: "API Calls", value: "1M+", icon: Icons.Zap }, | |
| { label: "Users", value: "5K+", icon: Icons.Award } | |
| ]; | |
| // Dynamic Classes based on Theme | |
| const glassClass = darkMode ? 'glass-dark glass' : 'glass-light glass'; | |
| const textClass = darkMode ? 'text-white' : 'text-gray-900'; | |
| const subTextClass = darkMode ? 'text-gray-400' : 'text-gray-600'; | |
| return ( | |
| <div className={`min-h-screen w-full transition-colors duration-700 overflow-x-hidden ${ | |
| darkMode | |
| ? 'bg-gradient-to-br from-slate-900 via-purple-950 to-slate-900' | |
| : 'bg-gradient-to-br from-indigo-50 via-purple-50 to-pink-50' | |
| }`}> | |
| {/* Background Particles */} | |
| <div className="fixed inset-0 overflow-hidden pointer-events-none"> | |
| {particles.map(p => ( | |
| <div key={p.id} | |
| className={`absolute rounded-full blur-sm transition-colors duration-700 ${darkMode ? 'bg-purple-500' : 'bg-pink-400'}`} | |
| style={{ | |
| width: p.size + 'px', | |
| height: p.size + 'px', | |
| left: p.x + '%', | |
| top: p.y + '%', | |
| animation: `float ${p.duration}s linear infinite`, | |
| animationDelay: p.delay + 's', | |
| opacity: 0.4 | |
| }} | |
| /> | |
| ))} | |
| <div className={`absolute top-0 left-0 w-96 h-96 rounded-full blur-[100px] opacity-20 ${darkMode ? 'bg-purple-600' : 'bg-pink-400'}`} style={{ animation: 'glow 8s ease-in-out infinite' }} /> | |
| <div className={`absolute bottom-0 right-0 w-96 h-96 rounded-full blur-[100px] opacity-20 ${darkMode ? 'bg-blue-600' : 'bg-orange-400'}`} style={{ animation: 'glow 10s ease-in-out infinite' }} /> | |
| </div> | |
| <div className="relative z-10 container mx-auto px-4 py-6 md:py-10 max-w-7xl"> | |
| {/* Header */} | |
| <div className="flex flex-row justify-between items-center mb-10 md:mb-16 animate-slide-up"> | |
| <div className="flex items-center gap-3 md:gap-4"> | |
| <div className={`p-2 md:p-3 rounded-2xl shadow-xl relative overflow-hidden transition-all hover:scale-105 ${ | |
| darkMode ? 'bg-gradient-to-br from-purple-600 to-pink-600' : 'bg-gradient-to-br from-orange-400 to-pink-500' | |
| }`}> | |
| <Icons.Shield className="w-8 h-8 md:w-10 md:h-10 text-white relative z-10" /> | |
| <div className="absolute inset-0 shimmer" /> | |
| </div> | |
| <div> | |
| <h1 className={`text-2xl md:text-4xl font-bold tracking-tight ${textClass}`}> | |
| DarkNeuron | |
| </h1> | |
| <p className={`text-xs md:text-sm font-semibold tracking-wider ${darkMode ? 'text-purple-400' : 'text-pink-600'}`}> | |
| SpamDex AI v1.0 | |
| </p> | |
| </div> | |
| </div> | |
| <button | |
| onClick={() => setDarkMode(!darkMode)} | |
| className={`p-3 md:p-4 rounded-full ${glassClass} hover:scale-110 active:scale-95 transition-all duration-300 shadow-lg group`} | |
| > | |
| <div className="group-hover:rotate-180 transition-transform duration-500"> | |
| {darkMode ? <Icons.Sun className="w-6 h-6 text-yellow-400" /> : <Icons.Moon className="w-6 h-6 text-indigo-600" />} | |
| </div> | |
| </button> | |
| </div> | |
| {/* Hero Section */} | |
| <div className="text-center mb-12 animate-slide-up" style={{ animationDelay: '0.1s' }}> | |
| <div className={`inline-flex items-center gap-2 px-4 py-2 rounded-full ${glassClass} mb-6 shadow-lg border-opacity-20`}> | |
| <Icons.Sparkles className={`w-4 h-4 md:w-5 md:h-5 ${darkMode ? 'text-purple-400' : 'text-pink-500'}`} /> | |
| <span className={`text-xs md:text-sm font-semibold ${darkMode ? 'text-purple-300' : 'text-pink-600'}`}> | |
| Powered by Advanced ML Algorithms | |
| </span> | |
| </div> | |
| <h2 className={`text-5xl md:text-7xl font-bold mb-6 leading-tight ${textClass}`}> | |
| Welcome to <br className="md:hidden"/> | |
| <span className="cursive gradient-text inline-block hover:scale-105 transition-transform cursor-default">SpamDex</span> | |
| </h2> | |
| <p className={`text-base md:text-xl ${subTextClass} max-w-3xl mx-auto leading-relaxed px-4`}> | |
| Unleash the power of <span className={`font-bold ${darkMode ? 'text-gray-200' : 'text-gray-800'}`}>AI-driven spam detection</span>. | |
| Secure your inbox with <span className={darkMode ? 'text-purple-400' : 'text-pink-600'}>98% Accuracy</span>. | |
| </p> | |
| </div> | |
| {/* Stats Bar (Grid Layout) */} | |
| <div className="grid grid-cols-2 lg:grid-cols-4 gap-4 md:gap-6 mb-12 animate-slide-up" style={{ animationDelay: '0.2s' }}> | |
| {stats.map((stat, idx) => ( | |
| <div key={idx} className={`${glassClass} rounded-2xl p-5 md:p-6 text-center hover:scale-105 hover:-translate-y-2 transition-all duration-300 shadow-lg cursor-default`}> | |
| <stat.icon className={`w-8 h-8 mx-auto mb-3 ${darkMode ? 'text-purple-400' : 'text-pink-500'}`} /> | |
| <div className={`text-2xl md:text-3xl font-bold mb-1 ${textClass}`}> | |
| {stat.value} | |
| </div> | |
| <div className={`text-xs md:text-sm ${subTextClass}`}> | |
| {stat.label} | |
| </div> | |
| </div> | |
| ))} | |
| </div> | |
| {/* Main Analysis Section */} | |
| <div className="max-w-6xl mx-auto animate-slide-up" style={{ animationDelay: '0.3s' }}> | |
| <div className={`${glassClass} rounded-3xl p-5 md:p-8 shadow-2xl relative overflow-hidden border-opacity-40`}> | |
| {/* Tab Header */} | |
| <div className="flex gap-3 mb-8 border-b border-gray-500/10 pb-4"> | |
| <button className={`flex items-center gap-2 md:gap-3 px-6 py-3 rounded-xl font-bold transition-all shadow-lg text-sm md:text-base ${ | |
| darkMode | |
| ? 'bg-gradient-to-r from-purple-600 to-pink-600 text-white' | |
| : 'bg-gradient-to-r from-orange-400 to-pink-500 text-white' | |
| }`}> | |
| <Icons.Mail className="w-5 h-5" /> | |
| Text Analysis | |
| </button> | |
| </div> | |
| <div className="grid lg:grid-cols-3 gap-8"> | |
| {/* Left Column: Input */} | |
| <div className="lg:col-span-2"> | |
| <div className="mb-6"> | |
| <div className="flex items-center gap-2 mb-3"> | |
| <Icons.Send className={`w-5 h-5 ${darkMode ? 'text-purple-400' : 'text-pink-500'}`} /> | |
| <label className={`text-lg font-bold ${textClass}`}> | |
| Enter Your Message | |
| </label> | |
| </div> | |
| <textarea | |
| value={inputText} | |
| onChange={(e) => setInputText(e.target.value)} | |
| placeholder="Paste email content or SMS text here..." | |
| className={`w-full h-48 p-4 rounded-xl resize-none focus:outline-none focus:ring-2 transition-all text-base md:text-lg ${ | |
| darkMode | |
| ? 'bg-slate-900/50 text-white placeholder-gray-500 focus:ring-purple-500/50 border border-white/10' | |
| : 'bg-white/60 text-gray-900 placeholder-gray-500 focus:ring-pink-500/50 border border-gray-200' | |
| }`} | |
| ></textarea> | |
| </div> | |
| <div className="flex flex-col sm:flex-row gap-4"> | |
| <button | |
| onClick={analyzeText} | |
| disabled={loading || !inputText.trim()} | |
| className={`flex-1 py-4 rounded-xl font-bold text-white text-lg transition-all transform hover:scale-[1.02] active:scale-95 disabled:opacity-50 disabled:cursor-not-allowed shadow-xl relative overflow-hidden group ${ | |
| darkMode | |
| ? 'bg-gradient-to-r from-purple-600 to-pink-600' | |
| : 'bg-gradient-to-r from-orange-500 to-pink-500' | |
| }`} | |
| > | |
| {loading && <div className="absolute inset-0 shimmer" />} | |
| {loading ? ( | |
| <span className="flex items-center justify-center gap-2"> | |
| <div className="w-5 h-5 border-2 border-white border-t-transparent rounded-full animate-spin" /> | |
| Analyzing... | |
| </span> | |
| ) : ( | |
| <span className="flex items-center justify-center gap-2"> | |
| <Icons.Brain className="w-5 h-5 group-hover:animate-bounce" /> | |
| Analyze Message | |
| </span> | |
| )} | |
| </button> | |
| <button | |
| onClick={() => { setInputText(''); setResult(null); }} | |
| className={`px-6 py-4 rounded-xl font-bold transition-all hover:bg-opacity-80 active:scale-95 ${ | |
| darkMode | |
| ? 'bg-red-500/10 text-red-400 hover:bg-red-500/20 border border-red-500/20' | |
| : 'bg-red-100 text-red-600 hover:bg-red-200' | |
| }`}> | |
| Clear | |
| </button> | |
| </div> | |
| </div> | |
| {/* Right Column: Results & Info */} | |
| <div className="lg:col-span-1 flex flex-col gap-6"> | |
| {result ? ( | |
| <div className={`rounded-2xl p-6 border-2 transition-all animate-slide-up h-full flex flex-col justify-center items-center shadow-2xl relative overflow-hidden ${ | |
| result.type === 'spam' | |
| ? darkMode ? 'border-red-500/50 bg-red-900/20' : 'border-red-400 bg-red-50' | |
| : darkMode ? 'border-green-500/50 bg-green-900/20' : 'border-green-400 bg-green-50' | |
| }`}> | |
| {/* Pulse Animation based on result */} | |
| <div style={{animation: result.type === 'spam' ? 'pulse-ring 2s infinite' : 'pulse-ring-green 2s infinite'}} | |
| className={`absolute w-32 h-32 rounded-full opacity-20 ${result.type === 'spam' ? 'bg-red-500' : 'bg-green-500'}`}></div> | |
| <div className={`w-20 h-20 rounded-full mb-4 flex items-center justify-center z-10 ${ | |
| result.type === 'spam' ? 'bg-red-100 text-red-600' : 'bg-green-100 text-green-600' | |
| }`}> | |
| {result.type === 'spam' ? <Icons.AlertCircle className="w-10 h-10"/> : <Icons.CheckCircle className="w-10 h-10"/>} | |
| </div> | |
| <h3 className={`text-2xl font-bold text-center mb-1 ${ | |
| result.type === 'spam' ? 'text-red-500' : 'text-green-600' | |
| }`}> | |
| {result.type === 'spam' ? 'SPAM DETECTED!' : 'SAFE MESSAGE'} | |
| </h3> | |
| <div className={`text-center ${subTextClass} mt-2`}> | |
| <p className="text-xs uppercase tracking-widest opacity-70 mb-1">Confidence Score</p> | |
| <p className={`text-4xl font-bold ${textClass}`}>{result.confidence.toFixed(1)}%</p> | |
| </div> | |
| </div> | |
| ) : ( | |
| <div className={`${glassClass} rounded-2xl p-6 h-full min-h-[250px] flex flex-col items-center justify-center text-center border border-dashed ${darkMode ? 'border-gray-700' : 'border-gray-300'}`}> | |
| <Icons.Brain className={`w-16 h-16 mb-4 opacity-30 ${textClass}`} /> | |
| <p className={`text-sm ${subTextClass}`}> | |
| Your analysis results will appear here automatically. | |
| </p> | |
| </div> | |
| )} | |
| </div> | |
| </div> | |
| {/* Examples */} | |
| <div className="mt-8 pt-6 border-t border-gray-500/20"> | |
| <div className="flex items-center gap-2 mb-4"> | |
| <Icons.Sparkles className={`w-4 h-4 ${darkMode ? 'text-purple-400' : 'text-pink-500'}`} /> | |
| <span className={`text-sm font-bold uppercase tracking-wider ${subTextClass}`}> | |
| Or try these examples | |
| </span> | |
| </div> | |
| <div className="grid grid-cols-1 md:grid-cols-2 gap-3"> | |
| {examples.map((example, idx) => ( | |
| <button | |
| key={idx} | |
| onClick={() => setInputText(example.text)} | |
| className={`p-4 rounded-xl text-left transition-all hover:-translate-y-1 group flex items-start gap-3 ${ | |
| darkMode | |
| ? 'bg-white/5 hover:bg-white/10 border border-white/5' | |
| : 'bg-white hover:bg-gray-50 border border-gray-200 shadow-sm' | |
| }`}> | |
| <div className={`mt-0.5 ${example.type === 'spam' ? 'text-red-500' : 'text-green-500'}`}> | |
| {example.type === 'spam' ? <Icons.AlertCircle className="w-4 h-4"/> : <Icons.CheckCircle className="w-4 h-4"/>} | |
| </div> | |
| <span className={`text-xs md:text-sm line-clamp-2 ${darkMode ? 'text-gray-300' : 'text-gray-600'}`}> | |
| {example.text} | |
| </span> | |
| </button> | |
| ))} | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| {/* Features Grid */} | |
| <div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-4 md:gap-6 my-12 animate-slide-up" style={{ animationDelay: '0.4s' }}> | |
| {features.map((feature, idx) => ( | |
| <div key={idx} className={`${glassClass} rounded-2xl p-6 text-center hover:scale-105 hover:shadow-xl transition-all duration-300 cursor-default group`}> | |
| <div className={`w-14 h-14 mx-auto mb-4 rounded-2xl bg-gradient-to-br ${feature.color} flex items-center justify-center shadow-lg group-hover:rotate-12 transition-transform`}> | |
| <feature.icon className="w-7 h-7 text-white" /> | |
| </div> | |
| <h3 className={`text-lg font-bold mb-2 ${textClass}`}> | |
| {feature.title} | |
| </h3> | |
| <p className={`text-xs md:text-sm ${subTextClass} leading-relaxed`}> | |
| {feature.desc} | |
| </p> | |
| </div> | |
| ))} | |
| </div> | |
| {/* API Section */} | |
| <div className={`${glassClass} rounded-3xl p-6 md:p-8 shadow-2xl animate-slide-up mb-10`} style={{ animationDelay: '0.5s' }}> | |
| <div className="flex items-center gap-3 mb-6"> | |
| <Icons.Code className={`w-7 h-7 md:w-8 md:h-8 ${darkMode ? 'text-purple-400' : 'text-pink-500'}`} /> | |
| <h3 className={`text-xl md:text-2xl font-bold ${textClass}`}> | |
| API Integration | |
| </h3> | |
| </div> | |
| <div className={`flex flex-col md:flex-row items-center gap-4 p-4 rounded-xl ${darkMode ? 'bg-gray-900/50 border border-white/10' : 'bg-gray-100 border border-gray-200'}`}> | |
| <div className="flex-1 w-full overflow-hidden"> | |
| <code className={`block text-xs md:text-base font-mono truncate ${darkMode ? 'text-cyan-400' : 'text-purple-600'}`}> | |
| POST {currentUrl}/api/predict | |
| </code> | |
| </div> | |
| <button | |
| onClick={copyApiEndpoint} | |
| className={`flex items-center gap-2 px-4 py-2 rounded-lg transition-all hover:scale-105 active:scale-95 font-medium text-sm ${ | |
| darkMode ? 'bg-white/10 hover:bg-white/20 text-white' : 'bg-white hover:bg-gray-50 text-gray-700 shadow-sm' | |
| }`}> | |
| {copied ? <Icons.Check className="w-4 h-4 text-green-500" /> : <Icons.Copy className="w-4 h-4" />} | |
| {copied ? 'Copied!' : 'Copy Endpoint'} | |
| </button> | |
| </div> | |
| <p className={`mt-4 text-sm ${subTextClass}`}> | |
| Send a JSON body <code>{`{"text": "your message"}`}</code> to get a prediction. | |
| </p> | |
| </div> | |
| {/* Footer */} | |
| <div className={`text-center pb-8 ${subTextClass} animate-slide-up`} style={{ animationDelay: '0.6s' }}> | |
| <p className="text-sm flex items-center justify-center gap-2"> | |
| Crafted with <span className="text-red-500 animate-pulse text-lg">β€οΈ</span> by | |
| <span className="font-bold cursive text-lg gradient-text">@Madara369Uchiha</span> | |
| </p> | |
| </div> | |
| </div> | |
| </div> | |
| ); | |
| }; | |
| const root = ReactDOM.createRoot(document.getElementById('root')); | |
| root.render(<SpamDexUI />); | |
| </script> | |
| </body> | |
| </html> |