bulletspace / templates /index.html
ChandimaPrabath's picture
add imgbb cdn
6a2d7ad
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Image Files Gallery</title>
<style>
body {
background-color: #1a1a1a; /* Dark background */
color: #ffffff; /* White text */
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
margin: 0;
padding: 0;
display: flex;
flex-direction: column;
align-items: center;
}
.navbar {
width: 100%;
background-color: #1c1c36; /* Darker navbar background */
padding: 10px;
display: flex;
text-align: center;
justify-content: center;
align-items: center;
box-shadow: 0 2px 5px rgba(0, 0, 0, 0.2);
position: fixed;
top: 0;
left: 0;
z-index: 1000;
}
.navbar h1 {
margin: 0;
font-size: 2em; /* Larger font size */
color: #bd93f9; /* Bright purple */
}
@keyframes colorChange {
0% {
color: rgb(67, 252, 0);
}
50% {
color: rgb(93, 195, 55);
}
100% {
color: rgb(67, 252, 0);
}
}
#version{
width: 130px;
position: relative;
top: -15px;
right: 21px;
font-weight: bolder;
animation: colorChange .5s infinite;
}
.image-container {
width: 100%;
max-width: 1200px;
margin-top: 75px; /* Adjusted margin for fixed navbar and sort button */
display: flex;
flex-wrap: wrap;
justify-content: center;
gap: 20px;
padding: 20px;
scroll-behavior: smooth;
}
.image-item {
background-color: #1f1f2a; /* Darker background */
border-radius: 8px;
overflow: hidden;
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
width: calc(33.33% - 20px); /* Three items per row with gap */
margin-bottom: 20px;
transition: transform 0.3s ease; /* Smooth transform animation */
display: flex; /* Make container flex to align image and metadata */
flex-direction: column; /* Stack image and metadata vertically */
align-items: center; /* Center items horizontally */
position: relative; /* Position for absolute time display */
}
.image-item:hover {
transform: scale(1.05); /* Scale up on hover */
}
.image-item img {
max-width: 100%;
border-radius: 8px;
cursor: pointer;
transition: transform 0.3s ease; /* Smooth transform animation */
}
.image-item:hover img {
transform: scale(1.1); /* Scale up image on hover */
}
.image-item .created-at {
background-color: #20203f; /* Dark background for yaml data */
color: #ffffff;
text-align: center;
padding: 8px 16px;
border-top-left-radius: 8px;
border-top-right-radius: 8px;
width: 100%; /* Full width */
font-size: 0.8em; /* Smaller font size */
position: absolute; /* Position at the top */
top: 0; /* Align to the top */
}
.pre {
max-width: 370px;
overflow: scroll;
}
.spinner {
border: 4px solid rgba(255, 255, 255, 0.3);
border-radius: 50%;
border-top: 4px solid #bd93f9; /* Bright purple */
width: 40px;
height: 40px;
animation: spin 1s linear infinite;
display: none;
margin-top: 20px;
}
.load-more {
top: -20px;
padding: 5px;
margin-bottom: 20px;
background-color: #a2a2a2; /* Bright purple */
color: #ffffff;
border: none;
padding: 10px;
border-radius: 100%;
cursor: pointer;
transition: background-color 0.3s ease, transform 0.2s ease; /* Smooth background and transform animations */
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.2); /* Box shadow for depth */
align-items: center;
display: flex;
}
.load-more:hover {
background-color: #6272a4; /* Darker hover color */
transform: translateY(-2px); /* Lift button slightly on hover */
}
.sort-btn {
margin-top: 85px;
background-color: #a2a2a2;
color: #ffffff;
border: none;
border-radius: 25px;
cursor: pointer;
transition: background-color 0.3s ease, transform 0.2s ease;
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.2);
position: fixed;
right: 4%;
z-index: 1;
}
.sort-btn:hover {
background-color: #6272a4; /* Darker hover color */
transform: translateY(-2px); /* Lift button slightly on hover */
}
.go-to-top-btn {
position: fixed;
bottom: 20px;
right: 4%;
background-color: #a2a2a2; /* Bright purple */
color: #ffffff;
border: none;
border-radius: 25px;
cursor: pointer;
display: none;
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.2);
transition: background-color 0.3s ease, transform 0.2s ease; /* Smooth background and transform animations */
}
.go-to-top-btn:hover {
background-color: #6272a4; /* Darker hover color */
transform: translateY(-2px); /* Lift button slightly on hover */
}
@keyframes spin {
0% { transform: rotate(0deg); }
100% { transform: rotate(360deg); }
}
@media (max-width: 1200px) {
.image-item {
width: calc(50% - 20px); /* Two items per row on smaller screens */
}
}
@media (max-width: 900px) {
.image-item {
width: calc(33.33% - 20px); /* Three items per row on smaller screens */
}
}
@media (max-width: 600px) {
.image-item {
width: calc(50% - 20px); /* Two items per row on smaller screens */
}
.navbar h1 {
font-size: 1.5em; /* Adjusted font size for smaller screens */
}
.image-item .created-at {
font-size: 0.7em; /* Adjusted font size for smaller screens */
}
}
@media (max-width: 400px) {
.image-item {
width: calc(100% - 20px); /* One item per row on very small screens */
}
}
</style>
</head>
<body>
<div class="navbar">
<img style="width:60px; border-radius: 20px; margin-right: 10px;" src="/statics/logo.png" alt="logo">
<h1 style="color: white;">SpaceBullet </h1>
<h1 style="margin-left: 10px;">Gallery</h1>
<version id="version"></version>
</div>
<button class="sort-btn" id="sort-btn" title="Sort"><img style="color: black;" width="30px" src="/statics/sort.svg" alt="sort"></button>
<div class="image-container" id="image-container"></div>
<div class="spinner" id="spinner"></div>
<button class="load-more" id="load-more-btn" title="Load More"><img width="30px" src="/statics/down-arrow.svg" alt="load-more"></button>
<button class="go-to-top-btn" id="go-to-top-btn" title="Go to Top"><img width="30px" src="/statics/up-arrow.svg" alt="Go to the Top"></button>
<script>
function getInfo() {
fetch('/info')
.then(response => {
if (!response.ok) {
throw new Error('Network response was not ok');
}
return response.json();
})
.then(data => {
// Assuming you want to log the retrieved information
console.log('Info retrieved:', data);
const versionElement = document.getElementById('version');
if (versionElement) {
// Extract the 'version' property from the data object
const version = data['version'];
versionElement.textContent = `${version}`;
}
})
.catch(error => {
console.error('Error fetching info:', error);
});
}
// Execute getInfo function when the window loads
window.onload = getInfo;
</script>
<script>
document.addEventListener('DOMContentLoaded', () => {
let allImages = [];
let page = 0;
const pageSize = 20;
let loading = false;
let loadedImageNames = new Set(); // Set to track loaded image names
let sortClicked = false; // Track if sort button has been clicked
async function fetchImages() {
const response = await fetch('/images');
return response.json();
}
function createImageItem(image, yaml) {
const container = document.createElement('div');
container.className = 'image-item';
const img = document.createElement('img');
img.src = `${image}`;
img.alt = image;
img.onerror = () => {
container.style.display = 'none';
};
const pre = document.createElement('pre');
pre.className = 'pre';
const pre_json = JSON.stringify(yaml, null, 2);
if (pre_json === '{}') {
console.log(`YAML data is null for image: ${image}`);
pre.textContent = "Prompt data unavailable";
// Fetch YAML data from /yaml route
fetch(`/yaml/${image}`)
.then(response => {
if (!response.ok) {
throw new Error(`HTTP error! Status: ${response.status}`);
}
return response.json();
})
.then(data => {
if (data) {
// Update the pre element with fetched YAML data
pre.textContent = JSON.stringify(data, null, 2);
} else {
console.log(`No YAML data found for image: ${image}`);
}
})
.catch(error => {
console.error('Error fetching YAML data:', error);
});
} else {
pre.textContent = pre_json;
}
const createdAt = document.createElement('div');
createdAt.className = 'created-at';
const createdAtDate = getImageCreatedDate(image); // Get formatted date from image name
createdAt.textContent = `Generated: ${createdAtDate}`;
container.appendChild(img);
container.appendChild(createdAt);
container.appendChild(pre);
return container;
}
function getImageCreatedDate(imageName) {
// Assuming imageName format: image_YYYYMMDDHHMMSS.jpg
const year = imageName.substr(6, 4);
const month = imageName.substr(10, 2);
const day = imageName.substr(12, 2);
const hour24 = imageName.substr(14, 2);
const minute = imageName.substr(16, 2);
const second = imageName.substr(18, 2);
// Convert 24-hour format to 12-hour format
let hour12 = hour24 % 12;
hour12 = hour12 ? hour12 : 12; // If hour is 0, set it to 12
const period = hour24 >= 12 ? 'PM' : 'AM';
return `${day}/${month}/${year} ${hour12}:${minute}:${second} ${period}`;
}
async function loadImages() {
if (loading) return;
loading = true;
const spinner = document.getElementById('spinner');
spinner.style.display = 'block';
const data = await fetchImages();
allImages = data.image_files.reverse(); // Reverse the order of images
displayImages(allImages.slice(0, pageSize));
spinner.style.display = 'none';
loading = false;
}
function displayImages(images) {
const imageContainer = document.getElementById('image-container');
images.forEach(item => {
if (!loadedImageNames.has(item.image)) { // Check if image is already loaded
const imageItem = createImageItem(item.image, item.yaml);
imageContainer.appendChild(imageItem);
loadedImageNames.add(item.image); // Add image name to loaded set
}
});
}
function sortDisplayImages(images) {
const imageContainer = document.getElementById('image-container');
imageContainer.innerHTML = '';
images.forEach(item => {
if (!loadedImageNames.has(item.image)) { // Check if image is already loaded
const imageItem = createImageItem(item.image, item.yaml);
imageContainer.appendChild(imageItem);
loadedImageNames.add(item.image); // Add image name to loaded set
}
});
}
async function loadMoreImages() {
if (loading) return;
loading = true;
const start = page * pageSize;
const end = start + pageSize;
const images = allImages.slice(start, end);
if (images.length === 0) {
const loadMoreBtn = document.getElementById('load-more-btn');
loadMoreBtn.style.display = 'none';
} else {
displayImages(images);
page++;
}
loading = false;
}
const loadMoreBtn = document.getElementById('load-more-btn');
loadMoreBtn.addEventListener('click', loadMoreImages);
const sortBtn = document.getElementById('sort-btn');
sortBtn.addEventListener('click', () => {
console.log('Sort button clicked');
if (!sortClicked) {
allImages.reverse();
sortDisplayImages(allImages.slice(0, pageSize));
sortClicked = true;
} else {
location.reload(); // Reload the webpage if sort button clicked again
}
});
const goToTopBtn = document.getElementById('go-to-top-btn');
window.addEventListener('scroll', () => {
if (window.scrollY > 300) {
goToTopBtn.style.display = 'block';
} else {
goToTopBtn.style.display = 'none';
}
});
goToTopBtn.addEventListener('click', () => {
window.scrollTo({
top: 0,
behavior: 'smooth'
});
});
loadImages(); // Initial load of images
});
</script>
</body>
</html>