|
<!DOCTYPE html> |
|
<html lang="ko"> |
|
<head> |
|
<meta charset="UTF-8"> |
|
<meta name="viewport" content="width=device-width, initial-scale=1.0"> |
|
<title>AI Models 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; |
|
} |
|
|
|
h1 { |
|
text-align: center; |
|
color: #2c3e50; |
|
margin-bottom: 40px; |
|
font-size: 3rem; |
|
text-shadow: 2px 2px 4px rgba(0,0,0,0.1); |
|
letter-spacing: -0.5px; |
|
} |
|
|
|
.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; |
|
} |
|
|
|
.filter-buttons { |
|
display: flex; |
|
gap: 15px; |
|
margin-bottom: 30px; |
|
justify-content: center; |
|
} |
|
|
|
.filter-btn { |
|
padding: 10px 25px; |
|
border: 2px solid #e0e0e0; |
|
background: rgba(255, 255, 255, 0.8); |
|
color: #666; |
|
border-radius: 25px; |
|
cursor: pointer; |
|
font-size: 1rem; |
|
font-weight: 500; |
|
transition: all 0.3s ease; |
|
backdrop-filter: blur(5px); |
|
} |
|
|
|
.filter-btn:hover { |
|
border-color: #667eea; |
|
transform: translateY(-2px); |
|
} |
|
|
|
.filter-btn.active { |
|
background: #667eea; |
|
color: white; |
|
border-color: #667eea; |
|
box-shadow: 0 4px 15px rgba(102, 126, 234, 0.3); |
|
} |
|
|
|
.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"> |
|
<h1>π AI Trending Ranking Dashboard</h1> |
|
|
|
<div class="category-tabs"> |
|
<button class="tab-button active" onclick="showCategory('hf-models')"> |
|
π€ HF Models |
|
</button> |
|
<button class="tab-button" onclick="showCategory('hf-spaces')"> |
|
π― HF Spaces |
|
</button> |
|
<button class="tab-button" onclick="showCategory('replicate')"> |
|
β‘ Replicate |
|
</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="replicate" class="content-section"> |
|
<div class="section-header"> |
|
<h2>β
Official Collection</h2> |
|
<p style="color: #7f8c8d; margin-top: 5px;">from replicate.com/collections/official</p> |
|
</div> |
|
<div class="search-container"> |
|
<div class="search-input-wrapper"> |
|
<input type="text" class="search-input" id="replicate-search" placeholder="Search replicate models..."> |
|
<button class="clear-search" onclick="clearSearch('replicate')">β</button> |
|
<span class="search-icon">π</span> |
|
</div> |
|
</div> |
|
<div class="search-stats" id="replicate-search-stats"></div> |
|
<div id="replicate-content" class="items-grid"> |
|
<div class="loading"> |
|
<div class="loading-spinner"></div> |
|
<p>Loading official models...</p> |
|
</div> |
|
</div> |
|
</div> |
|
</div> |
|
|
|
<button class="refresh-btn" onclick="refreshCurrent()">π</button> |
|
|
|
<script> |
|
let currentCategory = 'hf-models'; |
|
let allModelsData = []; |
|
let allSpacesData = []; |
|
let allReplicateData = []; |
|
let filteredModelsData = []; |
|
let filteredSpacesData = []; |
|
let filteredReplicateData = []; |
|
let searchHistory = []; |
|
|
|
|
|
const replicateUrls = [ |
|
"https://replicate.com/bytedance/seedance-1-pro", |
|
"https://replicate.com/bytedance/seedance-1-lite", |
|
"https://replicate.com/kwaivgi/kling-v2.1-master", |
|
"https://replicate.com/kwaivgi/kling-v2.1", |
|
"https://replicate.com/google/imagen-4-ultra", |
|
"https://replicate.com/google/imagen-4-fast", |
|
"https://replicate.com/openai/gpt-4.1", |
|
"https://replicate.com/luma/reframe-video", |
|
"https://replicate.com/anthropic/claude-4-sonnet", |
|
"https://replicate.com/google/veo-3", |
|
"https://replicate.com/resemble-ai/chatterbox-pro", |
|
"https://replicate.com/minimax/video-01-director", |
|
"https://replicate.com/minimax/video-01-live", |
|
"https://replicate.com/minimax/video-01", |
|
"https://replicate.com/google/imagen-4", |
|
"https://replicate.com/google/imagen-3-fast", |
|
"https://replicate.com/google/imagen-3", |
|
"https://replicate.com/google/lyria-2", |
|
"https://replicate.com/luma/ray-flash-2-720p", |
|
"https://replicate.com/luma/ray-2-720p", |
|
"https://replicate.com/luma/ray-flash-2-540p", |
|
"https://replicate.com/luma/ray-2-540p", |
|
"https://replicate.com/black-forest-labs/flux-dev-lora", |
|
"https://replicate.com/pixverse/pixverse-v4.5", |
|
"https://replicate.com/openai/o4-mini", |
|
"https://replicate.com/openai/o1", |
|
"https://replicate.com/openai/o1-mini", |
|
"https://replicate.com/openai/gpt-4o", |
|
"https://replicate.com/openai/gpt-4o-mini", |
|
"https://replicate.com/openai/gpt-4.1", |
|
"https://replicate.com/openai/gpt-4.1-nano", |
|
"https://replicate.com/kwaivgi/kling-lip-sync", |
|
"https://replicate.com/openai/gpt-4.1-mini", |
|
"https://replicate.com/kwaivgi/kling-v2.0", |
|
"https://replicate.com/pixverse/pixverse-v3.5", |
|
"https://replicate.com/openai/dall-e-2", |
|
"https://replicate.com/openai/dall-e-3", |
|
"https://replicate.com/fofr/color-matcher", |
|
"https://replicate.com/minimax/speech-02-turbo", |
|
"https://replicate.com/minimax/speech-02-hd", |
|
"https://replicate.com/minimax/voice-cloning", |
|
"https://replicate.com/ideogram-ai/ideogram-v3-balanced", |
|
"https://replicate.com/ideogram-ai/ideogram-v3-turbo", |
|
"https://replicate.com/ideogram-ai/ideogram-v3-quality", |
|
"https://replicate.com/easel/ai-avatars", |
|
"https://replicate.com/black-forest-labs/flux-1.1-pro-ultra-finetuned", |
|
"https://replicate.com/black-forest-labs/flux-pro-trainer", |
|
"https://replicate.com/openai/gpt-image-1", |
|
"https://replicate.com/topazlabs/image-upscale", |
|
"https://replicate.com/topazlabs/video-upscale", |
|
"https://replicate.com/ibm-granite/granite-3.3-8b-instruct", |
|
"https://replicate.com/meta/llama-4-maverick-instruct", |
|
"https://replicate.com/meta/llama-4-scout-instruct", |
|
"https://replicate.com/black-forest-labs/flux-schnell-lora", |
|
"https://replicate.com/black-forest-labs/flux-fill-dev", |
|
"https://replicate.com/black-forest-labs/flux-1.1-pro-ultra", |
|
"https://replicate.com/black-forest-labs/flux-1.1-pro", |
|
"https://replicate.com/black-forest-labs/flux-pro", |
|
"https://replicate.com/black-forest-labs/flux-fill-pro", |
|
"https://replicate.com/black-forest-labs/flux-canny-pro", |
|
"https://replicate.com/black-forest-labs/flux-depth-pro", |
|
"https://replicate.com/wavespeedai/wan-2.1-t2v-480p", |
|
"https://replicate.com/wavespeedai/wan-2.1-t2v-720p", |
|
"https://replicate.com/wavespeedai/wan-2.1-i2v-480p", |
|
"https://replicate.com/wavespeedai/wan-2.1-i2v-720p", |
|
"https://replicate.com/deepseek-ai/deepseek-v3", |
|
"https://replicate.com/recraft-ai/recraft-v3", |
|
"https://replicate.com/recraft-ai/recraft-v3-svg", |
|
"https://replicate.com/recraft-ai/recraft-20b-svg", |
|
"https://replicate.com/recraft-ai/recraft-20b", |
|
"https://replicate.com/black-forest-labs/flux-redux-schnell", |
|
"https://replicate.com/black-forest-labs/flux-redux-dev", |
|
"https://replicate.com/black-forest-labs/flux-schnell", |
|
"https://replicate.com/black-forest-labs/flux-depth-dev", |
|
"https://replicate.com/black-forest-labs/flux-canny-dev", |
|
"https://replicate.com/black-forest-labs/flux-dev", |
|
"https://replicate.com/easel/advanced-face-swap", |
|
"https://replicate.com/ibm-granite/granite-3.2-8b-instruct", |
|
"https://replicate.com/ideogram-ai/ideogram-v2a-turbo", |
|
"https://replicate.com/ideogram-ai/ideogram-v2a", |
|
"https://replicate.com/anthropic/claude-3.7-sonnet", |
|
"https://replicate.com/google/upscaler", |
|
"https://replicate.com/deepseek-ai/deepseek-r1", |
|
"https://replicate.com/recraft-ai/recraft-creative-upscale", |
|
"https://replicate.com/recraft-ai/recraft-crisp-upscale", |
|
"https://replicate.com/playht/play-dialog", |
|
"https://replicate.com/ibm-granite/granite-3.1-8b-instruct", |
|
"https://replicate.com/ibm-granite/granite-3.1-2b-instruct", |
|
"https://replicate.com/minimax/music-01", |
|
"https://replicate.com/haiper-ai/haiper-video-2", |
|
"https://replicate.com/stability-ai/stable-diffusion-3.5-medium", |
|
"https://replicate.com/stability-ai/stable-diffusion-3.5-large-turbo", |
|
"https://replicate.com/stability-ai/stable-diffusion-3.5-large", |
|
"https://replicate.com/ideogram-ai/ideogram-v2-turbo", |
|
"https://replicate.com/ideogram-ai/ideogram-v2", |
|
"https://replicate.com/ibm-granite/granite-8b-code-instruct-128k", |
|
"https://replicate.com/meta/meta-llama-3.1-405b-instruct", |
|
"https://replicate.com/stability-ai/stable-diffusion-3", |
|
"https://replicate.com/meta/meta-llama-3-70b-instruct", |
|
"https://replicate.com/meta/meta-llama-3-8b-instruct" |
|
]; |
|
|
|
|
|
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 === 'replicate') { |
|
loadReplicate(); |
|
} |
|
} |
|
|
|
|
|
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); |
|
} |
|
} |
|
|
|
|
|
function loadReplicate() { |
|
const container = document.getElementById('replicate-content'); |
|
|
|
|
|
allReplicateData = replicateUrls.map((url, index) => { |
|
const parts = url.split('/'); |
|
const author = parts[parts.length - 2]; |
|
const modelName = parts[parts.length - 1]; |
|
|
|
return { |
|
rank: index + 1, |
|
trendingRank: index + 1, |
|
title: modelName, |
|
author: author, |
|
url: url, |
|
icon: getModelIcon(modelName), |
|
badge: 'β
Official' |
|
}; |
|
}); |
|
|
|
|
|
filteredReplicateData = [...allReplicateData]; |
|
filterAndDisplayReplicate(); |
|
} |
|
|
|
|
|
async function tryProxyRequest(type, container) { |
|
try { |
|
const proxyUrl = 'https://api.allorigins.win/raw?url='; |
|
const targetUrl = type === 'models' |
|
? 'https://huggingface.co/api/models?sort=likes7d&direction=-1&limit=100' |
|
: 'https://huggingface.co/api/spaces?sort=likes7d&direction=-1&limit=100'; |
|
|
|
const response = await fetch(proxyUrl + encodeURIComponent(targetUrl)); |
|
const data = await response.json(); |
|
|
|
if (type === 'models') { |
|
displayHFModels(data, container); |
|
} else { |
|
displayHFSpaces(data, container); |
|
} |
|
} catch (error) { |
|
if (type === 'models') { |
|
displaySampleHFModels(container); |
|
} else { |
|
displaySampleHFSpaces(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: 'π€', |
|
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 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.runs !== undefined ? `<div class="stat">βΆοΈ ${formatNumber(data.runs)} runs</div>` : ''} |
|
</div> |
|
${data.tags && data.tags.length > 0 ? ` |
|
<div class="tags-container"> |
|
${data.tags.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 getModelIcon(name) { |
|
if (!name) return 'π€'; |
|
const lowerName = name.toLowerCase(); |
|
if (lowerName.includes('imagen') || lowerName.includes('dall-e') || lowerName.includes('stable-diffusion') || lowerName.includes('flux') || lowerName.includes('ideogram') || lowerName.includes('recraft')) return 'π¨'; |
|
if (lowerName.includes('video') || lowerName.includes('veo') || lowerName.includes('ray') || lowerName.includes('kling') || lowerName.includes('pixverse') || lowerName.includes('wan') || lowerName.includes('haiper')) return 'π¬'; |
|
if (lowerName.includes('audio') || lowerName.includes('music') || lowerName.includes('speech') || lowerName.includes('voice') || lowerName.includes('lyria') || lowerName.includes('play-dialog')) return 'π΅'; |
|
if (lowerName.includes('llama') || lowerName.includes('gpt') || lowerName.includes('claude') || lowerName.includes('granite') || lowerName.includes('mistral') || lowerName.includes('deepseek')) return 'π¬'; |
|
if (lowerName.includes('upscale') || lowerName.includes('upscaler')) return 'π'; |
|
if (lowerName.includes('swap') || lowerName.includes('avatar') || lowerName.includes('face')) return 'π€'; |
|
if (lowerName.includes('svg')) return 'π―'; |
|
if (lowerName.includes('color') || lowerName.includes('photon')) return 'π¨'; |
|
return 'π€'; |
|
} |
|
|
|
|
|
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'], createdAt: new Date(Date.now() - 10 * 24 * 60 * 60 * 1000).toISOString() }, |
|
{ 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', createdAt: new Date(Date.now() - 3 * 24 * 60 * 60 * 1000).toISOString() }, |
|
{ rank: 3, trendingRank: 3, title: 'Qwen3-235B-A22B', author: 'Qwen', likes: 5370, downloads: 225000, icon: 'π€', pipeline_tag: 'text-generation', tags: ['qwen', 'large'], badge: 'π New', createdAt: new Date(Date.now() - 5 * 24 * 60 * 60 * 1000).toISOString() }, |
|
{ rank: 4, trendingRank: 4, title: 'FLUX.1-dev', author: 'black-forest-labs', likes: 29100, downloads: 397000, icon: 'π€', pipeline_tag: 'text-to-image', tags: ['flux', 'diffusion'], createdAt: new Date(Date.now() - 30 * 24 * 60 * 60 * 1000).toISOString() }, |
|
{ rank: 5, trendingRank: 5, title: 'Phi-4', author: 'microsoft', likes: 8900, downloads: 156000, icon: 'π€', pipeline_tag: 'text-generation', tags: ['phi', 'small'], badge: 'π New', createdAt: new Date(Date.now() - 2 * 24 * 60 * 60 * 1000).toISOString() }, |
|
{ rank: 6, trendingRank: 6, title: 'stable-diffusion-3.5-large', author: 'stabilityai', likes: 7650, downloads: 234000, icon: 'π€', pipeline_tag: 'text-to-image', tags: ['sd3', 'large'], createdAt: new Date(Date.now() - 15 * 24 * 60 * 60 * 1000).toISOString() }, |
|
{ rank: 7, trendingRank: 7, title: 'whisper-large-v3-turbo', author: 'openai', likes: 6780, downloads: 189000, icon: 'π€', pipeline_tag: 'automatic-speech-recognition', tags: ['whisper', 'asr'], createdAt: new Date(Date.now() - 20 * 24 * 60 * 60 * 1000).toISOString() }, |
|
{ rank: 8, trendingRank: 8, title: 'MusicGen-Stereo-Melody', author: 'facebook', likes: 5430, downloads: 98000, icon: 'π€', pipeline_tag: 'text-to-audio', tags: ['music', 'generation'], createdAt: new Date(Date.now() - 25 * 24 * 60 * 60 * 1000).toISOString() }, |
|
{ rank: 9, trendingRank: 9, title: 'Florence-2-large', author: 'microsoft', likes: 4890, downloads: 87000, icon: 'π€', pipeline_tag: 'image-text-to-text', tags: ['vision', 'florence'], createdAt: new Date(Date.now() - 18 * 24 * 60 * 60 * 1000).toISOString() }, |
|
{ rank: 10, trendingRank: 10, title: 'QwQ-32B-Preview', author: 'Qwen', likes: 4560, downloads: 76000, icon: 'π€', pipeline_tag: 'text-generation', tags: ['reasoning', 'cot'], badge: 'π New', createdAt: new Date(Date.now() - 6 * 24 * 60 * 60 * 1000).toISOString() } |
|
]; |
|
|
|
sampleData.forEach(model => { |
|
model.url = `https://huggingface.co/${model.author}/${model.title}`; |
|
}); |
|
|
|
displayHFModels(sampleData, container); |
|
} |
|
|
|
function displaySampleHFSpaces(container) { |
|
const sampleData = [ |
|
{ rank: 1, trendingRank: 1, title: 'stable-diffusion-webui', author: 'stabilityai', likes: 75500, sdk: 'Gradio', icon: 'π¨', createdAt: new Date(Date.now() - 30 * 24 * 60 * 60 * 1000).toISOString() }, |
|
{ rank: 2, trendingRank: 2, title: 'chatgpt-clone', author: 'community', likes: 12300, sdk: 'Streamlit', icon: 'π¬', badge: 'π New', createdAt: new Date(Date.now() - 4 * 24 * 60 * 60 * 1000).toISOString() }, |
|
{ rank: 3, trendingRank: 3, title: 'InstantID', author: 'InstantX', likes: 8590, sdk: 'Gradio', icon: 'π€', createdAt: new Date(Date.now() - 12 * 24 * 60 * 60 * 1000).toISOString() }, |
|
{ rank: 4, trendingRank: 4, title: 'voice-clone-xtts', author: 'coqui', likes: 5640, sdk: 'Gradio', icon: 'π€', badge: 'π New', createdAt: new Date(Date.now() - 5 * 24 * 60 * 60 * 1000).toISOString() }, |
|
{ rank: 5, trendingRank: 5, title: 'code-llama-playground', author: 'meta', likes: 3210, sdk: 'Gradio', icon: 'π»', createdAt: new Date(Date.now() - 20 * 24 * 60 * 60 * 1000).toISOString() }, |
|
{ rank: 6, trendingRank: 6, title: 'image-to-3d-model', author: '3d-ai', likes: 2890, sdk: 'Gradio', icon: 'π²', createdAt: new Date(Date.now() - 15 * 24 * 60 * 60 * 1000).toISOString() }, |
|
{ rank: 7, trendingRank: 7, title: 'music-visualizer', author: 'audio-viz', likes: 2340, sdk: 'Streamlit', icon: 'π΅', badge: 'π New', createdAt: new Date(Date.now() - 3 * 24 * 60 * 60 * 1000).toISOString() }, |
|
{ rank: 8, trendingRank: 8, title: 'pdf-chatbot', author: 'doc-ai', likes: 1980, sdk: 'Gradio', icon: 'π', createdAt: new Date(Date.now() - 25 * 24 * 60 * 60 * 1000).toISOString() }, |
|
{ rank: 9, trendingRank: 9, title: 'anime-art-generator', author: 'waifu-ai', likes: 1670, sdk: 'Gradio', icon: 'π', createdAt: new Date(Date.now() - 10 * 24 * 60 * 60 * 1000).toISOString() }, |
|
{ rank: 10, trendingRank: 10, title: 'dataset-explorer', author: 'data-tools', likes: 1450, sdk: 'Streamlit', icon: 'π', createdAt: new Date(Date.now() - 8 * 24 * 60 * 60 * 1000).toISOString() } |
|
]; |
|
|
|
sampleData.forEach(space => { |
|
space.url = `https://huggingface.co/spaces/${space.author}/${space.title}`; |
|
}); |
|
|
|
displayHFSpaces(sampleData, container); |
|
} |
|
|
|
|
|
function refreshCurrent() { |
|
if (currentCategory === 'hf-models') { |
|
loadHFModels(); |
|
} else if (currentCategory === 'hf-spaces') { |
|
loadHFSpaces(); |
|
} else { |
|
loadReplicate(); |
|
} |
|
} |
|
|
|
|
|
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 replicateSearchInput = document.getElementById('replicate-search'); |
|
replicateSearchInput.addEventListener('input', debounce(() => { |
|
filterAndDisplayReplicate(); |
|
toggleClearButton('replicate', replicateSearchInput.value); |
|
}, 300)); |
|
|
|
|
|
modelsSearchInput.addEventListener('focus', () => showSearchSuggestions('models')); |
|
spacesSearchInput.addEventListener('focus', () => showSearchSuggestions('spaces')); |
|
|
|
|
|
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 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 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'); |
|
container.innerHTML = ''; |
|
|
|
if (filteredModelsData.length === 0) { |
|
container.innerHTML = ` |
|
<div class="no-results"> |
|
<div class="no-results-icon">π</div> |
|
<h3>No models found</h3> |
|
<p>Try adjusting your search or filters</p> |
|
</div> |
|
`; |
|
return; |
|
} |
|
|
|
filteredModelsData.forEach((model, index) => { |
|
const card = createModelCard({ |
|
...model, |
|
rank: index + 1 |
|
}); |
|
container.appendChild(card); |
|
}); |
|
} |
|
|
|
|
|
function displayFilteredSpaces() { |
|
const container = document.getElementById('hf-spaces-content'); |
|
container.innerHTML = ''; |
|
|
|
if (filteredSpacesData.length === 0) { |
|
container.innerHTML = ` |
|
<div class="no-results"> |
|
<div class="no-results-icon">π</div> |
|
<h3>No spaces found</h3> |
|
<p>Try adjusting your search or filters</p> |
|
</div> |
|
`; |
|
return; |
|
} |
|
|
|
filteredSpacesData.forEach((space, index) => { |
|
const card = createModelCard({ |
|
...space, |
|
rank: index + 1 |
|
}); |
|
container.appendChild(card); |
|
}); |
|
} |
|
|
|
|
|
function updateSearchSuggestions(type, searchTerm) { |
|
if (!searchTerm) { |
|
hideAllSuggestions(); |
|
return; |
|
} |
|
|
|
const data = type === 'models' ? allModelsData : allSpacesData; |
|
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 { |
|
filterAndDisplaySpaces(); |
|
} |
|
} |
|
|
|
|
|
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 filterAndDisplayReplicate() { |
|
const searchTerm = document.getElementById('replicate-search').value.toLowerCase(); |
|
|
|
|
|
filteredReplicateData = allReplicateData.filter(model => { |
|
return !searchTerm || |
|
model.title.toLowerCase().includes(searchTerm) || |
|
model.author.toLowerCase().includes(searchTerm); |
|
}); |
|
|
|
|
|
displayFilteredReplicate(); |
|
updateSearchStats('replicate', filteredReplicateData.length, allReplicateData.length); |
|
} |
|
|
|
|
|
function displayFilteredReplicate() { |
|
const container = document.getElementById('replicate-content'); |
|
container.innerHTML = ''; |
|
|
|
if (filteredReplicateData.length === 0) { |
|
container.innerHTML = ` |
|
<div class="no-results"> |
|
<div class="no-results-icon">π</div> |
|
<h3>No models found</h3> |
|
<p>Try adjusting your search</p> |
|
</div> |
|
`; |
|
return; |
|
} |
|
|
|
filteredReplicateData.forEach((model, index) => { |
|
const card = createModelCard({ |
|
...model, |
|
rank: index + 1 |
|
}); |
|
container.appendChild(card); |
|
}); |
|
} |
|
|
|
|
|
function clearSearch(type) { |
|
document.getElementById(`${type}-search`).value = ''; |
|
toggleClearButton(type, ''); |
|
if (type === 'models') { |
|
filterAndDisplayModels(); |
|
} else if (type === 'spaces') { |
|
filterAndDisplaySpaces(); |
|
} else if (type === 'replicate') { |
|
filterAndDisplayReplicate(); |
|
} |
|
} |
|
|
|
|
|
window.addEventListener('DOMContentLoaded', () => { |
|
setupSearchAndFilter(); |
|
loadHFModels(); |
|
}); |
|
</script> |
|
</body> |
|
</html> |