diff --git a/css/art/genAI.css b/css/art/genAI.css index 5e10009b5f18d3e53d59b16bd05bb2a83947c4e5..ddc5482cb17605044a76d698aaf1253c872fa30a 100644 --- a/css/art/genAI.css +++ b/css/art/genAI.css @@ -22,7 +22,6 @@ height: 100%; min-width: 100px; min-height: 100px; - cursor: pointer; float: right; border: 3px hidden; object-fit: contain; @@ -32,6 +31,68 @@ display: block; } +.ai-toolbar { + display: none; + position: absolute; + right: 1rem; + top: 1rem; +} + +.ai-art-container:hover .ai-toolbar { + display: flex; + flex-direction: column; +} + + +.ai-toolbar button { + /* position: absolute; */ + min-width: 2rem; + min-height: 2rem; + cursor: pointer; + border: none; + background: none; +} + +.zoom-in::after { + font-family: "tme-fa-icons"; + content: " \e83c"; /* Zoom in icon */ +} + +.refresh-icon::after { + font-family: "tme-fa-icons"; + content: " \e825"; /* Refresh icon */ +} + +.lightbox { + position: fixed; + top: 0; + width: 100%; + height: 100%; + background: rgba(0 0 0 / 0.2); +} + +.lightbox-background { + width: 100%; + height: 100%; + display: flex; + justify-content: center; + align-items: center; +} + +.lightbox img { + max-width: 80%; + max-height: 80%; +} + +.lightbox .close { + top: 4rem; + right: 4rem; + font-size: 2rem; + background: none; + position: absolute; + border: none; +} + @keyframes spin { 0% { transform: translate(-50%, -50%) rotate(0deg); diff --git a/src/art/artJS.js b/src/art/artJS.js index 5fa703a85ddfbf472fbb3badaa10451086137dd8..7b50e278b5020baae9f98a4227ecfa659bc9f070 100644 --- a/src/art/artJS.js +++ b/src/art/artJS.js @@ -469,30 +469,101 @@ async function renderAIArt(slave, imageSize) { App.Art.aiArtElement = function(slave, imageSize) { const container = document.createElement("div"); container.classList.add("ai-art-container"); + const toolbar = document.createElement('div'); + toolbar.classList.add('ai-toolbar'); + container.appendChild(toolbar); + /** @type {HTMLButtonElement} */ + let refreshButton; + /** @type {HTMLDivElement} */ + let spinner; + /** @type {HTMLButtonElement} */ + let zoomIn; + + /** + * @param {HTMLDivElement} toolbar + * @param {HTMLDivElement} container + */ + function makeZoomIn(toolbar, container) { + zoomIn = document.createElement('button'); + zoomIn.classList.add('zoom-in'); + zoomIn.title = 'Zoom'; + const onZoomInClick = () => { + const imageElement = container.querySelector('.ai-art-image'); + if (imageElement) { + const lightbox = document.createElement('div'); + lightbox.classList.add('lightbox'); + lightbox.classList.add('ui-front'); + // make a seperate background element so that the user can click on the image without lightbox closing + const lightboxBackground = document.createElement('div'); + lightboxBackground.classList.add('lightbox-background'); + lightboxBackground.addEventListener('click', () => { + console.log('background clicked'); + lightbox.remove(); + }); + lightbox.appendChild(lightboxBackground); + // Visible button for exiting, but clicking outside of image should automatically close it anyways + const closeButton = document.createElement('button'); + closeButton.classList.add('close'); + closeButton.innerText = '✕'; + lightboxBackground.appendChild(closeButton); + const lightboxImg = document.createElement('img'); + lightboxImg.src = imageElement.getAttribute('src'); + lightboxBackground.appendChild(lightboxImg); + + document.body.appendChild(lightbox); + } else { + console.error('No image element found to lightbox'); + } + }; + + zoomIn.addEventListener("click", onZoomInClick); + toolbar.appendChild(zoomIn); + } + makeZoomIn(toolbar, container); + + /** + * @param {HTMLDivElement} toolbar + * @param {HTMLDivElement} container + */ + function makeRefreshButton(toolbar, container) { + refreshButton = document.createElement("button"); + refreshButton.innerText = '⟳'; + refreshButton.title = 'Regenerate'; + refreshButton.addEventListener("click", function() { + console.log('clicked listner to refresh button'); + if (!container.classList.contains("refreshing")) { + updateAndRefresh(); + } + }); + toolbar.appendChild(refreshButton); + } + makeRefreshButton(toolbar, container); /** * @param {HTMLDivElement} container */ function makeSpinner(container) { - const spinner = document.createElement("div"); + spinner = document.createElement("div"); spinner.classList.add("spinner"); spinner.innerText = '⟳'; container.appendChild(spinner); } makeSpinner(container); + /** Refresh on click * @param {boolean} retry should we retry image generation or not? */ function refresh(retry) { - renderAIArt(slave, imageSize).then((imgElement) => { - jQuery(container).empty().append(imgElement); - makeSpinner(container); - }).catch(() => { - if (retry) { - updateAndRefresh(); - } - }); + renderAIArt(slave, imageSize) + .then((imgElement) => { + container.querySelector('.ai-art-image')?.remove(); + container.prepend(imgElement); // prepend it before the toolbar and spinner, otherwise you can't see them + }).catch(() => { + if (retry) { + updateAndRefresh(); + } + }); } function updateAndRefresh() { @@ -502,20 +573,13 @@ App.Art.aiArtElement = function(slave, imageSize) { imageGenerator.updateSlave(slave).then(() => { refresh(false); - - container.classList.remove("refreshing"); }).catch(error => { console.error(error); - + }).finally(() => { container.classList.remove("refreshing"); }); } - container.addEventListener("click", function() { - if (!container.classList.contains("refreshing")) { - updateAndRefresh(); - } - }); if (slave.custom.aiImageId === null) { updateAndRefresh();