Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 6 additions & 6 deletions index.html
Original file line number Diff line number Diff line change
Expand Up @@ -13,14 +13,14 @@
</head>
<body>
<div id="screensaver-container" class="screensaver hidden">
<img id="screensaver-image1" alt="Screensaver Image" style="opacity: 0;">
<img id="screensaver-image2" alt="Screensaver Image" style="opacity: 0;">
<div class="screensaver-thumbnail-carousel">
<button id="screensaver-thumb-left" class="thumb-nav" aria-label="Previous image">&#9664;</button>
<img id="screensaver-image1" alt="Screensaver Image" style="opacity: 0;">
<img id="screensaver-image2" alt="Screensaver Image" style="opacity: 0;">
<div id="screensaver-thumbnails-wrapper" class="screensaver-thumbnails-wrapper">
<button id="screensaver-thumb-left" class="thumb-nav" aria-label="Scroll thumbnails left">&#9664;</button>
<div id="screensaver-thumbnails" class="screensaver-thumbnails"></div>
<button id="screensaver-thumb-right" class="thumb-nav" aria-label="Next image">&#9654;</button>
<button id="screensaver-thumb-right" class="thumb-nav" aria-label="Scroll thumbnails right">&#9654;</button>
</div>
<div class="screensaver-controls">
<div class="screensaver-controls">
<div class="screensaver-settings">
<label>Prompt:
<textarea id="screensaver-prompt" rows="3"></textarea>
Expand Down
83 changes: 34 additions & 49 deletions screensaver.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,22 +5,22 @@ document.addEventListener("DOMContentLoaded", () => {
const stopButton = document.getElementById("screensaver-exit");
const playPauseButton = document.getElementById("screensaver-playpause");
const saveButton = document.getElementById("screensaver-save");
const copyButton = document.getElementById("screensaver-copy");
const hideButton = document.getElementById("screensaver-hide");
const thumbnailCarousel = document.querySelector('.screensaver-thumbnail-carousel');
const thumbnailContainer = document.getElementById('screensaver-thumbnails');
const thumbLeft = document.getElementById('screensaver-thumb-left');
const thumbRight = document.getElementById('screensaver-thumb-right');
const screensaverImage1 = document.getElementById("screensaver-image1");
const screensaverImage2 = document.getElementById("screensaver-image2");
const copyButton = document.getElementById("screensaver-copy");
const hideButton = document.getElementById("screensaver-hide");
const screensaverImage1 = document.getElementById("screensaver-image1");
const screensaverImage2 = document.getElementById("screensaver-image2");
const promptInput = document.getElementById("screensaver-prompt");
const timerInput = document.getElementById("screensaver-timer");
const aspectSelect = document.getElementById("screensaver-aspect");
const enhanceCheckbox = document.getElementById("screensaver-enhance");
const privateCheckbox = document.getElementById("screensaver-private");
const modelSelect = document.getElementById("screensaver-model");
const transitionDurationInput = document.getElementById("screensaver-transition-duration");
const restartPromptButton = document.getElementById("screensaver-restart-prompt");
const restartPromptButton = document.getElementById("screensaver-restart-prompt");
const thumbnailsWrapper = document.getElementById("screensaver-thumbnails-wrapper");
const thumbnailsContainer = document.getElementById("screensaver-thumbnails");
const thumbLeftButton = document.getElementById("screensaver-thumb-left");
const thumbRightButton = document.getElementById("screensaver-thumb-right");

let screensaverActive = false;
let imageInterval = null;
Expand Down Expand Up @@ -126,8 +126,17 @@ document.addEventListener("DOMContentLoaded", () => {
updateThumbnailHistory();
}

loadScreensaverSettings();
loadImageHistory();
loadScreensaverSettings();
loadImageHistory();

if (thumbLeftButton && thumbRightButton && thumbnailsContainer) {
thumbLeftButton.addEventListener("click", () => {
thumbnailsContainer.scrollBy({ left: -thumbnailsContainer.clientWidth, behavior: "smooth" });
});
thumbRightButton.addEventListener("click", () => {
thumbnailsContainer.scrollBy({ left: thumbnailsContainer.clientWidth, behavior: "smooth" });
});
}

async function fetchImageModels() {
try {
Expand Down Expand Up @@ -303,7 +312,7 @@ document.addEventListener("DOMContentLoaded", () => {
console.log("Current promptHistory length:", promptHistory.length, "Prompts:", promptHistory);
}

function updateThumbnailHistory(scrollToEnd = true) {
function updateThumbnailHistory() {
const thumbnailContainer = document.getElementById('screensaver-thumbnails');
if (!thumbnailContainer) {
console.error("Thumbnail container not found in DOM.");
Expand Down Expand Up @@ -337,13 +346,8 @@ document.addEventListener("DOMContentLoaded", () => {
console.log(`Added thumbnail ${index + 1}/${imageHistory.length} to DOM:`, thumb.src);
});

if (scrollToEnd) {
// keep the view scrolled to the latest thumbnail
thumbnailContainer.scrollTo({ left: thumbnailContainer.scrollWidth, behavior: 'smooth' });
} else {
const selected = thumbnailContainer.querySelector('img.thumbnail.selected');
if (selected) selected.scrollIntoView({ behavior: 'smooth', inline: 'center', block: 'nearest' });
}
// keep the view scrolled to the latest thumbnail
thumbnailContainer.scrollTo({ left: thumbnailContainer.scrollWidth, behavior: 'smooth' });
console.log("Updated thumbnail gallery with", imageHistory.length, "images. DOM count:", thumbnailContainer.children.length);

const offsetWidth = thumbnailContainer.offsetWidth;
Expand All @@ -360,23 +364,23 @@ document.addEventListener("DOMContentLoaded", () => {
const nextImgElement = document.getElementById(`screensaver-${nextImage}`);
currentImgElement.style.opacity = '0';
nextImgElement.onload = () => {
nextImgElement.style.opacity = '1';
currentImage = nextImage;
updateThumbnailHistory(false);
nextImgElement.style.opacity = '1';
currentImage = nextImage;
updateThumbnailHistory();
};
nextImgElement.onerror = () => {
nextImgElement.src = "https://via.placeholder.com/512?text=Image+Failed";
nextImgElement.style.opacity = '1';
currentImage = nextImage;
updateThumbnailHistory(false);
nextImgElement.src = "https://via.placeholder.com/512?text=Image+Failed";
nextImgElement.style.opacity = '1';
currentImage = nextImage;
updateThumbnailHistory();
};
nextImgElement.src = imageUrl;
nextImgElement.alt = "Screensaver Image";
if (nextImgElement.complete && nextImgElement.naturalWidth !== 0) {
nextImgElement.style.opacity = '1';
currentImgElement.style.opacity = '0';
currentImage = nextImage;
updateThumbnailHistory(false);
updateThumbnailHistory();
}
// restart the timer so new generations resume after viewing a historical image
setOrResetImageInterval();
Expand Down Expand Up @@ -506,14 +510,13 @@ document.addEventListener("DOMContentLoaded", () => {
function toggleControls() {
controlsHidden = !controlsHidden;
const controls = document.querySelector('.screensaver-controls');
const thumbnails = thumbnailCarousel;
if (controlsHidden) {
controls.classList.add('hidden-panel');
thumbnails.classList.add('hidden-panel');
thumbnailsWrapper.classList.add('hidden-panel');
hideButton.innerHTML = "🙉";
} else {
controls.classList.remove('hidden-panel');
thumbnails.classList.remove('hidden-panel');
thumbnailsWrapper.classList.remove('hidden-panel');
hideButton.innerHTML = "🙈";
}
window.showToast(controlsHidden ? "Controls hidden" : "Controls visible");
Expand Down Expand Up @@ -698,31 +701,13 @@ document.addEventListener("DOMContentLoaded", () => {
else window.showToast("Start the screensaver first!");
});

if (thumbLeft && thumbRight && thumbnailContainer) {
thumbLeft.addEventListener('click', (e) => {
e.stopPropagation();
thumbnailContainer.scrollBy({ left: -thumbnailContainer.clientWidth / 2, behavior: 'smooth' });
});
thumbRight.addEventListener('click', (e) => {
e.stopPropagation();
thumbnailContainer.scrollBy({ left: thumbnailContainer.clientWidth / 2, behavior: 'smooth' });
});
thumbnailContainer.addEventListener('wheel', (e) => {
if (Math.abs(e.deltaY) > Math.abs(e.deltaX)) {
e.preventDefault();
thumbnailContainer.scrollBy({ left: e.deltaY, behavior: 'smooth' });
}
}, { passive: false });
}

document.addEventListener('keydown', (e) => {
if (e.key === 'Escape' && screensaverActive && controlsHidden) {
e.stopPropagation();
e.preventDefault();
const controls = document.querySelector('.screensaver-controls');
const thumbnails = thumbnailCarousel;
controls.classList.add('hidden-panel');
thumbnails.classList.add('hidden-panel');
thumbnailsWrapper.classList.add('hidden-panel');
}
});

Expand Down
139 changes: 58 additions & 81 deletions styles.css
Original file line number Diff line number Diff line change
Expand Up @@ -790,47 +790,44 @@ body {
transition: opacity var(--transition-duration, 1s) ease;
}

.screensaver-thumbnail-carousel {
position: fixed;
bottom: 0;
left: 50%;
transform: translateX(-50%);
display: flex;
align-items: center;
width: 100%;
max-width: 1200px;
z-index: 2;
gap: 8px;
padding: 10px;
background: rgba(0, 0, 0, 0.7);
scrollbar-width: thin;
}

.screensaver-thumbnails {
flex: 1;
display: flex;
flex-direction: row;
flex-wrap: nowrap;
gap: 12px;
overflow-x: auto;
white-space: nowrap;
scroll-behavior: smooth;
}

.screensaver-thumbnails::-webkit-scrollbar {
height: 8px;
}

.screensaver-thumbnails::-webkit-scrollbar-track {
background: #333333;
border-radius: 4px;
}

.screensaver-thumbnails::-webkit-scrollbar-thumb {
background: #707070;
border-radius: 4px;
}

.screensaver-thumbnails {
position: fixed;
bottom: 0;
left: 0;
right: 0;
display: flex;
flex-direction: row;
flex-wrap: nowrap;
gap: 12px;
overflow-x: auto;
width: 100%;
height: 120px;
padding: 10px;
background: rgba(0, 0, 0, 0.7);
border-radius: 0;
z-index: 2;
transition: opacity 0.3s ease;
scrollbar-width: thin;
scrollbar-color: #707070 #333333;
white-space: nowrap;
direction: ltr;
scroll-behavior: smooth;
}

.screensaver-thumbnails::-webkit-scrollbar {
height: 8px;
}

.screensaver-thumbnails::-webkit-scrollbar-track {
background: #333333;
border-radius: 4px;
}

.screensaver-thumbnails::-webkit-scrollbar-thumb {
background: #707070;
border-radius: 4px;
}

.screensaver-thumbnails img.thumbnail {
width: 160px;
height: 90px;
Expand All @@ -843,37 +840,17 @@ body {
display: inline-block;
opacity: 1;
position: static;
scroll-snap-align: center;
}

.screensaver-thumbnails img.thumbnail:hover {
border: 3px solid #00ffcc;
transform: scale(1.05);
}

.screensaver-thumbnails img.thumbnail.selected {
border: 3px solid #ffcc00;
}

.thumb-nav {
background: rgba(0, 0, 0, 0.6);
color: #fff;
border: none;
cursor: pointer;
padding: 0 12px;
height: 90px;
border-radius: 8px;
font-size: 1.4rem;
display: flex;
align-items: center;
justify-content: center;
transition: background 0.2s;
}

.thumb-nav:hover {
background: rgba(0, 0, 0, 0.8);
}

.screensaver-thumbnails img.thumbnail:hover {
border: 3px solid #00ffcc;
transform: scale(1.05);
}

.screensaver-thumbnails img.thumbnail.selected {
border: 3px solid #ffcc00;
}

.screensaver-controls {
position: fixed;
bottom: 140px;
Expand All @@ -894,17 +871,17 @@ body {
transform: translateX(-50%) scale(1.02);
}

.screensaver:not(:hover) .screensaver-controls,
.screensaver:not(:hover) .screensaver-thumbnail-carousel {
opacity: 0.5;
}

.screensaver-controls.hidden-panel,
.screensaver-thumbnail-carousel.hidden-panel {
opacity: 0;
pointer-events: none;
transform: translateX(-50%) translateY(20px);
}
.screensaver:not(:hover) .screensaver-controls,
.screensaver:not(:hover) .screensaver-thumbnails {
opacity: 0.5;
}
.screensaver-controls.hidden-panel,
.screensaver-thumbnails.hidden-panel {
opacity: 0;
pointer-events: none;
transform: translateX(-50%) translateY(20px);
}

.screensaver-settings {
display: grid;
Expand Down
Loading