Add undo support

This commit is contained in:
JeLuF 2023-03-20 22:53:13 +01:00
parent 8aead029a8
commit 0f3a3da5ed
3 changed files with 85 additions and 36 deletions

View File

@ -282,18 +282,16 @@
</div> </div>
<div id="preview" class="col-free"> <div id="preview" class="col-free">
<div id="initial-text">
Type a prompt and press the "Make Image" button.<br/><br/>You can set an "Initial Image" if you want to guide the AI.<br/><br/>
You can also add modifiers like "Realistic", "Pencil Sketch", "ArtStation" etc by browsing through the "Image Modifiers" section
and selecting the desired modifiers.<br/><br/>
Click "Image Settings" for additional settings like seed, image size, number of images to generate etc.<br/><br/>Enjoy! :)
</div>
<div id="preview-content"> <div id="preview-content">
<div id="preview-tools"> <div id="preview-tools" class="displayNone">
<button id="clear-all-previews" class="secondaryButton"><i class="fa-solid fa-trash-can icon"></i> Clear All</button> <button id="clear-all-previews" class="secondaryButton"><i class="fa-solid fa-trash-can icon"></i> Clear All</button>
<button class="tertiaryButton" id="show-download-popup"><i class="fa-solid fa-download"></i> Download images</button> <button class="tertiaryButton" id="show-download-popup"><i class="fa-solid fa-download"></i> Download images</button>
<div class="display-settings"> <div class="display-settings">
<button id="undo" class="displayNone tertiaryButton">
Undo <i class="fa-solid fa-rotate-left icon"></i>
<span class="simple-tooltip left">Undo last remove</span>
</button>
<span class="auto-scroll"></span> <!-- hack for Rabbit Hole update --> <span class="auto-scroll"></span> <!-- hack for Rabbit Hole update -->
<button id="auto_scroll_btn" class="tertiaryButton"> <button id="auto_scroll_btn" class="tertiaryButton">
<i class="fa-solid fa-arrows-up-to-line icon"></i> <i class="fa-solid fa-arrows-up-to-line icon"></i>
@ -318,6 +316,12 @@
<div class="clearfix" style="clear: both;"></div> <div class="clearfix" style="clear: both;"></div>
</div> </div>
</div> </div>
<div id="initial-text">
Type a prompt and press the "Make Image" button.<br/><br/>You can set an "Initial Image" if you want to guide the AI.<br/><br/>
You can also add modifiers like "Realistic", "Pencil Sketch", "ArtStation" etc by browsing through the "Image Modifiers" section
and selecting the desired modifiers.<br/><br/>
Click "Image Settings" for additional settings like seed, image size, number of images to generate etc.<br/><br/>Enjoy! :)
</div>
</div> </div>
</div> </div>

View File

@ -559,7 +559,6 @@ div.img-preview img {
float: right; float: right;
} }
#preview-tools { #preview-tools {
display: none;
padding: 4pt; padding: 4pt;
} }
#preview-tools .display-settings .dropdown-content { #preview-tools .display-settings .dropdown-content {

View File

@ -83,6 +83,10 @@ const processOrder = document.querySelector('#process_order_toggle')
let imagePreview = document.querySelector("#preview") let imagePreview = document.querySelector("#preview")
let imagePreviewContent = document.querySelector("#preview-content") let imagePreviewContent = document.querySelector("#preview-content")
let undoButton = document.querySelector("#undo")
let undoBuffer = []
imagePreview.addEventListener('drop', function(ev) { imagePreview.addEventListener('drop', function(ev) {
const data = ev.dataTransfer?.getData("text/plain"); const data = ev.dataTransfer?.getData("text/plain");
if (!data) { if (!data) {
@ -251,6 +255,39 @@ function playSound() {
} }
} }
function undoableRemove(element, doubleUndo=false) {
let data = { 'element': element, 'parent': element.parentNode, 'prev': element.previousSibling, 'next': element.nextSibling, 'doubleUndo': doubleUndo }
undoBuffer.push(data)
if (undoBuffer.length > 10) {
// Remove item from memory and also remove it from the data structures
let item = undoBuffer.shift()
htmlTaskMap.delete(item.element)
item.element.querySelectorAll('[data-imagecounter]').forEach( (img) => { delete imageRequest[img.dataset['imagecounter']] })
}
element.remove()
if (undoBuffer.length != 0) {
undoButton.classList.remove('displayNone')
}
}
function undoRemove() {
let data = undoBuffer.pop()
if (data.next == null) {
data.parent.appendChild(data.element)
} else {
data.parent.insertBefore(data.element, data.next)
}
if (data.doubleUndo) {
undoRemove()
}
if (undoBuffer.length == 0) {
undoButton.classList.add('displayNone')
}
updateInitialText()
}
undoButton.addEventListener('click', () => { undoRemove() })
function showImages(reqBody, res, outputContainer, livePreview) { function showImages(reqBody, res, outputContainer, livePreview) {
let imageItemElements = outputContainer.querySelectorAll('.imgItem') let imageItemElements = outputContainer.querySelectorAll('.imgItem')
if(typeof res != 'object') return if(typeof res != 'object') return
@ -290,21 +327,19 @@ function showImages(reqBody, res, outputContainer, livePreview) {
const imageRemoveBtn = imageItemElem.querySelector('.imgPreviewItemClearBtn') const imageRemoveBtn = imageItemElem.querySelector('.imgPreviewItemClearBtn')
let parentTaskContainer = imageRemoveBtn.closest('.imageTaskContainer') let parentTaskContainer = imageRemoveBtn.closest('.imageTaskContainer')
imageRemoveBtn.addEventListener('click', (e) => { imageRemoveBtn.addEventListener('click', (e) => {
shiftOrConfirm(e, "Remove the image from the results?", () => { undoableRemove(imageItemElem)
imageItemElem.style.display = 'none' let allHidden = true;
let allHidden = true; let children = parentTaskContainer.querySelectorAll('.imgItem');
let children = parentTaskContainer.querySelectorAll('.imgItem'); for(let x = 0; x < children.length; x++) {
for(let x = 0; x < children.length; x++) { let child = children[x];
let child = children[x]; if(child.style.display != "none") {
if(child.style.display != "none") { allHidden = false;
allHidden = false;
}
} }
if(allHidden === true) { }
const req = htmlTaskMap.get(parentTaskContainer) if(allHidden === true) {
if(!req.isProcessing || req.batchesDone == req.batchCount) {parentTaskContainer.parentNode.removeChild(parentTaskContainer)} const req = htmlTaskMap.get(parentTaskContainer)
} if(!req.isProcessing || req.batchesDone == req.batchCount) { undoableRemove(parentTaskContainer, true) }
}) }
}) })
} }
const imageElem = imageItemElem.querySelector('img') const imageElem = imageItemElem.querySelector('img')
@ -566,7 +601,7 @@ function makeImage() {
})) }))
newTaskRequests.forEach(createTask) newTaskRequests.forEach(createTask)
initialText.style.display = 'none' updateInitialText()
} }
async function onIdle() { async function onIdle() {
@ -864,7 +899,7 @@ async function onTaskStart(task) {
setStatus('request', 'fetching..') setStatus('request', 'fetching..')
renderButtons.style.display = 'flex' renderButtons.style.display = 'flex'
renameMakeImageButton() renameMakeImageButton()
previewTools.style.display = 'block' updateInitialText()
} }
/* Hover effect for the init image in the task list */ /* Hover effect for the init image in the task list */
@ -993,13 +1028,16 @@ function createTask(task) {
task['stopTask'].addEventListener('click', (e) => { task['stopTask'].addEventListener('click', (e) => {
e.stopPropagation() e.stopPropagation()
let question = (task['isProcessing'] ? "Stop this task?" : "Remove this task?") if (task['isProcessing']) {
shiftOrConfirm(e, question, async function(e) { shiftOrConfirm(e, "Stop this task?", async function(e) {
if (task.batchesDone <= 0 || !task.isProcessing) { if (task.batchesDone <= 0 || !task.isProcessing) {
removeTask(taskEntry) removeTask(taskEntry)
} }
abortTask(task) abortTask(task)
}) })
} else {
removeTask(taskEntry)
}
}) })
task['useSettings'] = taskEntry.querySelector('.useSettings') task['useSettings'] = taskEntry.querySelector('.useSettings')
@ -1225,15 +1263,23 @@ async function stopAllTasks() {
}) })
} }
function removeTask(taskToRemove) { function updateInitialText() {
taskToRemove.remove()
if (document.querySelector('.imageTaskContainer') === null) { if (document.querySelector('.imageTaskContainer') === null) {
previewTools.style.display = 'none' if (undoBuffer.length == 0) {
initialText.style.display = 'block' previewTools.classList.add('displayNone')
}
initialText.classList.remove('displayNone')
} else {
initialText.classList.add('displayNone')
previewTools.classList.remove('displayNone')
} }
} }
function removeTask(taskToRemove) {
undoableRemove(taskToRemove)
updateInitialText()
}
clearAllPreviewsBtn.addEventListener('click', (e) => { shiftOrConfirm(e, "Clear all the results and tasks in this window?", async function() { clearAllPreviewsBtn.addEventListener('click', (e) => { shiftOrConfirm(e, "Clear all the results and tasks in this window?", async function() {
await stopAllTasks() await stopAllTasks()