Merge pull request #256 from cmdr2/beta

Prompt Queue and Negative Weights
This commit is contained in:
cmdr2 2022-09-28 14:40:23 +05:30 committed by GitHub
commit ed6c59b58a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 289 additions and 109 deletions

View File

@ -4,7 +4,7 @@
<meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="icon" type="image/png" href="/media/favicon-16x16.png" sizes="16x16"> <link rel="icon" type="image/png" href="/media/favicon-16x16.png" sizes="16x16">
<link rel="icon" type="image/png" href="/media/favicon-32x32.png" sizes="32x32"> <link rel="icon" type="image/png" href="/media/favicon-32x32.png" sizes="32x32">
<link rel="stylesheet" href="/media/main.css?v=4"> <link rel="stylesheet" href="/media/main.css?v=10">
<link rel="stylesheet" href="/media/modifier-thumbnails.css?v=1"> <link rel="stylesheet" href="/media/modifier-thumbnails.css?v=1">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.2.0/css/all.min.css"> <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.2.0/css/all.min.css">
<link rel="stylesheet" href="/media/drawingboard.min.css"> <link rel="stylesheet" href="/media/drawingboard.min.css">
@ -15,7 +15,7 @@
<div id="container"> <div id="container">
<div id="top-nav"> <div id="top-nav">
<div id="logo"> <div id="logo">
<h1>Stable Diffusion UI <small>v2.17 <span id="updateBranchLabel"></span></small></h1> <h1>Stable Diffusion UI <small>v2.19 <span id="updateBranchLabel"></span></small></h1>
</div> </div>
<ul id="top-nav-items"> <ul id="top-nav-items">
<li class="dropdown"> <li class="dropdown">
@ -78,7 +78,7 @@
</div> </div>
<button id="makeImage">Make Image</button> <button id="makeImage">Make Image</button>
<button id="stopImage">Stop</button> <button id="stopImage" class="secondaryButton">Stop All</button>
</div> </div>
<div class="line-separator">&nbsp;</div> <div class="line-separator">&nbsp;</div>
@ -153,8 +153,13 @@
<br/> <br/>
<li><b class="settings-subheader">Prompt Settings</b></li>
<li class="pl-5"><label for="negative_prompt">Negative Prompt:</label> <input id="negative_prompt" name="negative_prompt" size="55"></li>
<br/>
<li><b class="settings-subheader">Render Settings</b></li> <li><b class="settings-subheader">Render Settings</b></li>
<li class="pl-5"><input id="stream_image_progress" name="stream_image_progress" type="checkbox"> <label for="stream_image_progress">Show a live preview of the image <small>(consumes more VRAM, slightly slower image generation)</small></label></li> <li class="pl-5"><input id="stream_image_progress" name="stream_image_progress" type="checkbox"> <label for="stream_image_progress">Show a live preview of the image <small>(uses more VRAM, slightly slower image creation)</small></label></li>
<li class="pl-5"><input id="use_face_correction" name="use_face_correction" type="checkbox" checked> <label for="use_face_correction">Fix incorrect faces and eyes <small>(uses GFPGAN)</small></label></li> <li class="pl-5"><input id="use_face_correction" name="use_face_correction" type="checkbox" checked> <label for="use_face_correction">Fix incorrect faces and eyes <small>(uses GFPGAN)</small></label></li>
<li class="pl-5"> <li class="pl-5">
<input id="use_upscale" name="use_upscale" type="checkbox"> <label for="use_upscale">Upscale the image to 4x resolution using </label> <input id="use_upscale" name="use_upscale" type="checkbox"> <label for="use_upscale">Upscale the image to 4x resolution using </label>
@ -185,15 +190,11 @@
</div> </div>
<div id="preview" class="col-free"> <div id="preview" class="col-free">
<div id="preview-prompt"> <div id="initial-text">
<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 "Advanced Settings" for additional settings like seed, image size, number of images to generate etc.<br/><br/>Enjoy! :)
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 "Advanced Settings" for additional settings like seed, image size, number of images to generate etc.<br/><br/>Enjoy! :)
</div>
</div> </div>
<div id="preview-tools">
<div id="outputMsg"></div> <button id="clear-all-previews" class="secondaryButton"><i class="fa-solid fa-trash-can"></i> Clear All</button>
<div id="progressBar"></div>
<div id="current-images" class="img-preview">
</div> </div>
</div> </div>
</div> </div>
@ -212,7 +213,7 @@
</div> </div>
</body> </body>
<script src="media/main.js?v=6"></script> <script src="media/main.js?v=14"></script>
<script> <script>
async function init() { async function init() {
await loadModifiers() await loadModifiers()

View File

@ -54,7 +54,7 @@ label {
.editor-slider { .editor-slider {
vertical-align: middle; vertical-align: middle;
} }
#outputMsg { .outputMsg {
font-size: small; font-size: small;
padding-bottom: 3pt; padding-bottom: 3pt;
} }
@ -165,7 +165,7 @@ label {
display: none; display: none;
} }
#stopImage:hover { #stopImage:hover {
background: rgb(214, 32, 0); background: rgb(177, 27, 0);
} }
.flex-container { .flex-container {
display: flex; display: flex;
@ -174,7 +174,7 @@ label {
flex: 50%; flex: 50%;
} }
.col-fixed-10 { .col-fixed-10 {
flex: 0 0 400pt; flex: 0 0 380pt;
} }
.col-free { .col-free {
flex: 1; flex: 1;
@ -212,7 +212,7 @@ label {
padding-bottom: 10pt; padding-bottom: 10pt;
} }
#preview { #preview {
margin-left: 20pt; margin-left: 10pt;
} }
img { img {
box-shadow: 0 4px 8px 0 rgba(0, 0, 0, 0.15), 0 6px 20px 0 rgba(0, 0, 0, 0.15); box-shadow: 0 4px 8px 0 rgba(0, 0, 0, 0.15), 0 6px 20px 0 rgba(0, 0, 0, 0.15);
@ -246,7 +246,7 @@ img {
padding-left: 2pt; padding-left: 2pt;
font-size: 10pt; font-size: 10pt;
} }
#preview-prompt { .preview-prompt {
font-size: 16pt; font-size: 16pt;
margin-bottom: 10pt; margin-bottom: 10pt;
} }
@ -365,4 +365,48 @@ img {
} }
.dropdown:hover .dropdown-content { .dropdown:hover .dropdown-content {
display: block; display: block;
}
.imageTaskContainer {
border: 1px solid #333;
margin-bottom: 10pt;
padding: 5pt;
border-radius: 5pt;
box-shadow: 0 20px 28px 0 rgba(0, 0, 0, 0.15), 0 6px 20px 0 rgba(0, 0, 0, 0.15);
}
.taskStatusLabel {
float: left;
font-size: 8pt;
background:rgb(44, 45, 48);
border: 1px solid rgb(61, 62, 66);
padding: 2pt 4pt;
border-radius: 2pt;
margin-right: 5pt;
}
.activeTaskLabel {
background:rgb(0, 90, 30);
border: 1px solid rgb(0, 75, 19);
color:rgb(204, 255, 217)
}
.secondaryButton {
background: rgb(132, 8, 0);
border: 1px solid rgb(122, 29, 0);
color: rgb(255, 221, 255);
padding: 3pt 6pt;
border-radius: 5px;
}
.secondaryButton:hover {
background: rgb(177, 27, 0);
}
.stopTask {
float: right;
}
#preview-tools {
display: none;
padding: 4pt;
}
.taskConfig {
font-size: 10pt;
color: #aaa;
margin-bottom: 5pt;
} }

View File

@ -18,6 +18,7 @@ const IMAGE_REGEX = new RegExp('data:image/[A-Za-z]+;base64')
let sessionId = new Date().getTime() let sessionId = new Date().getTime()
let promptField = document.querySelector('#prompt') let promptField = document.querySelector('#prompt')
let negativePromptField = document.querySelector('#negative_prompt')
let numOutputsTotalField = document.querySelector('#num_outputs_total') let numOutputsTotalField = document.querySelector('#num_outputs_total')
let numOutputsParallelField = document.querySelector('#num_outputs_parallel') let numOutputsParallelField = document.querySelector('#num_outputs_parallel')
let numInferenceStepsField = document.querySelector('#num_inference_steps') let numInferenceStepsField = document.querySelector('#num_inference_steps')
@ -57,6 +58,10 @@ let initImagePreviewContainer = document.querySelector('#init_image_preview_cont
let initImageClearBtn = document.querySelector('.init_image_clear') let initImageClearBtn = document.querySelector('.init_image_clear')
let promptStrengthContainer = document.querySelector('#prompt_strength_container') let promptStrengthContainer = document.querySelector('#prompt_strength_container')
let initialText = document.querySelector("#initial-text")
let previewTools = document.querySelector("#preview-tools")
let clearAllPreviewsBtn = document.querySelector("#clear-all-previews")
// let maskSetting = document.querySelector('#editor-inputs-mask_setting') // let maskSetting = document.querySelector('#editor-inputs-mask_setting')
// let maskImagePreviewContainer = document.querySelector('#mask_preview_container') // let maskImagePreviewContainer = document.querySelector('#mask_preview_container')
// let maskImageClearBtn = document.querySelector('#mask_clear') // let maskImageClearBtn = document.querySelector('#mask_clear')
@ -66,18 +71,19 @@ let editorModifierEntries = document.querySelector('#editor-modifiers-entries')
let editorModifierTagsList = document.querySelector('#editor-inputs-tags-list') let editorModifierTagsList = document.querySelector('#editor-inputs-tags-list')
let editorTagsContainer = document.querySelector('#editor-inputs-tags-container') let editorTagsContainer = document.querySelector('#editor-inputs-tags-container')
let imagePreview = document.querySelector("#preview")
let previewImageField = document.querySelector('#preview-image') let previewImageField = document.querySelector('#preview-image')
previewImageField.onchange = () => changePreviewImages(previewImageField.value); previewImageField.onchange = () => changePreviewImages(previewImageField.value);
let modifierCardSizeSlider = document.querySelector('#modifier-card-size-slider') let modifierCardSizeSlider = document.querySelector('#modifier-card-size-slider')
modifierCardSizeSlider.onchange = () => resizeModifierCards(modifierCardSizeSlider.value); modifierCardSizeSlider.onchange = () => resizeModifierCards(modifierCardSizeSlider.value);
let previewPrompt = document.querySelector('#preview-prompt') // let previewPrompt = document.querySelector('#preview-prompt')
let showConfigToggle = document.querySelector('#configToggleBtn') let showConfigToggle = document.querySelector('#configToggleBtn')
// let configBox = document.querySelector('#config') // let configBox = document.querySelector('#config')
let outputMsg = document.querySelector('#outputMsg') // let outputMsg = document.querySelector('#outputMsg')
let progressBar = document.querySelector("#progressBar") // let progressBar = document.querySelector("#progressBar")
let soundToggle = document.querySelector('#sound_toggle') let soundToggle = document.querySelector('#sound_toggle')
@ -108,8 +114,10 @@ let serverStatus = 'offline'
let activeTags = [] let activeTags = []
let modifiers = [] let modifiers = []
let lastPromptUsed = '' let lastPromptUsed = ''
let taskStopped = true let bellPending = false
let batchesDone = 0
let taskQueue = []
let currentTask = null
const modifierThumbnailPath = 'media/modifier-thumbnails'; const modifierThumbnailPath = 'media/modifier-thumbnails';
const activeCardClass = 'modifier-card-active'; const activeCardClass = 'modifier-card-active';
@ -211,7 +219,7 @@ function setStatus(statusType, msg, msgType) {
} }
} }
function logMsg(msg, level) { function logMsg(msg, level, outputMsg) {
if (level === 'error') { if (level === 'error') {
outputMsg.innerHTML = '<span style="color: red">Error: ' + msg + '</span>' outputMsg.innerHTML = '<span style="color: red">Error: ' + msg + '</span>'
} else if (level === 'warn') { } else if (level === 'warn') {
@ -223,8 +231,8 @@ function logMsg(msg, level) {
console.log(level, msg) console.log(level, msg)
} }
function logError(msg, res) { function logError(msg, res, outputMsg) {
logMsg(msg, 'error') logMsg(msg, 'error', outputMsg)
console.log('request error', res) console.log('request error', res)
setStatus('request', 'error', 'error') setStatus('request', 'error', 'error')
@ -251,7 +259,7 @@ async function healthCheck() {
} }
} }
function makeImageElement(width, height) { function makeImageElement(width, height, outputContainer) {
let imgItem = document.createElement('div') let imgItem = document.createElement('div')
imgItem.className = 'imgItem' imgItem.className = 'imgItem'
@ -260,17 +268,25 @@ function makeImageElement(width, height) {
img.height = parseInt(height) img.height = parseInt(height)
imgItem.appendChild(img) imgItem.appendChild(img)
imagesContainer.insertBefore(imgItem, imagesContainer.firstChild) outputContainer.insertBefore(imgItem, outputContainer.firstChild)
return imgItem return imgItem
} }
// makes a single image. don't call this directly, use makeImage() instead // makes a single image. don't call this directly, use makeImage() instead
async function doMakeImage(reqBody, batchCount) { async function doMakeImage(task) {
if (taskStopped) { if (task.stopped) {
return return
} }
const reqBody = task.reqBody
const batchCount = task.batchCount
const outputContainer = task.outputContainer
const outputMsg = task['outputMsg']
const previewPrompt = task['previewPrompt']
const progressBar = task['progressBar']
let res = '' let res = ''
let seed = reqBody['seed'] let seed = reqBody['seed']
let numOutputs = parseInt(reqBody['num_outputs']) let numOutputs = parseInt(reqBody['num_outputs'])
@ -279,7 +295,7 @@ async function doMakeImage(reqBody, batchCount) {
function makeImageContainers(numImages) { function makeImageContainers(numImages) {
for (let i = images.length; i < numImages; i++) { for (let i = images.length; i < numImages; i++) {
images.push(makeImageElement(reqBody.width, reqBody.height)) images.push(makeImageElement(reqBody.width, reqBody.height, outputContainer))
} }
} }
@ -316,7 +332,7 @@ async function doMakeImage(reqBody, batchCount) {
finalJSON += jsonStr finalJSON += jsonStr
} else { } else {
let batchSize = stepUpdate.total_steps let batchSize = stepUpdate.total_steps
let overallStepCount = stepUpdate.step + batchesDone * batchSize let overallStepCount = stepUpdate.step + task.batchesDone * batchSize
let totalSteps = batchCount * batchSize let totalSteps = batchCount * batchSize
let percent = 100 * (overallStepCount / totalSteps) let percent = 100 * (overallStepCount / totalSteps)
percent = (percent > 100 ? 100 : percent) percent = (percent > 100 ? 100 : percent)
@ -326,7 +342,7 @@ async function doMakeImage(reqBody, batchCount) {
stepsRemaining = (stepsRemaining < 0 ? 0 : stepsRemaining) stepsRemaining = (stepsRemaining < 0 ? 0 : stepsRemaining)
timeRemaining = (timeTaken === -1 ? '' : stepsRemaining * timeTaken) // ms timeRemaining = (timeTaken === -1 ? '' : stepsRemaining * timeTaken) // ms
outputMsg.innerHTML = `Batch ${batchesDone+1} of ${batchCount}` outputMsg.innerHTML = `Batch ${task.batchesDone+1} of ${batchCount}`
outputMsg.innerHTML += `. Generating image(s): ${percent}%` outputMsg.innerHTML += `. Generating image(s): ${percent}%`
timeRemaining = (timeTaken !== -1 ? millisecondsToStr(timeRemaining) : '') timeRemaining = (timeTaken !== -1 ? millisecondsToStr(timeRemaining) : '')
@ -351,7 +367,7 @@ async function doMakeImage(reqBody, batchCount) {
prevTime = t prevTime = t
} catch (e) { } catch (e) {
logError('Stable Diffusion had an error. Please check the logs in the command-line window.', res) logError('Stable Diffusion had an error. Please check the logs in the command-line window.', res, outputMsg)
res = undefined res = undefined
throw e throw e
} }
@ -359,9 +375,9 @@ async function doMakeImage(reqBody, batchCount) {
if (res.status != 200) { if (res.status != 200) {
if (serverStatus === 'online') { if (serverStatus === 'online') {
logError('Stable Diffusion had an error: ' + await res.text(), res) logError('Stable Diffusion had an error: ' + await res.text(), res, outputMsg)
} else { } else {
logError("Stable Diffusion is still starting up, please wait. If this goes on beyond a few minutes, Stable Diffusion has probably crashed. Please check the error message in the command-line window.", res) logError("Stable Diffusion is still starting up, please wait. If this goes on beyond a few minutes, Stable Diffusion has probably crashed. Please check the error message in the command-line window.", res, outputMsg)
} }
res = undefined res = undefined
progressBar.style.display = 'none' progressBar.style.display = 'none'
@ -398,13 +414,13 @@ async function doMakeImage(reqBody, batchCount) {
} else { } else {
msg = res msg = res
} }
logError(msg, res) logError(msg, res, outputMsg)
res = undefined res = undefined
} }
} }
} catch (e) { } catch (e) {
console.log('request error', e) console.log('request error', e)
logError('Stable Diffusion had an error. Please check the logs in the command-line window. <br/><br/>' + e + '<br/><pre>' + e.stack + '</pre>', res) logError('Stable Diffusion had an error. Please check the logs in the command-line window. <br/><br/>' + e + '<br/><pre>' + e.stack + '</pre>', res, outputMsg)
setStatus('request', 'error', 'error') setStatus('request', 'error', 'error')
progressBar.style.display = 'none' progressBar.style.display = 'none'
res = undefined res = undefined
@ -493,48 +509,96 @@ async function doMakeImage(reqBody, batchCount) {
return true return true
} }
function validateInput() { async function checkTasks() {
let width = parseInt(widthField.value) if (taskQueue.length === 0) {
let height = parseInt(heightField.value) setStatus('request', 'done', 'success')
setTimeout(checkTasks, 500)
stopImageBtn.style.display = 'none'
makeImageBtn.innerHTML = 'Make Image'
if (IMAGE_REGEX.test(initImagePreview.src)) { currentTask = null
if (initImagePreview.naturalWidth > MAX_INIT_IMAGE_DIMENSION || initImagePreview.naturalHeight > MAX_INIT_IMAGE_DIMENSION) {
return {'isValid': false, 'warning': `The dimensions of your initial image are very large, and can cause 'Out of Memory' errors! Please ensure that its dimensions are equal (or smaller) than the desired output image. if (bellPending) {
<br/><br/> if (isSoundEnabled()) {
Your initial image size is ${initImagePreview.naturalWidth}x${initImagePreview.naturalHeight} pixels. Please try to keep it smaller than ${MAX_INIT_IMAGE_DIMENSION}x${MAX_INIT_IMAGE_DIMENSION}.`} playSound()
}
bellPending = false
} }
}
return {'isValid': true}
}
async function makeImage() {
if (serverStatus !== 'online') {
logError('The server is still starting up..')
return return
} }
let validation = validateInput()
if (validation['isValid']) {
outputMsg.innerHTML = 'Starting..'
} else {
if (validation['error']) {
logError(validation['error'])
return
} else if (validation['warning']) {
logMsg(validation['warning'], 'warn')
}
}
setStatus('request', 'fetching..') setStatus('request', 'fetching..')
makeImageBtn.innerHTML = 'Processing..'
makeImageBtn.disabled = true
makeImageBtn.style.display = 'none'
stopImageBtn.style.display = 'block' stopImageBtn.style.display = 'block'
makeImageBtn.innerHTML = 'Enqueue Next Image'
bellPending = true
taskStopped = false previewTools.style.display = 'block'
batchesDone = 0
let task = taskQueue.pop()
currentTask = task
let time = new Date().getTime()
let successCount = 0
task.isProcessing = true
task['stopTask'].innerHTML = '<i class="fa-solid fa-circle-stop"></i> Stop'
task['taskStatusLabel'].innerText = "Processing"
task['taskStatusLabel'].className += " activeTaskLabel"
console.log(task['taskStatusLabel'].className)
for (let i = 0; i < task.batchCount; i++) {
task.reqBody['seed'] = task.seed + (i * task.reqBody['num_outputs'])
let success = await doMakeImage(task)
task.batchesDone++
if (!task.isProcessing) {
break
}
if (success) {
successCount++
}
}
task.isProcessing = false
task['stopTask'].innerHTML = '<i class="fa-solid fa-trash-can"></i> Remove'
task['taskStatusLabel'].style.display = 'none'
time = new Date().getTime() - time
time /= 1000
if (successCount === task.batchCount) {
task.outputMsg.innerText = 'Processed ' + task.numOutputsTotal + ' images in ' + time + ' seconds'
// setStatus('request', 'done', 'success')
} else {
task.outputMsg.innerText = 'Task ended after ' + time + ' seconds'
}
if (randomSeedField.checked) {
seedField.value = task.seed
}
currentTask = null
setTimeout(checkTasks, 10)
}
setTimeout(checkTasks, 0)
async function makeImage() {
if (serverStatus !== 'online') {
alert('The server is still starting up..')
return
}
let task = {
stopped: false,
batchesDone: 0
}
let seed = (randomSeedField.checked ? Math.floor(Math.random() * 10000000) : parseInt(seedField.value)) let seed = (randomSeedField.checked ? Math.floor(Math.random() * 10000000) : parseInt(seedField.value))
let numOutputsTotal = parseInt(numOutputsTotalField.value) let numOutputsTotal = parseInt(numOutputsTotalField.value)
@ -550,11 +614,10 @@ async function makeImage() {
prompt += ", " + promptTags; prompt += ", " + promptTags;
} }
previewPrompt.innerText = prompt
let reqBody = { let reqBody = {
session_id: sessionId, session_id: sessionId,
prompt: prompt, prompt: prompt,
negative_prompt: negativePromptField.value.trim(),
num_outputs: batchSize, num_outputs: batchSize,
num_inference_steps: numInferenceStepsField.value, num_inference_steps: numInferenceStepsField.value,
guidance_scale: guidanceScaleField.value, guidance_scale: guidanceScaleField.value,
@ -597,45 +660,76 @@ async function makeImage() {
reqBody['use_upscale'] = upscaleModelField.value reqBody['use_upscale'] = upscaleModelField.value
} }
let time = new Date().getTime() let taskConfig = `Seed: ${seed}, Sampler: ${reqBody['sampler']}, Inference Steps: ${numInferenceStepsField.value}, Guidance Scale: ${guidanceScaleField.value}`
imagesContainer.innerHTML = ''
let successCount = 0 if (negativePromptField.value.trim() !== '') {
taskConfig += `, Negative Prompt: ${negativePromptField.value.trim()}`
}
for (let i = 0; i < batchCount; i++) { if (reqBody['init_image'] !== undefined) {
reqBody['seed'] = seed + (i * batchSize) taskConfig += `, Prompt Strength: ${promptStrengthField.value}`
}
let success = await doMakeImage(reqBody, batchCount) if (useFaceCorrectionField.checked) {
batchesDone++ taskConfig += `, Fix Faces: ${reqBody['use_face_correction']}`
}
if (success) { if (useUpscalingField.checked) {
successCount++ taskConfig += `, Upscale: ${reqBody['use_upscale']}`
}
task['reqBody'] = reqBody
task['seed'] = seed
task['batchCount'] = batchCount
task['isProcessing'] = false
let taskEntry = document.createElement('div')
taskEntry.className = 'imageTaskContainer'
taskEntry.innerHTML = ` <div class="taskStatusLabel">Enqueued</div>
<button class="secondaryButton stopTask"><i class="fa-solid fa-trash-can"></i> Remove</button>
<div class="preview-prompt collapsible active"></div>
<div class="taskConfig">${taskConfig}</div>
<div class="collapsible-content" style="display: block">
<div class="outputMsg"></div>
<div class="progressBar"></div>
<div class="img-preview">
</div>`
createCollapsibles(taskEntry)
task['numOutputsTotal'] = numOutputsTotal
task['taskStatusLabel'] = taskEntry.querySelector('.taskStatusLabel')
task['outputContainer'] = taskEntry.querySelector('.img-preview')
task['outputMsg'] = taskEntry.querySelector('.outputMsg')
task['previewPrompt'] = taskEntry.querySelector('.preview-prompt')
task['progressBar'] = taskEntry.querySelector('.progressBar')
task['stopTask'] = taskEntry.querySelector('.stopTask')
task['stopTask'].addEventListener('click', async function() {
if (task['isProcessing']) {
task.isProcessing = false
try {
let res = await fetch('/image/stop')
} catch (e) {
console.log(e)
}
} else {
let idx = taskQueue.indexOf(task)
if (idx >= 0) {
taskQueue.splice(idx, 1)
}
taskEntry.remove()
} }
} })
progressBar.style.display = 'none' imagePreview.insertBefore(taskEntry, previewTools.nextSibling)
makeImageBtn.innerText = 'Make Image' task['previewPrompt'].innerText = prompt
makeImageBtn.disabled = false
makeImageBtn.style.display = 'block'
stopImageBtn.style.display = 'none'
if (isSoundEnabled()) { taskQueue.unshift(task)
playSound()
}
time = new Date().getTime() - time initialText.style.display = 'none'
time /= 1000
if (successCount === batchCount) {
outputMsg.innerText = 'Processed ' + numOutputsTotal + ' images in ' + time + ' seconds'
setStatus('request', 'done', 'success')
}
if (randomSeedField.checked) {
seedField.value = seed
}
} }
// create a file name with embedded prompt and metadata // create a file name with embedded prompt and metadata
@ -674,17 +768,37 @@ function createFileName() {
return fileName return fileName
} }
stopImageBtn.addEventListener('click', async function() { async function stopAllTasks() {
taskQueue.forEach(task => {
task.isProcessing = false
})
taskQueue = []
if (currentTask !== null) {
currentTask.isProcessing = false
}
try { try {
let res = await fetch('/image/stop') let res = await fetch('/image/stop')
} catch (e) { } catch (e) {
console.log(e) console.log(e)
} }
}
stopImageBtn.style.display = 'none' clearAllPreviewsBtn.addEventListener('click', async function() {
makeImageBtn.style.display = 'block' await stopAllTasks()
taskStopped = true let taskEntries = document.querySelectorAll('.imageTaskContainer')
taskEntries.forEach(task => {
task.remove()
})
previewTools.style.display = 'none'
initialText.style.display = 'block'
})
stopImageBtn.addEventListener('click', async function() {
await stopAllTasks()
}) })
soundToggle.addEventListener('click', handleBoolSettingChange(SOUND_ENABLED_KEY)) soundToggle.addEventListener('click', handleBoolSettingChange(SOUND_ENABLED_KEY))
@ -781,7 +895,7 @@ updatePromptStrength()
useBetaChannelField.addEventListener('click', async function(e) { useBetaChannelField.addEventListener('click', async function(e) {
if (serverStatus !== 'online') { if (serverStatus !== 'online') {
logError('The server is still starting up..') // logError('The server is still starting up..')
alert('The server is still starting up..') alert('The server is still starting up..')
e.preventDefault() e.preventDefault()
return false return false
@ -942,6 +1056,22 @@ function millisecondsToStr(milliseconds) {
return s; return s;
} }
// https://gomakethings.com/finding-the-next-and-previous-sibling-elements-that-match-a-selector-with-vanilla-js/
function getNextSibling(elem, selector) {
// Get the next sibling element
var sibling = elem.nextElementSibling
// If there's no selector, return the first sibling
if (!selector) return sibling
// If the sibling matches our selector, use it
// If not, jump to the next sibling and continue the loop
while (sibling) {
if (sibling.matches(selector)) return sibling
sibling = sibling.nextElementSibling
}
}
function createCollapsibles(node) { function createCollapsibles(node) {
if (!node) { if (!node) {
node = document node = document
@ -961,7 +1091,7 @@ function createCollapsibles(node) {
c.addEventListener('click', function() { c.addEventListener('click', function() {
this.classList.toggle("active") this.classList.toggle("active")
let content = this.nextElementSibling let content = getNextSibling(this, '.collapsible-content')
if (content.style.display === "block") { if (content.style.display === "block") {
content.style.display = "none" content.style.display = "none"
handle.innerHTML = '&#x2795;' // plus handle.innerHTML = '&#x2795;' // plus

View File

@ -3,6 +3,7 @@ import json
class Request: class Request:
session_id: str = "session" session_id: str = "session"
prompt: str = "" prompt: str = ""
negative_prompt: str = ""
init_image: str = None # base64 init_image: str = None # base64
mask: str = None # base64 mask: str = None # base64
num_outputs: int = 1 num_outputs: int = 1
@ -30,6 +31,7 @@ class Request:
return { return {
"session_id": self.session_id, "session_id": self.session_id,
"prompt": self.prompt, "prompt": self.prompt,
"negative_prompt": self.negative_prompt,
"num_outputs": self.num_outputs, "num_outputs": self.num_outputs,
"num_inference_steps": self.num_inference_steps, "num_inference_steps": self.num_inference_steps,
"guidance_scale": self.guidance_scale, "guidance_scale": self.guidance_scale,
@ -46,6 +48,7 @@ class Request:
return f''' return f'''
session_id: {self.session_id} session_id: {self.session_id}
prompt: {self.prompt} prompt: {self.prompt}
negative_prompt: {self.negative_prompt}
seed: {self.seed} seed: {self.seed}
num_inference_steps: {self.num_inference_steps} num_inference_steps: {self.num_inference_steps}
sampler: {self.sampler} sampler: {self.sampler}

View File

@ -343,7 +343,7 @@ def do_mk_img(req: Request):
modelCS.to(device) modelCS.to(device)
uc = None uc = None
if opt_scale != 1.0: if opt_scale != 1.0:
uc = modelCS.get_learned_conditioning(batch_size * [""]) uc = modelCS.get_learned_conditioning(batch_size * [req.negative_prompt])
if isinstance(prompts, tuple): if isinstance(prompts, tuple):
prompts = list(prompts) prompts = list(prompts)

View File

@ -39,6 +39,7 @@ app.mount('/media', StaticFiles(directory=os.path.join(SD_UI_DIR, 'media/')), na
class ImageRequest(BaseModel): class ImageRequest(BaseModel):
session_id: str = "session" session_id: str = "session"
prompt: str = "" prompt: str = ""
negative_prompt: str = ""
init_image: str = None # base64 init_image: str = None # base64
mask: str = None # base64 mask: str = None # base64
num_outputs: int = 1 num_outputs: int = 1
@ -100,6 +101,7 @@ def image(req : ImageRequest):
r = Request() r = Request()
r.session_id = req.session_id r.session_id = req.session_id
r.prompt = req.prompt r.prompt = req.prompt
r.negative_prompt = req.negative_prompt
r.init_image = req.init_image r.init_image = req.init_image
r.mask = req.mask r.mask = req.mask
r.num_outputs = req.num_outputs r.num_outputs = req.num_outputs