407 lines
13 KiB
HTML
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>Image Archive</title>
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
background-color: black;
color: white;
font-family: sans-serif;
padding: 2rem;
}
h1 {
text-align: center;
margin-bottom: 2rem;
}
.gallery {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(500px, 1fr));
gap: 20px;
}
.gallery img {
width: 100%;
height: auto;
border-radius: 10px;
cursor: pointer;
transition: transform 0.2s ease;
}
.gallery img:hover {
transform: scale(1.02);
}
/* Lightbox styles */
.lightbox {
display: none;
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: rgba(0, 0, 0, 0.9);
justify-content: center;
align-items: center;
flex-direction: column;
z-index: 999;
padding: 20px 0;
}
.lightbox img {
max-width: 90%;
max-height: 80%;
border-radius: 10px;
}
.lightbox .close {
position: absolute;
top: 20px;
right: 30px;
font-size: 30px;
color: white;
cursor: pointer;
}
.arrow {
position: absolute;
top: 50%;
font-size: 40px;
color: white;
cursor: pointer;
user-select: none;
transform: translateY(-50%);
}
.arrow.left {
left: 20px;
}
.arrow.right {
right: 20px;
}
#lightbox-prompt {
color: #ccc;
font-family: monospace;
white-space: pre-wrap;
background: rgba(0, 0, 0, 0.6);
padding: 10px 20px;
border-radius: 10px;
max-width: 80%;
text-align: left;
margin-top: 20px;
max-height: 25vh;
/* NEW: restrict height */
overflow-y: auto;
/* NEW: allow vertical scroll */
}
/* Back button fixed top right */
.home-button {
position: fixed;
top: 20px;
right: 20px;
z-index: 500;
/* lower than lightbox (999) */
}
.favourites-button {
position: fixed;
top: 20px;
right: 120px;
z-index: 500;
}
.favourite-heart {
position: absolute;
top: 20px;
left: 30px;
font-size: 30px;
color: white;
cursor: pointer;
}
.button-link {
background: #333;
color: white;
text-decoration: none;
padding: 10px 20px;
border-radius: 8px;
font-size: 16px;
cursor: pointer;
transition: background 0.3s;
display: inline-block;
text-align: center;
border: none;
}
.button-link:hover {
background: #555;
}
@media (max-width: 600px) {
body {
padding: 1rem;
font-size: 14px;
}
.gallery {
grid-template-columns: repeat(auto-fill, minmax(250px, 1fr));
gap: 12px;
}
.gallery img {
border-radius: 8px;
}
.lightbox .close {
font-size: 36px;
top: 10px;
right: 15px;
}
.arrow {
font-size: 48px;
top: 50%;
}
#lightbox-prompt {
font-size: 14px;
max-width: 90%;
padding: 8px 16px;
max-height: 20vh;
/* smaller height for mobile */
overflow-y: auto;
/* keep scroll on mobile too */
}
.button-link {
font-size: 14px;
padding: 8px 16px;
}
}
</style>
</head>
<body>
<a href="/" class="button-link home-button">Home</a>
<button class="button-link favourites-button" id="favourites-button" onclick="toggleFavouritesView()">Show Favourites</button>
<h1>Image Archive</h1>
<!-- Empty gallery container; images will be loaded incrementally -->
<div class="gallery" id="gallery"></div>
<!-- Lightbox -->
<div class="lightbox" id="lightbox">
<span class="close" onclick="closeLightbox()">&times;</span>
<span class="favourite-heart" id="favourite-heart" onclick="toggleFavourite()">&#9825;</span>
<span class="arrow left" onclick="prevImage()">&#10094;</span>
<img id="lightbox-img" src="" />
<p id="lightbox-prompt"></p>
<span class="arrow right" onclick="nextImage()">&#10095;</span>
</div>
<!-- Pass image filenames from Flask to JS -->
<script>
let allImages = [
{% for image in images %}
{ filename: "{{ image.filename }}", favourited: {{ 'true' if image.favourited else 'false' }} },
{% endfor %}
];
</script>
<script>
const gallery = document.getElementById('gallery');
const batchSize = 9; // images to load per batch
let loadedCount = 0;
let currentIndex = 0;
const detailsCache = {}; // Cache for image details
let showingFavourites = false;
let filteredImages = allImages;
function createImageElement(image) {
const img = document.createElement('img');
img.src = `/images/thumbnails/${image.filename}`;
img.dataset.fullsrc = `/images/${image.filename}`;
img.dataset.filename = image.filename;
img.dataset.favourited = image.favourited;
img.loading = 'lazy';
img.style.cursor = 'pointer';
img.style.borderRadius = '10px';
img.addEventListener('click', () => openLightbox(img));
return img;
}
function loadNextBatch() {
const imagesToLoad = showingFavourites ? filteredImages : allImages;
const nextImages = imagesToLoad.slice(loadedCount, loadedCount + batchSize);
nextImages.forEach(image => {
const imgEl = createImageElement(image);
gallery.appendChild(imgEl);
});
loadedCount += nextImages.length;
}
function renderGallery() {
gallery.innerHTML = '';
loadedCount = 0;
loadNextBatch();
}
function toggleFavouritesView() {
showingFavourites = !showingFavourites;
const button = document.getElementById('favourites-button');
if (showingFavourites) {
filteredImages = allImages.filter(img => img.favourited);
button.textContent = 'Show All';
} else {
filteredImages = allImages;
button.textContent = 'Show Favourites';
}
renderGallery();
}
// Load initial batch
renderGallery();
// Load more images when scrolling near bottom
window.addEventListener('scroll', () => {
const imagesToLoad = showingFavourites ? filteredImages : allImages;
if (loadedCount >= imagesToLoad.length) return; // all loaded
if ((window.innerHeight + window.scrollY) >= (document.body.offsetHeight - 100)) {
loadNextBatch();
}
});
// Get current images in gallery for lightbox navigation
function getGalleryImages() {
return Array.from(gallery.querySelectorAll('img'));
}
function openLightbox(imgEl) {
const images = getGalleryImages();
currentIndex = images.indexOf(imgEl);
showImageAndLoadDetails(currentIndex);
document.getElementById("lightbox").style.display = "flex";
}
function updateFavouriteHeart(isFavourited) {
const heart = document.getElementById('favourite-heart');
heart.innerHTML = isFavourited ? '&#9829;' : '&#9825;'; // solid vs outline heart
heart.style.color = isFavourited ? 'red' : 'white';
}
function toggleFavourite() {
const images = getGalleryImages();
const imgEl = images[currentIndex];
const filename = imgEl.dataset.filename;
let isFavourited = imgEl.dataset.favourited === 'true';
const url = isFavourited ? '/favourites/remove' : '/favourites/add';
fetch(url, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ filename: filename })
})
.then(response => response.json())
.then(data => {
if (data.status === 'success') {
isFavourited = !isFavourited;
imgEl.dataset.favourited = isFavourited;
updateFavouriteHeart(isFavourited);
// Update the main image list
const originalImage = allImages.find(img => img.filename === filename);
if (originalImage) {
originalImage.favourited = isFavourited;
}
}
});
}
function showImageAndLoadDetails(index) {
const images = getGalleryImages();
const imgEl = images[index];
const filename = imgEl.dataset.filename;
const fullsrc = imgEl.dataset.fullsrc;
const isFavourited = imgEl.dataset.favourited === 'true';
document.getElementById("lightbox-img").src = fullsrc;
updateFavouriteHeart(isFavourited);
if (detailsCache[filename]) {
document.getElementById("lightbox-prompt").textContent =
`Model:${detailsCache[filename].model} - Created:${detailsCache[filename].date}\n\n${detailsCache[filename].prompt}`;
} else {
document.getElementById("lightbox-prompt").textContent = "Loading…";
fetch(`/image-details/${encodeURIComponent(filename)}`)
.then(response => {
if (!response.ok) throw new Error("Network response was not ok");
return response.json();
})
.then(data => {
detailsCache[filename] = data; // Cache the data
document.getElementById("lightbox-prompt").textContent =
`Model:${data.model} - Created:${data.date}\n\n${data.prompt}`;
})
.catch(() => {
document.getElementById("lightbox-prompt").textContent = "Couldnt load details.";
});
}
}
function nextImage() {
const images = getGalleryImages();
const imagesToLoad = showingFavourites ? filteredImages : allImages;
if (currentIndex + 1 >= images.length && loadedCount < imagesToLoad.length) {
loadNextBatch();
// Wait briefly to ensure DOM updates
setTimeout(() => {
const updatedImages = getGalleryImages();
if (currentIndex + 1 < updatedImages.length) {
currentIndex++;
showImageAndLoadDetails(currentIndex);
}
}, 100);
} else {
currentIndex = (currentIndex + 1) % images.length;
showImageAndLoadDetails(currentIndex);
}
}
function prevImage() {
const images = getGalleryImages();
currentIndex = (currentIndex - 1 + images.length) % images.length;
showImageAndLoadDetails(currentIndex);
}
function closeLightbox() {
document.getElementById("lightbox").style.display = "none";
if (showingFavourites) {
// Refresh the gallery if a favourite was removed
const currentImage = getGalleryImages()[currentIndex];
const wasFavourited = currentImage.dataset.favourited === 'true';
const originalImage = allImages.find(img => img.filename === currentImage.dataset.filename);
if (originalImage && !originalImage.favourited) {
renderGallery();
}
}
}
</script>
</body>
</html>