|
<!DOCTYPE html> |
|
<html lang="ko"> |
|
<head> |
|
<meta charset="UTF-8"> |
|
<meta name="viewport" content="width=device-width, initial-scale=1.0"> |
|
<title>HuggingFace & Replicate ์ค์๊ฐ ํธ๋ ๋ฉ/์ ๊ท ๋ฆฌ์คํธ</title> |
|
<style> |
|
* { |
|
box-sizing: border-box; |
|
margin: 0; |
|
padding: 0; |
|
} |
|
|
|
body { |
|
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; |
|
background: #f5f5f5; |
|
min-height: 100vh; |
|
padding: 20px; |
|
} |
|
|
|
.container { |
|
max-width: 1400px; |
|
margin: 0 auto; |
|
} |
|
|
|
h1 { |
|
text-align: center; |
|
color: #333; |
|
margin-bottom: 30px; |
|
font-size: 2.5rem; |
|
} |
|
|
|
.category-tabs { |
|
display: flex; |
|
gap: 10px; |
|
margin-bottom: 20px; |
|
flex-wrap: wrap; |
|
justify-content: center; |
|
} |
|
|
|
.tab-button { |
|
padding: 12px 24px; |
|
border: none; |
|
background: #fff; |
|
color: #666; |
|
border-radius: 8px; |
|
cursor: pointer; |
|
font-size: 1rem; |
|
transition: all 0.3s ease; |
|
box-shadow: 0 2px 5px rgba(0,0,0,0.1); |
|
} |
|
|
|
.tab-button:hover { |
|
transform: translateY(-2px); |
|
box-shadow: 0 4px 10px rgba(0,0,0,0.15); |
|
} |
|
|
|
.tab-button.active { |
|
background: linear-gradient(135deg, #667eea, #764ba2); |
|
color: white; |
|
} |
|
|
|
.content-section { |
|
display: none; |
|
} |
|
|
|
.content-section.active { |
|
display: block; |
|
} |
|
|
|
.filter-buttons { |
|
display: flex; |
|
gap: 10px; |
|
margin-bottom: 20px; |
|
justify-content: center; |
|
} |
|
|
|
.filter-btn { |
|
padding: 8px 16px; |
|
border: 1px solid #ddd; |
|
background: white; |
|
color: #666; |
|
border-radius: 20px; |
|
cursor: pointer; |
|
font-size: 0.9rem; |
|
transition: all 0.3s ease; |
|
} |
|
|
|
.filter-btn.active { |
|
background: #667eea; |
|
color: white; |
|
border-color: #667eea; |
|
} |
|
|
|
.items-grid { |
|
display: grid; |
|
grid-template-columns: repeat(auto-fill, minmax(320px, 1fr)); |
|
gap: 20px; |
|
margin-top: 20px; |
|
} |
|
|
|
.item-card { |
|
background: white; |
|
border-radius: 12px; |
|
padding: 20px; |
|
box-shadow: 0 2px 10px rgba(0,0,0,0.08); |
|
transition: all 0.3s ease; |
|
cursor: pointer; |
|
position: relative; |
|
overflow: hidden; |
|
} |
|
|
|
.item-card:hover { |
|
transform: translateY(-5px); |
|
box-shadow: 0 5px 20px rgba(0,0,0,0.15); |
|
} |
|
|
|
.item-card::before { |
|
content: ''; |
|
position: absolute; |
|
top: 0; |
|
left: 0; |
|
width: 100%; |
|
height: 4px; |
|
background: linear-gradient(90deg, #667eea, #764ba2); |
|
} |
|
|
|
.item-header { |
|
display: flex; |
|
align-items: start; |
|
gap: 12px; |
|
margin-bottom: 12px; |
|
} |
|
|
|
.item-icon { |
|
width: 40px; |
|
height: 40px; |
|
background: #f0f0f0; |
|
border-radius: 8px; |
|
display: flex; |
|
align-items: center; |
|
justify-content: center; |
|
font-size: 1.5rem; |
|
} |
|
|
|
.item-info { |
|
flex: 1; |
|
} |
|
|
|
.item-title { |
|
font-weight: 600; |
|
color: #333; |
|
margin-bottom: 4px; |
|
font-size: 1.1rem; |
|
overflow: hidden; |
|
text-overflow: ellipsis; |
|
white-space: nowrap; |
|
} |
|
|
|
.item-author { |
|
color: #666; |
|
font-size: 0.9rem; |
|
} |
|
|
|
.item-stats { |
|
display: flex; |
|
gap: 15px; |
|
margin-top: 12px; |
|
font-size: 0.85rem; |
|
color: #666; |
|
} |
|
|
|
.stat { |
|
display: flex; |
|
align-items: center; |
|
gap: 5px; |
|
} |
|
|
|
.loading { |
|
text-align: center; |
|
padding: 40px; |
|
color: #666; |
|
} |
|
|
|
.loading-spinner { |
|
display: inline-block; |
|
width: 40px; |
|
height: 40px; |
|
border: 4px solid #f3f3f3; |
|
border-top: 4px solid #667eea; |
|
border-radius: 50%; |
|
animation: spin 1s linear infinite; |
|
} |
|
|
|
@keyframes spin { |
|
0% { transform: rotate(0deg); } |
|
100% { transform: rotate(360deg); } |
|
} |
|
|
|
.error { |
|
background: #fee; |
|
color: #c33; |
|
padding: 20px; |
|
border-radius: 8px; |
|
text-align: center; |
|
margin: 20px 0; |
|
} |
|
|
|
.refresh-btn { |
|
position: fixed; |
|
bottom: 30px; |
|
right: 30px; |
|
width: 60px; |
|
height: 60px; |
|
background: linear-gradient(135deg, #667eea, #764ba2); |
|
color: white; |
|
border: none; |
|
border-radius: 50%; |
|
cursor: pointer; |
|
font-size: 1.5rem; |
|
box-shadow: 0 4px 15px rgba(0,0,0,0.2); |
|
transition: all 0.3s ease; |
|
z-index: 100; |
|
} |
|
|
|
.refresh-btn:hover { |
|
transform: scale(1.1); |
|
box-shadow: 0 6px 20px rgba(0,0,0,0.3); |
|
} |
|
|
|
.note { |
|
background: #fef3c7; |
|
border: 1px solid #fbbf24; |
|
border-radius: 8px; |
|
padding: 15px; |
|
margin: 20px 0; |
|
color: #92400e; |
|
text-align: center; |
|
} |
|
|
|
@media (max-width: 600px) { |
|
.items-grid { |
|
grid-template-columns: 1fr; |
|
} |
|
|
|
h1 { |
|
font-size: 1.8rem; |
|
} |
|
} |
|
</style> |
|
</head> |
|
<body> |
|
<div class="container"> |
|
<h1>๐ AI ๋ชจ๋ธ & ์คํ์ด์ค ์ค์๊ฐ ํธ๋ ๋ฉ</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="filter-buttons"> |
|
<button class="filter-btn active" onclick="loadHFModels('trending')">๐ ํธ๋ ๋ฉ</button> |
|
<button class="filter-btn" onclick="loadHFModels('new')">๐ ์ ๊ท</button> |
|
</div> |
|
<div id="hf-models-content" class="items-grid"> |
|
<div class="loading"> |
|
<div class="loading-spinner"></div> |
|
<p>๋ชจ๋ธ ๋ก๋ฉ ์ค...</p> |
|
</div> |
|
</div> |
|
</div> |
|
|
|
|
|
<div id="hf-spaces" class="content-section"> |
|
<div class="filter-buttons"> |
|
<button class="filter-btn active" onclick="loadHFSpaces('trending')">๐ ํธ๋ ๋ฉ</button> |
|
<button class="filter-btn" onclick="loadHFSpaces('new')">๐ ์ ๊ท</button> |
|
</div> |
|
<div id="hf-spaces-content" class="items-grid"> |
|
<div class="loading"> |
|
<div class="loading-spinner"></div> |
|
<p>์คํ์ด์ค ๋ก๋ฉ ์ค...</p> |
|
</div> |
|
</div> |
|
</div> |
|
|
|
|
|
<div id="replicate" class="content-section"> |
|
<div class="filter-buttons"> |
|
<button class="filter-btn active" onclick="loadReplicate()">๐ฅ ์ธ๊ธฐ ๋ชจ๋ธ</button> |
|
</div> |
|
<div id="replicate-content" class="items-grid"> |
|
<div class="loading"> |
|
<div class="loading-spinner"></div> |
|
<p>๋ชจ๋ธ ๋ก๋ฉ ์ค...</p> |
|
</div> |
|
</div> |
|
</div> |
|
|
|
<div class="note"> |
|
โ ๏ธ CORS ์ ์ฑ
์ผ๋ก ์ธํด ์ผ๋ถ ๋ฐ์ดํฐ๊ฐ ๋ก๋๋์ง ์์ ์ ์์ต๋๋ค. |
|
ํ๋ก์ ์๋ฒ๋ ๋ฐฑ์๋ API๋ฅผ ์ฌ์ฉํ๋ฉด ๋ชจ๋ ๋ฐ์ดํฐ๋ฅผ ๊ฐ์ ธ์ฌ ์ ์์ต๋๋ค. |
|
</div> |
|
</div> |
|
|
|
<button class="refresh-btn" onclick="refreshCurrent()">๐</button> |
|
|
|
<script> |
|
let currentCategory = 'hf-models'; |
|
let currentFilter = 'trending'; |
|
|
|
|
|
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('trending'); |
|
} else if (category === 'hf-spaces') { |
|
loadHFSpaces('trending'); |
|
} else if (category === 'replicate') { |
|
loadReplicate(); |
|
} |
|
} |
|
|
|
|
|
async function loadHFModels(filter) { |
|
currentFilter = filter; |
|
|
|
|
|
document.querySelectorAll('#hf-models .filter-btn').forEach(btn => { |
|
btn.classList.remove('active'); |
|
}); |
|
event.target.classList.add('active'); |
|
|
|
const container = document.getElementById('hf-models-content'); |
|
container.innerHTML = '<div class="loading"><div class="loading-spinner"></div><p>๋ชจ๋ธ ๋ก๋ฉ ์ค...</p></div>'; |
|
|
|
try { |
|
|
|
const sort = filter === 'trending' ? 'likes' : 'created'; |
|
const response = await fetch(`https://huggingface.co/api/models?sort=${sort}&limit=20`); |
|
|
|
if (!response.ok) throw new Error('API ์์ฒญ ์คํจ'); |
|
|
|
const data = await response.json(); |
|
displayHFModels(data, container); |
|
} catch (error) { |
|
|
|
displaySampleHFModels(filter, container); |
|
} |
|
} |
|
|
|
|
|
async function loadHFSpaces(filter) { |
|
currentFilter = filter; |
|
|
|
|
|
document.querySelectorAll('#hf-spaces .filter-btn').forEach(btn => { |
|
btn.classList.remove('active'); |
|
}); |
|
event.target.classList.add('active'); |
|
|
|
const container = document.getElementById('hf-spaces-content'); |
|
container.innerHTML = '<div class="loading"><div class="loading-spinner"></div><p>์คํ์ด์ค ๋ก๋ฉ ์ค...</p></div>'; |
|
|
|
try { |
|
|
|
const sort = filter === 'trending' ? 'likes' : 'created'; |
|
const response = await fetch(`https://huggingface.co/api/spaces?sort=${sort}&limit=20`); |
|
|
|
if (!response.ok) throw new Error('API ์์ฒญ ์คํจ'); |
|
|
|
const data = await response.json(); |
|
displayHFSpaces(data, container); |
|
} catch (error) { |
|
|
|
displaySampleHFSpaces(filter, container); |
|
} |
|
} |
|
|
|
|
|
async function loadReplicate() { |
|
const container = document.getElementById('replicate-content'); |
|
container.innerHTML = '<div class="loading"><div class="loading-spinner"></div><p>๋ชจ๋ธ ๋ก๋ฉ ์ค...</p></div>'; |
|
|
|
|
|
displaySampleReplicateModels(container); |
|
} |
|
|
|
|
|
function displayHFModels(models, container) { |
|
container.innerHTML = ''; |
|
models.forEach(model => { |
|
const card = createModelCard({ |
|
title: model.modelId || model.id, |
|
author: model.author || 'Unknown', |
|
likes: model.likes || 0, |
|
downloads: model.downloads || 0, |
|
url: `https://huggingface.co/${model.modelId || model.id}`, |
|
icon: '๐ค' |
|
}); |
|
container.appendChild(card); |
|
}); |
|
} |
|
|
|
|
|
function displayHFSpaces(spaces, container) { |
|
container.innerHTML = ''; |
|
spaces.forEach(space => { |
|
const card = createModelCard({ |
|
title: space.id.split('/')[1] || space.id, |
|
author: space.id.split('/')[0] || 'Unknown', |
|
likes: space.likes || 0, |
|
sdk: space.sdk || 'Unknown', |
|
url: `https://huggingface.co/spaces/${space.id}`, |
|
icon: '๐ฏ' |
|
}); |
|
container.appendChild(card); |
|
}); |
|
} |
|
|
|
|
|
function createModelCard(data) { |
|
const card = document.createElement('div'); |
|
card.className = 'item-card'; |
|
card.onclick = () => window.open(data.url, '_blank'); |
|
|
|
card.innerHTML = ` |
|
<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">by ${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.runs !== undefined ? `<div class="stat">โถ๏ธ ${formatNumber(data.runs)}</div>` : ''} |
|
</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 displaySampleHFModels(filter, container) { |
|
const sampleData = filter === 'trending' ? [ |
|
{ title: 'meta-llama/Llama-3.3-70B', author: 'meta-llama', likes: 177000, downloads: 1150000, icon: '๐ค' }, |
|
{ title: 'DeepSeek-R1', author: 'deepseek-ai', likes: 10400, downloads: 567000, icon: '๐ค' }, |
|
{ title: 'Qwen/Qwen3-235B', author: 'Qwen', likes: 5370, downloads: 225000, icon: '๐ค' }, |
|
{ title: 'FLUX.1-dev', author: 'black-forest-labs', likes: 29100, downloads: 397000, icon: '๐ค' }, |
|
{ title: 'microsoft/Phi-4', author: 'microsoft', likes: 8900, downloads: 156000, icon: '๐ค' } |
|
] : [ |
|
{ title: 'OmniGen2', author: 'research-lab', likes: 156, downloads: 890, icon: '๐ค' }, |
|
{ title: 'VideoGen-Pro', author: 'ai-startup', likes: 89, downloads: 234, icon: '๐ค' }, |
|
{ title: 'CodeLLM-7B', author: 'dev-team', likes: 234, downloads: 1200, icon: '๐ค' }, |
|
{ title: 'MusicGen-V2', author: 'audio-ai', likes: 67, downloads: 456, icon: '๐ค' }, |
|
{ title: 'ImageEdit-XL', author: 'vision-lab', likes: 145, downloads: 789, icon: '๐ค' } |
|
]; |
|
|
|
container.innerHTML = ''; |
|
sampleData.forEach(model => { |
|
model.url = `https://huggingface.co/${model.author}/${model.title}`; |
|
container.appendChild(createModelCard(model)); |
|
}); |
|
} |
|
|
|
function displaySampleHFSpaces(filter, container) { |
|
const sampleData = filter === 'trending' ? [ |
|
{ title: 'stable-diffusion-webui', author: 'stabilityai', likes: 75500, sdk: 'Gradio', icon: '๐ฏ' }, |
|
{ title: 'chatgpt-clone', author: 'community', likes: 12300, sdk: 'Streamlit', icon: '๐ฏ' }, |
|
{ title: 'image-to-3d', author: 'research', likes: 8590, sdk: 'Gradio', icon: '๐ฏ' }, |
|
{ title: 'voice-clone', author: 'audio-ml', likes: 5640, sdk: 'Gradio', icon: '๐ฏ' }, |
|
{ title: 'code-generator', author: 'dev-tools', likes: 3210, sdk: 'Streamlit', icon: '๐ฏ' } |
|
] : [ |
|
{ title: 'new-llm-demo', author: 'researcher', likes: 23, sdk: 'Gradio', icon: '๐ฏ' }, |
|
{ title: 'video-editor', author: 'creator', likes: 45, sdk: 'Streamlit', icon: '๐ฏ' }, |
|
{ title: 'data-viz', author: 'analyst', likes: 67, sdk: 'Streamlit', icon: '๐ฏ' }, |
|
{ title: 'music-mixer', author: 'musician', likes: 34, sdk: 'Gradio', icon: '๐ฏ' }, |
|
{ title: 'text-analyzer', author: 'nlp-dev', likes: 56, sdk: 'Gradio', icon: '๐ฏ' } |
|
]; |
|
|
|
container.innerHTML = ''; |
|
sampleData.forEach(space => { |
|
space.url = `https://huggingface.co/spaces/${space.author}/${space.title}`; |
|
container.appendChild(createModelCard(space)); |
|
}); |
|
} |
|
|
|
function displaySampleReplicateModels(container) { |
|
const sampleData = [ |
|
{ title: 'FLUX1.1-pro', author: 'black-forest-labs', runs: 2910000, icon: 'โก' }, |
|
{ title: 'stable-diffusion-3', author: 'stability-ai', runs: 1750000, icon: 'โก' }, |
|
{ title: 'whisper', author: 'openai', runs: 890000, icon: 'โก' }, |
|
{ title: 'llama-2-70b', author: 'meta', runs: 670000, icon: 'โก' }, |
|
{ title: 'musicgen', author: 'facebook', runs: 450000, icon: 'โก' }, |
|
{ title: 'animate-diff', author: 'lucataco', runs: 320000, icon: 'โก' }, |
|
{ title: 'real-esrgan', author: 'xinntao', runs: 280000, icon: 'โก' }, |
|
{ title: 'rembg', author: 'cjwbw', runs: 210000, icon: 'โก' } |
|
]; |
|
|
|
container.innerHTML = ''; |
|
sampleData.forEach(model => { |
|
model.url = `https://replicate.com/${model.author}/${model.title}`; |
|
container.appendChild(createModelCard(model)); |
|
}); |
|
} |
|
|
|
|
|
function refreshCurrent() { |
|
if (currentCategory === 'hf-models') { |
|
loadHFModels(currentFilter); |
|
} else if (currentCategory === 'hf-spaces') { |
|
loadHFSpaces(currentFilter); |
|
} else { |
|
loadReplicate(); |
|
} |
|
} |
|
|
|
|
|
loadHFModels('trending'); |
|
</script> |
|
</body> |
|
</html> |