SpenditSmart / index.html
tylopez's picture
Upload index.html with huggingface_hub
84accfb verified
Raw History Blame Contribute Delete
33.5 kB
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>ZeroGPU Spaces Tracker - Hugging Face</title>
<link rel="preconnect" href="https://huggingface.co">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.0/css/all.min.css">
<style>
:root {
--primary: #5533ff;
--secondary: #6b46c1;
--light: #f8f9fa;
--dark: #212529;
--success: #28a745;
--warning: #ffc107;
--danger: #dc3545;
--border-radius: 12px;
--shadow: 0 10px 25px rgba(0, 0, 0, 0.1);
--transition: all 0.3s ease;
}
* {
margin: 0;
padding: 0;
box-sizing: border-box;
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
}
body {
background-color: #f5f7ff;
color: var(--dark);
line-height: 1.6;
}
header {
background: linear-gradient(135deg, var(--primary), var(--secondary));
color: white;
padding: 1.5rem 0;
box-shadow: var(--shadow);
position: relative;
overflow: hidden;
}
header::before {
content: "";
position: absolute;
top: -50%;
left: -50%;
width: 200%;
height: 200%;
background: radial-gradient(circle, rgba(255,255,255,0.1) 0%, transparent 70%);
z-index: 0;
}
header .container {
position: relative;
z-index: 1;
display: flex;
justify-content: space-between;
align-items: center;
flex-wrap: wrap;
}
.logo {
display: flex;
align-items: center;
gap: 10px;
}
.logo img {
height: 40px;
}
.logo h1 {
font-size: 1.8rem;
font-weight: 700;
margin: 0;
}
.built-with {
display: inline-block;
color: rgba(255,255,255,0.9);
text-decoration: none;
font-size: 0.9rem;
margin-left: 1rem;
padding: 0.25rem 0.75rem;
border-radius: 20px;
background-color: rgba(255,255,255,0.2);
transition: var(--transition);
}
.built-with:hover {
background-color: rgba(255,255,255,0.3);
transform: translateY(-2px);
}
.navbar {
display: flex;
gap: 1.5rem;
}
.navbar a {
color: white;
text-decoration: none;
font-weight: 500;
transition: var(--transition);
padding: 0.5rem 1rem;
border-radius: 8px;
}
.navbar a:hover {
background-color: rgba(255,255,255,0.2);
}
.container {
max-width: 1200px;
margin: 0 auto;
padding: 2rem;
}
.hero {
text-align: center;
margin-bottom: 3rem;
padding: 2rem;
background-color: white;
border-radius: var(--border-radius);
box-shadow: var(--shadow);
}
.hero h2 {
font-size: 2.5rem;
margin-bottom: 1rem;
color: var(--primary);
}
.hero p {
font-size: 1.2rem;
color: #666;
max-width: 800px;
margin: 0 auto;
}
.stats {
display: flex;
justify-content: space-around;
flex-wrap: wrap;
margin: 2rem 0;
gap: 1rem;
}
.stat-card {
background-color: white;
border-radius: var(--border-radius);
padding: 1.5rem;
text-align: center;
box-shadow: var(--shadow);
flex: 1;
min-width: 200px;
transition: var(--transition);
}
.stat-card:hover {
transform: translateY(-5px);
}
.stat-number {
font-size: 2.5rem;
font-weight: 700;
color: var(--primary);
margin-bottom: 0.5rem;
}
.stat-label {
color: #666;
font-size: 1rem;
text-transform: uppercase;
letter-spacing: 1px;
}
.spaces-grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(300px, 1fr));
gap: 1.5rem;
margin-bottom: 3rem;
}
.space-card {
background-color: white;
border-radius: var(--border-radius);
overflow: hidden;
box-shadow: var(--shadow);
transition: var(--transition);
cursor: pointer;
}
.space-card:hover {
transform: translateY(-5px);
box-shadow: 0 15px 30px rgba(0, 0, 0, 0.15);
}
.space-header {
padding: 1.25rem;
background-color: var(--primary);
color: white;
}
.space-header h3 {
font-size: 1.3rem;
margin: 0;
display: flex;
align-items: center;
gap: 0.5rem;
}
.space-header .badge {
margin-left: auto;
padding: 0.3rem 0.7rem;
border-radius: 20px;
font-size: 0.8rem;
font-weight: 600;
background-color: rgba(255,255,255,0.2);
}
.space-content {
padding: 1.25rem;
}
.space-meta {
display: flex;
justify-content: space-between;
margin-bottom: 1rem;
color: #666;
font-size: 0.9rem;
}
.space-description {
color: #555;
margin-bottom: 1.25rem;
font-size: 0.95rem;
line-height: 1.5;
}
.space-footer {
display: flex;
justify-content: space-between;
align-items: center;
border-top: 1px solid #eee;
padding-top: 1rem;
}
.space-link {
color: var(--primary);
text-decoration: none;
font-weight: 600;
display: flex;
align-items: center;
gap: 0.5rem;
}
.space-link i {
transition: var(--transition);
}
.space-link:hover i {
transform: translateX(3px);
}
.loading {
text-align: center;
padding: 3rem;
color: #666;
font-size: 1.2rem;
}
.error {
text-align: center;
padding: 3rem;
color: var(--danger);
font-size: 1.2rem;
}
.filter-section {
background-color: white;
border-radius: var(--border-radius);
padding: 1.5rem;
margin-bottom: 2rem;
box-shadow: var(--shadow);
}
.filter-controls {
display: flex;
flex-wrap: wrap;
gap: 1rem;
align-items: center;
}
.filter-group {
flex: 1;
min-width: 200px;
}
.filter-group label {
display: block;
margin-bottom: 0.5rem;
font-weight: 600;
color: #555;
}
.filter-group input,
.filter-group select {
width: 100%;
padding: 0.75rem;
border: 1px solid #ddd;
border-radius: 8px;
font-size: 1rem;
transition: var(--transition);
}
.filter-group input:focus,
.filter-group select:focus {
outline: none;
border-color: var(--primary);
box-shadow: 0 0 0 2px rgba(85, 51, 255, 0.2);
}
.filter-btn {
padding: 0.75rem 1.5rem;
background-color: var(--primary);
color: white;
border: none;
border-radius: 8px;
font-size: 1rem;
font-weight: 600;
cursor: pointer;
transition: var(--transition);
white-space: nowrap;
}
.filter-btn:hover {
background-color: var(--secondary);
transform: translateY(-2px);
}
.refresh-btn {
display: flex;
justify-content: center;
margin: 2rem 0;
}
.refresh-btn button {
padding: 0.75rem 2rem;
background-color: var(--secondary);
color: white;
border: none;
border-radius: 8px;
font-size: 1rem;
font-weight: 600;
cursor: pointer;
transition: var(--transition);
display: flex;
align-items: center;
gap: 0.5rem;
}
.refresh-btn button:hover {
background-color: #5533ff;
transform: translateY(-2px);
}
.info-panel {
background-color: #e6f2ff;
border-left: 4px solid var(--primary);
padding: 1.5rem;
border-radius: 0 var(--border-radius) var(--border-radius) 0;
margin: 2rem 0;
}
.quota-info {
background-color: #fff3cd;
border: 1px solid #ffeaa7;
padding: 1rem;
border-radius: 8px;
margin: 1rem 0;
font-size: 0.95rem;
color: #856404;
}
.quota-info strong {
color: #856404;
}
.info-panel h3 {
color: var(--primary);
margin-bottom: 1rem;
display: flex;
align-items: center;
gap: 0.5rem;
}
.info-panel ul {
margin-bottom: 1rem;
padding-left: 1.5rem;
}
.info-panel li {
margin-bottom: 0.5rem;
}
footer {
text-align: center;
padding: 2rem 0;
color: #666;
font-size: 0.9rem;
background-color: white;
margin-top: 2rem;
}
@media (max-width: 768px) {
header .container {
flex-direction: column;
text-align: center;
}
.logo {
margin-bottom: 1rem;
}
.navbar {
margin-top: 1rem;
flex-wrap: wrap;
justify-content: center;
}
.hero h2 {
font-size: 2rem;
}
.stats {
flex-direction: column;
}
.spaces-grid {
grid-template-columns: 1fr;
}
.filter-controls {
flex-direction: column;
}
}
.badge-verified {
background-color: #28a745;
color: white;
font-weight: 600;
text-transform: uppercase;
letter-spacing: 0.5px;
padding: 0.3rem 0.7rem;
border-radius: 20px;
font-size: 0.8rem;
box-shadow: 0 2px 4px rgba(40, 167, 69, 0.3);
}
.badge-featured {
background-color: #ffc107;
color: #212529;
font-weight: 600;
text-transform: uppercase;
letter-spacing: 0.5px;
padding: 0.3rem 0.7rem;
border-radius: 20px;
font-size: 0.8rem;
box-shadow: 0 2px 4px rgba(255, 193, 7, 0.3);
}
.badge-new {
background-color: #17a2b8;
color: white;
font-weight: 600;
text-transform: uppercase;
letter-spacing: 0.5px;
padding: 0.3rem 0.7rem;
border-radius: 20px;
font-size: 0.8rem;
box-shadow: 0 2px 4px rgba(23, 162, 184, 0.3);
}
.empty-state {
text-align: center;
padding: 4rem;
color: #666;
}
.empty-state i {
font-size: 4rem;
color: #ddd;
margin-bottom: 1rem;
}
.empty-state h3 {
margin-bottom: 0.5rem;
}
.empty-state p {
max-width: 600px;
margin: 0 auto 1.5rem;
}
.empty-state .cta-btn {
display: inline-block;
padding: 0.75rem 1.5rem;
background-color: var(--primary);
color: white;
border: none;
border-radius: 8px;
font-size: 1rem;
font-weight: 600;
cursor: pointer;
transition: var(--transition);
text-decoration: none;
}
.empty-state .cta-btn:hover {
background-color: var(--secondary);
transform: translateY(-2px);
}
.empty-state i {
font-size: 4rem;
color: #ddd;
margin-bottom: 1rem;
}
.empty-state h3 {
margin-bottom: 0.5rem;
}
.active-indicator {
display: inline-block;
width: 8px;
height: 8px;
border-radius: 50%;
background-color: #28a745;
margin-right: 0.5rem;
}
.api-info {
background-color: #f8f9fa;
border: 1px dashed #dee2e6;
padding: 1rem;
border-radius: 8px;
margin: 1rem 0;
font-size: 0.9rem;
color: #666;
}
.api-info code {
background-color: #e9ecef;
padding: 0.2rem 0.4rem;
border-radius: 4px;
}
</style>
</head>
<body>
<header>
<div class="container">
<div class="logo">
<i class="fas fa-robot" style="font-size: 2rem;"></i>
<h1>ZeroGPU Spaces Tracker</h1>
</div>
<div class="navbar">
<a href="#" id="home-link">Home</a>
<a href="#" id="about-link">About</a>
<a href="https://huggingface.co/spaces" target="_blank">Explore Spaces</a>
</div>
<a href="https://huggingface.co/docs" class="built-with" target="_blank">Hugging Face Docs</a>
</div>
</header>
<div class="container">
<section class="hero">
<h2>Discover Free ZeroGPU Spaces on Hugging Face</h2>
<p>Track and discover the latest AI applications running on Hugging Face's free ZeroGPU infrastructure with dynamic GPU allocation.</p>
</section>
<div class="info-panel">
<h3><i class="fas fa-info-circle"></i> About ZeroGPU Spaces</h3>
<p>ZeroGPU Spaces are Hugging Face's free tier for running AI applications with dynamic GPU allocation on NVIDIA H200 hardware. These spaces are optimized for efficiency and are available to all users with a daily quota.</p>
<ul>
<li>✅ Free to use - no subscription required</li>
<li>✅ Dynamic GPU allocation on NVIDIA H200</li>
<li>✅ 25 minutes daily quota for PRO users, 5 minutes for free users</li>
<li>✅ Compatible with Gradio 4+ and PyTorch 2.1.0+</li>
<li>✅ Automatically scales based on demand</li>
</ul>
<div class="api-info">
<strong>Important:</strong> The free tier quota is shared across all ZeroGPU spaces you use. For non-PRO users, the daily quota is approximately 5 minutes, while PRO users get 25 minutes of GPU time per day. This quota resets every 24 hours.
</div>
<div class="quota-info">
<strong>Current Quota Status:</strong> Your daily ZeroGPU quota is based on your Hugging Face account type. Free users: ~5 minutes/day, PRO users: ~25 minutes/day. This quota resets every 24 hours. <em>Note: Actual usage may vary based on system load and demand.</em>
</div>
</div>
<div class="filter-section">
<h3>Filter Spaces</h3>
<p style="color: #666; margin-bottom: 1rem; font-size: 0.9rem;">Filter and sort ZeroGPU spaces by category, search terms, and sorting options. Results update automatically as you type.</p>
<div class="filter-controls">
<div class="filter-group">
<label for="search-input">Search by name or description</label>
<input type="text" id="search-input" placeholder="Search for spaces like 'image generation', 'text-to-speech', 'audio', etc.">
</div>
<div class="filter-group">
<label for="category-filter">Category</label>
<select id="category-filter">
<option value="">All Categories</option>
<option value="Text">Text</option>
<option value="Image">Image</option>
<option value="Audio">Audio</option>
<option value="Video">Video</option>
<option value="3D">3D</option>
<option value="Research">Research</option>
<option value="Tools">Tools</option>
</select>
</div>
<div class="filter-group">
<label for="sort-filter">Sort By</label>
<select id="sort-filter">
<option value="recent">Most Recent</option>
<option value="popular">Most Popular</option>
<option value="name">Name (A-Z)</option>
</select>
</div>
<button class="filter-btn" id="apply-filters">Apply Filters</button>
</div>
</div>
<div class="refresh-btn">
<button id="refresh-btn">
<i class="fas fa-sync-alt"></i> Refresh Available Spaces
</button>
</div>
<div id="spaces-container" class="spaces-grid">
<div class="loading">
<i class="fas fa-spinner fa-spin"></i>
Loading latest ZeroGPU spaces...
</div>
</div>
<div id="empty-state" class="empty-state" style="display: none;">
<i class="fas fa-search"></i>
<h3>No ZeroGPU Spaces Found</h3>
<p>Try adjusting your search criteria or check back later as new spaces are added frequently. ZeroGPU spaces are dynamic and change often based on community usage patterns.</p>
<a href="#" class="cta-btn" id="reset-filters">Reset Filters</a>
</div>
</div>
<footer>
<p>ZeroGPU Spaces Tracker &copy; 2023 | This is an independent tool not affiliated with Hugging Face</p>
<p>Information about ZeroGPU spaces is based on Hugging Face's public documentation and community reports.</p>
<p style="margin-top: 1rem;">
<a href="https://huggingface.co/docs" target="_blank" style="color: #666; text-decoration: none; margin: 0 0.5rem;">Hugging Face Docs</a>
<a href="https://huggingface.co/spaces" target="_blank" style="color: #666; text-decoration: none; margin: 0 0.5rem;">Explore Spaces</a>
<a href="https://github.com/huggingface" target="_blank" style="color: #666; text-decoration: none; margin: 0 0.5rem;">GitHub</a>
</p>
</footer>
<script>
document.addEventListener('DOMContentLoaded', function() {
// DOM elements
const spacesContainer = document.getElementById('spaces-container');
const refreshBtn = document.getElementById('refresh-btn');
const searchInput = document.getElementById('search-input');
const categoryFilter = document.getElementById('category-filter');
const sortFilter = document.getElementById('sort-filter');
const applyFiltersBtn = document.getElementById('apply-filters');
const emptyState = document.getElementById('empty-state');
// Sample data for ZeroGPU spaces (simulating real API response)
// Note: In a real app, this would come from Hugging Face API
const mockZeroGpuSpaces = [
{
id: "1",
name: "ShieldGemma",
username: "google",
description: "A powerful content moderation tool using Google's Gemma model to detect harmful text.",
category: "Text",
lastUpdated: "2023-12-05T10:30:00Z",
stars: 1247,
isVerified: true,
isFeatured: false,
isRecent: true,
url: "https://huggingface.co/spaces/google/shieldgemma"
},
{
id: "2",
name: "DreamFusion",
username: "stability-ai",
description: "Generate stunning 3D models from text prompts using the latest diffusion techniques.",
category: "3D",
lastUpdated: "2023-12-04T15:20:00Z",
stars: 2034,
isVerified: true,
isFeatured: true,
isRecent: true,
url: "https://huggingface.co/spaces/stability-ai/dreamfusion"
},
{
id: "3",
name: "AudioLDM",
username: "audioldm",
description: "Generate high-quality audio from text descriptions with AudioLDM - the audio version of Stable Diffusion.",
category: "Audio",
lastUpdated: "2023-12-04T09:15:00Z",
stars: 987,
isVerified: false,
isFeatured: true,
isRecent: true,
url: "https://huggingface.co/spaces/audioldm/audioldm"
},
{
id: "4",
name: "Text2Video-Zero",
username: "huggingface",
description: "Create videos from text prompts using the new Text2Video-Zero model powered by ZeroGPU.",
category: "Video",
lastUpdated: "2023-12-03T14:00:00Z",
stars: 1562,
isVerified: true,
isFeatured: false,
isRecent: true,
url: "https://huggingface.co/spaces/huggingface/text2video-zero"
},
{
id: "5",
name: "DiffusionBee",
username: "diffusionbee",
description: "A simple interface for generating images with Stable Diffusion without needing any technical knowledge.",
category: "Image",
lastUpdated: "2023-12-02T20:45:00Z",
stars: 2567,
isVerified: false,
isFeatured: false,
isRecent: false,
url: "https://huggingface.co/spaces/diffusionbee/diffusionbee"
},
{
id: "6",
name: "ImageCaptioner",
username: "captions",
description: "Automatic image captioning using a state-of-the-art vision-language model.",
category: "Image",
lastUpdated: "2023-12-01T12:30:00Z",
stars: 889,
isVerified: true,
isFeatured: false,
isRecent: false,
url: "https://huggingface.co/spaces/captions/image-captioner"
},
{
id: "7",
name: "TextSummarizer",
username: "summarize",
description: "Summarize long articles and documents with a fine-tuned BART model.",
category: "Text",
lastUpdated: "2023-11-30T08:10:00Z",
stars: 1123,
isVerified: false,
isFeatured: false,
isRecent: false,
url: "https://huggingface.co/spaces/summarize/text-summarizer"
},
{
id: "8",
name: "VoiceCloner",
username: "voice-cloning",
description: "Clone a voice from a short audio sample and generate speech in that voice.",
category: "Audio",
lastUpdated: "2023-11-29T16:40:00Z",
stars: 778,
isVerified: false,
isFeatured: false,
isRecent: false,
url: "https://huggingface.co/spaces/voice-cloning/voice-cloner"
}
];
// Function to format date for display
function formatDate(dateString) {
const date = new Date(dateString);
const now = new Date();
const diffInHours = (now - date) / (1000 * 60 * 60);
if (diffInHours < 1) {
const minutes = Math.floor(diffInHours * 60);
return `${minutes} minute${minutes !== 1 ? 's' : ''} ago`;
} else if (diffInHours < 24) {
return `${Math.floor(diffInHours)} hour${Math.floor(diffInHours) !== 1 ? 's' : ''} ago`;
} else if (diffInHours < 168) { // 7 days
return `${Math.floor(diffInHours / 24)} day${Math.floor(diffInHours / 24) !== 1 ? 's' : ''} ago`;
} else {
return date.toLocaleDateString('en-US', {
year: 'numeric',
month: 'short',
day: 'numeric'
});
}
}
// Function to create a space card element
function createSpaceCard(space) {
const card = document.createElement('div');
card.className = 'space-card';
// Determine badge classes
let badgeClass = '';
let badgeText = '';
if (space.isVerified) {
badgeClass = 'badge badge-verified';
badgeText = 'Verified';
} else if (space.isFeatured) {
badgeClass = 'badge badge-featured';
badgeText = 'Featured';
} else if (space.isRecent) {
badgeClass = 'badge badge-new';
badgeText = 'New';
}
const badgeHTML = badgeClass ? `<span class="${badgeClass}">${badgeText}</span>` : '';
card.innerHTML = `
<div class="space-header">
<h3>${space.name} <small>@${space.username}</small></h3>
${badgeHTML}
</div>
<div class="space-content">
<div class="space-meta">
<span><i class="fas fa-folder"></i> ${space.category}</span>
<span><i class="fas fa-star"></i> ${space.stars.toLocaleString()}</span>
</div>
<p class="space-description">${space.description}</p>
<div class="space-footer">
<div>
<span class="active-indicator"></span>
<span style="font-size: 0.9rem; color: #555;">Available</span>
</div>
<a href="${space.url}" class="space-link" target="_blank">
Visit Space <i class="fas fa-external-link-alt"></i>
</a>
</div>
</div>
`;
return card;
}
// Function to filter and sort spaces
function filterAndSortSpaces(spaces) {
const searchTerm = searchInput.value.toLowerCase().trim();
const category = categoryFilter.value;
const sortBy = sortFilter.value;
let filtered = spaces.filter(space => {
const matchesSearch = !searchTerm ||
space.name.toLowerCase().includes(searchTerm) ||
space.description.toLowerCase().includes(searchTerm) ||
space.username.toLowerCase().includes(searchTerm);
const matchesCategory = !category || space.category === category;
return matchesSearch && matchesCategory;
});
// Sort based on selection
if (sortBy === 'recent') {
filtered.sort((a, b) => new Date(b.lastUpdated) - new Date(a.lastUpdated));
} else if (sortBy === 'popular') {
filtered.sort((a, b) => b.stars - a.stars);
} else if (sortBy === 'name') {
filtered.sort((a, b) => a.name.localeCompare(b.name));
}
return filtered;
}
// Function to render spaces
function renderSpaces() {
const filteredSpaces = filterAndSortSpaces(mockZeroGpuSpaces);
if (filteredSpaces.length === 0) {
spacesContainer.innerHTML = '';
emptyState.style.display = 'block';
return;
}
emptyState.style.display = 'none';
spacesContainer.innerHTML = '';
filteredSpaces.forEach(space => {
const card = createSpaceCard(space);
spacesContainer.appendChild(card);
});
}
// Event listeners
refreshBtn.addEventListener('click', function() {
// Show loading state
spacesContainer.innerHTML = '<div class="loading"><i class="fas fa-spinner fa-spin"></i> Refreshing available spaces...</div>';
emptyState.style.display = 'none';
// Simulate API call delay
setTimeout(() => {
renderSpaces();
// Show success message
const prevText = refreshBtn.innerHTML;
refreshBtn.innerHTML = '<i class="fas fa-check"></i> Updated!';
refreshBtn.style.backgroundColor = 'var(--success)';
setTimeout(() => {
refreshBtn.innerHTML = prevText;
refreshBtn.style.backgroundColor = 'var(--secondary)';
}, 2000);
}, 1500);
});
applyFiltersBtn.addEventListener('click', renderSpaces);
// Debounce search input
let searchTimeout;
searchInput.addEventListener('input', () => {
clearTimeout(searchTimeout);
searchTimeout = setTimeout(renderSpaces, 500);
});
categoryFilter.addEventListener('change', renderSpaces);
sortFilter.addEventListener('change', renderSpaces);
// Initialize
renderSpaces();
// Reset filters button in empty state
document.getElementById('reset-filters').addEventListener('click', function(e) {
e.preventDefault();
searchInput.value = '';
categoryFilter.value = '';
sortFilter.value = 'recent';
renderSpaces();
});
// Set up navbar links
document.getElementById('home-link').addEventListener('click', function(e) {
e.preventDefault();
window.scrollTo({ top: 0, behavior: 'smooth' });
});
document.getElementById('about-link').addEventListener('click', function(e) {
e.preventDefault();
document.querySelector('.info-panel').scrollIntoView({ behavior: 'smooth' });
});
});
</script>
</body>
</html>