import React, { useState, useEffect } from 'react'; import { Link, useNavigate } from 'react-router-dom'; import { useTranslation } from '../js/i18n'; import SEO from '../components/SEO'; const tools = [ { id: 'handwrite', icon: '/assets/icon/handwrite.png', path: '/handwriting' }, { id: 'quoteCard', icon: '/assets/icon/quotecard.png', path: '/quote-card' }, { id: 'markdown2image', icon: '/assets/icon/markdown2image.png', path: '/markdown-to-image' }, { id: 'subtitleGenerator', icon: '/assets/icon/subtitle2image.png', path: '/subtitle-to-image' }, { id: 'imageCompressor', icon: '/assets/icon/image-compressor.png', path: '/image-compressor' }, { id: 'imageWatermark', icon: '/assets/icon/image-watermark.png', path: '/image-watermark' }, { id: 'imageBackgroundRemover', icon: '/assets/icon/image-background-remover.png', path: '/background-remover' }, { id: 'textBehindImage', icon: '/assets/icon/text-behind-image.png', path: '/text-behind-image' }, { id: 'latex2image', icon: '/assets/icon/latex2image.png', path: '/latex-to-image' }, { id: 'jsonFormatter', icon: '/assets/icon/json-format.png', path: '/json-formatter' }, { id: 'urlEncodeDecode', icon: '/assets/icon/url-endecode.png', path: '/url-encode-and-decode' }, { id: 'imageBase64Converter', icon: '/assets/icon/image-base64.png', path: '/image-base64' }, { id: 'textDiff', icon: '/assets/icon/diff.png', path: '/text-diff' }, { id: 'openAITimeline', icon: '/assets/icon/openai_small.svg', path: '/openai-timeline' }, { id: 'anthropicTimeline', icon: '/assets/icon/anthropic_small.svg', path: '/anthropic-timeline' }, { id: 'modelPrice', icon: '/assets/icon/openai_small.svg', path: '/llm-model-price' }, { id: 'fisherai', icon: '/assets/icon/fisherai.png', path: 'https://chromewebstore.google.com/detail/fisherai-your-best-summar/ipfiijaobcenaibdpaacbbpbjefgekbj', external: true } ]; const Home = () => { const { t } = useTranslation(); const [loading, setLoading] = useState(''); const navigate = useNavigate(); useEffect(() => { const handleClearLoading = () => { setLoading(''); }; window.addEventListener('clearLoadingState', handleClearLoading); return () => { window.removeEventListener('clearLoadingState', handleClearLoading); }; }, []); const handleNavigate = (tool) => { if (tool.external) { window.open(tool.path, '_blank', 'noopener,noreferrer'); return; } setLoading(tool.id); window.scrollTo(0, 0); navigate(tool.path); }; const renderToolLink = (tool) => { const content = ( <div className={`group flex items-center gap-4 p-6 bg-white rounded-xl shadow-lg hover:shadow-xl transform hover:-translate-y-1 transition-all duration-300 relative ${loading === tool.id ? 'pointer-events-none' : ''}`}> {loading === tool.id && ( <div className="absolute inset-0 bg-white/80 rounded-xl flex items-center justify-center z-10"> <div className="w-8 h-8 border-4 border-indigo-500 border-t-transparent rounded-full animate-spin"></div> </div> )} <img src={tool.icon} alt={`${t(`tools.${tool.id}.title`)} icon`} className="w-12 h-12 object-contain group-hover:scale-110 transition-transform duration-300" loading="lazy" /> <div className="flex-1"> <h3 className="text-lg font-semibold text-gray-800 mb-1 group-hover:text-indigo-600 transition-colors duration-300"> {t(`tools.${tool.id}.title`)} </h3> <p className="text-sm text-gray-600 overflow-hidden text-ellipsis [-webkit-line-clamp:2] [display:-webkit-box] [-webkit-box-orient:vertical]"> {t(`tools.${tool.id}.description`)} </p> </div> </div> ); return tool.external ? ( <a onClick={() => handleNavigate(tool)} className="block cursor-pointer" > {content} </a> ) : ( <div onClick={() => handleNavigate(tool)} className="block cursor-pointer"> {content} </div> ); }; return ( <> <SEO title={t('title')} description={t('slogan')} /> <main className="min-h-screen bg-gradient-to-br from-indigo-50/50 via-white to-indigo-50/50 pt-16"> {/* Hero Section */} <div className="relative overflow-hidden"> <div className="absolute inset-0 bg-grid-pattern opacity-5"></div> <div className="max-w-7xl mx-auto px-4 pt-20 sm:pt-32 pb-12 sm:pb-20"> <div className="text-center relative z-10"> <h1 className="text-4xl sm:text-5xl font-bold text-indigo-900/90 mb-4 sm:mb-6 animate-fade-in"> AI Toolbox </h1> <p className="text-lg sm:text-xl text-indigo-800/80 max-w-2xl mx-auto mb-8 sm:mb-12 animate-fade-in-delay px-4"> {t('slogan')} </p> <div className="w-full h-0.5 max-w-xs mx-auto bg-gradient-to-r from-transparent via-indigo-400/50 to-transparent opacity-75"></div> </div> </div> </div> {/* Tools Grid */} <div className="max-w-7xl mx-auto px-4 py-8 sm:py-16"> <div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4 sm:gap-8"> {tools.map(tool => ( <React.Fragment key={tool.id}> {renderToolLink(tool)} </React.Fragment> ))} </div> </div> {/* Footer Navigation */} <div className="max-w-7xl mx-auto px-4 pb-12 sm:pb-20"> <div className="flex flex-wrap justify-center gap-4 sm:gap-8"> <a href="https://github.com/fisherdaddy/ai-toolbox" target="_blank" rel="noopener noreferrer" className="group flex items-center px-6 py-3 rounded-full bg-white/80 hover:bg-white shadow-sm hover:shadow-md transition-all duration-300" > <svg className="w-5 h-5 mr-3 text-gray-700 group-hover:text-indigo-500 transition-colors" viewBox="0 0 24 24" fill="currentColor"> <path d="M12 0C5.37 0 0 5.37 0 12c0 5.31 3.435 9.795 8.205 11.385.6.105.825-.255.825-.57 0-.285-.015-1.23-.015-2.235-3.015.555-3.795-.735-4.035-1.41-.135-.345-.72-1.41-1.23-1.695-.42-.225-1.02-.78-.015-.795.945-.015 1.62.87 1.845 1.23 1.08 1.815 2.805 1.305 3.495.99.105-.78.42-1.305.765-1.605-2.67-.3-5.46-1.335-5.46-5.925 0-1.305.465-2.385 1.23-3.225-.12-.3-.54-1.53.12-3.18 0 0 1.005-.315 3.3 1.23.96-.27 1.98-.405 3-.405s2.04.135 3 .405c2.295-1.56 3.3-1.23 3.3-1.23.66 1.65.24 2.88.12 3.18.765.84 1.23 1.905 1.23 3.225 0 4.605-2.805 5.625-5.475 5.925.435.375.81 1.095.81 2.22 0 1.605-.015 2.895-.015 3.3 0 .315.225.69.825.57A12.02 12.02 0 0024 12c0-6.63-5.37-12-12-12z"/> </svg> <span className="text-gray-700 group-hover:text-indigo-500 font-medium transition-colors">GitHub</span> </a> <Link to="/about" className="group flex items-center px-6 py-3 rounded-full bg-white/80 hover:bg-white shadow-sm hover:shadow-md transition-all duration-300" > <svg className="w-5 h-5 mr-3 text-gray-700 group-hover:text-indigo-500 transition-colors" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2"> <path strokeLinecap="round" strokeLinejoin="round" d="M13 16h-1v-4h-1m1-4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z" /> </svg> <span className="text-gray-700 group-hover:text-indigo-500 font-medium transition-colors">{t('navigation.about')}</span> </Link> </div> </div> </main> <style jsx global>{` .bg-grid-pattern { background-image: radial-gradient(circle at 1px 1px, rgb(226 232 240 / 30%) 1px, transparent 0); background-size: 24px 24px; } .animate-fade-in { animation: fadeIn 0.8s ease-out; } .animate-fade-in-delay { animation: fadeIn 0.8s ease-out 0.2s both; } @keyframes fadeIn { from { opacity: 0; transform: translateY(10px); } to { opacity: 1; transform: translateY(0); } } `}</style> </> ); }; export default Home;