Spaces:
Running
Running
| <html lang="en"> | |
| <head> | |
| <meta charset="UTF-8"> | |
| <meta name="viewport" content="width=device-width, initial-scale=1.0"> | |
| <title>My Gallery</title> | |
| <script src="https://cdn.tailwindcss.com"></script> | |
| <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css"> | |
| <style> | |
| .fade-in { | |
| animation: fadeIn 0.5s ease-in-out; | |
| } | |
| @keyframes fadeIn { | |
| from { opacity: 0; } | |
| to { opacity: 1; } | |
| } | |
| .grid-view { | |
| display: grid; | |
| grid-template-columns: repeat(auto-fill, minmax(250px, 1fr)); | |
| gap: 20px; | |
| } | |
| .list-view .gallery-item { | |
| display: flex; | |
| flex-direction: row; | |
| width: 100%; | |
| max-width: 100%; | |
| } | |
| .list-view .gallery-item img { | |
| width: 150px; | |
| height: 100px; | |
| object-fit: cover; | |
| } | |
| .list-view .gallery-item .item-info { | |
| padding-left: 15px; | |
| text-align: left; | |
| } | |
| .gallery-item { | |
| transition: all 0.3s ease; | |
| } | |
| .gallery-item:hover { | |
| transform: scale(1.02); | |
| box-shadow: 0 10px 15px -3px rgba(0, 0, 0, 0.1), 0 4px 6px -2px rgba(0, 0, 0, 0.05); | |
| } | |
| .login-container { | |
| background: linear-gradient(135deg, #f5f7fa 0%, #c3cfe2 100%); | |
| } | |
| .gallery-container { | |
| background: linear-gradient(to right, #fdfbfb, #ebedee); | |
| } | |
| /* Modal styles */ | |
| .modal { | |
| display: none; | |
| position: fixed; | |
| top: 0; | |
| left: 0; | |
| width: 100%; | |
| height: 100%; | |
| background-color: rgba(0, 0, 0, 0.8); | |
| z-index: 1000; | |
| justify-content: center; | |
| align-items: center; | |
| } | |
| .modal-content { | |
| max-width: 90%; | |
| max-height: 90%; | |
| } | |
| .modal-content img { | |
| max-width: 100%; | |
| max-height: 80vh; | |
| object-fit: contain; | |
| } | |
| .close-modal { | |
| position: absolute; | |
| top: 20px; | |
| right: 30px; | |
| color: white; | |
| font-size: 30px; | |
| cursor: pointer; | |
| } | |
| /* Dark mode styles */ | |
| .dark .login-container { | |
| background: linear-gradient(135deg, #1a1a2e 0%, #16213e 100%); | |
| } | |
| .dark .gallery-container { | |
| background: linear-gradient(to right, #0f0c29, #302b63); | |
| } | |
| .dark .bg-white { | |
| background-color: #1e293b ; | |
| } | |
| .dark .text-gray-800 { | |
| color: #e2e8f0 ; | |
| } | |
| .dark .text-gray-700 { | |
| color: #cbd5e1 ; | |
| } | |
| .dark .text-gray-600 { | |
| color: #94a3b8 ; | |
| } | |
| .dark .border-gray-300 { | |
| border-color: #475569 ; | |
| } | |
| .dark .gallery-item { | |
| background-color: #1e293b ; | |
| color: #e2e8f0 ; | |
| } | |
| .dark .text-gray-500 { | |
| color: #94a3b8 ; | |
| } | |
| .dark .bg-red-50 { | |
| background-color: #7f1d1d ; | |
| color: #fecaca ; | |
| } | |
| .dark .hover\:bg-gray-50:hover { | |
| background-color: #334155 ; | |
| } | |
| .dark .shadow-sm { | |
| box-shadow: 0 1px 2px 0 rgba(0, 0, 0, 0.5) ; | |
| } | |
| .dark .bg-indigo-600 { | |
| background-color: #4338ca ; | |
| } | |
| .dark .hover\:bg-indigo-700:hover { | |
| background-color: #3730a3 ; | |
| } | |
| </style> | |
| </head> | |
| <body class="bg-gray-100"> | |
| <!-- Login Section (shown by default) --> | |
| <div id="loginSection" class="min-h-screen flex items-center justify-center p-4 login-container"> | |
| <div class="bg-white p-8 rounded-lg shadow-xl w-full max-w-md fade-in"> | |
| <div class="text-center mb-8"> | |
| <i class="fas fa-camera-retro text-5xl text-indigo-500 mb-4"></i> | |
| <h1 class="text-3xl font-bold text-gray-800">Welcome to My Gallery</h1> | |
| <p class="text-gray-600 mt-2">Please login to view your photos</p> | |
| </div> | |
| <form id="loginForm" class="space-y-6"> | |
| <div> | |
| <label for="username" class="block text-sm font-medium text-gray-700 mb-1">Username</label> | |
| <input type="text" id="username" required | |
| class="w-full px-4 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-indigo-500"> | |
| </div> | |
| <div> | |
| <label for="password" class="block text-sm font-medium text-gray-700 mb-1">Password</label> | |
| <input type="password" id="password" required | |
| class="w-full px-4 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-indigo-500"> | |
| </div> | |
| <div class="flex items-center justify-between"> | |
| <div class="flex items-center"> | |
| <input id="remember-me" type="checkbox" | |
| class="h-4 w-4 text-indigo-600 focus:ring-indigo-500 border-gray-300 rounded"> | |
| <label for="remember-me" class="ml-2 block text-sm text-gray-700">Remember me</label> | |
| </div> | |
| <a href="#" class="text-sm text-indigo-600 hover:text-indigo-500">Forgot password?</a> | |
| </div> | |
| <button type="submit" | |
| class="w-full bg-indigo-600 text-white py-2 px-4 rounded-md hover:bg-indigo-700 focus:outline-none focus:ring-2 focus:ring-indigo-500 focus:ring-offset-2 transition duration-150 ease-in-out"> | |
| Sign in | |
| </button> | |
| </form> | |
| <div class="mt-6 text-center"> | |
| <p class="text-sm text-gray-600"> | |
| Don't have an account? | |
| <a href="#" class="font-medium text-indigo-600 hover:text-indigo-500">Sign up</a> | |
| </p> | |
| </div> | |
| </div> | |
| </div> | |
| <!-- Gallery Section (hidden by default) --> | |
| <div id="gallerySection" class="hidden min-h-screen p-6 gallery-container"> | |
| <div class="max-w-7xl mx-auto"> | |
| <!-- Header --> | |
| <div class="flex flex-col md:flex-row md:items-center md:justify-between mb-8"> | |
| <div class="mb-4 md:mb-0"> | |
| <h1 class="text-3xl font-bold text-gray-800">My Gallery</h1> | |
| <p class="text-gray-600">Your personal photo collection</p> | |
| </div> | |
| <div class="flex items-center space-x-4"> | |
| <button id="themeToggle" class="flex items-center space-x-2 bg-white px-4 py-2 rounded-md shadow-sm hover:bg-gray-50"> | |
| <i class="fas fa-moon"></i> | |
| <span>Dark Mode</span> | |
| </button> | |
| <div class="relative"> | |
| <button id="viewToggle" class="flex items-center space-x-2 bg-white px-4 py-2 rounded-md shadow-sm hover:bg-gray-50"> | |
| <i class="fas fa-th"></i> | |
| <span>Grid View</span> | |
| </button> | |
| </div> | |
| <button id="logoutBtn" class="flex items-center space-x-2 bg-red-50 text-red-600 px-4 py-2 rounded-md shadow-sm hover:bg-red-100"> | |
| <i class="fas fa-sign-out-alt"></i> | |
| <span>Logout</span> | |
| </button> | |
| </div> | |
| </div> | |
| <!-- Gallery Content --> | |
| <div id="galleryContent" class="grid-view fade-in"> | |
| <!-- Sample images will be loaded here --> | |
| </div> | |
| </div> | |
| </div> | |
| <!-- Modal for viewing images --> | |
| <div id="imageModal" class="modal"> | |
| <span class="close-modal">×</span> | |
| <div class="modal-content"> | |
| <img id="modalImage" src="" alt=""> | |
| <div class="text-center mt-4 text-white"> | |
| <button id="downloadFromModal" class="bg-blue-500 hover:bg-blue-600 text-white px-4 py-2 rounded"> | |
| <i class="fas fa-download mr-2"></i>Download | |
| </button> | |
| </div> | |
| </div> | |
| </div> | |
| <script> | |
| // Sample image data with real Unsplash images | |
| const sampleImages = [ | |
| { id: 1, title: "Mountain View", url: "https://images.unsplash.com/photo-1464822759023-fed622ff2c3b?ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D&auto=format&fit=crop&w=600&h=400&q=80", date: "2023-05-15" }, | |
| { id: 2, title: "Ocean Sunset", url: "https://images.unsplash.com/photo-1505118380757-91f5f5632de0?ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D&auto=format&fit=crop&w=600&h=400&q=80", date: "2023-06-22" }, | |
| { id: 3, title: "City Lights", url: "https://images.unsplash.com/photo-1477959858617-67f85cf4f1df?ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D&auto=format&fit=crop&w=600&h=400&q=80", date: "2023-07-10" }, | |
| { id: 4, title: "Forest Path", url: "https://images.unsplash.com/photo-1448375240586-882707db888b?ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D&auto=format&fit=crop&w=600&h=400&q=80", date: "2023-04-05" }, | |
| { id: 5, title: "Desert Landscape", url: "https://images.unsplash.com/photo-1509316785289-025f5b8b4db1?ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D&auto=format&fit=crop&w=600&h=400&q=80", date: "2023-03-18" }, | |
| { id: 6, title: "Autumn Colors", url: "https://images.unsplash.com/photo-1504208434309-cb69f4fe52b0?ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D&auto=format&fit=crop&w=600&h=400&q=80", date: "2023-10-12" }, | |
| { id: 7, title: "Winter Wonderland", url: "https://images.unsplash.com/photo-1483728642387-6c3bdd6c93e5?ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D&auto=format&fit=crop&w=600&h=400&q=80", date: "2023-01-30" }, | |
| { id: 8, title: "Spring Flowers", url: "https://images.unsplash.com/photo-1490750967868-88aa4486c946?ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D&auto=format&fit=crop&w=600&h=400&q=80", date: "2023-05-02" }, | |
| { id: 9, title: "Wildlife", url: "https://images.unsplash.com/photo-1470114716159-e389f8712fda?ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D&auto=format&fit=crop&w=600&h=400&q=80", date: "2023-08-19" }, | |
| { id: 10, title: "Architecture", url: "https://images.unsplash.com/photo-1487958449943-2429e8be8625?ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D&auto=format&fit=crop&w=600&h=400&q=80", date: "2023-09-25" }, | |
| { id: 11, title: "Beach Day", url: "https://images.unsplash.com/photo-1507525428034-b723cf961d3e?ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D&auto:format&fit=crop&w=600&h=400&q=80", date: "2023-07-28" }, | |
| { id: 12, title: "Waterfall", url: "https://images.unsplash.com/photo-1511497584788-876760111969?ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D&auto:format&fit=crop&w=600&h=400&q=80", date: "2023-06-14" } | |
| ]; | |
| // DOM elements | |
| const loginForm = document.getElementById('loginForm'); | |
| const loginSection = document.getElementById('loginSection'); | |
| const gallerySection = document.getElementById('gallerySection'); | |
| const galleryContent = document.getElementById('galleryContent'); | |
| const viewToggle = document.getElementById('viewToggle'); | |
| const themeToggle = document.getElementById('themeToggle'); | |
| const logoutBtn = document.getElementById('logoutBtn'); | |
| const modal = document.getElementById('imageModal'); | |
| const modalImage = document.getElementById('modalImage'); | |
| const closeModal = document.querySelector('.close-modal'); | |
| const downloadFromModal = document.getElementById('downloadFromModal'); | |
| // Current view mode (true for grid, false for list) | |
| let isGridView = true; | |
| // Current theme mode (false for light, true for dark) | |
| let isDarkMode = false; | |
| // Current image being viewed/downloaded | |
| let currentImageUrl = ''; | |
| let currentImageTitle = ''; | |
| // Function to download an image | |
| function downloadImage(imageUrl, imageTitle) { | |
| fetch(imageUrl) | |
| .then(response => response.blob()) | |
| .then(blob => { | |
| const url = window.URL.createObjectURL(blob); | |
| const a = document.createElement('a'); | |
| a.href = url; | |
| a.download = imageTitle || 'image'; | |
| document.body.appendChild(a); | |
| a.click(); | |
| window.URL.revokeObjectURL(url); | |
| document.body.removeChild(a); | |
| }) | |
| .catch(() => { | |
| // Fallback method if fetch fails (CORS issues) | |
| const a = document.createElement('a'); | |
| a.href = imageUrl; | |
| a.download = imageTitle || 'image'; | |
| document.body.appendChild(a); | |
| a.click(); | |
| document.body.removeChild(a); | |
| }); | |
| } | |
| // Function to view an image in modal | |
| function viewImage(imageUrl, imageTitle) { | |
| currentImageUrl = imageUrl; | |
| currentImageTitle = imageTitle; | |
| modalImage.src = imageUrl; | |
| modalImage.alt = imageTitle; | |
| modal.style.display = 'flex'; | |
| } | |
| // Load gallery images | |
| function loadGalleryImages() { | |
| galleryContent.innerHTML = ''; | |
| sampleImages.forEach(image => { | |
| const item = document.createElement('div'); | |
| item.className = 'gallery-item bg-white rounded-lg overflow-hidden shadow-md'; | |
| if (isGridView) { | |
| item.innerHTML = ` | |
| <img src="${image.url}" alt="${image.title}" class="w-full h-48 object-cover cursor-pointer" onclick="viewImage('${image.url}', '${image.title}')"> | |
| <div class="p-4"> | |
| <h3 class="font-semibold text-lg mb-1">${image.title}</h3> | |
| <p class="text-gray-500 text-sm">${image.date}</p> | |
| <div class="mt-2 flex space-x-2"> | |
| <button onclick="viewImage('${image.url}', '${image.title}')" class="text-blue-500 hover:text-blue-700"> | |
| <i class="fas fa-eye"></i> View | |
| </button> | |
| <button onclick="downloadImage('${image.url}', '${image.title}')" class="text-green-500 hover:text-green-700"> | |
| <i class="fas fa-download"></i> Download | |
| </button> | |
| </div> | |
| </div> | |
| `; | |
| } else { | |
| item.innerHTML = ` | |
| <img src="${image.url}" alt="${image.title}" class="rounded-l-lg cursor-pointer" onclick="viewImage('${image.url}', '${image.title}')"> | |
| <div class="item-info p-4 flex-grow"> | |
| <h3 class="font-semibold text-lg mb-1">${image.title}</h3> | |
| <p class="text-gray-500 text-sm mb-2">${image.date}</p> | |
| <div class="flex space-x-2"> | |
| <button onclick="viewImage('${image.url}', '${image.title}')" class="text-blue-500 hover:text-blue-700"> | |
| <i class="fas fa-eye"></i> View | |
| </button> | |
| <button onclick="downloadImage('${image.url}', '${image.title}')" class="text-green-500 hover:text-green-700"> | |
| <i class="fas fa-download"></i> Download | |
| </button> | |
| <button class="text-red-500 hover:text-red-700"> | |
| <i class="fas fa-trash"></i> Delete | |
| </button> | |
| </div> | |
| </div> | |
| `; | |
| } | |
| galleryContent.appendChild(item); | |
| }); | |
| } | |
| // Toggle view mode | |
| function toggleViewMode() { | |
| isGridView = !isGridView; | |
| if (isGridView) { | |
| galleryContent.classList.remove('list-view'); | |
| galleryContent.classList.add('grid-view'); | |
| viewToggle.innerHTML = '<i class="fas fa-th"></i><span>Grid View</span>'; | |
| } else { | |
| galleryContent.classList.remove('grid-view'); | |
| galleryContent.classList.add('list-view'); | |
| viewToggle.innerHTML = '<i class="fas fa-list"></i><span>List View</span>'; | |
| } | |
| loadGalleryImages(); | |
| } | |
| // Toggle theme mode | |
| function toggleThemeMode() { | |
| isDarkMode = !isDarkMode; | |
| if (isDarkMode) { | |
| document.documentElement.classList.add('dark'); | |
| themeToggle.innerHTML = '<i class="fas fa-sun"></i><span>Light Mode</span>'; | |
| // Store preference in localStorage | |
| localStorage.setItem('darkMode', 'enabled'); | |
| } else { | |
| document.documentElement.classList.remove('dark'); | |
| themeToggle.innerHTML = '<i class="fas fa-moon"></i><span>Dark Mode</span>'; | |
| // Store preference in localStorage | |
| localStorage.setItem('darkMode', 'disabled'); | |
| } | |
| } | |
| // Check for saved theme preference | |
| function checkThemePreference() { | |
| const savedMode = localStorage.getItem('darkMode'); | |
| if (savedMode === 'enabled') { | |
| isDarkMode = true; | |
| document.documentElement.classList.add('dark'); | |
| themeToggle.innerHTML = '<i class="fas fa-sun"></i><span>Light Mode</span>'; | |
| } | |
| } | |
| // Event listeners | |
| loginForm.addEventListener('submit', function(e) { | |
| e.preventDefault(); | |
| const username = document.getElementById('username').value; | |
| const password = document.getElementById('password').value; | |
| // Simple validation (in a real app, you would check against a database) | |
| if (username && password) { | |
| // Hide login, show gallery | |
| loginSection.classList.add('hidden'); | |
| gallerySection.classList.remove('hidden'); | |
| // Load gallery images | |
| loadGalleryImages(); | |
| } else { | |
| alert('Please enter both username and password'); | |
| } | |
| }); | |
| viewToggle.addEventListener('click', toggleViewMode); | |
| themeToggle.addEventListener('click', toggleThemeMode); | |
| logoutBtn.addEventListener('click', function() { | |
| // Hide gallery, show login | |
| gallerySection.classList.add('hidden'); | |
| loginSection.classList.remove('hidden'); | |
| // Reset form | |
| loginForm.reset(); | |
| }); | |
| // Modal events | |
| closeModal.addEventListener('click', function() { | |
| modal.style.display = 'none'; | |
| }); | |
| downloadFromModal.addEventListener('click', function() { | |
| downloadImage(currentImageUrl, currentImageTitle); | |
| }); | |
| // Close modal when clicking outside the image | |
| window.addEventListener('click', function(event) { | |
| if (event.target === modal) { | |
| modal.style.display = 'none'; | |
| } | |
| }); | |
| // Initialize theme preference | |
| checkThemePreference(); | |
| // Make functions available globally for inline event handlers | |
| window.viewImage = viewImage; | |
| window.downloadImage = downloadImage; | |
| </script> | |
| <p style="border-radius: 8px; text-align: center; font-size: 12px; color: #fff; margin-top: 16px;position: fixed; left: 8px; bottom: 8px; z-index: 10; background: rgba(0, 0, 0, 0.8); padding: 4px 8px;">Made with <img src="https://enzostvs-deepsite.hf.space/logo.svg" alt="DeepSite Logo" style="width: 16px; height: 16px; vertical-align: middle;display:inline-block;margin-right:3px;filter:brightness(0) invert(1);"><a href="https://enzostvs-deepsite.hf.space" style="color: #fff;text-decoration: underline;" target="_blank" >DeepSite</a> - 🧬 <a href="https://enzostvs-deepsite.hf.space?remix=Gabszap/gallery-test" style="color: #fff;text-decoration: underline;" target="_blank" >Remix</a></p></body> | |
| </html> |