Spaces:
Running
Running
| <html lang="en"> | |
| <head> | |
| <meta charset="UTF-8"> | |
| <meta name="viewport" content="width=device-width, initial-scale=1.0"> | |
| <title>Screenshot Gallery</title> | |
| <script src="https://cdn.tailwindcss.com"></script> | |
| <style> | |
| /* Ensure html and body take full height */ | |
| html, body { | |
| height: 100%; | |
| margin: 0; | |
| padding: 0; | |
| background-color: #1a202c; /* Dark background */ | |
| } | |
| /* Ensure the grid container takes full height */ | |
| #screenshot-grid { | |
| } | |
| /* Style for individual grid items */ | |
| .grid-item { | |
| border-radius:12px; | |
| } | |
| .grid-item:hover { | |
| filter: brightness(1.2); | |
| } | |
| /* Style for images within grid items */ | |
| .grid-item img { | |
| } | |
| </style> | |
| </head> | |
| <body class="bg-gray-900 text-gray-100"> | |
| <header class="p-4 flex justify-between items-center border-b border-gray-700"> | |
| <h1 class="text-2xl font-bold"><span class="font-normal">Made with</span> DeepSite</h1> | |
| <p class="max-sm:text-xs max-sm:w-40">⚠️ Do not share personal information. User-submitted apps may contain malicious code.</p> | |
| <a href="https://huggingface.co/spaces/enzostvs/deepsite" target="_blank" rel="noopener noreferrer" class="bg-blue-500 hover:bg-blue-700 text-white font-bold py-2 px-4 rounded"> | |
| Open DeepSite | |
| </a> | |
| </header> | |
| <div id="screenshot-grid" class="grid grid-cols-2 sm:grid-cols-3 2xl:grid-cols-4 gap-2 p-4"> | |
| <!-- Screenshots will be loaded here --> | |
| </div> | |
| <script> | |
| async function loadScreenshots() { | |
| try { | |
| const response = await fetch('screenshots.json'); | |
| if (!response.ok) { | |
| throw new Error(`HTTP error! status: ${response.status}`); | |
| } | |
| // Read the JSON body ONCE | |
| const screenshotsData = await response.json(); | |
| // screenshotsData is now an array of objects: [{filename: "...", rating: N}, ...] | |
| const grid = document.getElementById('screenshot-grid'); | |
| grid.innerHTML = ''; // Clear existing content | |
| const fragment = document.createDocumentFragment(); // Create a fragment | |
| // No need to filter/sort here, assuming screenshots.json is already sorted and correct | |
| if (!Array.isArray(screenshotsData)) { | |
| throw new Error("screenshots.json is not a valid array."); | |
| } | |
| // Build elements in the fragment | |
| screenshotsData.forEach(item => { | |
| if (!item || typeof item.filename !== 'string' || !item.filename.endsWith('.png')) { | |
| console.warn("Skipping invalid item in screenshots.json:", item); | |
| return; | |
| } | |
| // --- Filter by rating --- | |
| if (typeof item.rating !== 'number' || item.rating < 50) { | |
| // console.log(`Skipping ${item.filename} due to rating: ${item.rating}`); | |
| return; // Skip items with rating below 50 or invalid rating | |
| } | |
| // --- End filter --- | |
| const filename = item.filename; | |
| // const rating = item.rating; // Rating is available if needed for display | |
| const gridItem = document.createElement('div'); | |
| gridItem.className = 'grid-item relative'; // Added relative for potential badge positioning | |
| const img = document.createElement('img'); | |
| img.src = `screenshots/${filename}`; | |
| img.alt = `Screenshot of ${filename.replace(/^space-|-/g, ' ').replace('.png', '')}`; // Improved alt text | |
| img.loading = 'lazy'; // Lazy load images | |
| img.decoding = 'async'; // Hint for async decoding | |
| img.className = 'w-full h-auto object-cover rounded-lg shadow-md'; // Added some styling | |
| // Create the link element | |
| const link = document.createElement('a'); | |
| // Parse filename to create the URL (remove 'space-' prefix and '.png' suffix) | |
| try { | |
| const namePart = filename.replace(/^space-/, '').replace(/\.png$/, ''); | |
| // Replace the *first* hyphen only to separate owner/repo | |
| const parts = namePart.split(/-(.+)/); // Split on the first hyphen | |
| if (parts.length >= 2 && parts[0] && parts[1]) { | |
| const username = parts[0]; | |
| const spacename = parts[1]; | |
| link.href = `https://huggingface.co/spaces/${username}/${spacename}`; | |
| link.target = '_blank'; // Open in new tab | |
| link.rel = 'noopener noreferrer'; // Security best practice | |
| } else { | |
| // Handle cases where the format might be unexpected | |
| console.warn(`Could not parse username/spacename from: ${filename}`); | |
| // Make it non-clickable by just appending the image | |
| gridItem.appendChild(img); | |
| fragment.appendChild(gridItem); // Add directly to fragment | |
| return; // Skip appending link for this item | |
| } | |
| } catch (e) { | |
| console.error(`Error parsing filename: ${filename}`, e); | |
| // Append image directly if parsing fails | |
| gridItem.appendChild(img); | |
| fragment.appendChild(gridItem); // Add directly to fragment | |
| return; // Skip appending link for this item | |
| } | |
| link.appendChild(img); // Place the image inside the link | |
| gridItem.appendChild(link); // Place the link (with image) inside the grid item | |
| // Optional: Display rating badge (Example) | |
| // if (typeof rating === 'number') { | |
| // const badge = document.createElement('span'); | |
| // badge.className = 'absolute top-2 right-2 bg-blue-500 text-white text-xs font-bold px-2 py-1 rounded-full'; | |
| // badge.textContent = rating; | |
| // gridItem.appendChild(badge); | |
| // } | |
| fragment.appendChild(gridItem); // Add the item to the fragment | |
| }); | |
| // Append the fragment to the grid once | |
| grid.appendChild(fragment); | |
| } catch (error) { | |
| console.error('Failed to load screenshots:', error); | |
| const grid = document.getElementById('screenshot-grid'); | |
| grid.innerHTML = '<p class="text-red-500 text-center col-span-3">Failed to load screenshots. Check console for details.</p>'; | |
| } | |
| } | |
| document.addEventListener('DOMContentLoaded', loadScreenshots); | |
| </script> | |
| </body> | |
| </html> | |