| <!DOCTYPE html> |
| <html lang="ko"> |
| <head> |
| <meta charset="UTF-8"> |
| <meta name="viewport" content="width=device-width, initial-scale=1.0"> |
| <title>HuggingFace Trending Dashboard</title> |
| <style> |
| * { |
| box-sizing: border-box; |
| margin: 0; |
| padding: 0; |
| } |
| |
| body { |
| font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; |
| background: linear-gradient(135deg, #f5f7fa 0%, #c3cfe2 100%); |
| min-height: 100vh; |
| padding: 20px; |
| } |
| |
| .container { |
| max-width: 1400px; |
| margin: 0 auto; |
| } |
| |
| .header { |
| text-align: center; |
| margin-bottom: 40px; |
| } |
| |
| h1 { |
| color: #2c3e50; |
| font-size: 3rem; |
| text-shadow: 2px 2px 4px rgba(0,0,0,0.1); |
| letter-spacing: -0.5px; |
| margin-bottom: 10px; |
| } |
| |
| .subtitle { |
| color: #7f8c8d; |
| font-size: 1.2rem; |
| } |
| |
| .category-tabs { |
| display: flex; |
| gap: 15px; |
| margin-bottom: 30px; |
| flex-wrap: wrap; |
| justify-content: center; |
| } |
| |
| .tab-button { |
| padding: 15px 35px; |
| border: none; |
| background: rgba(255, 255, 255, 0.9); |
| color: #555; |
| border-radius: 30px; |
| cursor: pointer; |
| font-size: 1.1rem; |
| font-weight: 500; |
| transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1); |
| box-shadow: 0 4px 15px rgba(0,0,0,0.1); |
| backdrop-filter: blur(10px); |
| } |
| |
| .tab-button:hover { |
| transform: translateY(-3px) scale(1.05); |
| box-shadow: 0 8px 25px rgba(0,0,0,0.15); |
| } |
| |
| .tab-button.active { |
| background: linear-gradient(135deg, #667eea, #764ba2); |
| color: white; |
| transform: scale(1.05); |
| } |
| |
| .content-section { |
| display: none; |
| animation: fadeIn 0.5s ease-out; |
| } |
| |
| .content-section.active { |
| display: block; |
| } |
| |
| @keyframes fadeIn { |
| from { opacity: 0; transform: translateY(20px); } |
| to { opacity: 1; transform: translateY(0); } |
| } |
| |
| .section-header { |
| text-align: center; |
| margin-bottom: 30px; |
| } |
| |
| .section-header h2 { |
| color: #667eea; |
| font-size: 2rem; |
| margin-bottom: 10px; |
| } |
| |
| .search-container { |
| display: flex; |
| gap: 15px; |
| margin: 20px auto 30px; |
| max-width: 800px; |
| flex-wrap: wrap; |
| justify-content: center; |
| } |
| |
| .search-input-wrapper { |
| position: relative; |
| flex: 1; |
| min-width: 300px; |
| } |
| |
| .search-input { |
| width: 100%; |
| padding: 12px 45px 12px 20px; |
| border: 2px solid #e0e0e0; |
| border-radius: 25px; |
| font-size: 1rem; |
| transition: all 0.3s ease; |
| background: rgba(255, 255, 255, 0.9); |
| backdrop-filter: blur(10px); |
| } |
| |
| .search-input:focus { |
| outline: none; |
| border-color: #667eea; |
| box-shadow: 0 0 0 3px rgba(102, 126, 234, 0.1); |
| } |
| |
| .search-icon { |
| position: absolute; |
| right: 20px; |
| top: 50%; |
| transform: translateY(-50%); |
| color: #999; |
| pointer-events: none; |
| } |
| |
| .filter-select { |
| padding: 12px 20px; |
| border: 2px solid #e0e0e0; |
| border-radius: 25px; |
| font-size: 1rem; |
| background: rgba(255, 255, 255, 0.9); |
| backdrop-filter: blur(10px); |
| cursor: pointer; |
| transition: all 0.3s ease; |
| min-width: 150px; |
| } |
| |
| .filter-select:focus { |
| outline: none; |
| border-color: #667eea; |
| box-shadow: 0 0 0 3px rgba(102, 126, 234, 0.1); |
| } |
| |
| .search-suggestions { |
| position: absolute; |
| top: 100%; |
| left: 0; |
| right: 0; |
| background: white; |
| border-radius: 15px; |
| box-shadow: 0 5px 20px rgba(0,0,0,0.1); |
| margin-top: 5px; |
| max-height: 300px; |
| overflow-y: auto; |
| display: none; |
| z-index: 1000; |
| } |
| |
| .search-suggestions.active { |
| display: block; |
| } |
| |
| .suggestion-item { |
| padding: 12px 20px; |
| cursor: pointer; |
| transition: background 0.2s; |
| border-bottom: 1px solid #f0f0f0; |
| } |
| |
| .suggestion-item:hover { |
| background: #f5f7fa; |
| } |
| |
| .suggestion-item:last-child { |
| border-bottom: none; |
| } |
| |
| .suggestion-highlight { |
| font-weight: bold; |
| color: #667eea; |
| } |
| |
| .search-stats { |
| text-align: center; |
| color: #666; |
| margin-bottom: 20px; |
| font-size: 0.95rem; |
| } |
| |
| .no-results { |
| text-align: center; |
| padding: 60px 20px; |
| color: #999; |
| } |
| |
| .no-results-icon { |
| font-size: 4rem; |
| margin-bottom: 20px; |
| opacity: 0.5; |
| } |
| |
| .clear-search { |
| position: absolute; |
| right: 45px; |
| top: 50%; |
| transform: translateY(-50%); |
| background: #e0e0e0; |
| border: none; |
| border-radius: 50%; |
| width: 20px; |
| height: 20px; |
| cursor: pointer; |
| display: none; |
| align-items: center; |
| justify-content: center; |
| font-size: 0.8rem; |
| color: #666; |
| transition: all 0.2s; |
| } |
| |
| .clear-search:hover { |
| background: #d0d0d0; |
| } |
| |
| .clear-search.active { |
| display: flex; |
| } |
| |
| .items-grid { |
| display: grid; |
| grid-template-columns: repeat(auto-fill, minmax(340px, 1fr)); |
| gap: 25px; |
| margin-top: 30px; |
| } |
| |
| .item-card { |
| background: rgba(255, 255, 255, 0.95); |
| border-radius: 20px; |
| padding: 25px; |
| padding-top: 35px; |
| box-shadow: 0 5px 20px rgba(0,0,0,0.08); |
| transition: all 0.4s cubic-bezier(0.4, 0, 0.2, 1); |
| cursor: pointer; |
| position: relative; |
| overflow: hidden; |
| backdrop-filter: blur(10px); |
| border: 1px solid rgba(255, 255, 255, 0.8); |
| } |
| |
| .item-card:hover { |
| transform: translateY(-10px) scale(1.02); |
| box-shadow: 0 15px 40px rgba(0,0,0,0.15); |
| } |
| |
| .item-card::before { |
| content: ''; |
| position: absolute; |
| top: 0; |
| left: 0; |
| width: 100%; |
| height: 5px; |
| background: linear-gradient(90deg, #667eea, #764ba2, #f093fb); |
| opacity: 0.9; |
| } |
| |
| .rank-badge { |
| position: absolute; |
| top: 15px; |
| right: 15px; |
| width: 40px; |
| height: 40px; |
| border-radius: 50%; |
| display: flex; |
| align-items: center; |
| justify-content: center; |
| font-weight: bold; |
| font-size: 1.1rem; |
| color: white; |
| box-shadow: 0 3px 10px rgba(0,0,0,0.2); |
| } |
| |
| .badge-label { |
| position: absolute; |
| top: 15px; |
| left: 15px; |
| background: rgba(0,0,0,0.8); |
| color: white; |
| padding: 5px 12px; |
| border-radius: 20px; |
| font-size: 0.8rem; |
| font-weight: 500; |
| } |
| |
| .item-header { |
| display: flex; |
| align-items: start; |
| gap: 15px; |
| margin-bottom: 15px; |
| } |
| |
| .item-icon { |
| width: 50px; |
| height: 50px; |
| background: linear-gradient(135deg, #f5f7fa, #c3cfe2); |
| border-radius: 15px; |
| display: flex; |
| align-items: center; |
| justify-content: center; |
| font-size: 1.8rem; |
| box-shadow: 0 3px 10px rgba(0,0,0,0.1); |
| } |
| |
| .item-info { |
| flex: 1; |
| } |
| |
| .item-title { |
| font-weight: 700; |
| color: #2c3e50; |
| margin-bottom: 5px; |
| font-size: 1.2rem; |
| overflow: hidden; |
| text-overflow: ellipsis; |
| white-space: nowrap; |
| } |
| |
| .item-author { |
| color: #7f8c8d; |
| font-size: 0.95rem; |
| font-weight: 500; |
| } |
| |
| .item-stats { |
| display: flex; |
| gap: 20px; |
| margin-top: 15px; |
| font-size: 0.9rem; |
| color: #666; |
| flex-wrap: wrap; |
| } |
| |
| .stat { |
| display: flex; |
| align-items: center; |
| gap: 6px; |
| background: rgba(102, 126, 234, 0.1); |
| padding: 5px 12px; |
| border-radius: 15px; |
| font-weight: 500; |
| } |
| |
| .tags-container { |
| margin-top: 12px; |
| display: flex; |
| flex-wrap: wrap; |
| gap: 6px; |
| } |
| |
| .tag { |
| background: linear-gradient(135deg, #e0e7ff, #f0f4ff); |
| padding: 4px 12px; |
| border-radius: 15px; |
| font-size: 0.8rem; |
| color: #667eea; |
| font-weight: 500; |
| } |
| |
| .loading { |
| text-align: center; |
| padding: 60px; |
| color: #666; |
| } |
| |
| .loading-spinner { |
| display: inline-block; |
| width: 50px; |
| height: 50px; |
| border: 4px solid rgba(102, 126, 234, 0.2); |
| border-top: 4px solid #667eea; |
| border-radius: 50%; |
| animation: spin 1s linear infinite; |
| margin-bottom: 20px; |
| } |
| |
| @keyframes spin { |
| 0% { transform: rotate(0deg); } |
| 100% { transform: rotate(360deg); } |
| } |
| |
| .refresh-btn { |
| position: fixed; |
| bottom: 40px; |
| right: 40px; |
| width: 70px; |
| height: 70px; |
| background: linear-gradient(135deg, #667eea, #764ba2); |
| color: white; |
| border: none; |
| border-radius: 50%; |
| cursor: pointer; |
| font-size: 1.8rem; |
| box-shadow: 0 5px 20px rgba(102, 126, 234, 0.4); |
| transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1); |
| z-index: 100; |
| display: flex; |
| align-items: center; |
| justify-content: center; |
| } |
| |
| .refresh-btn:hover { |
| transform: scale(1.1) rotate(180deg); |
| box-shadow: 0 8px 30px rgba(102, 126, 234, 0.5); |
| } |
| |
| @media (max-width: 768px) { |
| .items-grid { |
| grid-template-columns: 1fr; |
| } |
| |
| h1 { |
| font-size: 2rem; |
| } |
| |
| .tab-button { |
| padding: 12px 25px; |
| font-size: 1rem; |
| } |
| } |
| </style> |
| </head> |
| <body> |
| <div class="container"> |
| <div class="header"> |
| <h1>๐ค HuggingFace Trending</h1> |
| <p class="subtitle">Discover trending AI models, spaces, and datasets</p> |
| </div> |
|
|
| <div class="category-tabs"> |
| <button class="tab-button active" onclick="showCategory('hf-models')"> |
| ๐ค Models |
| </button> |
| <button class="tab-button" onclick="showCategory('hf-spaces')"> |
| ๐ฏ Spaces |
| </button> |
| <button class="tab-button" onclick="showCategory('hf-datasets')"> |
| ๐ Datasets |
| </button> |
| </div> |
|
|
| |
| <div id="hf-models" class="content-section active"> |
| <div class="section-header"> |
| <h2>๐ Trending Models</h2> |
| </div> |
| <div class="search-container"> |
| <div class="search-input-wrapper"> |
| <input type="text" class="search-input" id="models-search" placeholder="Search models, authors, or tags..."> |
| <button class="clear-search" onclick="clearSearch('models')">โ</button> |
| <span class="search-icon">๐</span> |
| <div class="search-suggestions" id="models-suggestions"></div> |
| </div> |
| <select class="filter-select" id="models-category-filter"> |
| <option value="">All Categories</option> |
| <option value="text-generation">Text Generation</option> |
| <option value="text-to-image">Image Generation</option> |
| <option value="automatic-speech-recognition">Speech Recognition</option> |
| <option value="text-to-audio">Audio Generation</option> |
| <option value="image-text-to-text">Vision-Language</option> |
| </select> |
| <select class="filter-select" id="models-sort-filter"> |
| <option value="trending">Trending (7d)</option> |
| <option value="likes">Most Liked</option> |
| <option value="downloads">Most Downloaded</option> |
| <option value="recent">Recently Added</option> |
| </select> |
| </div> |
| <div class="search-stats" id="models-search-stats"></div> |
| <div id="hf-models-content" class="items-grid"> |
| <div class="loading"> |
| <div class="loading-spinner"></div> |
| <p>Loading trending models...</p> |
| </div> |
| </div> |
| </div> |
|
|
| |
| <div id="hf-spaces" class="content-section"> |
| <div class="section-header"> |
| <h2>๐ฏ Trending Spaces</h2> |
| </div> |
| <div class="search-container"> |
| <div class="search-input-wrapper"> |
| <input type="text" class="search-input" id="spaces-search" placeholder="Search spaces or authors..."> |
| <button class="clear-search" onclick="clearSearch('spaces')">โ</button> |
| <span class="search-icon">๐</span> |
| <div class="search-suggestions" id="spaces-suggestions"></div> |
| </div> |
| <select class="filter-select" id="spaces-sdk-filter"> |
| <option value="">All SDKs</option> |
| <option value="gradio">Gradio</option> |
| <option value="streamlit">Streamlit</option> |
| <option value="static">Static</option> |
| <option value="docker">Docker</option> |
| </select> |
| <select class="filter-select" id="spaces-sort-filter"> |
| <option value="trending">Trending (7d)</option> |
| <option value="likes">Most Liked</option> |
| <option value="recent">Recently Added</option> |
| </select> |
| </div> |
| <div class="search-stats" id="spaces-search-stats"></div> |
| <div id="hf-spaces-content" class="items-grid"> |
| <div class="loading"> |
| <div class="loading-spinner"></div> |
| <p>Loading trending spaces...</p> |
| </div> |
| </div> |
| </div> |
|
|
| |
| <div id="hf-datasets" class="content-section"> |
| <div class="section-header"> |
| <h2>๐ Trending Datasets</h2> |
| </div> |
| <div class="search-container"> |
| <div class="search-input-wrapper"> |
| <input type="text" class="search-input" id="datasets-search" placeholder="Search datasets, authors, or tags..."> |
| <button class="clear-search" onclick="clearSearch('datasets')">โ</button> |
| <span class="search-icon">๐</span> |
| <div class="search-suggestions" id="datasets-suggestions"></div> |
| </div> |
| <select class="filter-select" id="datasets-task-filter"> |
| <option value="">All Tasks</option> |
| <option value="text-classification">Text Classification</option> |
| <option value="question-answering">Question Answering</option> |
| <option value="image-classification">Image Classification</option> |
| <option value="translation">Translation</option> |
| <option value="text-generation">Text Generation</option> |
| </select> |
| <select class="filter-select" id="datasets-sort-filter"> |
| <option value="trending">Trending (7d)</option> |
| <option value="likes">Most Liked</option> |
| <option value="downloads">Most Downloaded</option> |
| <option value="recent">Recently Added</option> |
| </select> |
| </div> |
| <div class="search-stats" id="datasets-search-stats"></div> |
| <div id="hf-datasets-content" class="items-grid"> |
| <div class="loading"> |
| <div class="loading-spinner"></div> |
| <p>Loading trending datasets...</p> |
| </div> |
| </div> |
| </div> |
| </div> |
|
|
| <button class="refresh-btn" onclick="refreshCurrent()">๐</button> |
|
|
| <script> |
| let currentCategory = 'hf-models'; |
| let allModelsData = []; |
| let allSpacesData = []; |
| let allDatasetsData = []; |
| let filteredModelsData = []; |
| let filteredSpacesData = []; |
| let filteredDatasetsData = []; |
| |
| |
| function showCategory(category) { |
| currentCategory = category; |
| |
| document.querySelectorAll('.tab-button').forEach(btn => { |
| btn.classList.remove('active'); |
| }); |
| event.target.classList.add('active'); |
| |
| document.querySelectorAll('.content-section').forEach(section => { |
| section.classList.remove('active'); |
| }); |
| document.getElementById(category).classList.add('active'); |
| |
| if (category === 'hf-models') { |
| loadHFModels(); |
| } else if (category === 'hf-spaces') { |
| loadHFSpaces(); |
| } else if (category === 'hf-datasets') { |
| loadHFDatasets(); |
| } |
| } |
| |
| |
| async function loadHFModels() { |
| const container = document.getElementById('hf-models-content'); |
| container.innerHTML = '<div class="loading"><div class="loading-spinner"></div><p>Loading trending models...</p></div>'; |
| |
| try { |
| const response = await fetch('https://huggingface.co/api/models?sort=likes7d&direction=-1&limit=100'); |
| const data = await response.json(); |
| displayHFModels(data, container); |
| } catch (error) { |
| tryProxyRequest('models', container); |
| } |
| } |
| |
| |
| async function loadHFSpaces() { |
| const container = document.getElementById('hf-spaces-content'); |
| container.innerHTML = '<div class="loading"><div class="loading-spinner"></div><p>Loading trending spaces...</p></div>'; |
| |
| try { |
| const response = await fetch('https://huggingface.co/api/spaces?sort=likes7d&direction=-1&limit=100'); |
| const data = await response.json(); |
| displayHFSpaces(data, container); |
| } catch (error) { |
| tryProxyRequest('spaces', container); |
| } |
| } |
| |
| |
| async function loadHFDatasets() { |
| const container = document.getElementById('hf-datasets-content'); |
| container.innerHTML = '<div class="loading"><div class="loading-spinner"></div><p>Loading trending datasets...</p></div>'; |
| |
| try { |
| const response = await fetch('https://huggingface.co/api/datasets?sort=likes7d&direction=-1&limit=100'); |
| const data = await response.json(); |
| displayHFDatasets(data, container); |
| } catch (error) { |
| tryProxyRequest('datasets', container); |
| } |
| } |
| |
| |
| async function tryProxyRequest(type, container) { |
| try { |
| const proxyUrl = 'https://api.allorigins.win/raw?url='; |
| const typeMap = { |
| 'models': 'https://huggingface.co/api/models?sort=likes7d&direction=-1&limit=100', |
| 'spaces': 'https://huggingface.co/api/spaces?sort=likes7d&direction=-1&limit=100', |
| 'datasets': 'https://huggingface.co/api/datasets?sort=likes7d&direction=-1&limit=100' |
| }; |
| |
| const response = await fetch(proxyUrl + encodeURIComponent(typeMap[type])); |
| const data = await response.json(); |
| |
| if (type === 'models') { |
| displayHFModels(data, container); |
| } else if (type === 'spaces') { |
| displayHFSpaces(data, container); |
| } else if (type === 'datasets') { |
| displayHFDatasets(data, container); |
| } |
| } catch (error) { |
| if (type === 'models') { |
| displaySampleHFModels(container); |
| } else if (type === 'spaces') { |
| displaySampleHFSpaces(container); |
| } else if (type === 'datasets') { |
| displaySampleHFDatasets(container); |
| } |
| } |
| } |
| |
| |
| function displayHFModels(models, container) { |
| allModelsData = models.map((model, index) => { |
| let isNew = false; |
| if (model.createdAt) { |
| const createdDate = new Date(model.createdAt); |
| const oneWeekAgo = new Date(); |
| oneWeekAgo.setDate(oneWeekAgo.getDate() - 7); |
| isNew = createdDate > oneWeekAgo; |
| } |
| |
| return { |
| rank: index + 1, |
| trendingRank: index + 1, |
| title: model.id?.split('/')[1] || model.modelId || 'Unknown', |
| author: model.id?.split('/')[0] || model.author || 'Unknown', |
| likes: model.likes || 0, |
| downloads: model.downloads || 0, |
| tags: model.tags || [], |
| pipeline_tag: model.pipeline_tag || '', |
| url: `https://huggingface.co/${model.id || model.modelId}`, |
| icon: getModelIcon(model.pipeline_tag), |
| badge: isNew ? '๐ New' : null, |
| createdAt: model.createdAt |
| }; |
| }); |
| |
| filteredModelsData = [...allModelsData]; |
| filterAndDisplayModels(); |
| } |
| |
| |
| function displayHFSpaces(spaces, container) { |
| allSpacesData = spaces.map((space, index) => { |
| let isNew = false; |
| if (space.createdAt) { |
| const createdDate = new Date(space.createdAt); |
| const oneWeekAgo = new Date(); |
| oneWeekAgo.setDate(oneWeekAgo.getDate() - 7); |
| isNew = createdDate > oneWeekAgo; |
| } |
| |
| return { |
| rank: index + 1, |
| trendingRank: index + 1, |
| title: space.id?.split('/')[1] || 'Unknown', |
| author: space.id?.split('/')[0] || 'Unknown', |
| likes: space.likes || 0, |
| sdk: space.sdk || 'Unknown', |
| emoji: space.emoji || '๐ฏ', |
| url: `https://huggingface.co/spaces/${space.id}`, |
| icon: space.emoji || '๐ฏ', |
| badge: isNew ? '๐ New' : null, |
| createdAt: space.createdAt |
| }; |
| }); |
| |
| filteredSpacesData = [...allSpacesData]; |
| filterAndDisplaySpaces(); |
| } |
| |
| |
| function displayHFDatasets(datasets, container) { |
| allDatasetsData = datasets.map((dataset, index) => { |
| let isNew = false; |
| if (dataset.createdAt) { |
| const createdDate = new Date(dataset.createdAt); |
| const oneWeekAgo = new Date(); |
| oneWeekAgo.setDate(oneWeekAgo.getDate() - 7); |
| isNew = createdDate > oneWeekAgo; |
| } |
| |
| return { |
| rank: index + 1, |
| trendingRank: index + 1, |
| title: dataset.id?.split('/')[1] || 'Unknown', |
| author: dataset.id?.split('/')[0] || 'Unknown', |
| likes: dataset.likes || 0, |
| downloads: dataset.downloads || 0, |
| tags: dataset.tags || [], |
| task_categories: dataset.task_categories || [], |
| url: `https://huggingface.co/datasets/${dataset.id}`, |
| icon: getDatasetIcon(dataset.task_categories), |
| badge: isNew ? '๐ New' : null, |
| createdAt: dataset.createdAt, |
| size: dataset.size || null |
| }; |
| }); |
| |
| filteredDatasetsData = [...allDatasetsData]; |
| filterAndDisplayDatasets(); |
| } |
| |
| |
| function getModelIcon(pipeline_tag) { |
| const iconMap = { |
| 'text-generation': '๐ฌ', |
| 'text-to-image': '๐จ', |
| 'automatic-speech-recognition': '๐ค', |
| 'text-to-audio': '๐ต', |
| 'image-text-to-text': '๐๏ธ', |
| 'text-classification': '๐ท๏ธ', |
| 'image-classification': '๐ผ๏ธ', |
| 'translation': '๐', |
| 'question-answering': 'โ', |
| 'summarization': '๐' |
| }; |
| return iconMap[pipeline_tag] || '๐ค'; |
| } |
| |
| |
| function getDatasetIcon(task_categories) { |
| if (!task_categories || task_categories.length === 0) return '๐'; |
| const firstTask = task_categories[0]; |
| const iconMap = { |
| 'text-classification': '๐ท๏ธ', |
| 'image-classification': '๐ผ๏ธ', |
| 'question-answering': 'โ', |
| 'translation': '๐', |
| 'text-generation': '๐ฌ', |
| 'summarization': '๐', |
| 'conversational': '๐ญ', |
| 'text-to-image': '๐จ', |
| 'object-detection': '๐ฏ' |
| }; |
| return iconMap[firstTask] || '๐'; |
| } |
| |
| |
| function createModelCard(data) { |
| const card = document.createElement('div'); |
| card.className = 'item-card'; |
| card.onclick = () => window.open(data.url, '_blank'); |
| |
| let rankColor = '#999'; |
| const displayRank = data.trendingRank || data.rank; |
| if (displayRank === 1) rankColor = '#FFD700'; |
| else if (displayRank === 2) rankColor = '#C0C0C0'; |
| else if (displayRank === 3) rankColor = '#CD7F32'; |
| else if (displayRank <= 10) rankColor = '#667eea'; |
| |
| card.innerHTML = ` |
| <div class="rank-badge" style="background: ${rankColor};"> |
| ${data.rank} |
| </div> |
| ${data.badge ? `<div class="badge-label">${data.badge}</div>` : ''} |
| <div class="item-header"> |
| <div class="item-icon">${data.icon}</div> |
| <div class="item-info"> |
| <div class="item-title">${data.title}</div> |
| <div class="item-author">@${data.author}</div> |
| </div> |
| </div> |
| <div class="item-stats"> |
| ${data.likes !== undefined ? `<div class="stat">โค๏ธ ${formatNumber(data.likes)}</div>` : ''} |
| ${data.downloads !== undefined ? `<div class="stat">โฌ๏ธ ${formatNumber(data.downloads)}</div>` : ''} |
| ${data.sdk ? `<div class="stat">๐ ๏ธ ${data.sdk}</div>` : ''} |
| ${data.pipeline_tag ? `<div class="stat">๐ท๏ธ ${data.pipeline_tag}</div>` : ''} |
| ${data.size ? `<div class="stat">๐พ ${data.size}</div>` : ''} |
| </div> |
| ${(data.tags && data.tags.length > 0) || (data.task_categories && data.task_categories.length > 0) ? ` |
| <div class="tags-container"> |
| ${(data.tags || data.task_categories || []).slice(0, 3).map(tag => `<span class="tag">${tag}</span>`).join('')} |
| </div> |
| ` : ''} |
| `; |
| |
| return card; |
| } |
| |
| |
| function formatNumber(num) { |
| if (num >= 1000000) return (num / 1000000).toFixed(1) + 'M'; |
| if (num >= 1000) return (num / 1000).toFixed(1) + 'K'; |
| return num.toString(); |
| } |
| |
| |
| function filterAndDisplayModels() { |
| const searchTerm = document.getElementById('models-search').value.toLowerCase(); |
| const categoryFilter = document.getElementById('models-category-filter').value; |
| const sortFilter = document.getElementById('models-sort-filter').value || 'trending'; |
| |
| filteredModelsData = allModelsData.filter(model => { |
| const matchesSearch = !searchTerm || |
| model.title.toLowerCase().includes(searchTerm) || |
| model.author.toLowerCase().includes(searchTerm) || |
| (model.tags && model.tags.some(tag => tag.toLowerCase().includes(searchTerm))); |
| |
| const matchesCategory = !categoryFilter || model.pipeline_tag === categoryFilter; |
| |
| return matchesSearch && matchesCategory; |
| }); |
| |
| sortData(filteredModelsData, sortFilter); |
| displayFilteredModels(); |
| updateSearchStats('models', filteredModelsData.length, allModelsData.length); |
| } |
| |
| function filterAndDisplaySpaces() { |
| const searchTerm = document.getElementById('spaces-search').value.toLowerCase(); |
| const sdkFilter = document.getElementById('spaces-sdk-filter').value; |
| const sortFilter = document.getElementById('spaces-sort-filter').value || 'trending'; |
| |
| filteredSpacesData = allSpacesData.filter(space => { |
| const matchesSearch = !searchTerm || |
| space.title.toLowerCase().includes(searchTerm) || |
| space.author.toLowerCase().includes(searchTerm); |
| |
| const matchesSdk = !sdkFilter || |
| (space.sdk && space.sdk.toLowerCase() === sdkFilter.toLowerCase()); |
| |
| return matchesSearch && matchesSdk; |
| }); |
| |
| sortData(filteredSpacesData, sortFilter); |
| displayFilteredSpaces(); |
| updateSearchStats('spaces', filteredSpacesData.length, allSpacesData.length); |
| } |
| |
| function filterAndDisplayDatasets() { |
| const searchTerm = document.getElementById('datasets-search').value.toLowerCase(); |
| const taskFilter = document.getElementById('datasets-task-filter').value; |
| const sortFilter = document.getElementById('datasets-sort-filter').value || 'trending'; |
| |
| filteredDatasetsData = allDatasetsData.filter(dataset => { |
| const matchesSearch = !searchTerm || |
| dataset.title.toLowerCase().includes(searchTerm) || |
| dataset.author.toLowerCase().includes(searchTerm) || |
| (dataset.tags && dataset.tags.some(tag => tag.toLowerCase().includes(searchTerm))); |
| |
| const matchesTask = !taskFilter || |
| (dataset.task_categories && dataset.task_categories.includes(taskFilter)); |
| |
| return matchesSearch && matchesTask; |
| }); |
| |
| sortData(filteredDatasetsData, sortFilter); |
| displayFilteredDatasets(); |
| updateSearchStats('datasets', filteredDatasetsData.length, allDatasetsData.length); |
| } |
| |
| |
| function sortData(data, sortType) { |
| switch(sortType) { |
| case 'trending': |
| data.sort((a, b) => (a.trendingRank || 999) - (b.trendingRank || 999)); |
| break; |
| case 'likes': |
| data.sort((a, b) => (b.likes || 0) - (a.likes || 0)); |
| break; |
| case 'downloads': |
| data.sort((a, b) => (b.downloads || 0) - (a.downloads || 0)); |
| break; |
| case 'recent': |
| data.sort((a, b) => { |
| const dateA = new Date(a.createdAt || 0); |
| const dateB = new Date(b.createdAt || 0); |
| return dateB - dateA; |
| }); |
| break; |
| } |
| } |
| |
| |
| function displayFilteredModels() { |
| const container = document.getElementById('hf-models-content'); |
| displayFiltered(container, filteredModelsData, 'models'); |
| } |
| |
| function displayFilteredSpaces() { |
| const container = document.getElementById('hf-spaces-content'); |
| displayFiltered(container, filteredSpacesData, 'spaces'); |
| } |
| |
| function displayFilteredDatasets() { |
| const container = document.getElementById('hf-datasets-content'); |
| displayFiltered(container, filteredDatasetsData, 'datasets'); |
| } |
| |
| function displayFiltered(container, data, type) { |
| container.innerHTML = ''; |
| |
| if (data.length === 0) { |
| container.innerHTML = ` |
| <div class="no-results"> |
| <div class="no-results-icon">๐</div> |
| <h3>No ${type} found</h3> |
| <p>Try adjusting your search or filters</p> |
| </div> |
| `; |
| return; |
| } |
| |
| data.forEach((item, index) => { |
| const card = createModelCard({ |
| ...item, |
| rank: index + 1 |
| }); |
| container.appendChild(card); |
| }); |
| } |
| |
| |
| function setupSearchAndFilter() { |
| |
| const modelsSearchInput = document.getElementById('models-search'); |
| const modelsCategoryFilter = document.getElementById('models-category-filter'); |
| const modelsSortFilter = document.getElementById('models-sort-filter'); |
| |
| modelsSearchInput.addEventListener('input', debounce(() => { |
| filterAndDisplayModels(); |
| updateSearchSuggestions('models', modelsSearchInput.value); |
| toggleClearButton('models', modelsSearchInput.value); |
| }, 300)); |
| |
| modelsCategoryFilter.addEventListener('change', filterAndDisplayModels); |
| modelsSortFilter.addEventListener('change', filterAndDisplayModels); |
| |
| |
| const spacesSearchInput = document.getElementById('spaces-search'); |
| const spacesSdkFilter = document.getElementById('spaces-sdk-filter'); |
| const spacesSortFilter = document.getElementById('spaces-sort-filter'); |
| |
| spacesSearchInput.addEventListener('input', debounce(() => { |
| filterAndDisplaySpaces(); |
| updateSearchSuggestions('spaces', spacesSearchInput.value); |
| toggleClearButton('spaces', spacesSearchInput.value); |
| }, 300)); |
| |
| spacesSdkFilter.addEventListener('change', filterAndDisplaySpaces); |
| spacesSortFilter.addEventListener('change', filterAndDisplaySpaces); |
| |
| |
| const datasetsSearchInput = document.getElementById('datasets-search'); |
| const datasetsTaskFilter = document.getElementById('datasets-task-filter'); |
| const datasetsSortFilter = document.getElementById('datasets-sort-filter'); |
| |
| datasetsSearchInput.addEventListener('input', debounce(() => { |
| filterAndDisplayDatasets(); |
| updateSearchSuggestions('datasets', datasetsSearchInput.value); |
| toggleClearButton('datasets', datasetsSearchInput.value); |
| }, 300)); |
| |
| datasetsTaskFilter.addEventListener('change', filterAndDisplayDatasets); |
| datasetsSortFilter.addEventListener('change', filterAndDisplayDatasets); |
| |
| |
| modelsSearchInput.addEventListener('focus', () => showSearchSuggestions('models')); |
| spacesSearchInput.addEventListener('focus', () => showSearchSuggestions('spaces')); |
| datasetsSearchInput.addEventListener('focus', () => showSearchSuggestions('datasets')); |
| |
| |
| document.addEventListener('click', (e) => { |
| if (!e.target.closest('.search-input-wrapper')) { |
| hideAllSuggestions(); |
| } |
| }); |
| } |
| |
| function debounce(func, wait) { |
| let timeout; |
| return function executedFunction(...args) { |
| const later = () => { |
| clearTimeout(timeout); |
| func(...args); |
| }; |
| clearTimeout(timeout); |
| timeout = setTimeout(later, wait); |
| }; |
| } |
| |
| function updateSearchSuggestions(type, searchTerm) { |
| if (!searchTerm) { |
| hideAllSuggestions(); |
| return; |
| } |
| |
| const dataMap = { |
| 'models': allModelsData, |
| 'spaces': allSpacesData, |
| 'datasets': allDatasetsData |
| }; |
| |
| const data = dataMap[type]; |
| const suggestions = data |
| .filter(item => |
| item.title.toLowerCase().includes(searchTerm.toLowerCase()) || |
| item.author.toLowerCase().includes(searchTerm.toLowerCase()) |
| ) |
| .slice(0, 5); |
| |
| const suggestionsContainer = document.getElementById(`${type}-suggestions`); |
| suggestionsContainer.innerHTML = suggestions.map(item => ` |
| <div class="suggestion-item" onclick="selectSuggestion('${type}', '${item.title}')"> |
| <strong>${highlightMatch(item.title, searchTerm)}</strong> |
| <span style="color: #999; font-size: 0.9rem;"> by ${item.author}</span> |
| </div> |
| `).join(''); |
| |
| suggestionsContainer.classList.add('active'); |
| } |
| |
| function highlightMatch(text, searchTerm) { |
| const regex = new RegExp(`(${searchTerm})`, 'gi'); |
| return text.replace(regex, '<span class="suggestion-highlight">$1</span>'); |
| } |
| |
| function selectSuggestion(type, value) { |
| document.getElementById(`${type}-search`).value = value; |
| hideAllSuggestions(); |
| |
| if (type === 'models') filterAndDisplayModels(); |
| else if (type === 'spaces') filterAndDisplaySpaces(); |
| else if (type === 'datasets') filterAndDisplayDatasets(); |
| } |
| |
| function showSearchSuggestions(type) { |
| const searchTerm = document.getElementById(`${type}-search`).value; |
| if (searchTerm) { |
| updateSearchSuggestions(type, searchTerm); |
| } |
| } |
| |
| function hideAllSuggestions() { |
| document.querySelectorAll('.search-suggestions').forEach(el => { |
| el.classList.remove('active'); |
| }); |
| } |
| |
| function updateSearchStats(type, shown, total) { |
| const statsEl = document.getElementById(`${type}-search-stats`); |
| if (shown < total) { |
| statsEl.textContent = `Showing ${shown} of ${total} ${type}`; |
| } else { |
| statsEl.textContent = ''; |
| } |
| } |
| |
| function toggleClearButton(type, value) { |
| const clearBtn = document.querySelector(`#${type}-search + .clear-search`); |
| if (value) { |
| clearBtn.classList.add('active'); |
| } else { |
| clearBtn.classList.remove('active'); |
| } |
| } |
| |
| function clearSearch(type) { |
| document.getElementById(`${type}-search`).value = ''; |
| toggleClearButton(type, ''); |
| |
| if (type === 'models') filterAndDisplayModels(); |
| else if (type === 'spaces') filterAndDisplaySpaces(); |
| else if (type === 'datasets') filterAndDisplayDatasets(); |
| } |
| |
| |
| function displaySampleHFModels(container) { |
| const sampleData = [ |
| { rank: 1, trendingRank: 1, title: 'Llama-3.3-70B-Instruct', author: 'meta-llama', likes: 177000, downloads: 1150000, icon: '๐ฌ', pipeline_tag: 'text-generation', tags: ['llama', '70b', 'instruct'] }, |
| { rank: 2, trendingRank: 2, title: 'DeepSeek-R1-Distill-Qwen-32B', author: 'deepseek-ai', likes: 10400, downloads: 567000, icon: '๐ฌ', pipeline_tag: 'text-generation', tags: ['deepseek', 'reasoning'], badge: '๐ New' }, |
| { rank: 3, trendingRank: 3, title: 'FLUX.1-dev', author: 'black-forest-labs', likes: 29100, downloads: 397000, icon: '๐จ', pipeline_tag: 'text-to-image', tags: ['flux', 'diffusion'] }, |
| { rank: 4, trendingRank: 4, title: 'Phi-4', author: 'microsoft', likes: 8900, downloads: 156000, icon: '๐ฌ', pipeline_tag: 'text-generation', tags: ['phi', 'small'], badge: '๐ New' }, |
| { rank: 5, trendingRank: 5, title: 'whisper-large-v3-turbo', author: 'openai', likes: 6780, downloads: 189000, icon: '๐ค', pipeline_tag: 'automatic-speech-recognition', tags: ['whisper', 'asr'] } |
| ]; |
| |
| sampleData.forEach(model => { |
| model.url = `https://huggingface.co/${model.author}/${model.title}`; |
| model.createdAt = new Date(Date.now() - Math.random() * 30 * 24 * 60 * 60 * 1000).toISOString(); |
| }); |
| |
| displayHFModels(sampleData, container); |
| } |
| |
| function displaySampleHFSpaces(container) { |
| const sampleData = [ |
| { rank: 1, trendingRank: 1, title: 'stable-diffusion-webui', author: 'stabilityai', likes: 75500, sdk: 'Gradio', icon: '๐จ' }, |
| { rank: 2, trendingRank: 2, title: 'chatgpt-clone', author: 'community', likes: 12300, sdk: 'Streamlit', icon: '๐ฌ', badge: '๐ New' }, |
| { rank: 3, trendingRank: 3, title: 'InstantID', author: 'InstantX', likes: 8590, sdk: 'Gradio', icon: '๐ค' }, |
| { rank: 4, trendingRank: 4, title: 'voice-clone-xtts', author: 'coqui', likes: 5640, sdk: 'Gradio', icon: '๐ค' }, |
| { rank: 5, trendingRank: 5, title: 'code-llama-playground', author: 'meta', likes: 3210, sdk: 'Gradio', icon: '๐ป' } |
| ]; |
| |
| sampleData.forEach(space => { |
| space.url = `https://huggingface.co/spaces/${space.author}/${space.title}`; |
| space.createdAt = new Date(Date.now() - Math.random() * 30 * 24 * 60 * 60 * 1000).toISOString(); |
| }); |
| |
| displayHFSpaces(sampleData, container); |
| } |
| |
| function displaySampleHFDatasets(container) { |
| const sampleData = [ |
| { rank: 1, trendingRank: 1, title: 'OpenHermes-2.5', author: 'teknium', likes: 2340, downloads: 450000, icon: '๐ฌ', task_categories: ['text-generation'], tags: ['conversational'] }, |
| { rank: 2, trendingRank: 2, title: 'SlimOrca', author: 'Open-Orca', likes: 1890, downloads: 320000, icon: '๐ฌ', task_categories: ['text-generation'], badge: '๐ New' }, |
| { rank: 3, trendingRank: 3, title: 'ImageNet-1k', author: 'imagenet', likes: 1560, downloads: 890000, icon: '๐ผ๏ธ', task_categories: ['image-classification'], size: '150GB' }, |
| { rank: 4, trendingRank: 4, title: 'Common-Voice-17', author: 'mozilla', likes: 1230, downloads: 234000, icon: '๐ค', task_categories: ['automatic-speech-recognition'] }, |
| { rank: 5, trendingRank: 5, title: 'COCO-2017', author: 'microsoft', likes: 980, downloads: 456000, icon: '๐ฏ', task_categories: ['object-detection'], size: '25GB' } |
| ]; |
| |
| sampleData.forEach(dataset => { |
| dataset.url = `https://huggingface.co/datasets/${dataset.author}/${dataset.title}`; |
| dataset.createdAt = new Date(Date.now() - Math.random() * 30 * 24 * 60 * 60 * 1000).toISOString(); |
| }); |
| |
| displayHFDatasets(sampleData, container); |
| } |
| |
| |
| function refreshCurrent() { |
| if (currentCategory === 'hf-models') { |
| loadHFModels(); |
| } else if (currentCategory === 'hf-spaces') { |
| loadHFSpaces(); |
| } else if (currentCategory === 'hf-datasets') { |
| loadHFDatasets(); |
| } |
| } |
| |
| |
| window.addEventListener('DOMContentLoaded', () => { |
| setupSearchAndFilter(); |
| loadHFModels(); |
| }); |
| </script> |
| </body> |
| </html> |