2022-10-11 04:31:47 +02:00
"use strict" // Opt in to a restricted variant of JavaScript
2022-09-23 16:18:48 +02:00
const MAX _INIT _IMAGE _DIMENSION = 768
2022-11-10 12:04:01 +01:00
const MIN _GPUS _TO _SHOW _SELECTION = 2
2022-09-23 16:18:48 +02:00
const IMAGE _REGEX = new RegExp ( 'data:image/[A-Za-z]+;base64' )
2022-12-06 12:34:08 +01:00
const htmlTaskMap = new WeakMap ( )
2022-09-23 16:18:48 +02:00
2023-03-14 06:10:24 +01:00
let imageCounter = 0
let imageRequest = [ ]
2022-09-23 16:18:48 +02:00
let promptField = document . querySelector ( '#prompt' )
2022-10-07 20:16:56 +02:00
let promptsFromFileSelector = document . querySelector ( '#prompt_from_file' )
let promptsFromFileBtn = document . querySelector ( '#promptsFromFileBtn' )
2022-09-27 15:39:28 +02:00
let negativePromptField = document . querySelector ( '#negative_prompt' )
2022-09-23 16:18:48 +02:00
let numOutputsTotalField = document . querySelector ( '#num_outputs_total' )
let numOutputsParallelField = document . querySelector ( '#num_outputs_parallel' )
let numInferenceStepsField = document . querySelector ( '#num_inference_steps' )
let guidanceScaleSlider = document . querySelector ( '#guidance_scale_slider' )
let guidanceScaleField = document . querySelector ( '#guidance_scale' )
2022-12-05 06:32:33 +01:00
let outputQualitySlider = document . querySelector ( '#output_quality_slider' )
let outputQualityField = document . querySelector ( '#output_quality' )
let outputQualityRow = document . querySelector ( '#output_quality_row' )
2022-09-23 16:18:48 +02:00
let randomSeedField = document . querySelector ( "#random_seed" )
let seedField = document . querySelector ( '#seed' )
let widthField = document . querySelector ( '#width' )
let heightField = document . querySelector ( '#height' )
2023-03-12 20:43:54 +01:00
let smallImageWarning = document . querySelector ( '#small_image_warning' )
2022-09-23 16:18:48 +02:00
let initImageSelector = document . querySelector ( "#init_image" )
let initImagePreview = document . querySelector ( "#init_image_preview" )
2022-10-17 08:10:01 +02:00
let initImageSizeBox = document . querySelector ( "#init_image_size_box" )
2022-09-23 16:18:48 +02:00
let maskImageSelector = document . querySelector ( "#mask" )
let maskImagePreview = document . querySelector ( "#mask_preview" )
2022-12-09 15:09:56 +01:00
let applyColorCorrectionField = document . querySelector ( '#apply_color_correction' )
let colorCorrectionSetting = document . querySelector ( '#apply_color_correction_setting' )
2022-09-23 16:18:48 +02:00
let promptStrengthSlider = document . querySelector ( '#prompt_strength_slider' )
let promptStrengthField = document . querySelector ( '#prompt_strength' )
2022-12-12 10:51:02 +01:00
let samplerField = document . querySelector ( '#sampler_name' )
2022-09-23 16:18:48 +02:00
let samplerSelectionContainer = document . querySelector ( "#samplerSelection" )
let useFaceCorrectionField = document . querySelector ( "#use_face_correction" )
2023-02-14 02:42:36 +01:00
let gfpganModelField = new ModelDropdown ( document . querySelector ( "#gfpgan_model" ) , 'gfpgan' )
2022-09-23 16:18:48 +02:00
let useUpscalingField = document . querySelector ( "#use_upscale" )
let upscaleModelField = document . querySelector ( "#upscale_model" )
2022-12-27 11:50:16 +01:00
let upscaleAmountField = document . querySelector ( "#upscale_amount" )
2023-02-12 10:18:09 +01:00
let stableDiffusionModelField = new ModelDropdown ( document . querySelector ( '#stable_diffusion_model' ) , 'stable-diffusion' )
let vaeModelField = new ModelDropdown ( document . querySelector ( '#vae_model' ) , 'vae' , 'None' )
let hypernetworkModelField = new ModelDropdown ( document . querySelector ( '#hypernetwork_model' ) , 'hypernetwork' , 'None' )
2022-12-07 06:54:16 +01:00
let hypernetworkStrengthSlider = document . querySelector ( '#hypernetwork_strength_slider' )
let hypernetworkStrengthField = document . querySelector ( '#hypernetwork_strength' )
2022-10-06 11:35:34 +02:00
let outputFormatField = document . querySelector ( '#output_format' )
2023-02-18 10:31:13 +01:00
let blockNSFWField = document . querySelector ( '#block_nsfw' )
2022-09-23 16:18:48 +02:00
let showOnlyFilteredImageField = document . querySelector ( "#show_only_filtered_image" )
let updateBranchLabel = document . querySelector ( "#updateBranchLabel" )
let streamImageProgressField = document . querySelector ( "#stream_image_progress" )
2023-02-22 22:05:16 +01:00
let thumbnailSizeField = document . querySelector ( "#thumbnail_size-input" )
2023-03-01 13:57:48 +01:00
let autoscrollBtn = document . querySelector ( "#auto_scroll_btn" )
let autoScroll = document . querySelector ( "#auto_scroll" )
2022-09-23 16:18:48 +02:00
let makeImageBtn = document . querySelector ( '#makeImage' )
let stopImageBtn = document . querySelector ( '#stopImage' )
2022-12-11 02:31:23 +01:00
let pauseBtn = document . querySelector ( '#pause' )
let resumeBtn = document . querySelector ( '#resume' )
2022-12-11 14:57:01 +01:00
let renderButtons = document . querySelector ( '#render-buttons' )
2022-09-23 16:18:48 +02:00
let imagesContainer = document . querySelector ( '#current-images' )
let initImagePreviewContainer = document . querySelector ( '#init_image_preview_container' )
let initImageClearBtn = document . querySelector ( '.init_image_clear' )
let promptStrengthContainer = document . querySelector ( '#prompt_strength_container' )
2022-09-27 14:39:07 +02:00
let initialText = document . querySelector ( "#initial-text" )
let previewTools = document . querySelector ( "#preview-tools" )
let clearAllPreviewsBtn = document . querySelector ( "#clear-all-previews" )
2023-03-14 06:10:24 +01:00
let showDownloadPopupBtn = document . querySelector ( "#show-download-popup" )
let saveAllImagesPopup = document . querySelector ( "#download-images-popup" )
2023-02-14 15:03:25 +01:00
let saveAllImagesBtn = document . querySelector ( "#save-all-images" )
2023-03-14 06:10:24 +01:00
let saveAllZipToggle = document . querySelector ( "#zip_toggle" )
let saveAllTreeToggle = document . querySelector ( "#tree_toggle" )
let saveAllJSONToggle = document . querySelector ( "#json_toggle" )
let saveAllFoldersOption = document . querySelector ( "#download-add-folders" )
2022-09-27 14:39:07 +02:00
2022-09-23 16:18:48 +02:00
let maskSetting = document . querySelector ( '#enable_mask' )
2022-12-06 12:34:08 +01:00
const processOrder = document . querySelector ( '#process_order_toggle' )
2022-09-23 16:18:48 +02:00
2022-12-06 12:34:08 +01:00
let imagePreview = document . querySelector ( "#preview" )
2023-01-16 08:34:56 +01:00
let imagePreviewContent = document . querySelector ( "#preview-content" )
2022-12-06 12:34:08 +01:00
imagePreview . addEventListener ( 'drop' , function ( ev ) {
const data = ev . dataTransfer ? . getData ( "text/plain" ) ;
if ( ! data ) {
return
}
const movedTask = document . getElementById ( data )
if ( ! movedTask ) {
return
}
ev . preventDefault ( )
let moveTarget = ev . target
2023-02-12 01:02:27 +01:00
while ( moveTarget && typeof moveTarget === 'object' && moveTarget . parentNode !== imagePreviewContent ) {
2022-12-06 12:34:08 +01:00
moveTarget = moveTarget . parentNode
}
if ( moveTarget === initialText || moveTarget === previewTools ) {
moveTarget = null
}
if ( moveTarget === movedTask ) {
return
}
if ( moveTarget ) {
2023-02-12 01:02:27 +01:00
const childs = Array . from ( imagePreviewContent . children )
2022-12-06 12:34:08 +01:00
if ( moveTarget . nextSibling && childs . indexOf ( movedTask ) < childs . indexOf ( moveTarget ) ) {
// Move after the target if lower than current position.
moveTarget = moveTarget . nextSibling
}
}
2023-02-12 01:02:27 +01:00
const newNode = imagePreviewContent . insertBefore ( movedTask , moveTarget || previewTools . nextSibling )
2022-12-06 12:34:08 +01:00
if ( newNode === movedTask ) {
return
}
2023-02-12 01:02:27 +01:00
imagePreviewContent . removeChild ( movedTask )
2022-12-06 12:34:08 +01:00
const task = htmlTaskMap . get ( movedTask )
if ( task ) {
htmlTaskMap . delete ( movedTask )
}
if ( task ) {
htmlTaskMap . set ( newNode , task )
}
} )
2022-09-23 16:18:48 +02:00
2022-12-19 19:45:42 +01:00
2022-12-06 12:34:08 +01:00
let showConfigToggle = document . querySelector ( '#configToggleBtn' )
// let configBox = document.querySelector('#config')
// let outputMsg = document.querySelector('#outputMsg')
2022-09-24 14:01:36 +02:00
2022-12-06 12:34:08 +01:00
let soundToggle = document . querySelector ( '#sound_toggle' )
2022-09-27 14:39:07 +02:00
2022-12-06 12:34:08 +01:00
let serverStatusColor = document . querySelector ( '#server-status-color' )
let serverStatusMsg = document . querySelector ( '#server-status-msg' )
2022-09-23 16:18:48 +02:00
function getLocalStorageBoolItem ( key , fallback ) {
let item = localStorage . getItem ( key )
if ( item === null ) {
return fallback
}
return ( item === 'true' ? true : false )
}
function handleBoolSettingChange ( key ) {
return function ( e ) {
localStorage . setItem ( key , e . target . checked . toString ( ) )
}
}
function handleStringSettingChange ( key ) {
return function ( e ) {
localStorage . setItem ( key , e . target . value . toString ( ) )
}
}
function isSoundEnabled ( ) {
2022-10-19 05:49:58 +02:00
return getSetting ( "sound_toggle" )
2022-09-23 16:18:48 +02:00
}
function getSavedDiskPath ( ) {
2022-10-19 05:49:58 +02:00
return getSetting ( "diskPath" )
2022-10-08 14:52:01 +02:00
}
2022-09-23 16:18:48 +02:00
function setStatus ( statusType , msg , msgType ) {
2022-10-14 09:47:25 +02:00
}
2022-09-23 16:18:48 +02:00
2022-12-06 12:34:08 +01:00
function setServerStatus ( event ) {
switch ( event . type ) {
2022-10-14 09:47:25 +02:00
case 'online' :
2023-03-12 20:43:54 +01:00
serverStatusColor . style . color = 'var(--status-green)'
serverStatusMsg . style . color = 'var(--status-green)'
2022-12-06 12:34:08 +01:00
serverStatusMsg . innerText = 'Stable Diffusion is ' + event . message
2022-10-14 09:47:25 +02:00
break
case 'busy' :
2023-03-12 20:43:54 +01:00
serverStatusColor . style . color = 'var(--status-orange)'
serverStatusMsg . style . color = 'var(--status-orange)'
2022-12-06 12:34:08 +01:00
serverStatusMsg . innerText = 'Stable Diffusion is ' + event . message
2022-10-14 09:47:25 +02:00
break
case 'error' :
2023-03-12 20:43:54 +01:00
serverStatusColor . style . color = 'var(--status-red)'
serverStatusMsg . style . color = 'var(--status-red)'
2022-10-14 09:47:25 +02:00
serverStatusMsg . innerText = 'Stable Diffusion has stopped'
break
}
2022-12-06 12:34:08 +01:00
if ( SD . serverState . devices ) {
setDeviceInfo ( SD . serverState . devices )
2022-09-23 16:18:48 +02:00
}
}
2022-11-22 21:27:36 +01:00
// shiftOrConfirm(e, prompt, fn)
// e : MouseEvent
// prompt : Text to be shown as prompt. Should be a question to which "yes" is a good answer.
// fn : function to be called if the user confirms the dialog or has the shift key pressed
//
// If the user had the shift key pressed while clicking, the function fn will be executed.
2022-11-23 23:05:30 +01:00
// If the setting "confirm_dangerous_actions" in the system settings is disabled, the function
// fn will be executed.
2022-11-22 21:27:36 +01:00
// Otherwise, a confirmation dialog is shown. If the user confirms, the function fn will also
// be executed.
function shiftOrConfirm ( e , prompt , fn ) {
e . stopPropagation ( )
2022-11-23 23:05:30 +01:00
if ( e . shiftKey || ! confirmDangerousActionsField . checked ) {
2022-11-22 21:27:36 +01:00
fn ( e )
} else {
2022-12-01 09:24:49 +01:00
$ . confirm ( {
theme : 'modern' ,
2022-11-22 21:27:36 +01:00
title : prompt ,
2022-12-01 09:24:49 +01:00
useBootstrap : false ,
animateFromElement : false ,
content : '<small>Tip: To skip this dialog, use shift-click or disable the "Confirm dangerous actions" setting in the Settings tab.</small>' ,
2022-11-22 21:27:36 +01:00
buttons : {
yes : ( ) => { fn ( e ) } ,
cancel : ( ) => { }
}
} ) ;
}
}
2022-09-27 14:39:07 +02:00
function logMsg ( msg , level , outputMsg ) {
2022-10-12 03:53:08 +02:00
if ( outputMsg . hasChildNodes ( ) ) {
outputMsg . appendChild ( document . createElement ( 'br' ) )
}
2022-09-23 16:18:48 +02:00
if ( level === 'error' ) {
2022-10-12 03:53:08 +02:00
outputMsg . innerHTML += '<span style="color: red">Error: ' + msg + '</span>'
2022-09-23 16:18:48 +02:00
} else if ( level === 'warn' ) {
2022-10-12 03:53:08 +02:00
outputMsg . innerHTML += '<span style="color: orange">Warning: ' + msg + '</span>'
2022-09-23 16:18:48 +02:00
} else {
2022-10-12 03:53:08 +02:00
outputMsg . innerText += msg
2022-09-23 16:18:48 +02:00
}
console . log ( level , msg )
}
2022-09-27 14:39:07 +02:00
function logError ( msg , res , outputMsg ) {
logMsg ( msg , 'error' , outputMsg )
2022-09-23 16:18:48 +02:00
console . log ( 'request error' , res )
setStatus ( 'request' , 'error' , 'error' )
}
function playSound ( ) {
const audio = new Audio ( '/media/ding.mp3' )
audio . volume = 0.2
2022-10-20 06:12:01 +02:00
var promise = audio . play ( )
2022-10-17 08:10:01 +02:00
if ( promise !== undefined ) {
promise . then ( _ => { } ) . catch ( error => {
2022-10-20 06:12:01 +02:00
console . warn ( "browser blocked autoplay" )
} )
2022-10-17 08:10:01 +02:00
}
2022-09-23 16:18:48 +02:00
}
2022-10-10 08:32:27 +02:00
function showImages ( reqBody , res , outputContainer , livePreview ) {
2022-09-29 09:38:42 +02:00
let imageItemElements = outputContainer . querySelectorAll ( '.imgItem' )
2022-10-10 08:32:27 +02:00
if ( typeof res != 'object' ) return
2022-09-29 10:22:48 +02:00
res . output . reverse ( )
2022-09-27 16:23:19 +02:00
res . output . forEach ( ( result , index ) => {
2022-10-17 11:52:54 +02:00
const imageData = result ? . data || result ? . path + '?t=' + Date . now ( ) ,
2022-10-15 04:46:31 +02:00
imageSeed = result ? . seed ,
2022-10-17 09:34:33 +02:00
imagePrompt = reqBody . prompt ,
imageInferenceSteps = reqBody . num _inference _steps ,
imageGuidanceScale = reqBody . guidance _scale ,
imageWidth = reqBody . width ,
imageHeight = reqBody . height ;
2022-10-15 04:46:31 +02:00
2022-09-27 23:05:34 +02:00
if ( ! imageData . includes ( '/' ) ) {
2022-09-27 16:23:19 +02:00
// res contained no data for the image, stop execution
2022-09-29 09:38:42 +02:00
setStatus ( 'request' , 'invalid image' , 'error' )
return
2022-09-25 01:55:11 +02:00
}
2022-09-29 09:31:18 +02:00
let imageItemElem = ( index < imageItemElements . length ? imageItemElements [ index ] : null )
2022-09-27 16:23:19 +02:00
if ( ! imageItemElem ) {
2022-09-29 09:38:42 +02:00
imageItemElem = document . createElement ( 'div' )
imageItemElem . className = 'imgItem'
2022-09-27 16:23:19 +02:00
imageItemElem . innerHTML = `
< div class = "imgContainer" >
< img / >
< div class = "imgItemInfo" >
2023-03-15 14:55:55 +01:00
< div >
< span class = "imgInfoLabel imgExpandBtn" > < i class = "fa-solid fa-expand" > < / i > < / s p a n > < s p a n c l a s s = " i m g I n f o L a b e l i m g S e e d L a b e l " > < / s p a n >
< / d i v >
2022-09-27 16:23:19 +02:00
< / d i v >
2023-02-06 10:44:47 +01:00
< button class = "imgPreviewItemClearBtn image_clear_btn" > < i class = "fa-solid fa-xmark" > < / i > < / b u t t o n >
2023-03-01 10:52:38 +01:00
< span class = "img_bottom_label" > < / s p a n >
2022-09-25 01:55:11 +02:00
< / d i v >
2022-09-29 09:38:42 +02:00
`
2022-10-10 08:32:27 +02:00
outputContainer . appendChild ( imageItemElem )
2023-02-17 00:54:41 +01:00
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 ;
}
}
2023-03-13 23:05:11 +01:00
if ( allHidden === true ) {
const req = htmlTaskMap . get ( parentTaskContainer )
if ( ! req . isProcessing || req . batchesDone == req . batchCount ) { parentTaskContainer . classList . add ( "displayNone" ) }
}
2023-02-17 00:54:41 +01:00
} )
} )
2022-10-10 08:32:27 +02:00
}
const imageElem = imageItemElem . querySelector ( 'img' )
imageElem . src = imageData
imageElem . width = parseInt ( imageWidth )
imageElem . height = parseInt ( imageHeight )
2022-10-15 04:46:31 +02:00
imageElem . setAttribute ( 'data-prompt' , imagePrompt )
imageElem . setAttribute ( 'data-steps' , imageInferenceSteps )
imageElem . setAttribute ( 'data-guidance' , imageGuidanceScale )
2023-03-01 10:52:38 +01:00
imageElem . addEventListener ( 'load' , function ( ) {
imageItemElem . querySelector ( '.img_bottom_label' ) . innerText = ` ${ this . naturalWidth } x ${ this . naturalHeight } `
} )
2023-03-15 14:55:55 +01:00
imageElem . addEventListener ( 'click' , function ( ) {
imageModal ( this . src )
} )
2023-03-01 10:52:38 +01:00
2023-03-15 14:55:55 +01:00
const imageExpandBtn = imageItemElem . querySelector ( '.imgExpandBtn' )
imageExpandBtn . addEventListener ( 'click' , function ( ) {
imageModal ( imageElem . src )
} )
2022-12-12 14:33:16 +01:00
2022-10-10 08:32:27 +02:00
const imageInfo = imageItemElem . querySelector ( '.imgItemInfo' )
imageInfo . style . visibility = ( livePreview ? 'hidden' : 'visible' )
if ( 'seed' in result && ! imageElem . hasAttribute ( 'data-seed' ) ) {
const req = Object . assign ( { } , reqBody , {
seed : result ? . seed || reqBody . seed
} )
imageElem . setAttribute ( 'data-seed' , req . seed )
2023-03-14 06:10:24 +01:00
imageElem . setAttribute ( 'data-imagecounter' , ++ imageCounter )
imageRequest [ imageCounter ] = req
2022-10-10 08:32:27 +02:00
const imageSeedLabel = imageItemElem . querySelector ( '.imgSeedLabel' )
imageSeedLabel . innerText = 'Seed: ' + req . seed
2022-10-19 17:51:19 +02:00
let buttons = [
{ text : 'Use as Input' , on _click : onUseAsInputClick } ,
2023-03-15 15:11:19 +01:00
[
{ html : '<i class="fa-solid fa-download"></i> Download Image' , on _click : onDownloadImageClick , class : "download-img" } ,
{ html : '<i class="fa-solid fa-download"></i> JSON' , on _click : onDownloadJSONClick , class : "download-json" }
] ,
2022-10-19 18:28:51 +02:00
{ text : 'Make Similar Images' , on _click : onMakeSimilarClick } ,
2022-10-19 18:38:42 +02:00
{ text : 'Draw another 25 steps' , on _click : onContinueDrawingClick } ,
2023-03-15 15:11:19 +01:00
[
{ text : 'Upscale' , on _click : onUpscaleClick , filter : ( req , img ) => ! req . use _upscale } ,
{ text : 'Fix Faces' , on _click : onFixFacesClick , filter : ( req , img ) => ! req . use _face _correction }
]
2022-10-19 17:51:19 +02:00
]
2022-10-19 16:20:05 +02:00
// include the plugins
2022-10-19 17:51:19 +02:00
buttons = buttons . concat ( PLUGINS [ 'IMAGE_INFO_BUTTONS' ] )
2022-10-19 16:20:05 +02:00
2022-10-10 01:56:51 +02:00
const imgItemInfo = imageItemElem . querySelector ( '.imgItemInfo' )
2022-10-19 16:40:45 +02:00
const img = imageItemElem . querySelector ( 'img' )
2022-10-19 17:51:19 +02:00
const createButton = function ( btnInfo ) {
2023-03-15 14:14:43 +01:00
if ( Array . isArray ( btnInfo ) ) {
const wrapper = document . createElement ( 'div' ) ;
btnInfo
. map ( createButton )
. forEach ( buttonElement => wrapper . appendChild ( buttonElement ) )
return wrapper
}
const isLabel = btnInfo . type === 'label'
const newButton = document . createElement ( isLabel ? 'span' : 'button' )
2022-10-10 01:56:51 +02:00
newButton . classList . add ( 'tasksBtns' )
2023-03-15 14:14:43 +01:00
if ( btnInfo . html ) {
const html = typeof btnInfo . html === 'function' ? btnInfo . html ( ) : btnInfo . html
if ( html instanceof HTMLElement ) {
newButton . appendChild ( html )
} else {
newButton . innerHTML = html
}
} else {
newButton . innerText = typeof btnInfo . text === 'function' ? btnInfo . text ( ) : btnInfo . text
}
if ( btnInfo . on _click || ! isLabel ) {
newButton . addEventListener ( 'click' , function ( event ) {
btnInfo . on _click ( req , img , event )
} )
}
2022-12-24 01:02:38 +01:00
if ( btnInfo . class !== undefined ) {
2023-03-15 14:14:43 +01:00
if ( Array . isArray ( btnInfo . class ) ) {
newButton . classList . add ( ... btnInfo . class )
} else {
newButton . classList . add ( btnInfo . class )
}
2022-12-24 01:02:38 +01:00
}
2023-03-15 14:14:43 +01:00
return newButton
2022-10-10 01:56:51 +02:00
}
2022-10-19 17:51:19 +02:00
buttons . forEach ( btn => {
2023-03-15 14:14:43 +01:00
if ( Array . isArray ( btn ) ) {
2023-03-15 16:50:56 +01:00
btn = btn . filter ( btnInfo => ! btnInfo . filter || btnInfo . filter ( req , img ) === true )
if ( btn . length === 0 ) {
2023-03-15 14:14:43 +01:00
return
}
} else if ( btn . filter && btn . filter ( req , img ) === false ) {
2022-10-19 16:40:45 +02:00
return
}
2023-03-15 14:14:43 +01:00
try {
imgItemInfo . appendChild ( createButton ( btn ) )
} catch ( err ) {
console . error ( 'Error creating image info button from plugin: ' , btn , err )
}
2022-10-19 16:40:45 +02:00
} )
2022-09-27 16:23:19 +02:00
}
2022-09-29 09:38:42 +02:00
} )
2022-09-23 16:18:48 +02:00
}
2022-10-19 16:20:05 +02:00
function onUseAsInputClick ( req , img ) {
const imgData = img . src
2022-09-29 09:31:18 +02:00
2022-10-19 13:56:35 +02:00
initImageSelector . value = null
initImagePreview . src = imgData
2022-09-29 09:31:18 +02:00
2022-10-19 13:56:35 +02:00
maskSetting . checked = false
2022-09-29 09:31:18 +02:00
}
2023-03-14 06:10:24 +01:00
function getDownloadFilename ( img , suffix ) {
2022-10-19 16:20:05 +02:00
const imageSeed = img . getAttribute ( 'data-seed' )
const imagePrompt = img . getAttribute ( 'data-prompt' )
const imageInferenceSteps = img . getAttribute ( 'data-steps' )
const imageGuidanceScale = img . getAttribute ( 'data-guidance' )
2023-03-14 06:10:24 +01:00
return createFileName ( imagePrompt , imageSeed , imageInferenceSteps , imageGuidanceScale , suffix )
}
2022-10-19 13:56:35 +02:00
2023-03-14 06:10:24 +01:00
function onDownloadJSONClick ( req , img ) {
const name = getDownloadFilename ( img , 'json' )
const blob = new Blob ( [ JSON . stringify ( req , null , 2 ) ] , { type : 'text/plain' } )
saveAs ( blob , name )
}
function onDownloadImageClick ( req , img ) {
const name = getDownloadFilename ( img , req [ 'output_format' ] )
const blob = dataURItoBlob ( img . src )
saveAs ( blob , name )
2022-09-29 09:31:18 +02:00
}
2022-10-19 13:56:35 +02:00
2022-10-30 19:09:12 +01:00
function modifyCurrentRequest ( ... reqDiff ) {
2022-10-20 11:40:34 +02:00
const newTaskRequest = getCurrentUserRequest ( )
2022-10-19 13:56:35 +02:00
2022-10-30 19:09:12 +01:00
newTaskRequest . reqBody = Object . assign ( newTaskRequest . reqBody , ... reqDiff , {
2022-10-20 11:40:34 +02:00
use _cpu : useCPUField . checked
} )
newTaskRequest . seed = newTaskRequest . reqBody . seed
return newTaskRequest
}
function onMakeSimilarClick ( req , img ) {
const newTaskRequest = modifyCurrentRequest ( req , {
2022-10-19 13:56:35 +02:00
num _outputs : 1 ,
num _inference _steps : 50 ,
guidance _scale : 7.5 ,
prompt _strength : 0.7 ,
2022-10-19 16:20:05 +02:00
init _image : img . src ,
2022-10-19 13:56:35 +02:00
seed : Math . floor ( Math . random ( ) * 10000000 )
} )
newTaskRequest . numOutputsTotal = 5
newTaskRequest . batchCount = 5
delete newTaskRequest . reqBody . mask
createTask ( newTaskRequest )
}
2022-10-20 11:40:34 +02:00
function enqueueImageVariationTask ( req , img , reqDiff ) {
2022-10-19 18:28:51 +02:00
const imageSeed = img . getAttribute ( 'data-seed' )
2022-12-05 06:16:10 +01:00
const newRequestBody = {
2022-10-20 11:40:34 +02:00
num _outputs : 1 , // this can be user-configurable in the future
2022-10-19 18:28:51 +02:00
seed : imageSeed
2022-12-05 06:16:10 +01:00
}
// If the user is editing pictures, stop modifyCurrentRequest from importing
// new values by setting the missing properties to undefined
if ( ! ( 'init_image' in req ) && ! ( 'init_image' in reqDiff ) ) {
newRequestBody . init _image = undefined
newRequestBody . mask = undefined
} else if ( ! ( 'mask' in req ) && ! ( 'mask' in reqDiff ) ) {
newRequestBody . mask = undefined
}
2022-10-19 18:28:51 +02:00
2022-12-05 06:16:10 +01:00
const newTaskRequest = modifyCurrentRequest ( req , reqDiff , newRequestBody )
2022-10-20 11:40:34 +02:00
newTaskRequest . numOutputsTotal = 1 // this can be user-configurable in the future
2022-10-19 18:28:51 +02:00
newTaskRequest . batchCount = 1
createTask ( newTaskRequest )
}
2022-10-20 11:40:34 +02:00
function onUpscaleClick ( req , img ) {
2022-12-05 06:16:10 +01:00
enqueueImageVariationTask ( req , img , {
use _upscale : upscaleModelField . value
} )
2022-10-20 11:40:34 +02:00
}
2022-10-19 18:32:59 +02:00
2022-10-20 11:40:34 +02:00
function onFixFacesClick ( req , img ) {
enqueueImageVariationTask ( req , img , {
2023-01-19 20:49:54 +01:00
use _face _correction : gfpganModelField . value
2022-10-20 11:40:34 +02:00
} )
2022-10-19 18:32:59 +02:00
}
2022-10-19 18:38:42 +02:00
function onContinueDrawingClick ( req , img ) {
2022-10-20 11:40:34 +02:00
enqueueImageVariationTask ( req , img , {
num _inference _steps : parseInt ( req . num _inference _steps ) + 25
2022-10-19 18:38:42 +02:00
} )
}
2022-12-06 12:34:08 +01:00
function getUncompletedTaskEntries ( ) {
const taskEntries = Array . from (
document . querySelectorAll ( '#preview .imageTaskContainer .taskStatusLabel' )
) . filter ( ( taskLabel ) => taskLabel . style . display !== 'none'
) . map ( function ( taskLabel ) {
2022-12-12 14:33:16 +01:00
let imageTaskContainer = taskLabel . parentNode
while ( ! imageTaskContainer . classList . contains ( 'imageTaskContainer' ) && imageTaskContainer . parentNode ) {
imageTaskContainer = imageTaskContainer . parentNode
}
return imageTaskContainer
2022-12-06 12:34:08 +01:00
} )
if ( ! processOrder . checked ) {
taskEntries . reverse ( )
2022-09-23 16:18:48 +02:00
}
2022-12-06 12:34:08 +01:00
return taskEntries
}
2022-09-23 16:18:48 +02:00
2022-12-06 12:34:08 +01:00
function makeImage ( ) {
2023-01-09 15:03:23 +01:00
if ( typeof performance == "object" && performance . mark ) {
performance . mark ( 'click-makeImage' )
}
2022-12-06 12:34:08 +01:00
if ( ! SD . isServerAvailable ( ) ) {
alert ( 'The server is not available.' )
return
}
if ( ! randomSeedField . checked && seedField . value == '' ) {
alert ( 'The "Seed" field must not be empty.' )
return
}
if ( numInferenceStepsField . value == '' ) {
alert ( 'The "Inference Steps" field must not be empty.' )
return
}
2022-12-12 06:41:33 +01:00
if ( numOutputsTotalField . value == '' || numOutputsTotalField . value == 0 ) {
2022-12-06 12:34:08 +01:00
numOutputsTotalField . value = 1
}
2022-12-12 06:41:33 +01:00
if ( numOutputsParallelField . value == '' || numOutputsParallelField . value == 0 ) {
2022-12-06 12:34:08 +01:00
numOutputsParallelField . value = 1
}
if ( guidanceScaleField . value == '' ) {
guidanceScaleField . value = guidanceScaleSlider . value / 10
}
const taskTemplate = getCurrentUserRequest ( )
const newTaskRequests = getPrompts ( ) . map ( ( prompt ) => Object . assign ( { } , taskTemplate , {
reqBody : Object . assign ( { prompt : prompt } , taskTemplate . reqBody )
} ) )
newTaskRequests . forEach ( createTask )
2022-10-20 13:52:01 +02:00
2022-12-06 12:34:08 +01:00
initialText . style . display = 'none'
}
2022-09-29 10:13:25 +02:00
2022-12-11 06:52:52 +01:00
async function onIdle ( ) {
2022-12-08 06:42:46 +01:00
const serverCapacity = SD . serverCapacity
2022-12-11 14:57:01 +01:00
if ( pauseClient === true ) {
await resumeClient ( )
}
2022-12-06 12:34:08 +01:00
for ( const taskEntry of getUncompletedTaskEntries ( ) ) {
2022-12-08 06:42:46 +01:00
if ( SD . activeTasks . size >= serverCapacity ) {
break
2022-12-06 12:34:08 +01:00
}
const task = htmlTaskMap . get ( taskEntry )
if ( ! task ) {
const taskStatusLabel = taskEntry . querySelector ( '.taskStatusLabel' )
taskStatusLabel . style . display = 'none'
continue
}
2022-12-11 06:52:52 +01:00
await onTaskStart ( task )
2022-12-06 12:34:08 +01:00
}
}
2022-09-27 14:39:07 +02:00
2022-12-06 12:34:08 +01:00
function getTaskUpdater ( task , reqBody , outputContainer ) {
2022-09-27 14:39:07 +02:00
const outputMsg = task [ 'outputMsg' ]
const progressBar = task [ 'progressBar' ]
2022-10-28 02:03:09 +02:00
const progressBarInner = progressBar . querySelector ( "div" )
2022-09-27 14:39:07 +02:00
2022-12-06 12:34:08 +01:00
const batchCount = task . batchCount
let lastStatus = undefined
return async function ( event ) {
if ( this . status !== lastStatus ) {
lastStatus = this . status
switch ( this . status ) {
case SD . TaskStatus . pending :
task [ 'taskStatusLabel' ] . innerText = "Pending"
task [ 'taskStatusLabel' ] . classList . add ( 'waitingTaskLabel' )
break
case SD . TaskStatus . waiting :
task [ 'taskStatusLabel' ] . innerText = "Waiting"
task [ 'taskStatusLabel' ] . classList . add ( 'waitingTaskLabel' )
task [ 'taskStatusLabel' ] . classList . remove ( 'activeTaskLabel' )
break
case SD . TaskStatus . processing :
case SD . TaskStatus . completed :
task [ 'taskStatusLabel' ] . innerText = "Processing"
task [ 'taskStatusLabel' ] . classList . add ( 'activeTaskLabel' )
task [ 'taskStatusLabel' ] . classList . remove ( 'waitingTaskLabel' )
break
case SD . TaskStatus . stopped :
break
case SD . TaskStatus . failed :
if ( ! SD . isServerAvailable ( ) ) {
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." , event , outputMsg )
} else if ( typeof event ? . response === 'object' ) {
let msg = 'Stable Diffusion had an error reading the response:<br/><pre>'
if ( this . exception ) {
msg += ` Error: ${ this . exception . message } <br/> `
}
try { // 'Response': body stream already read
msg += 'Read: ' + await event . response . text ( )
} catch ( e ) {
msg += 'Unexpected end of stream. '
}
const bufferString = event . reader . bufferedString
if ( bufferString ) {
msg += 'Buffered data: ' + bufferString
}
msg += '</pre>'
logError ( msg , event , outputMsg )
2022-10-12 06:33:00 +02:00
} else {
2022-12-06 12:34:08 +01:00
let msg = ` Unexpected Read Error:<br/><pre>Error: ${ this . exception } <br/>EventInfo: ${ JSON . stringify ( event , undefined , 4 ) } </pre> `
logError ( msg , event , outputMsg )
2022-10-12 06:33:00 +02:00
}
2022-12-06 12:34:08 +01:00
break
2022-10-14 09:47:25 +02:00
}
2022-09-23 16:18:48 +02:00
}
2022-12-06 12:34:08 +01:00
if ( 'update' in event ) {
const stepUpdate = event . update
if ( ! ( 'step' in stepUpdate ) ) {
return
2022-09-23 16:18:48 +02:00
}
2022-12-06 12:34:08 +01:00
// task.instances can be a mix of different tasks with uneven number of steps (Render Vs Filter Tasks)
const overallStepCount = task . instances . reduce (
( sum , instance ) => sum + ( instance . isPending ? Math . max ( 0 , instance . step || stepUpdate . step ) / ( instance . total _steps || stepUpdate . total _steps ) : 1 ) ,
0 // Initial value
) * stepUpdate . total _steps // Scale to current number of steps.
const totalSteps = task . instances . reduce (
( sum , instance ) => sum + ( instance . total _steps || stepUpdate . total _steps ) ,
stepUpdate . total _steps * ( batchCount - task . batchesDone ) // Initial value at (unstarted task count * Nbr of steps)
)
const percent = Math . min ( 100 , 100 * ( overallStepCount / totalSteps ) ) . toFixed ( 0 )
const timeTaken = stepUpdate . step _time // sec
const stepsRemaining = Math . max ( 0 , totalSteps - overallStepCount )
const timeRemaining = ( timeTaken < 0 ? '' : millisecondsToStr ( stepsRemaining * timeTaken * 1000 ) )
outputMsg . innerHTML = ` Batch ${ task . batchesDone } of ${ batchCount } . Generating image(s): ${ percent } %. Time remaining (approx): ${ timeRemaining } `
outputMsg . style . display = 'block'
progressBarInner . style . width = ` ${ percent } % `
if ( stepUpdate . output ) {
showImages ( reqBody , stepUpdate , outputContainer , true )
2022-10-12 02:10:40 +02:00
}
}
2022-12-06 12:34:08 +01:00
}
}
2022-10-12 00:40:05 +02:00
2022-12-06 12:34:08 +01:00
function abortTask ( task ) {
if ( ! task . isProcessing ) {
2022-10-11 22:42:27 +02:00
return false
}
2022-12-06 12:34:08 +01:00
task . isProcessing = false
task . progressBar . classList . remove ( "active" )
task [ 'taskStatusLabel' ] . style . display = 'none'
task [ 'stopTask' ] . innerHTML = '<i class="fa-solid fa-trash-can"></i> Remove'
if ( ! task . instances ? . some ( ( r ) => r . isPending ) ) {
return
}
task . instances . forEach ( ( instance ) => {
try {
instance . abort ( )
} catch ( e ) {
console . error ( e )
}
} )
2022-09-23 16:18:48 +02:00
}
2022-12-06 12:34:08 +01:00
function onTaskErrorHandler ( task , reqBody , instance , reason ) {
if ( ! task . isProcessing ) {
return
}
console . log ( 'Render request %o, Instance: %o, Error: %s' , reqBody , instance , reason )
abortTask ( task )
const outputMsg = task [ 'outputMsg' ]
logError ( 'Stable Diffusion had an error. Please check the logs in the command-line window. <br/><br/>' + reason + '<br/><pre>' + reason . stack + '</pre>' , task , outputMsg )
setStatus ( 'request' , 'error' , 'error' )
}
2022-09-28 09:47:45 +02:00
2022-12-06 12:34:08 +01:00
function onTaskCompleted ( task , reqBody , instance , outputContainer , stepUpdate ) {
2022-12-10 06:50:37 +01:00
if ( typeof stepUpdate === 'object' ) {
if ( stepUpdate . status === 'succeeded' ) {
showImages ( reqBody , stepUpdate , outputContainer , false )
2022-12-06 12:34:08 +01:00
} else {
2022-12-10 06:50:37 +01:00
task . isProcessing = false
const outputMsg = task [ 'outputMsg' ]
let msg = ''
if ( 'detail' in stepUpdate && typeof stepUpdate . detail === 'string' && stepUpdate . detail . length > 0 ) {
msg = stepUpdate . detail
if ( msg . toLowerCase ( ) . includes ( 'out of memory' ) ) {
msg += ` <br/><br/>
< b > Suggestions < / b > :
< br / >
1. If you have set an initial image , please try reducing its dimension to $ { MAX _INIT _IMAGE _DIMENSION } x$ { MAX _INIT _IMAGE _DIMENSION } or smaller . < br / >
2022-12-16 07:04:49 +01:00
2. Try picking a lower level in the '<em>GPU Memory Usage</em>' setting ( in the '<em>Settings</em>' tab ) . < br / >
2022-12-10 06:50:37 +01:00
3. Try generating a smaller image . < br / > `
}
} else {
msg = ` Unexpected Read Error:<br/><pre>StepUpdate: ${ JSON . stringify ( stepUpdate , undefined , 4 ) } </pre> `
}
logError ( msg , stepUpdate , outputMsg )
2022-09-23 16:18:48 +02:00
}
2022-12-06 12:34:08 +01:00
}
if ( task . isProcessing && task . batchesDone < task . batchCount ) {
task [ 'taskStatusLabel' ] . innerText = "Pending"
task [ 'taskStatusLabel' ] . classList . add ( 'waitingTaskLabel' )
task [ 'taskStatusLabel' ] . classList . remove ( 'activeTaskLabel' )
2022-09-27 14:39:07 +02:00
return
2022-09-23 16:18:48 +02:00
}
2022-12-06 12:34:08 +01:00
if ( 'instances' in task && task . instances . some ( ( ins ) => ins != instance && ins . isPending ) ) {
return
2022-11-28 10:19:31 +01:00
}
2022-09-27 14:39:07 +02:00
task . isProcessing = false
task [ 'stopTask' ] . innerHTML = '<i class="fa-solid fa-trash-can"></i> Remove'
2022-09-27 15:07:21 +02:00
task [ 'taskStatusLabel' ] . style . display = 'none'
2022-09-27 14:39:07 +02:00
2022-12-18 07:20:42 +01:00
let time = millisecondsToStr ( Date . now ( ) - task . startTime )
2022-09-27 14:39:07 +02:00
2022-12-06 12:34:08 +01:00
if ( task . batchesDone == task . batchCount ) {
2022-12-14 12:23:50 +01:00
if ( ! task . outputMsg . innerText . toLowerCase ( ) . includes ( 'error' ) ) {
2022-12-18 07:20:42 +01:00
task . outputMsg . innerText = ` Processed ${ task . numOutputsTotal } images in ${ time } `
2022-12-14 12:23:50 +01:00
}
2022-10-28 07:47:08 +02:00
task . progressBar . style . height = "0px"
task . progressBar . style . border = "0px solid var(--background-color3)"
task . progressBar . classList . remove ( "active" )
2022-12-06 12:34:08 +01:00
setStatus ( 'request' , 'done' , 'success' )
2022-09-23 16:18:48 +02:00
} else {
2023-02-03 17:10:08 +01:00
task . outputMsg . innerText += ` . Task ended after ${ time } `
2022-09-23 16:18:48 +02:00
}
2022-12-06 12:34:08 +01:00
if ( randomSeedField . checked ) {
seedField . value = task . seed
}
2022-09-23 16:18:48 +02:00
2022-12-06 12:34:08 +01:00
if ( SD . activeTasks . size > 0 ) {
return
}
const uncompletedTasks = getUncompletedTaskEntries ( )
if ( uncompletedTasks && uncompletedTasks . length > 0 ) {
return
2022-10-11 04:30:17 +02:00
}
2022-09-23 16:18:48 +02:00
2023-02-05 17:33:43 +01:00
if ( pauseClient ) {
resumeBtn . click ( )
}
2022-12-11 14:57:01 +01:00
renderButtons . style . display = 'none'
2022-12-06 12:34:08 +01:00
renameMakeImageButton ( )
2022-09-23 16:18:48 +02:00
2022-12-06 12:34:08 +01:00
if ( isSoundEnabled ( ) ) {
playSound ( )
}
}
2022-10-09 13:17:43 +02:00
2022-12-07 06:54:16 +01:00
2022-12-11 06:52:52 +01:00
async function onTaskStart ( task ) {
2022-12-06 12:34:08 +01:00
if ( ! task . isProcessing || task . batchesDone >= task . batchCount ) {
return
2022-09-23 16:18:48 +02:00
}
2022-12-06 12:34:08 +01:00
if ( typeof task . startTime !== 'number' ) {
task . startTime = Date . now ( )
2022-09-23 16:18:48 +02:00
}
2022-12-06 12:34:08 +01:00
if ( ! ( 'instances' in task ) ) {
task [ 'instances' ] = [ ]
2022-09-23 16:18:48 +02:00
}
2022-12-06 12:34:08 +01:00
task [ 'stopTask' ] . innerHTML = '<i class="fa-solid fa-circle-stop"></i> Stop'
task [ 'taskStatusLabel' ] . innerText = "Starting"
task [ 'taskStatusLabel' ] . classList . add ( 'waitingTaskLabel' )
let newTaskReqBody = task . reqBody
if ( task . batchCount > 1 ) {
// Each output render batch needs it's own task reqBody instance to avoid altering the other runs after they are completed.
newTaskReqBody = Object . assign ( { } , task . reqBody )
2023-02-05 17:09:56 +01:00
if ( task . batchesDone == task . batchCount - 1 ) {
// Last batch of the task
// If the number of parallel jobs is no factor of the total number of images, the last batch must create less than "parallel jobs count" images
// E.g. with numOutputsTotal = 6 and num_outputs = 5, the last batch shall only generate 1 image.
newTaskReqBody . num _outputs = task . numOutputsTotal - task . reqBody . num _outputs * ( task . batchCount - 1 )
}
2022-09-23 16:18:48 +02:00
}
2022-12-06 12:34:08 +01:00
const startSeed = task . seed || newTaskReqBody . seed
const genSeeds = Boolean ( typeof newTaskReqBody . seed !== 'number' || ( newTaskReqBody . seed === task . seed && task . numOutputsTotal > 1 ) )
if ( genSeeds ) {
2023-02-05 17:09:56 +01:00
newTaskReqBody . seed = parseInt ( startSeed ) + ( task . batchesDone * task . reqBody . num _outputs )
2022-10-09 13:17:43 +02:00
}
2022-12-06 12:34:08 +01:00
// Update the seed *before* starting the processing so it's retained if user stops the task
if ( randomSeedField . checked ) {
seedField . value = task . seed
}
2022-11-19 21:00:41 +01:00
2022-12-06 12:34:08 +01:00
const outputContainer = document . createElement ( 'div' )
outputContainer . className = 'img-batch'
task . outputContainer . insertBefore ( outputContainer , task . outputContainer . firstChild )
const eventInfo = { reqBody : newTaskReqBody }
2022-12-11 06:52:52 +01:00
const callbacksPromises = PLUGINS [ 'TASK_CREATE' ] . map ( ( hook ) => {
2022-12-06 12:34:08 +01:00
if ( typeof hook !== 'function' ) {
console . error ( 'The provided TASK_CREATE hook is not a function. Hook: %o' , hook )
2022-12-11 06:52:52 +01:00
return Promise . reject ( new Error ( 'hook is not a function.' ) )
2022-12-06 12:34:08 +01:00
}
try {
2022-12-11 06:52:52 +01:00
return Promise . resolve ( hook . call ( task , eventInfo ) )
2022-12-06 12:34:08 +01:00
} catch ( err ) {
console . error ( err )
2022-12-11 06:52:52 +01:00
return Promise . reject ( err )
2022-12-06 12:34:08 +01:00
}
} )
2022-12-11 06:52:52 +01:00
await Promise . allSettled ( callbacksPromises )
2022-12-06 12:34:08 +01:00
let instance = eventInfo . instance
if ( ! instance ) {
2022-12-07 05:04:04 +01:00
const factory = PLUGINS . OUTPUTS _FORMATS . get ( eventInfo . reqBody ? . output _format || newTaskReqBody . output _format )
2022-12-06 12:34:08 +01:00
if ( factory ) {
2022-12-11 06:52:52 +01:00
instance = await Promise . resolve ( factory ( eventInfo . reqBody || newTaskReqBody ) )
2022-12-06 12:34:08 +01:00
}
if ( ! instance ) {
2022-12-07 05:04:04 +01:00
console . error ( ` ${ factory ? "Factory " + String ( factory ) : 'No factory defined' } for output format ${ eventInfo . reqBody ? . output _format || newTaskReqBody . output _format } . Instance is ${ instance || 'undefined' } . Using default renderer. ` )
instance = new SD . RenderTask ( eventInfo . reqBody || newTaskReqBody )
2022-12-06 12:34:08 +01:00
}
2022-09-23 16:18:48 +02:00
}
2022-12-06 12:34:08 +01:00
task [ 'instances' ] . push ( instance )
task . batchesDone ++
instance . enqueue ( getTaskUpdater ( task , newTaskReqBody , outputContainer ) ) . then (
( renderResult ) => {
onTaskCompleted ( task , newTaskReqBody , instance , outputContainer , renderResult )
} ,
( reason ) => {
onTaskErrorHandler ( task , newTaskReqBody , instance , reason )
}
)
setStatus ( 'request' , 'fetching..' )
2022-12-11 14:57:01 +01:00
renderButtons . style . display = 'flex'
2022-12-06 12:34:08 +01:00
renameMakeImageButton ( )
previewTools . style . display = 'block'
2022-10-09 13:17:43 +02:00
}
2022-09-23 16:18:48 +02:00
2022-12-10 17:17:37 +01:00
/* Hover effect for the init image in the task list */
function createInitImageHover ( taskEntry ) {
var $tooltip = $ ( taskEntry . querySelector ( '.task-fs-initimage' ) )
2022-12-17 01:30:30 +01:00
var img = document . createElement ( 'img' )
img . src = taskEntry . querySelector ( 'div.task-initimg > img' ) . src
$tooltip . append ( img )
$tooltip . append ( ` <div class="top-right"><button>Use as Input</button></div> ` )
2023-02-08 03:02:42 +01:00
$tooltip . find ( 'button' ) . on ( 'click' , ( e ) => {
e . stopPropagation ( )
onUseAsInputClick ( null , img )
} )
2022-12-10 17:17:37 +01:00
}
2022-12-19 20:34:06 +01:00
let startX , startY ;
function onTaskEntryDragOver ( event ) {
imagePreview . querySelectorAll ( ".imageTaskContainer" ) . forEach ( itc => {
if ( itc != event . target . closest ( ".imageTaskContainer" ) ) {
itc . classList . remove ( 'dropTargetBefore' , 'dropTargetAfter' ) ;
}
2022-12-10 17:17:37 +01:00
} ) ;
2022-12-19 20:34:06 +01:00
if ( event . target . closest ( ".imageTaskContainer" ) ) {
if ( startX && startY ) {
if ( event . target . closest ( ".imageTaskContainer" ) . offsetTop > startY ) {
event . target . closest ( ".imageTaskContainer" ) . classList . add ( 'dropTargetAfter' ) ;
} else if ( event . target . closest ( ".imageTaskContainer" ) . offsetTop < startY ) {
event . target . closest ( ".imageTaskContainer" ) . classList . add ( 'dropTargetBefore' ) ;
} else if ( event . target . closest ( ".imageTaskContainer" ) . offsetLeft > startX ) {
event . target . closest ( ".imageTaskContainer" ) . classList . add ( 'dropTargetAfter' ) ;
} else if ( event . target . closest ( ".imageTaskContainer" ) . offsetLeft < startX ) {
event . target . closest ( ".imageTaskContainer" ) . classList . add ( 'dropTargetBefore' ) ;
}
}
}
2022-12-10 17:17:37 +01:00
}
2022-10-09 13:17:43 +02:00
function createTask ( task ) {
2022-12-10 17:17:37 +01:00
let taskConfig = ''
if ( task . reqBody . init _image !== undefined ) {
let h = 80
2022-12-12 11:17:13 +01:00
let w = task . reqBody . width * h / task . reqBody . height >> 0
2022-12-10 17:17:37 +01:00
taskConfig += ` <div class="task-initimg" style="float:left;"><img style="width: ${ w } px;height: ${ h } px;" src=" ${ task . reqBody . init _image } "><div class="task-fs-initimage"></div></div> `
}
2022-12-12 11:17:13 +01:00
taskConfig += ` <b>Seed:</b> ${ task . seed } , <b>Sampler:</b> ${ task . reqBody . sampler _name } , <b>Inference Steps:</b> ${ task . reqBody . num _inference _steps } , <b>Guidance Scale:</b> ${ task . reqBody . guidance _scale } , <b>Model:</b> ${ task . reqBody . use _stable _diffusion _model } `
2022-11-08 12:24:15 +01:00
if ( task . reqBody . use _vae _model . trim ( ) !== '' ) {
2022-11-15 11:36:35 +01:00
taskConfig += ` , <b>VAE:</b> ${ task . reqBody . use _vae _model } `
2022-11-08 12:24:15 +01:00
}
if ( task . reqBody . negative _prompt . trim ( ) !== '' ) {
2022-11-15 11:36:35 +01:00
taskConfig += ` , <b>Negative Prompt:</b> ${ task . reqBody . negative _prompt } `
2022-09-27 15:39:28 +02:00
}
2022-10-09 13:17:43 +02:00
if ( task . reqBody . init _image !== undefined ) {
2022-11-15 11:36:35 +01:00
taskConfig += ` , <b>Prompt Strength:</b> ${ task . reqBody . prompt _strength } `
2022-09-27 14:39:07 +02:00
}
2022-10-10 01:59:34 +02:00
if ( task . reqBody . use _face _correction ) {
2022-11-15 11:36:35 +01:00
taskConfig += ` , <b>Fix Faces:</b> ${ task . reqBody . use _face _correction } `
2022-09-27 14:39:07 +02:00
}
2022-10-10 01:59:34 +02:00
if ( task . reqBody . use _upscale ) {
2022-12-28 08:36:43 +01:00
taskConfig += ` , <b>Upscale:</b> ${ task . reqBody . use _upscale } ( ${ task . reqBody . upscale _amount || 4 } x) `
2022-09-23 16:18:48 +02:00
}
2022-12-07 06:54:16 +01:00
if ( task . reqBody . use _hypernetwork _model ) {
taskConfig += ` , <b>Hypernetwork:</b> ${ task . reqBody . use _hypernetwork _model } `
taskConfig += ` , <b>Hypernetwork Strength:</b> ${ task . reqBody . hypernetwork _strength } `
}
2022-12-11 15:04:07 +01:00
if ( task . reqBody . preserve _init _image _color _profile ) {
taskConfig += ` , <b>Preserve Color Profile:</b> true `
2022-12-09 15:09:56 +01:00
}
2022-09-23 16:18:48 +02:00
2022-09-27 14:39:07 +02:00
let taskEntry = document . createElement ( 'div' )
2022-12-06 12:34:08 +01:00
taskEntry . id = ` imageTaskContainer- ${ Date . now ( ) } `
2022-09-27 14:39:07 +02:00
taskEntry . className = 'imageTaskContainer'
2022-11-09 04:22:14 +01:00
taskEntry . innerHTML = ` <div class="header-content panel collapsible active">
2022-12-19 00:14:57 +01:00
< i class = "drag-handle fa-solid fa-grip" > < / i >
2022-11-09 04:22:14 +01:00
< div class = "taskStatusLabel" > Enqueued < / d i v >
< button class = "secondaryButton stopTask" > < i class = "fa-solid fa-trash-can" > < / i > R e m o v e < / b u t t o n >
2023-02-28 11:07:38 +01:00
< button class = "tertiaryButton useSettings" > < i class = "fa-solid fa-redo" > < / i > U s e t h e s e s e t t i n g s < / b u t t o n >
2022-12-22 09:12:47 +01:00
< div class = "preview-prompt" > < / d i v >
2022-11-09 04:22:14 +01:00
< div class = "taskConfig" > $ { taskConfig } < / d i v >
2022-09-28 10:14:48 +02:00
< div class = "outputMsg" > < / d i v >
2022-10-28 02:03:09 +02:00
< div class = "progress-bar active" > < div > < / d i v > < / d i v >
2022-11-09 04:22:14 +01:00
< / d i v >
< div class = "collapsible-content" >
2022-09-28 10:14:48 +02:00
< div class = "img-preview" >
< / d i v > `
createCollapsibles ( taskEntry )
2022-12-24 10:42:43 +01:00
2022-12-19 00:14:57 +01:00
let draghandle = taskEntry . querySelector ( '.drag-handle' )
2022-12-24 10:42:43 +01:00
draghandle . addEventListener ( 'mousedown' , ( e ) => {
taskEntry . setAttribute ( 'draggable' , true )
} )
// Add a debounce delay to allow mobile to bouble tap.
draghandle . addEventListener ( 'mouseup' , debounce ( ( e ) => {
taskEntry . setAttribute ( 'draggable' , false )
} , 2000 ) )
2022-12-24 10:55:28 +01:00
draghandle . addEventListener ( 'click' , ( e ) => {
e . preventDefault ( ) // Don't allow the results to be collapsed...
} )
2022-12-24 10:42:43 +01:00
taskEntry . addEventListener ( 'dragend' , ( e ) => {
taskEntry . setAttribute ( 'draggable' , false ) ;
2022-12-19 19:45:42 +01:00
imagePreview . querySelectorAll ( ".imageTaskContainer" ) . forEach ( itc => {
itc . classList . remove ( 'dropTargetBefore' , 'dropTargetAfter' ) ;
} ) ;
2022-12-19 20:34:06 +01:00
imagePreview . removeEventListener ( "dragover" , onTaskEntryDragOver ) ;
2022-12-19 19:45:42 +01:00
} )
2022-12-19 00:14:57 +01:00
taskEntry . addEventListener ( 'dragstart' , function ( e ) {
2022-12-19 20:34:06 +01:00
imagePreview . addEventListener ( "dragover" , onTaskEntryDragOver ) ;
2022-12-19 00:14:57 +01:00
e . dataTransfer . setData ( "text/plain" , taskEntry . id ) ;
2022-12-19 19:45:42 +01:00
startX = e . target . closest ( ".imageTaskContainer" ) . offsetLeft ;
startY = e . target . closest ( ".imageTaskContainer" ) . offsetTop ;
2022-12-19 00:14:57 +01:00
} )
2022-09-27 14:39:07 +02:00
2022-12-10 17:17:37 +01:00
if ( task . reqBody . init _image !== undefined ) {
createInitImageHover ( taskEntry )
}
2022-09-27 14:39:07 +02:00
task [ 'taskStatusLabel' ] = taskEntry . querySelector ( '.taskStatusLabel' )
task [ 'outputContainer' ] = taskEntry . querySelector ( '.img-preview' )
task [ 'outputMsg' ] = taskEntry . querySelector ( '.outputMsg' )
task [ 'previewPrompt' ] = taskEntry . querySelector ( '.preview-prompt' )
2022-10-28 02:03:09 +02:00
task [ 'progressBar' ] = taskEntry . querySelector ( '.progress-bar' )
2022-09-27 14:39:07 +02:00
task [ 'stopTask' ] = taskEntry . querySelector ( '.stopTask' )
2022-12-01 09:24:49 +01:00
task [ 'stopTask' ] . addEventListener ( 'click' , ( e ) => {
2022-12-12 15:26:27 +01:00
e . stopPropagation ( )
2022-12-01 09:24:49 +01:00
let question = ( task [ 'isProcessing' ] ? "Stop this task?" : "Remove this task?" )
shiftOrConfirm ( e , question , async function ( e ) {
2022-12-06 12:34:08 +01:00
if ( task . batchesDone <= 0 || ! task . isProcessing ) {
2022-12-13 07:32:43 +01:00
removeTask ( taskEntry )
2022-12-01 09:24:49 +01:00
}
2022-12-06 12:34:08 +01:00
abortTask ( task )
2022-12-01 09:24:49 +01:00
} )
} )
2022-09-23 16:18:48 +02:00
2022-11-11 03:36:39 +01:00
task [ 'useSettings' ] = taskEntry . querySelector ( '.useSettings' )
2022-11-18 11:08:17 +01:00
task [ 'useSettings' ] . addEventListener ( 'click' , function ( e ) {
e . stopPropagation ( )
restoreTaskToUI ( task , TASK _REQ _NO _EXPORT )
2022-11-11 03:36:39 +01:00
} )
2022-12-06 12:34:08 +01:00
task . isProcessing = true
2023-01-16 08:34:56 +01:00
taskEntry = imagePreviewContent . insertBefore ( taskEntry , previewTools . nextSibling )
2022-12-06 12:34:08 +01:00
htmlTaskMap . set ( taskEntry , task )
2022-09-23 16:18:48 +02:00
2022-10-09 13:17:43 +02:00
task . previewPrompt . innerText = task . reqBody . prompt
2022-10-18 15:32:34 +02:00
if ( task . previewPrompt . innerText . trim ( ) === '' ) {
task . previewPrompt . innerHTML = ' ' // allows the results to be collapsed
}
2023-01-04 16:04:52 +01:00
return taskEntry . id
2022-12-06 12:34:08 +01:00
}
function getCurrentUserRequest ( ) {
const numOutputsTotal = parseInt ( numOutputsTotalField . value )
const numOutputsParallel = parseInt ( numOutputsParallelField . value )
const seed = ( randomSeedField . checked ? Math . floor ( Math . random ( ) * 10000000 ) : parseInt ( seedField . value ) )
const newTask = {
batchesDone : 0 ,
numOutputsTotal : numOutputsTotal ,
batchCount : Math . ceil ( numOutputsTotal / numOutputsParallel ) ,
seed ,
reqBody : {
seed ,
2023-02-10 11:59:34 +01:00
used _random _seed : randomSeedField . checked ,
2022-12-06 12:34:08 +01:00
negative _prompt : negativePromptField . value . trim ( ) ,
num _outputs : numOutputsParallel ,
num _inference _steps : parseInt ( numInferenceStepsField . value ) ,
guidance _scale : parseFloat ( guidanceScaleField . value ) ,
width : parseInt ( widthField . value ) ,
height : parseInt ( heightField . value ) ,
// allow_nsfw: allowNSFWField.checked,
2022-12-16 07:04:49 +01:00
vram _usage _level : vramUsageLevelField . value ,
2022-12-06 12:34:08 +01:00
//render_device: undefined, // Set device affinity. Prefer this device, but wont activate.
2023-02-12 14:16:09 +01:00
use _stable _diffusion _model : stableDiffusionModelField . value ,
2022-12-06 12:34:08 +01:00
use _vae _model : vaeModelField . value ,
stream _progress _updates : true ,
stream _image _progress : ( numOutputsTotal > 50 ? false : streamImageProgressField . checked ) ,
show _only _filtered _image : showOnlyFilteredImageField . checked ,
2023-02-18 10:31:13 +01:00
block _nsfw : blockNSFWField . checked ,
2022-12-06 12:34:08 +01:00
output _format : outputFormatField . value ,
output _quality : parseInt ( outputQualityField . value ) ,
2023-01-24 10:53:22 +01:00
metadata _output _format : metadataOutputFormatField . value ,
2022-12-06 12:34:08 +01:00
original _prompt : promptField . value ,
2022-12-28 22:41:36 +01:00
active _tags : ( activeTags . map ( x => x . name ) ) ,
inactive _tags : ( activeTags . filter ( tag => tag . inactive === true ) . map ( x => x . name ) )
2022-12-06 12:34:08 +01:00
}
}
if ( IMAGE _REGEX . test ( initImagePreview . src ) ) {
newTask . reqBody . init _image = initImagePreview . src
newTask . reqBody . prompt _strength = parseFloat ( promptStrengthField . value )
// if (IMAGE_REGEX.test(maskImagePreview.src)) {
// newTask.reqBody.mask = maskImagePreview.src
// }
if ( maskSetting . checked ) {
newTask . reqBody . mask = imageInpainter . getImg ( )
}
2022-12-11 15:04:07 +01:00
newTask . reqBody . preserve _init _image _color _profile = applyColorCorrectionField . checked
2022-12-12 10:51:02 +01:00
newTask . reqBody . sampler _name = 'ddim'
2022-12-06 12:34:08 +01:00
} else {
2022-12-12 10:51:02 +01:00
newTask . reqBody . sampler _name = samplerField . value
2022-12-06 12:34:08 +01:00
}
if ( saveToDiskField . checked && diskPathField . value . trim ( ) !== '' ) {
newTask . reqBody . save _to _disk _path = diskPathField . value . trim ( )
}
if ( useFaceCorrectionField . checked ) {
2023-01-19 20:49:54 +01:00
newTask . reqBody . use _face _correction = gfpganModelField . value
2022-12-06 12:34:08 +01:00
}
if ( useUpscalingField . checked ) {
newTask . reqBody . use _upscale = upscaleModelField . value
2022-12-27 11:50:16 +01:00
newTask . reqBody . upscale _amount = upscaleAmountField . value
2022-12-06 12:34:08 +01:00
}
2022-12-07 08:05:36 +01:00
if ( hypernetworkModelField . value ) {
newTask . reqBody . use _hypernetwork _model = hypernetworkModelField . value
newTask . reqBody . hypernetwork _strength = parseFloat ( hypernetworkStrengthField . value )
}
2022-12-06 12:34:08 +01:00
return newTask
2022-09-23 16:18:48 +02:00
}
2022-12-06 12:34:08 +01:00
function getPrompts ( prompts ) {
if ( typeof prompts === 'undefined' ) {
prompts = promptField . value
}
2022-12-18 02:06:07 +01:00
if ( prompts . trim ( ) === '' && activeTags . length === 0 ) {
2022-10-18 15:32:34 +02:00
return [ '' ]
}
2022-12-18 02:06:07 +01:00
let promptsToMake = [ ]
if ( prompts . trim ( ) !== '' ) {
prompts = prompts . split ( '\n' )
prompts = prompts . map ( prompt => prompt . trim ( ) )
prompts = prompts . filter ( prompt => prompt !== '' )
promptsToMake = applyPermuteOperator ( prompts )
promptsToMake = applySetOperator ( promptsToMake )
}
2022-12-01 10:40:36 +01:00
const newTags = activeTags . filter ( tag => tag . inactive === undefined || tag . inactive === false )
if ( newTags . length > 0 ) {
const promptTags = newTags . map ( x => x . name ) . join ( ", " )
2022-12-18 02:06:07 +01:00
if ( promptsToMake . length > 0 ) {
promptsToMake = promptsToMake . map ( ( prompt ) => ` ${ prompt } , ${ promptTags } ` )
}
else
{
promptsToMake . push ( promptTags )
}
2022-10-30 08:22:01 +01:00
}
2022-11-30 07:48:34 +01:00
2022-10-19 10:20:05 +02:00
promptsToMake = applyPermuteOperator ( promptsToMake )
2022-12-09 18:34:25 +01:00
promptsToMake = applySetOperator ( promptsToMake )
2022-10-19 10:20:05 +02:00
2023-02-08 17:26:55 +01:00
PLUGINS [ 'GET_PROMPTS_HOOK' ] . forEach ( fn => { promptsToMake = fn ( promptsToMake ) } )
2022-10-30 08:22:01 +01:00
return promptsToMake
2022-10-19 10:20:05 +02:00
}
function applySetOperator ( prompts ) {
2022-10-08 12:26:56 +02:00
let promptsToMake = [ ]
2022-10-19 10:20:05 +02:00
let braceExpander = new BraceExpander ( )
2022-10-08 12:26:56 +02:00
prompts . forEach ( prompt => {
2022-10-19 10:20:05 +02:00
let expandedPrompts = braceExpander . expand ( prompt )
promptsToMake = promptsToMake . concat ( expandedPrompts )
} )
return promptsToMake
}
2022-10-08 12:26:56 +02:00
2022-10-19 10:20:05 +02:00
function applyPermuteOperator ( prompts ) {
let promptsToMake = [ ]
prompts . forEach ( prompt => {
2022-10-08 12:26:56 +02:00
let promptMatrix = prompt . split ( '|' )
prompt = promptMatrix . shift ( ) . trim ( )
promptsToMake . push ( prompt )
promptMatrix = promptMatrix . map ( p => p . trim ( ) )
promptMatrix = promptMatrix . filter ( p => p !== '' )
if ( promptMatrix . length > 0 ) {
let promptPermutations = permutePrompts ( prompt , promptMatrix )
promptsToMake = promptsToMake . concat ( promptPermutations )
}
} )
2022-10-19 10:20:05 +02:00
return promptsToMake
2022-10-08 12:26:56 +02:00
}
function permutePrompts ( promptBase , promptMatrix ) {
let prompts = [ ]
let permutations = permute ( promptMatrix )
permutations . forEach ( perm => {
let prompt = promptBase
if ( perm . length > 0 ) {
let promptAddition = perm . join ( ', ' )
if ( promptAddition . trim ( ) === '' ) {
return
}
prompt += ', ' + promptAddition
}
prompts . push ( prompt )
} )
return prompts
}
2022-09-23 16:18:48 +02:00
// create a file name with embedded prompt and metadata
// for easier cateloging and comparison
2022-10-15 04:46:31 +02:00
function createFileName ( prompt , seed , steps , guidance , outputFormat ) {
2022-09-23 16:18:48 +02:00
// Most important information is the prompt
2022-10-15 04:46:31 +02:00
let underscoreName = prompt . replace ( /[^a-zA-Z0-9]/g , '_' )
2022-09-23 16:18:48 +02:00
underscoreName = underscoreName . substring ( 0 , 100 )
2022-10-15 04:46:31 +02:00
//const steps = numInferenceStepsField.value
//const guidance = guidanceScaleField.value
2022-09-23 16:18:48 +02:00
// name and the top level metadata
let fileName = ` ${ underscoreName } _Seed- ${ seed } _Steps- ${ steps } _Guidance- ${ guidance } `
// add the tags
2022-09-29 09:38:42 +02:00
// let tags = []
// let tagString = ''
2022-09-23 16:18:48 +02:00
// document.querySelectorAll(modifyTagsSelector).forEach(function(tag) {
2022-09-29 09:38:42 +02:00
// tags.push(tag.innerHTML)
2022-09-23 16:18:48 +02:00
// })
// join the tags with a pipe
// if (activeTags.length > 0) {
2022-09-29 09:38:42 +02:00
// tagString = '_Tags-'
// tagString += tags.join('|')
2022-09-23 16:18:48 +02:00
// }
// // append empty or populated tags
2022-09-29 09:38:42 +02:00
// fileName += `${tagString}`
2022-09-23 16:18:48 +02:00
// add the file extension
2023-02-19 04:37:34 +01:00
fileName += '.' + outputFormat
2022-09-23 16:18:48 +02:00
return fileName
}
2022-09-27 14:39:07 +02:00
async function stopAllTasks ( ) {
2022-12-06 12:34:08 +01:00
getUncompletedTaskEntries ( ) . forEach ( ( taskEntry ) => {
const taskStatusLabel = taskEntry . querySelector ( '.taskStatusLabel' )
if ( taskStatusLabel ) {
taskStatusLabel . style . display = 'none'
}
const task = htmlTaskMap . get ( taskEntry )
if ( ! task ) {
return
}
abortTask ( task )
2022-09-27 14:39:07 +02:00
} )
}
2022-11-27 21:25:46 +01:00
function removeTask ( taskToRemove ) {
taskToRemove . remove ( )
2022-11-28 09:14:12 +01:00
if ( document . querySelector ( '.imageTaskContainer' ) === null ) {
2022-11-27 21:25:46 +01:00
previewTools . style . display = 'none'
initialText . style . display = 'block'
}
}
2022-12-01 09:24:49 +01:00
clearAllPreviewsBtn . addEventListener ( 'click' , ( e ) => { shiftOrConfirm ( e , "Clear all the results and tasks in this window?" , async function ( ) {
2022-09-27 14:39:07 +02:00
await stopAllTasks ( )
2022-09-23 16:18:48 +02:00
2022-09-27 14:39:07 +02:00
let taskEntries = document . querySelectorAll ( '.imageTaskContainer' )
2022-11-30 11:44:28 +01:00
taskEntries . forEach ( removeTask )
2022-11-22 21:27:36 +01:00
} ) } )
2022-09-27 14:39:07 +02:00
2023-03-14 06:10:24 +01:00
/* Download images popup */
showDownloadPopupBtn . addEventListener ( "click" , ( e ) => { saveAllImagesPopup . classList . add ( "active" ) } )
saveAllZipToggle . addEventListener ( 'change' , ( e ) => {
if ( saveAllZipToggle . checked ) {
saveAllFoldersOption . classList . remove ( 'displayNone' )
} else {
saveAllFoldersOption . classList . add ( 'displayNone' )
}
} )
// convert base64 to raw binary data held in a string
function dataURItoBlob ( dataURI ) {
var byteString = atob ( dataURI . split ( ',' ) [ 1 ] )
// separate out the mime component
var mimeString = dataURI . split ( ',' ) [ 0 ] . split ( ':' ) [ 1 ] . split ( ';' ) [ 0 ]
// write the bytes of the string to an ArrayBuffer
var ab = new ArrayBuffer ( byteString . length )
// create a view into the buffer
var ia = new Uint8Array ( ab )
// set the bytes of the buffer to the correct values
for ( var i = 0 ; i < byteString . length ; i ++ ) {
ia [ i ] = byteString . charCodeAt ( i )
}
// write the ArrayBuffer to a blob, and you're done
return new Blob ( [ ab ] , { type : mimeString } )
}
function downloadAllImages ( ) {
2023-02-21 00:49:04 +01:00
let i = 0
2023-03-14 06:10:24 +01:00
let optZIP = saveAllZipToggle . checked
let optTree = optZIP && saveAllTreeToggle . checked
let optJSON = saveAllJSONToggle . checked
let zip = new JSZip ( )
let folder = zip
2023-02-14 15:03:25 +01:00
document . querySelectorAll ( ".imageTaskContainer" ) . forEach ( container => {
2023-03-14 06:10:24 +01:00
if ( optTree ) {
let name = ++ i + '-' + container . querySelector ( '.preview-prompt' ) . textContent . replace ( /[^a-zA-Z0-9]/g , '_' )
folder = zip . folder ( name )
}
2023-02-14 15:03:25 +01:00
container . querySelectorAll ( ".imgContainer img" ) . forEach ( img => {
2023-03-14 06:10:24 +01:00
let imgItem = img . closest ( '.imgItem' )
if ( imgItem . style . display === 'none' ) {
2023-02-14 15:03:25 +01:00
return
}
2023-03-14 06:10:24 +01:00
let req = imageRequest [ img . dataset [ 'imagecounter' ] ]
if ( optZIP ) {
let suffix = img . dataset [ 'imagecounter' ] + '.' + req [ 'output_format' ]
folder . file ( getDownloadFilename ( img , suffix ) , dataURItoBlob ( img . src ) )
if ( optJSON ) {
suffix = img . dataset [ 'imagecounter' ] + '.json'
folder . file ( getDownloadFilename ( img , suffix ) , JSON . stringify ( req , null , 2 ) )
}
} else {
setTimeout ( ( ) => { imgItem . querySelector ( '.download-img' ) . click ( ) } , i * 200 )
i = i + 1
if ( optJSON ) {
setTimeout ( ( ) => { imgItem . querySelector ( '.download-json' ) . click ( ) } , i * 200 )
i = i + 1
}
}
2023-02-14 15:03:25 +01:00
} )
} )
2023-03-14 06:10:24 +01:00
if ( optZIP ) {
let now = new Date ( )
zip . generateAsync ( { type : "blob" } ) . then ( function ( blob ) {
saveAs ( blob , ` EasyDiffusion-Images- ${ now . toISOString ( ) } .zip ` ) ;
} )
}
}
saveAllImagesBtn . addEventListener ( 'click' , ( e ) => { downloadAllImages ( ) } )
2023-02-14 15:03:25 +01:00
2022-12-01 09:24:49 +01:00
stopImageBtn . addEventListener ( 'click' , ( e ) => { shiftOrConfirm ( e , "Stop all the tasks?" , async function ( e ) {
2022-09-27 14:39:07 +02:00
await stopAllTasks ( )
2022-11-30 09:24:42 +01:00
} ) } )
2022-09-23 16:18:48 +02:00
2022-10-18 18:39:11 +02:00
widthField . addEventListener ( 'change' , onDimensionChange )
heightField . addEventListener ( 'change' , onDimensionChange )
2022-10-21 11:48:05 +02:00
function renameMakeImageButton ( ) {
2023-01-13 22:05:25 +01:00
let totalImages = Math . max ( parseInt ( numOutputsTotalField . value ) , parseInt ( numOutputsParallelField . value ) ) * getPrompts ( ) . length
2022-10-21 11:48:05 +02:00
let imageLabel = 'Image'
if ( totalImages > 1 ) {
imageLabel = totalImages + ' Images'
}
2022-12-06 12:34:08 +01:00
if ( SD . activeTasks . size == 0 ) {
2022-10-21 11:48:05 +02:00
makeImageBtn . innerText = 'Make ' + imageLabel
} else {
makeImageBtn . innerText = 'Enqueue Next ' + imageLabel
}
}
numOutputsTotalField . addEventListener ( 'change' , renameMakeImageButton )
2023-03-14 06:07:55 +01:00
numOutputsTotalField . addEventListener ( 'keyup' , debounce ( renameMakeImageButton , 300 ) )
2022-10-21 11:48:05 +02:00
numOutputsParallelField . addEventListener ( 'change' , renameMakeImageButton )
2023-03-14 06:07:55 +01:00
numOutputsParallelField . addEventListener ( 'keyup' , debounce ( renameMakeImageButton , 300 ) )
2022-10-21 11:48:05 +02:00
2022-10-18 18:39:11 +02:00
function onDimensionChange ( ) {
let widthValue = parseInt ( widthField . value )
let heightValue = parseInt ( heightField . value )
2022-12-01 11:31:09 +01:00
if ( ! initImagePreviewContainer . classList . contains ( "has-image" ) ) {
imageEditor . setImage ( null , widthValue , heightValue )
}
else {
imageInpainter . setImage ( initImagePreview . src , widthValue , heightValue )
}
2023-03-12 20:43:54 +01:00
if ( widthValue < 512 && heightValue < 512 ) {
smallImageWarning . classList . remove ( 'displayNone' )
} else {
smallImageWarning . classList . add ( 'displayNone' )
}
2022-10-18 18:39:11 +02:00
}
2022-09-23 16:18:48 +02:00
2022-10-29 03:25:54 +02:00
diskPathField . disabled = ! saveToDiskField . checked
2023-01-24 10:47:48 +01:00
metadataOutputFormatField . disabled = ! saveToDiskField . checked
2022-09-23 16:18:48 +02:00
2023-01-19 20:49:54 +01:00
gfpganModelField . disabled = ! useFaceCorrectionField . checked
useFaceCorrectionField . addEventListener ( 'change' , function ( e ) {
gfpganModelField . disabled = ! this . checked
} )
2022-10-29 03:25:54 +02:00
upscaleModelField . disabled = ! useUpscalingField . checked
2022-12-27 11:50:16 +01:00
upscaleAmountField . disabled = ! useUpscalingField . checked
2022-10-29 03:25:54 +02:00
useUpscalingField . addEventListener ( 'change' , function ( e ) {
2022-09-23 16:18:48 +02:00
upscaleModelField . disabled = ! this . checked
2022-12-27 11:50:16 +01:00
upscaleAmountField . disabled = ! this . checked
2022-09-23 16:18:48 +02:00
} )
makeImageBtn . addEventListener ( 'click' , makeImage )
2022-11-10 10:29:01 +01:00
document . onkeydown = function ( e ) {
if ( e . ctrlKey && e . code === 'Enter' ) {
makeImage ( )
e . preventDefault ( )
}
}
2022-09-23 16:18:48 +02:00
2022-12-05 06:32:33 +01:00
/********************* Guidance **************************/
2022-09-23 16:18:48 +02:00
function updateGuidanceScale ( ) {
guidanceScaleField . value = guidanceScaleSlider . value / 10
2022-10-20 06:12:01 +02:00
guidanceScaleField . dispatchEvent ( new Event ( "change" ) )
2022-09-23 16:18:48 +02:00
}
function updateGuidanceScaleSlider ( ) {
if ( guidanceScaleField . value < 0 ) {
guidanceScaleField . value = 0
} else if ( guidanceScaleField . value > 50 ) {
guidanceScaleField . value = 50
}
guidanceScaleSlider . value = guidanceScaleField . value * 10
2022-10-20 11:56:18 +02:00
guidanceScaleSlider . dispatchEvent ( new Event ( "change" ) )
2022-09-23 16:18:48 +02:00
}
guidanceScaleSlider . addEventListener ( 'input' , updateGuidanceScale )
guidanceScaleField . addEventListener ( 'input' , updateGuidanceScaleSlider )
updateGuidanceScale ( )
2022-12-05 06:32:33 +01:00
/********************* Prompt Strength *******************/
2022-09-23 16:18:48 +02:00
function updatePromptStrength ( ) {
promptStrengthField . value = promptStrengthSlider . value / 100
2022-10-20 06:12:01 +02:00
promptStrengthField . dispatchEvent ( new Event ( "change" ) )
2022-09-23 16:18:48 +02:00
}
function updatePromptStrengthSlider ( ) {
if ( promptStrengthField . value < 0 ) {
promptStrengthField . value = 0
} else if ( promptStrengthField . value > 0.99 ) {
promptStrengthField . value = 0.99
}
promptStrengthSlider . value = promptStrengthField . value * 100
2022-10-20 11:56:18 +02:00
promptStrengthSlider . dispatchEvent ( new Event ( "change" ) )
2022-09-23 16:18:48 +02:00
}
promptStrengthSlider . addEventListener ( 'input' , updatePromptStrength )
promptStrengthField . addEventListener ( 'input' , updatePromptStrengthSlider )
updatePromptStrength ( )
2022-12-07 06:54:16 +01:00
/********************* Hypernetwork Strength **********************/
function updateHypernetworkStrength ( ) {
hypernetworkStrengthField . value = hypernetworkStrengthSlider . value / 100
hypernetworkStrengthField . dispatchEvent ( new Event ( "change" ) )
}
function updateHypernetworkStrengthSlider ( ) {
if ( hypernetworkStrengthField . value < 0 ) {
hypernetworkStrengthField . value = 0
} else if ( hypernetworkStrengthField . value > 0.99 ) {
hypernetworkStrengthField . value = 0.99
}
hypernetworkStrengthSlider . value = hypernetworkStrengthField . value * 100
hypernetworkStrengthSlider . dispatchEvent ( new Event ( "change" ) )
}
hypernetworkStrengthSlider . addEventListener ( 'input' , updateHypernetworkStrength )
hypernetworkStrengthField . addEventListener ( 'input' , updateHypernetworkStrengthSlider )
updateHypernetworkStrength ( )
2022-12-12 15:01:59 +01:00
function updateHypernetworkStrengthContainer ( ) {
document . querySelector ( "#hypernetwork_strength_container" ) . style . display = ( hypernetworkModelField . value === "" ? 'none' : '' )
}
hypernetworkModelField . addEventListener ( 'change' , updateHypernetworkStrengthContainer )
updateHypernetworkStrengthContainer ( )
2023-02-19 04:37:34 +01:00
/********************* JPEG/WEBP Quality **********************/
2022-12-05 06:32:33 +01:00
function updateOutputQuality ( ) {
outputQualityField . value = 0 | outputQualitySlider . value
outputQualityField . dispatchEvent ( new Event ( "change" ) )
}
function updateOutputQualitySlider ( ) {
if ( outputQualityField . value < 10 ) {
outputQualityField . value = 10
} else if ( outputQualityField . value > 95 ) {
outputQualityField . value = 95
}
outputQualitySlider . value = 0 | outputQualityField . value
outputQualitySlider . dispatchEvent ( new Event ( "change" ) )
}
outputQualitySlider . addEventListener ( 'input' , updateOutputQuality )
2022-12-06 12:34:08 +01:00
outputQualityField . addEventListener ( 'input' , debounce ( updateOutputQualitySlider , 1500 ) )
2022-12-05 06:32:33 +01:00
updateOutputQuality ( )
outputFormatField . addEventListener ( 'change' , e => {
2023-02-19 04:37:34 +01:00
if ( outputFormatField . value === 'png' ) {
2022-12-05 06:32:33 +01:00
outputQualityRow . style . display = 'none'
2023-02-19 04:37:34 +01:00
} else {
outputQualityRow . style . display = 'table-row'
2022-12-05 06:32:33 +01:00
}
} )
2023-02-22 15:02:00 +01:00
/********************* Zoom Slider **********************/
2023-02-22 15:26:45 +01:00
thumbnailSizeField . addEventListener ( 'change' , ( ) => {
2023-02-22 15:02:00 +01:00
( function ( s ) {
for ( var j = 0 ; j < document . styleSheets . length ; j ++ ) {
let cssSheet = document . styleSheets [ j ]
for ( var i = 0 ; i < cssSheet . cssRules . length ; i ++ ) {
var rule = cssSheet . cssRules [ i ] ;
if ( rule . selectorText == "div.img-preview img" ) {
rule . style [ 'max-height' ] = s + 'vh' ;
rule . style [ 'max-width' ] = s + 'vw' ;
return ;
}
}
}
} ) ( thumbnailSizeField . value )
} )
2023-03-01 13:57:48 +01:00
function onAutoScrollUpdate ( ) {
if ( autoScroll . checked ) {
autoscrollBtn . classList . add ( 'pressed' )
} else {
autoscrollBtn . classList . remove ( 'pressed' )
}
autoscrollBtn . querySelector ( ".state" ) . innerHTML = ( autoScroll . checked ? "ON" : "OFF" )
}
autoscrollBtn . addEventListener ( 'click' , function ( ) {
autoScroll . checked = ! autoScroll . checked
autoScroll . dispatchEvent ( new Event ( "change" ) )
onAutoScrollUpdate ( )
} )
autoScroll . addEventListener ( 'change' , onAutoScrollUpdate )
2022-12-05 06:32:33 +01:00
2022-09-23 16:18:48 +02:00
function checkRandomSeed ( ) {
if ( randomSeedField . checked ) {
seedField . disabled = true
2022-11-28 10:19:31 +01:00
//seedField.value = "0" // This causes the seed to be lost if the user changes their mind after toggling the checkbox
2022-09-23 16:18:48 +02:00
} else {
seedField . disabled = false
}
}
randomSeedField . addEventListener ( 'input' , checkRandomSeed )
checkRandomSeed ( )
2022-12-06 09:26:51 +01:00
function loadImg2ImgFromFile ( ) {
2022-09-23 16:18:48 +02:00
if ( initImageSelector . files . length === 0 ) {
return
}
let reader = new FileReader ( )
let file = initImageSelector . files [ 0 ]
2022-10-17 08:10:01 +02:00
reader . addEventListener ( 'load' , function ( event ) {
2022-09-23 16:18:48 +02:00
initImagePreview . src = reader . result
} )
if ( file ) {
reader . readAsDataURL ( file )
}
}
2022-12-06 09:26:51 +01:00
initImageSelector . addEventListener ( 'change' , loadImg2ImgFromFile )
loadImg2ImgFromFile ( )
2022-09-23 16:18:48 +02:00
2022-12-06 09:26:51 +01:00
function img2imgLoad ( ) {
2022-12-01 11:31:09 +01:00
promptStrengthContainer . style . display = 'table-row'
2022-12-06 09:26:51 +01:00
samplerSelectionContainer . style . display = "none"
2022-12-01 11:31:09 +01:00
initImagePreviewContainer . classList . add ( "has-image" )
2022-12-09 15:09:56 +01:00
colorCorrectionSetting . style . display = ''
2022-12-01 11:31:09 +01:00
2022-10-17 08:10:01 +02:00
initImageSizeBox . textContent = initImagePreview . naturalWidth + " x " + initImagePreview . naturalHeight
2022-12-01 11:31:09 +01:00
imageEditor . setImage ( this . src , initImagePreview . naturalWidth , initImagePreview . naturalHeight )
imageInpainter . setImage ( this . src , parseInt ( widthField . value ) , parseInt ( heightField . value ) )
2022-12-06 09:26:51 +01:00
}
2022-09-23 16:18:48 +02:00
2022-12-06 09:26:51 +01:00
function img2imgUnload ( ) {
2022-09-23 16:18:48 +02:00
initImageSelector . value = null
initImagePreview . src = ''
maskSetting . checked = false
2022-12-06 09:26:51 +01:00
promptStrengthContainer . style . display = "none"
samplerSelectionContainer . style . display = ""
2022-12-01 11:31:09 +01:00
initImagePreviewContainer . classList . remove ( "has-image" )
2022-12-09 15:09:56 +01:00
colorCorrectionSetting . style . display = 'none'
2022-12-01 11:31:09 +01:00
imageEditor . setImage ( null , parseInt ( widthField . value ) , parseInt ( heightField . value ) )
2022-12-06 09:26:51 +01:00
}
initImagePreview . addEventListener ( 'load' , img2imgLoad )
initImageClearBtn . addEventListener ( 'click' , img2imgUnload )
2022-09-23 16:18:48 +02:00
maskSetting . addEventListener ( 'click' , function ( ) {
2022-10-18 18:39:11 +02:00
onDimensionChange ( )
2022-09-23 16:18:48 +02:00
} )
2022-10-07 20:16:56 +02:00
promptsFromFileBtn . addEventListener ( 'click' , function ( ) {
promptsFromFileSelector . click ( )
} )
promptsFromFileSelector . addEventListener ( 'change' , function ( ) {
if ( promptsFromFileSelector . files . length === 0 ) {
return
}
let reader = new FileReader ( )
let file = promptsFromFileSelector . files [ 0 ]
reader . addEventListener ( 'load' , function ( ) {
promptField . value = reader . result
} )
if ( file ) {
reader . readAsText ( file )
}
} )
2022-10-29 01:48:32 +02:00
/* setup popup handlers */
document . querySelectorAll ( '.popup' ) . forEach ( popup => {
popup . addEventListener ( 'click' , event => {
if ( event . target == popup ) {
popup . classList . remove ( "active" )
}
} )
var closeButton = popup . querySelector ( ".close-button" )
if ( closeButton ) {
closeButton . addEventListener ( 'click' , ( ) => {
popup . classList . remove ( "active" )
} )
}
} )
2022-12-01 11:31:09 +01:00
var tabElements = [ ]
function selectTab ( tab _id ) {
let tabInfo = tabElements . find ( t => t . tab . id == tab _id )
if ( ! tabInfo . tab . classList . contains ( "active" ) ) {
tabElements . forEach ( info => {
2023-02-06 09:16:40 +01:00
if ( info . tab . classList . contains ( "active" ) && info . tab . parentNode === tabInfo . tab . parentNode ) {
2022-12-01 11:31:09 +01:00
info . tab . classList . toggle ( "active" )
info . content . classList . toggle ( "active" )
}
} )
tabInfo . tab . classList . toggle ( "active" )
tabInfo . content . classList . toggle ( "active" )
}
2022-12-16 10:45:58 +01:00
document . dispatchEvent ( new CustomEvent ( 'tabClick' , { detail : tabInfo } ) )
2022-12-01 11:31:09 +01:00
}
2022-11-16 12:24:28 +01:00
function linkTabContents ( tab ) {
2022-12-01 11:31:09 +01:00
var name = tab . id . replace ( "tab-" , "" )
2022-11-09 04:54:41 +01:00
var content = document . getElementById ( ` tab-content- ${ name } ` )
tabElements . push ( {
name : name ,
tab : tab ,
content : content
} )
2022-12-01 11:31:09 +01:00
tab . addEventListener ( "click" , event => selectTab ( tab . id ) )
2022-11-16 12:24:28 +01:00
}
2023-02-06 09:18:18 +01:00
function isTabActive ( tab ) {
return tab . classList . contains ( "active" )
}
2022-11-16 12:24:28 +01:00
2022-12-11 02:31:23 +01:00
let pauseClient = false
function resumeClient ( ) {
if ( pauseClient ) {
document . body . classList . remove ( 'wait-pause' )
document . body . classList . add ( 'pause' )
}
return new Promise ( resolve => {
let playbuttonclick = function ( ) {
resumeBtn . removeEventListener ( "click" , playbuttonclick ) ;
resolve ( "resolved" ) ;
}
resumeBtn . addEventListener ( "click" , playbuttonclick )
} )
}
2023-01-13 22:05:25 +01:00
promptField . addEventListener ( "input" , debounce ( renameMakeImageButton , 1000 ) )
2022-12-11 02:31:23 +01:00
pauseBtn . addEventListener ( "click" , function ( ) {
pauseClient = true
pauseBtn . style . display = "none"
2022-12-11 14:57:01 +01:00
resumeBtn . style . display = "inline"
2022-12-11 02:31:23 +01:00
document . body . classList . add ( 'wait-pause' )
} )
2022-12-11 14:57:01 +01:00
resumeBtn . addEventListener ( "click" , function ( ) {
pauseClient = false
resumeBtn . style . display = "none"
pauseBtn . style . display = "inline"
document . body . classList . remove ( 'pause' )
document . body . classList . remove ( 'wait-pause' )
} )
2023-03-14 06:10:24 +01:00
/* Pause function */
2022-11-16 12:24:28 +01:00
document . querySelectorAll ( ".tab" ) . forEach ( linkTabContents )
2022-11-09 04:54:41 +01:00
2022-11-10 23:23:20 +01:00
window . addEventListener ( "beforeunload" , function ( e ) {
const msg = "Unsaved pictures will be lost!" ;
let elementList = document . getElementsByClassName ( "imageTaskContainer" ) ;
if ( elementList . length != 0 ) {
e . preventDefault ( ) ;
( e || window . event ) . returnValue = msg ;
return msg ;
} else {
return true ;
}
} ) ;
2022-10-22 05:08:19 +02:00
createCollapsibles ( )
2022-11-19 21:00:41 +01:00
prettifyInputs ( document ) ;
2023-02-22 15:41:19 +01:00
// set the textbox as focused on start
promptField . focus ( )
promptField . selectionStart = promptField . value . length
2023-03-15 02:43:49 +01:00
/ * *
*
* @ param { string } tag
* @ param { object } attributes
* @ param { string | Array < string > } classes
* @ param { string | HTMLElement | Array < string | HTMLElement > }
* @ returns { HTMLElement }
* /
function createElement ( tagName , attributes , classes , textOrElements ) {
const element = document . createElement ( tagName )
if ( attributes ) {
Object . entries ( attributes ) . forEach ( ( [ key , value ] ) => {
element . setAttribute ( key , value )
} ) ;
}
if ( classes ) {
( Array . isArray ( classes ) ? classes : [ classes ] ) . forEach ( className => element . classList . add ( className ) )
}
if ( textOrElements ) {
const children = Array . isArray ( textOrElements ) ? textOrElements : [ textOrElements ]
children . forEach ( textOrElem => {
if ( textOrElem instanceof HTMLElement ) {
element . appendChild ( textOrElem )
} else {
element . appendChild ( document . createTextNode ( textOrElem ) )
}
} )
}
return element
}