AI / index.html
openfree's picture
Update index.html
bad2dea verified
<!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>
<!-- HuggingFace Models Section -->
<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>
<!-- HuggingFace Spaces Section -->
<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>
<!-- HuggingFace Datasets Section -->
<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();
}
}
// HuggingFace Models ๋กœ๋“œ
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);
}
}
// HuggingFace Spaces ๋กœ๋“œ
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);
}
}
// HuggingFace Datasets ๋กœ๋“œ
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);
}
}
}
// HuggingFace Models ํ‘œ์‹œ
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();
}
// HuggingFace Spaces ํ‘œ์‹œ
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();
}
// HuggingFace Datasets ํ‘œ์‹œ
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() {
// Models
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);
// Spaces
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);
// Datasets
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);
// Focus events
modelsSearchInput.addEventListener('focus', () => showSearchSuggestions('models'));
spacesSearchInput.addEventListener('focus', () => showSearchSuggestions('spaces'));
datasetsSearchInput.addEventListener('focus', () => showSearchSuggestions('datasets'));
// Hide suggestions on outside click
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>