diff --git a/ui/index.html b/ui/index.html
index ce769e1d..f4298c70 100644
--- a/ui/index.html
+++ b/ui/index.html
@@ -292,18 +292,16 @@
-
- Type a prompt and press the "Make Image" button.
You can set an "Initial Image" if you want to guide the AI.
- You can also add modifiers like "Realistic", "Pencil Sketch", "ArtStation" etc by browsing through the "Image Modifiers" section
- and selecting the desired modifiers.
- Click "Image Settings" for additional settings like seed, image size, number of images to generate etc.
Enjoy! :)
-
diff --git a/ui/media/css/main.css b/ui/media/css/main.css
index 987bbcd0..88b8fc58 100644
--- a/ui/media/css/main.css
+++ b/ui/media/css/main.css
@@ -558,7 +558,6 @@ div.img-preview img {
float: right;
}
#preview-tools {
- display: none;
padding: 4pt;
}
#preview-tools .display-settings .dropdown-content {
diff --git a/ui/media/js/main.js b/ui/media/js/main.js
index 65aa643f..781bb480 100644
--- a/ui/media/js/main.js
+++ b/ui/media/js/main.js
@@ -88,6 +88,11 @@ const processOrder = document.querySelector('#process_order_toggle')
let imagePreview = document.querySelector("#preview")
let imagePreviewContent = document.querySelector("#preview-content")
+
+let undoButton = document.querySelector("#undo")
+let undoBuffer = []
+const UNDO_LIMIT = 20
+
imagePreview.addEventListener('drop', function(ev) {
const data = ev.dataTransfer?.getData("text/plain");
if (!data) {
@@ -256,6 +261,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 > UNDO_LIMIT) {
+ // 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) {
let imageItemElements = outputContainer.querySelectorAll('.imgItem')
if(typeof res != 'object') return
@@ -295,21 +333,19 @@ function showImages(reqBody, res, outputContainer, livePreview) {
const imageRemoveBtn = imageItemElem.querySelector('.imgPreviewItemClearBtn')
let parentTaskContainer = imageRemoveBtn.closest('.imageTaskContainer')
imageRemoveBtn.addEventListener('click', (e) => {
- shiftOrConfirm(e, "Remove the image from the results?", () => {
- imageItemElem.style.display = 'none'
- let allHidden = true;
- let children = parentTaskContainer.querySelectorAll('.imgItem');
- for(let x = 0; x < children.length; x++) {
- let child = children[x];
- if(child.style.display != "none") {
- allHidden = false;
- }
+ undoableRemove(imageItemElem)
+ let allHidden = true;
+ let children = parentTaskContainer.querySelectorAll('.imgItem');
+ for(let x = 0; x < children.length; x++) {
+ let child = children[x];
+ if(child.style.display != "none") {
+ allHidden = false;
}
- if(allHidden === true) {
- const req = htmlTaskMap.get(parentTaskContainer)
- if(!req.isProcessing || req.batchesDone == req.batchCount) {parentTaskContainer.parentNode.removeChild(parentTaskContainer)}
- }
- })
+ }
+ if(allHidden === true) {
+ const req = htmlTaskMap.get(parentTaskContainer)
+ if(!req.isProcessing || req.batchesDone == req.batchCount) { undoableRemove(parentTaskContainer, true) }
+ }
})
}
const imageElem = imageItemElem.querySelector('img')
@@ -571,7 +607,7 @@ function makeImage() {
}))
newTaskRequests.forEach(createTask)
- initialText.style.display = 'none'
+ updateInitialText()
}
async function onIdle() {
@@ -869,7 +905,7 @@ async function onTaskStart(task) {
setStatus('request', 'fetching..')
renderButtons.style.display = 'flex'
renameMakeImageButton()
- previewTools.style.display = 'block'
+ updateInitialText()
}
/* Hover effect for the init image in the task list */
@@ -1001,13 +1037,16 @@ function createTask(task) {
task['stopTask'].addEventListener('click', (e) => {
e.stopPropagation()
- let question = (task['isProcessing'] ? "Stop this task?" : "Remove this task?")
- shiftOrConfirm(e, question, async function(e) {
- if (task.batchesDone <= 0 || !task.isProcessing) {
- removeTask(taskEntry)
- }
- abortTask(task)
- })
+ if (task['isProcessing']) {
+ shiftOrConfirm(e, "Stop this task?", async function(e) {
+ if (task.batchesDone <= 0 || !task.isProcessing) {
+ removeTask(taskEntry)
+ }
+ abortTask(task)
+ })
+ } else {
+ removeTask(taskEntry)
+ }
})
task['useSettings'] = taskEntry.querySelector('.useSettings')
@@ -1217,15 +1256,23 @@ async function stopAllTasks() {
})
}
-function removeTask(taskToRemove) {
- taskToRemove.remove()
-
+function updateInitialText() {
if (document.querySelector('.imageTaskContainer') === null) {
- previewTools.style.display = 'none'
- initialText.style.display = 'block'
+ if (undoBuffer.length == 0) {
+ 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() {
await stopAllTasks()