| <!DOCTYPE html> |
| <html lang="en"> |
| <head> |
| <meta charset="UTF-8"> |
| <meta name="viewport" content="width=device-width, initial-scale=1.0"> |
| <title>π€ AI-Hub Live Connector - v4.0</title> |
| <style> |
| |
| :root { |
| --bg-dark: #0a0f14; |
| --bg-card: #11181f; |
| --bg-input: #141a20; |
| --neon-green: #00ffaa; |
| --neon-blue: #00d4ff; |
| --neon-red: #ff3366; |
| --text-main: #e0e6ed; |
| --text-muted: #7a8b9e; |
| --border-color: #2a3540; |
| } |
| * { box-sizing: border-box; margin: 0; padding: 0; font-family: 'Segoe UI', 'Courier New', sans-serif; } |
| |
| body { |
| background-color: var(--bg-dark); |
| color: var(--text-main); |
| padding: 20px; |
| display: flex; |
| justify-content: center; |
| min-height: 100vh; |
| background-image: radial-gradient(circle at 50% 0%, #11181f 0%, #0a0f14 100%); |
| } |
| |
| .app-container { |
| max-width: 1200px; |
| width: 100%; |
| background: rgba(20, 26, 32, 0.8); |
| border: 1px solid rgba(0, 255, 170, 0.1); |
| border-radius: 20px; |
| padding: 30px; |
| box-shadow: 0 10px 40px rgba(0,0,0,0.8); |
| backdrop-filter: blur(5px); |
| } |
| |
| |
| .header { display: flex; justify-content: space-between; align-items: center; border-bottom: 1px solid var(--border-color); padding-bottom: 15px; margin-bottom: 25px; flex-wrap: wrap;} |
| h1 { color: var(--neon-green); text-shadow: 0 0 20px rgba(0, 255, 170, 0.2); font-size: 1.8rem; } |
| .status-dot { display: inline-block; width: 10px; height: 10px; background: var(--neon-green); border-radius: 50%; animation: pulse 2s infinite; } |
| @keyframes pulse { 0% { opacity: 0.5; } 50% { opacity: 1; } 100% { opacity: 0.5; } } |
| |
| |
| .search-section { background: var(--bg-card); border: 1px solid var(--border-color); border-radius: 12px; padding: 20px; margin-bottom: 25px; } |
| .search-row { display: flex; gap: 12px; flex-wrap: wrap; } |
| |
| .search-wrapper { flex: 1; position: relative; min-width: 200px; } |
| .search-wrapper input { |
| width: 100%; padding: 14px 20px; background: var(--bg-input); |
| border: 1px solid var(--border-color); border-radius: 50px; color: white; |
| font-size: 1rem; outline: none; transition: 0.3s; |
| } |
| .search-wrapper input:focus { border-color: var(--neon-blue); box-shadow: 0 0 15px rgba(0, 212, 255, 0.1); } |
| .search-wrapper input::placeholder { color: #555; } |
| |
| .search-row select { |
| background: var(--bg-input); border: 1px solid var(--border-color); |
| color: white; padding: 0 15px; border-radius: 50px; outline: none; cursor: pointer; |
| } |
| .search-row button { |
| background: transparent; border: 1px solid var(--neon-green); color: var(--neon-green); |
| padding: 12px 28px; border-radius: 50px; cursor: pointer; font-weight: 600; |
| transition: 0.3s; text-transform: uppercase; letter-spacing: 1px; |
| } |
| .search-row button:hover { background: rgba(0, 255, 170, 0.1); box-shadow: 0 0 20px rgba(0, 255, 170, 0.2); } |
| .search-row button:disabled { opacity: 0.5; cursor: not-allowed; box-shadow: none; } |
| |
| |
| .status-bar { display: flex; justify-content: space-between; align-items: center; margin-top: 15px; font-size: 0.9rem; color: var(--text-muted); flex-wrap: wrap; gap: 10px; } |
| .status-msg { padding: 8px 15px; border-radius: 20px; } |
| .status-msg.error { background: rgba(255, 51, 102, 0.1); border: 1px solid var(--neon-red); color: var(--neon-red); } |
| .status-msg.success { background: rgba(0, 255, 170, 0.1); border: 1px solid var(--neon-green); color: var(--neon-green); } |
| |
| |
| .results-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(300px, 1fr)); gap: 20px; margin-top: 20px; } |
| |
| .result-card { |
| background: var(--bg-card); border: 1px solid var(--border-color); |
| border-radius: 12px; padding: 20px; transition: all 0.3s cubic-bezier(0.175, 0.885, 0.32, 1.275); |
| position: relative; overflow: hidden; |
| } |
| .result-card:hover { border-color: var(--neon-blue); transform: translateY(-5px) scale(1.02); box-shadow: 0 10px 20px rgba(0,0,0,0.5); } |
| |
| .card-badge { position: absolute; top: 10px; right: 10px; font-size: 0.7rem; background: #2a3540; padding: 4px 10px; border-radius: 12px; color: var(--text-muted); } |
| .model-name { color: var(--neon-green); font-weight: bold; font-size: 1.2rem; margin-bottom: 10px; word-break: break-all; } |
| .model-detail { display: flex; justify-content: space-between; color: var(--text-muted); font-size: 0.9rem; margin-bottom: 8px; border-bottom: 1px dotted #222; padding-bottom: 5px; } |
| |
| .action-btn { |
| display: inline-flex; align-items: center; gap: 5px; margin-top: 15px; |
| color: var(--neon-blue); text-decoration: none; border: 1px solid var(--neon-blue); |
| padding: 6px 15px; border-radius: 20px; font-size: 0.85rem; transition: 0.2s; width: fit-content; |
| } |
| .action-btn:hover { background: rgba(0, 212, 255, 0.1); box-shadow: 0 0 15px rgba(0,212,255,0.1); } |
| |
| |
| .skeleton { background: linear-gradient(90deg, var(--bg-card) 25%, #1f2a33 50%, var(--bg-card) 75%); background-size: 200% 100%; animation: load 1.5s infinite; border-radius: 6px; height: 20px; margin-bottom: 10px; width: 100%; } |
| @keyframes load { 0% { background-position: 200% 0; } 100% { background-position: -200% 0; } } |
| |
| |
| .history-dropdown { background: var(--bg-input); border: 1px solid var(--border-color); border-radius: 8px; padding: 10px; margin-top: 15px; display: none; max-height: 200px; overflow-y: auto; } |
| .history-dropdown.active { display: block; } |
| .history-item { padding: 8px; cursor: pointer; border-bottom: 1px solid #222; transition: 0.2s; } |
| .history-item:hover { background: #2a3540; color: var(--neon-green); } |
| |
| |
| .social-footer { margin-top: 40px; padding-top: 20px; border-top: 1px solid var(--border-color); text-align: center; } |
| .social-links { display: flex; justify-content: center; gap: 15px; flex-wrap: wrap; margin-top: 10px; } |
| .social-btn { display: inline-flex; align-items: center; gap: 8px; border: 1px solid var(--border-color); padding: 8px 20px; border-radius: 30px; text-decoration: none; color: var(--text-muted); transition: 0.2s; font-size: 0.9rem; } |
| .social-btn:hover { border-color: var(--neon-blue); color: white; transform: translateY(-2px); } |
| .creator-credit { margin-top: 20px; font-size: 0.9rem; color: #555; } |
| .creator-credit strong { color: var(--neon-blue); } |
| |
| |
| @media (max-width: 600px) { |
| .app-container { padding: 15px; } |
| .search-row button { width: 100%; justify-content: center; } |
| .search-wrapper input { padding: 12px; } |
| h1 { font-size: 1.4rem; } |
| } |
| </style> |
| </head> |
| <body> |
|
|
| <div class="app-container"> |
| |
| <div class="header"> |
| <h1>π€ AI-Hub <span style="color:var(--text-muted); font-weight:300;">Live Connector</span></h1> |
| <div style="display:flex; align-items:center; gap:8px; font-size:0.9rem; color:var(--text-muted);"> |
| <span class="status-dot"></span> API Connected |
| </div> |
| </div> |
|
|
| <div class="search-section"> |
| <div class="search-row"> |
| <div class="search-wrapper"> |
| <input type="text" id="searchInput" placeholder="Search models, datasets, spaces... (e.g., 'Llama', 'Diffusers')" onkeypress="if(event.key==='Enter') handleSearch()"> |
| </div> |
| <select id="searchType"> |
| <option value="models">Models</option> |
| <option value="datasets">Datasets</option> |
| <option value="spaces">Spaces</option> |
| </select> |
| <button onclick="handleSearch()" id="searchBtn">π Search Hub</button> |
| </div> |
|
|
| <div class="status-bar"> |
| <div id="statusMessage" class="status-msg success">Ready. Enter a query to explore the Hub.</div> |
| <div style="display:flex; gap:10px; align-items:center;"> |
| <span id="resultCount">0 Results</span> |
| <button onclick="toggleHistory()" style="background:transparent; border:1px solid var(--border-color); color:var(--text-muted); padding:4px 12px; border-radius:15px; cursor:pointer; font-size:0.8rem;">History π</button> |
| </div> |
| </div> |
| |
| <div class="history-dropdown" id="historyDropdown"> |
| <div style="color:var(--text-muted); font-size:0.8rem; margin-bottom:5px;">Recent Searches</div> |
| <div id="historyList"></div> |
| </div> |
| </div> |
|
|
| <div id="resultsContainer"> |
| <div style="text-align:center; color:var(--text-muted); padding:50px 0;"> |
| <div style="font-size:3rem; margin-bottom:10px;">π</div> |
| Awaiting your first search.<br> <span style="font-size:0.9rem;">Searching accesses the live Hugging Face API.</span> |
| </div> |
| </div> |
|
|
| <div class="social-footer"> |
| <div class="social-links"> |
| <a href="https://www.facebook.com/" target="_blank" class="social-btn">π Facebook</a> |
| <a href="https://github.com/pathum25" target="_blank" class="social-btn">π GitHub</a> |
| <a href="https://huggingface.co/Pq234" target="_blank" class="social-btn">π€ Hugging Face</a> |
| <a href="https://wa.me/94767605853" target="_blank" class="social-btn">π¬ WhatsApp</a> |
| </div> |
| <div class="creator-credit">Built by <strong>A.P.PATHUm LAKMAL</strong> Β© 2026</div> |
| </div> |
| </div> |
|
|
| <script> |
| |
| |
| |
| |
| |
| |
| |
| |
| const HF_TOKEN = ""; |
| const BASE_URL = "https://huggingface.co/api"; |
| |
| |
| const state = { |
| isLoading: false, |
| history: JSON.parse(localStorage.getItem('hf_search_history') || '[]'), |
| currentType: 'models', |
| currentQuery: '' |
| }; |
| |
| |
| const dom = { |
| input: document.getElementById('searchInput'), |
| type: document.getElementById('searchType'), |
| btn: document.getElementById('searchBtn'), |
| status: document.getElementById('statusMessage'), |
| count: document.getElementById('resultCount'), |
| container: document.getElementById('resultsContainer'), |
| historyDropdown: document.getElementById('historyDropdown'), |
| historyList: document.getElementById('historyList') |
| }; |
| |
| |
| async function handleSearch() { |
| const query = dom.input.value.trim(); |
| const type = dom.type.value; |
| |
| if (!query) { |
| setStatus("Please enter a search term.", "error"); |
| return; |
| } |
| |
| |
| state.isLoading = true; |
| dom.btn.disabled = true; |
| dom.btn.textContent = "β³ Scanning Hub..."; |
| dom.container.innerHTML = renderSkeletons(6); |
| setStatus(`Connecting to Hugging Face Cloud to search "${query}"...`, "success"); |
| |
| try { |
| |
| let apiUrl = `${BASE_URL}/${type}?search=${encodeURIComponent(query)}&limit=12&sort=downloads`; |
| |
| const headers = {}; |
| if (HF_TOKEN) headers['Authorization'] = `Bearer ${HF_TOKEN}`; |
| |
| const response = await fetch(apiUrl, { headers }); |
| |
| if (!response.ok) throw new Error(`API Error ${response.status}`); |
| |
| const data = await response.json(); |
| const items = Array.isArray(data) ? data : data.models || data.datasets || data.spaces || []; |
| |
| |
| saveToHistory(query, type); |
| |
| |
| if (items.length === 0) { |
| setStatus(`No results found for "${query}".`, "error"); |
| dom.container.innerHTML = `<div style="text-align:center; color:var(--text-muted); padding:40px 0;">No ${type} found matching your criteria.</div>`; |
| } else { |
| setStatus(`β
Found ${items.length} ${type} for "${query}".`, "success"); |
| dom.container.innerHTML = renderCards(items, type); |
| } |
| |
| dom.count.textContent = `${items.length} Results`; |
| |
| } catch (err) { |
| console.error(err); |
| setStatus(`Error: ${err.message}. Check console.`, "error"); |
| dom.container.innerHTML = `<div style="text-align:center; color:var(--neon-red); padding:40px 0;">Failed to connect to Hub. ${err.message}</div>`; |
| } finally { |
| state.isLoading = false; |
| dom.btn.disabled = false; |
| dom.btn.textContent = "π Search Hub"; |
| } |
| } |
| |
| |
| function renderCards(items, type) { |
| return `<div class="results-grid"> |
| ${items.map(item => { |
| // Normalize data |
| const id = item.modelId || item._id || item.id || "Unknown"; |
| const downloads = item.downloads ? formatNumber(item.downloads) : "New"; |
| const likes = item.likes ? `β€οΈ ${item.likes}` : ""; |
| const tags = (item.tags || []).slice(0, 3).join(', '); |
| |
| return ` |
| <div class="result-card"> |
| <span class="card-badge">${type.slice(0,-1).toUpperCase()}</span> |
| <div class="model-name">${id}</div> |
| <div class="model-detail"><span>Downloads</span> <span>${downloads}</span></div> |
| <div class="model-detail"><span>Likes</span> <span>${likes || 'None'}</span></div> |
| <div style="color:var(--text-muted); font-size:0.8rem; margin-top:5px; white-space:nowrap; overflow:hidden; text-overflow:ellipsis;">${tags}</div> |
| <a href="https://huggingface.co/${id}" target="_blank" class="action-btn">π Read Docs →</a> |
| </div> |
| `; |
| }).join('')} |
| </div>`; |
| } |
| |
| function renderSkeletons(count) { |
| let html = `<div class="results-grid">`; |
| for(let i=0; i<count; i++) { |
| html += ` |
| <div class="result-card"> |
| <div class="skeleton" style="height:25px; width:80%;"></div> |
| <div class="skeleton" style="height:15px; width:50%;"></div> |
| <div class="skeleton" style="height:15px; width:40%;"></div> |
| <div class="skeleton" style="height:15px; width:60%;"></div> |
| <div class="skeleton" style="height:35px; width:120px; margin-top:15px;"></div> |
| </div> |
| `; |
| } |
| html += `</div>`; |
| return html; |
| } |
| |
| |
| function saveToHistory(query, type) { |
| const entry = { query, type, time: new Date().toLocaleTimeString() }; |
| state.history = state.history.filter(h => h.query !== query); |
| state.history.unshift(entry); |
| if(state.history.length > 5) state.history.pop(); |
| localStorage.setItem('hf_search_history', JSON.stringify(state.history)); |
| renderHistory(); |
| } |
| |
| function renderHistory() { |
| dom.historyList.innerHTML = state.history.map(h => ` |
| <div class="history-item" onclick="loadHistory('${h.query}', '${h.type}')"> |
| <span style="color:var(--neon-green);">${h.query}</span> <span style="color:#555; font-size:0.8rem;">(${h.type})</span> |
| <span style="float:right; font-size:0.7rem; color:#444;">${h.time}</span> |
| </div> |
| `).join(''); |
| } |
| |
| function loadHistory(query, type) { |
| dom.input.value = query; |
| dom.type.value = type; |
| dom.historyDropdown.classList.remove('active'); |
| handleSearch(); |
| } |
| |
| function toggleHistory() { |
| dom.historyDropdown.classList.toggle('active'); |
| renderHistory(); |
| } |
| |
| |
| function setStatus(msg, type = 'success') { |
| dom.status.className = `status-msg ${type}`; |
| dom.status.textContent = msg; |
| } |
| |
| function formatNumber(num) { |
| if (num >= 1000000) return (num / 1000000).toFixed(1) + 'M'; |
| if (num >= 1000) return (num / 1000).toFixed(1) + 'k'; |
| return num.toString(); |
| } |
| |
| |
| |
| renderHistory(); |
| console.log("π€ A.P.PATHUm LAKMAL's AI-Hub Live Connector initialized."); |
| console.log("π‘ Tip: Add your HF Token in the JS to prevent API blocking."); |
| </script> |
| </body> |
| </html> |