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
2023-04-27 19:56:56 +02:00
const IMAGE _REGEX = new RegExp ( "data:image/[A-Za-z]+;base64" )
2022-09-23 16:18:48 +02:00
2023-07-28 19:18:41 +02:00
const spinnerPacmanHtml =
'<div class="loadingio-spinner-bean-eater-x0y3u8qky4n"><div class="ldio-8f673ktaleu"><div><div></div><div></div><div></div></div><div><div></div><div></div><div></div></div></div></div>'
2023-03-29 19:52:41 +02:00
const taskConfigSetup = {
taskConfig : {
2023-04-27 19:56:56 +02:00
seed : { value : ( { seed } ) => seed , label : "Seed" } ,
dimensions : { value : ( { reqBody } ) => ` ${ reqBody ? . width } x ${ reqBody ? . height } ` , label : "Dimensions" } ,
sampler _name : "Sampler" ,
2024-10-09 08:13:45 +02:00
scheduler _name : {
label : "Scheduler" ,
visible : ( { reqBody } ) => reqBody ? . scheduler _name ,
} ,
2023-04-27 19:56:56 +02:00
num _inference _steps : "Inference Steps" ,
guidance _scale : "Guidance Scale" ,
2024-10-09 07:42:28 +02:00
distilled _guidance _scale : {
label : "Distilled Guidance Scale" ,
visible : ( { reqBody } ) => reqBody ? . distilled _guidance _scale ,
} ,
2023-04-27 19:56:56 +02:00
use _stable _diffusion _model : "Model" ,
2023-05-18 13:55:45 +02:00
clip _skip : {
label : "Clip Skip" ,
visible : ( { reqBody } ) => reqBody ? . clip _skip ,
value : ( { reqBody } ) => "yes" ,
} ,
2023-05-25 00:16:14 +02:00
tiling : {
label : "Tiling" ,
2023-08-30 16:02:29 +02:00
visible : ( { reqBody } ) =>
reqBody ? . tiling != "none" && reqBody ? . tiling !== null && reqBody ? . tiling !== undefined ,
2023-05-25 00:16:14 +02:00
value : ( { reqBody } ) => reqBody ? . tiling ,
} ,
2023-04-27 19:56:56 +02:00
use _vae _model : {
label : "VAE" ,
2023-04-28 12:20:44 +02:00
visible : ( { reqBody } ) => reqBody ? . use _vae _model !== undefined && reqBody ? . use _vae _model . trim ( ) !== "" ,
2023-04-27 19:56:56 +02:00
} ,
negative _prompt : {
label : "Negative Prompt" ,
2023-04-28 12:20:44 +02:00
visible : ( { reqBody } ) => reqBody ? . negative _prompt !== undefined && reqBody ? . negative _prompt . trim ( ) !== "" ,
2023-04-27 19:56:56 +02:00
} ,
prompt _strength : "Prompt Strength" ,
use _face _correction : "Fix Faces" ,
upscale : {
value : ( { reqBody } ) => ` ${ reqBody ? . use _upscale } ( ${ reqBody ? . upscale _amount || 4 } x) ` ,
label : "Upscale" ,
2023-04-28 12:20:44 +02:00
visible : ( { reqBody } ) => ! ! reqBody ? . use _upscale ,
2023-04-27 19:56:56 +02:00
} ,
use _hypernetwork _model : "Hypernetwork" ,
hypernetwork _strength : {
label : "Hypernetwork Strength" ,
2023-04-28 12:20:44 +02:00
visible : ( { reqBody } ) => ! ! reqBody ? . use _hypernetwork _model ,
2023-04-27 19:56:56 +02:00
} ,
use _lora _model : { label : "Lora Model" , visible : ( { reqBody } ) => ! ! reqBody ? . use _lora _model } ,
lora _alpha : { label : "Lora Strength" , visible : ( { reqBody } ) => ! ! reqBody ? . use _lora _model } ,
2023-04-28 12:20:44 +02:00
preserve _init _image _color _profile : "Preserve Color Profile" ,
2023-07-30 10:21:19 +02:00
strict _mask _border : "Strict Mask Border" ,
2023-08-18 10:49:50 +02:00
use _controlnet _model : "ControlNet Model" ,
2024-05-28 15:15:08 +02:00
control _alpha : {
label : "ControlNet Strength" ,
visible : ( { reqBody } ) => ! ! reqBody ? . use _controlnet _model ,
} ,
2023-03-29 19:52:41 +02:00
} ,
pluginTaskConfig : { } ,
2023-04-27 19:56:56 +02:00
getCSSKey : ( key ) =>
key
. split ( "_" )
. map ( ( s ) => s . charAt ( 0 ) . toUpperCase ( ) + s . slice ( 1 ) )
2023-04-28 12:20:44 +02:00
. join ( "" ) ,
2023-03-29 19:52:41 +02:00
}
2023-03-14 06:10:24 +01:00
let imageCounter = 0
let imageRequest = [ ]
2023-04-27 19:56:56 +02:00
let promptField = document . querySelector ( "#prompt" )
let promptsFromFileSelector = document . querySelector ( "#prompt_from_file" )
let promptsFromFileBtn = document . querySelector ( "#promptsFromFileBtn" )
let negativePromptField = document . querySelector ( "#negative_prompt" )
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" )
2024-10-09 07:42:28 +02:00
let distilledGuidanceScaleSlider = document . querySelector ( "#distilled_guidance_scale_slider" )
let distilledGuidanceScaleField = document . querySelector ( "#distilled_guidance_scale" )
2023-04-27 19:56:56 +02: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" )
2023-04-27 19:56:56 +02:00
let seedField = document . querySelector ( "#seed" )
let widthField = document . querySelector ( "#width" )
let heightField = document . querySelector ( "#height" )
2023-07-30 06:46:04 +02:00
let customWidthField = document . querySelector ( "#custom-width" )
let customHeightField = document . querySelector ( "#custom-height" )
2023-07-29 18:12:48 +02:00
let recentResolutionsButton = document . querySelector ( "#recent-resolutions-button" )
let recentResolutionsPopup = document . querySelector ( "#recent-resolutions-popup" )
let recentResolutionList = document . querySelector ( "#recent-resolution-list" )
2023-08-14 14:56:33 +02:00
let commonResolutionList = document . querySelector ( "#common-resolution-list" )
let resizeSlider = document . querySelector ( "#resize-slider" )
let enlargeButtons = document . querySelector ( "#enlarge-buttons" )
2023-07-29 18:12:48 +02:00
let swapWidthHeightButton = document . querySelector ( "#swap-width-height" )
2023-04-27 19:56:56 +02: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" )
2023-08-01 12:09:15 +02:00
let controlImageSelector = document . querySelector ( "#control_image" )
let controlImagePreview = document . querySelector ( "#control_image_preview" )
let controlImageClearBtn = document . querySelector ( ".control_image_clear" )
let controlImageContainer = document . querySelector ( "#control_image_wrapper" )
let controlImageFilterField = document . querySelector ( "#control_image_filter" )
2024-05-28 15:15:08 +02:00
let controlAlphaSlider = document . querySelector ( "#controlnet_alpha_slider" )
let controlAlphaField = document . querySelector ( "#controlnet_alpha" )
2023-04-27 19:56:56 +02:00
let applyColorCorrectionField = document . querySelector ( "#apply_color_correction" )
2023-07-30 10:21:19 +02:00
let strictMaskBorderField = document . querySelector ( "#strict_mask_border" )
2023-04-27 19:56:56 +02:00
let colorCorrectionSetting = document . querySelector ( "#apply_color_correction_setting" )
2023-07-30 10:21:19 +02:00
let strictMaskBorderSetting = document . querySelector ( "#strict_mask_border_setting" )
2023-04-27 19:56:56 +02:00
let promptStrengthSlider = document . querySelector ( "#prompt_strength_slider" )
let promptStrengthField = document . querySelector ( "#prompt_strength" )
let samplerField = document . querySelector ( "#sampler_name" )
2022-09-23 16:18:48 +02:00
let samplerSelectionContainer = document . querySelector ( "#samplerSelection" )
2024-10-09 08:13:45 +02:00
let schedulerField = document . querySelector ( "#scheduler_name" )
let schedulerSelectionContainer = document . querySelector ( "#schedulerSelection" )
2022-09-23 16:18:48 +02:00
let useFaceCorrectionField = document . querySelector ( "#use_face_correction" )
2023-06-08 12:39:11 +02:00
let gfpganModelField = new ModelDropdown ( document . querySelector ( "#gfpgan_model" ) , [ "gfpgan" , "codeformer" ] , "" , false )
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-05-23 13:23:53 +02:00
let latentUpscalerSettings = document . querySelector ( "#latent_upscaler_settings" )
let latentUpscalerStepsSlider = document . querySelector ( "#latent_upscaler_steps_slider" )
let latentUpscalerStepsField = document . querySelector ( "#latent_upscaler_steps" )
2023-06-06 12:46:21 +02:00
let codeformerFidelitySlider = document . querySelector ( "#codeformer_fidelity_slider" )
let codeformerFidelityField = document . querySelector ( "#codeformer_fidelity" )
2023-04-27 19:56:56 +02:00
let stableDiffusionModelField = new ModelDropdown ( document . querySelector ( "#stable_diffusion_model" ) , "stable-diffusion" )
2023-05-18 13:55:45 +02:00
let clipSkipField = document . querySelector ( "#clip_skip" )
2023-05-25 00:16:14 +02:00
let tilingField = document . querySelector ( "#tiling" )
2023-08-01 14:09:04 +02:00
let controlnetModelField = new ModelDropdown ( document . querySelector ( "#controlnet_model" ) , "controlnet" , "None" , false )
2023-04-27 19:56:56 +02:00
let vaeModelField = new ModelDropdown ( document . querySelector ( "#vae_model" ) , "vae" , "None" )
2023-08-18 09:48:06 +02:00
let loraModelField = new MultiModelSelector ( document . querySelector ( "#lora_model" ) , "lora" , "LoRA" , 0.5 , 0.02 )
2023-04-27 19:56:56 +02:00
let hypernetworkModelField = new ModelDropdown ( document . querySelector ( "#hypernetwork_model" ) , "hypernetwork" , "None" )
let hypernetworkStrengthSlider = document . querySelector ( "#hypernetwork_strength_slider" )
let hypernetworkStrengthField = document . querySelector ( "#hypernetwork_strength" )
let outputFormatField = document . querySelector ( "#output_format" )
let outputLosslessField = document . querySelector ( "#output_lossless" )
let outputLosslessContainer = document . querySelector ( "#output_lossless_container" )
2023-12-11 17:58:19 +01:00
let enableVAETilingField = document . querySelector ( "#enable_vae_tiling" )
2023-04-27 19:56:56 +02: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" )
2023-06-30 08:41:15 +02:00
let embeddingsButton = document . querySelector ( "#embeddings-button" )
2023-07-16 11:00:45 +02:00
let negativeEmbeddingsButton = document . querySelector ( "#negative-embeddings-button" )
2023-06-30 08:41:15 +02:00
let embeddingsDialog = document . querySelector ( "#embeddings-dialog" )
let embeddingsDialogCloseBtn = embeddingsDialog . querySelector ( "#embeddings-dialog-close-button" )
let embeddingsSearchBox = document . querySelector ( "#embeddings-search-box" )
let embeddingsList = document . querySelector ( "#embeddings-list" )
let embeddingsModeField = document . querySelector ( "#embeddings-mode" )
2023-08-17 08:03:05 +02:00
let embeddingsCardSizeSelector = document . querySelector ( "#embedding-card-size-selector" )
2023-08-19 06:20:40 +02:00
let addEmbeddingsThumb = document . querySelector ( "#add-embeddings-thumb" )
let addEmbeddingsThumbInput = document . querySelector ( "#add-embeddings-thumb-input" )
2023-07-18 05:41:19 +02:00
2023-07-16 11:00:45 +02:00
let positiveEmbeddingText = document . querySelector ( "#positive-embedding-text" )
let negativeEmbeddingText = document . querySelector ( "#negative-embedding-text" )
2023-07-18 00:08:38 +02:00
let embeddingsCollapsiblesBtn = document . querySelector ( "#embeddings-action-collapsibles-btn" )
2022-09-23 16:18:48 +02:00
2023-04-27 19:56:56 +02:00
let makeImageBtn = document . querySelector ( "#makeImage" )
let stopImageBtn = document . querySelector ( "#stopImage" )
let renderButtons = document . querySelector ( "#render-buttons" )
2022-09-23 16:18:48 +02:00
2023-04-27 19:56:56 +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-23 16:18:48 +02:00
2022-09-27 14:39:07 +02:00
let initialText = document . querySelector ( "#initial-text" )
2023-08-23 16:20:38 +02:00
let supportBanner = document . querySelector ( "#supportBanner" )
2023-04-22 11:42:22 +02:00
let versionText = document . querySelector ( "#version" )
2022-09-27 14:39:07 +02:00
let previewTools = document . querySelector ( "#preview-tools" )
let clearAllPreviewsBtn = document . querySelector ( "#clear-all-previews" )
2023-07-04 11:29:50 +02:00
let showDownloadDialogBtn = document . querySelector ( "#show-download-popup" )
2023-06-28 19:11:37 +02:00
let saveAllImagesDialog = document . querySelector ( "#download-images-dialog" )
2023-02-14 15:03:25 +01:00
let saveAllImagesBtn = document . querySelector ( "#save-all-images" )
2023-06-28 19:11:37 +02:00
let saveAllImagesCloseBtn = document . querySelector ( "#download-images-close-button" )
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" )
2023-04-22 11:42:22 +02:00
let splashScreenPopup = document . querySelector ( "#splash-screen" )
2023-08-17 08:03:05 +02:00
let useAsThumbDialog = document . querySelector ( "#use-as-thumb-dialog" )
let useAsThumbDialogCloseBtn = document . querySelector ( "#use-as-thumb-dialog-close-button" )
let useAsThumbImageContainer = document . querySelector ( "#use-as-thumb-img-container" )
let useAsThumbSelect = document . querySelector ( "#use-as-thumb-select" )
let useAsThumbSaveBtn = document . querySelector ( "#use-as-thumb-save" )
let useAsThumbCancelBtn = document . querySelector ( "#use-as-thumb-cancel" )
2022-09-27 14:39:07 +02:00
2023-04-27 19:56:56 +02:00
let maskSetting = document . querySelector ( "#enable_mask" )
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" )
2023-03-20 22:53:13 +01:00
let undoButton = document . querySelector ( "#undo" )
let undoBuffer = [ ]
2023-03-21 01:31:12 +01:00
const UNDO _LIMIT = 20
2023-07-29 06:37:41 +02:00
const MAX _IMG _UNDO _ENTRIES = 5
2023-03-20 22:53:13 +01:00
2023-08-03 14:10:26 +02:00
let IMAGE _STEP _SIZE = 64
2023-07-15 16:03:49 +02:00
let loraModels = [ ]
2023-04-27 19:56:56 +02:00
imagePreview . addEventListener ( "drop" , function ( ev ) {
const data = ev . dataTransfer ? . getData ( "text/plain" )
2022-12-06 12:34:08 +01:00
if ( ! data ) {
return
}
const movedTask = document . getElementById ( data )
if ( ! movedTask ) {
return
}
ev . preventDefault ( )
let moveTarget = ev . target
2023-04-27 19:56:56 +02: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
2023-04-27 19:56:56 +02:00
let showConfigToggle = document . querySelector ( "#configToggleBtn" )
2022-12-06 12:34:08 +01:00
// let configBox = document.querySelector('#config')
// let outputMsg = document.querySelector('#outputMsg')
2022-09-24 14:01:36 +02:00
2023-04-27 19:56:56 +02:00
let soundToggle = document . querySelector ( "#sound_toggle" )
2022-09-27 14:39:07 +02:00
2023-04-27 19:56:56 +02: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
}
2023-04-27 19:56:56 +02:00
return item === "true" ? true : false
2022-09-23 16:18:48 +02:00
}
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
}
2023-04-27 19:56:56 +02:00
function setStatus ( statusType , msg , msgType ) { }
2022-09-23 16:18:48 +02:00
2022-12-06 12:34:08 +01:00
function setServerStatus ( event ) {
2023-04-27 19:56:56 +02:00
switch ( event . type ) {
case "online" :
serverStatusColor . style . color = "var(--status-green)"
serverStatusMsg . style . color = "var(--status-green)"
serverStatusMsg . innerText = "Stable Diffusion is " + event . message
2022-10-14 09:47:25 +02:00
break
2023-04-27 19:56:56 +02:00
case "busy" :
serverStatusColor . style . color = "var(--status-orange)"
serverStatusMsg . style . color = "var(--status-orange)"
serverStatusMsg . innerText = "Stable Diffusion is " + event . message
2022-10-14 09:47:25 +02:00
break
2023-04-27 19:56:56 +02:00
case "error" :
serverStatusColor . style . color = "var(--status-red)"
serverStatusMsg . style . color = "var(--status-red)"
serverStatusMsg . innerText = "Stable Diffusion has stopped"
2022-10-14 09:47:25 +02:00
break
}
2022-12-06 12:34:08 +01:00
if ( SD . serverState . devices ) {
2023-05-23 13:23:53 +02:00
document . dispatchEvent ( new CustomEvent ( "system_info_update" , { detail : 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
2023-07-29 17:39:27 +02:00
// allowSkip: Allow skipping the dialog using the shift key or the confirm_dangerous_actions setting (default: true)
2022-11-22 21:27:36 +01:00
//
// If the user had the shift key pressed while clicking, the function fn will be executed.
2023-04-27 19:56:56 +02:00
// If the setting "confirm_dangerous_actions" in the system settings is disabled, the function
2022-11-23 23:05:30 +01:00
// 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.
2023-07-29 17:39:27 +02:00
function shiftOrConfirm ( e , prompt , fn , allowSkip = true ) {
2022-11-22 21:27:36 +01:00
e . stopPropagation ( )
2023-07-29 17:39:27 +02:00
let tip = allowSkip
? '<small>Tip: To skip this dialog, use shift-click or disable the "Confirm dangerous actions" setting in the Settings tab.</small>'
: ""
if ( allowSkip && ( e . shiftKey || ! confirmDangerousActionsField . checked ) ) {
2023-04-27 19:56:56 +02:00
fn ( e )
2022-11-22 21:27:36 +01:00
} else {
2023-07-29 17:39:27 +02:00
confirm ( tip , prompt , ( ) => {
fn ( e )
} )
2022-11-22 21:27:36 +01:00
}
}
2023-04-27 19:56:56 +02:00
function undoableRemove ( element , doubleUndo = false ) {
let data = {
element : element ,
parent : element . parentNode ,
prev : element . previousSibling ,
next : element . nextSibling ,
2023-04-28 12:20:44 +02:00
doubleUndo : doubleUndo ,
2023-04-27 19:56:56 +02:00
}
2023-03-20 22:53:13 +01:00
undoBuffer . push ( data )
2023-03-21 01:31:12 +01:00
if ( undoBuffer . length > UNDO _LIMIT ) {
2023-03-20 22:53:13 +01:00
// Remove item from memory and also remove it from the data structures
let item = undoBuffer . shift ( )
htmlTaskMap . delete ( item . element )
2023-04-27 19:56:56 +02:00
item . element . querySelectorAll ( "[data-imagecounter]" ) . forEach ( ( img ) => {
delete imageRequest [ img . dataset [ "imagecounter" ] ]
} )
2023-03-20 22:53:13 +01:00
}
element . remove ( )
if ( undoBuffer . length != 0 ) {
2023-04-27 19:56:56 +02:00
undoButton . classList . remove ( "displayNone" )
2023-03-20 22:53:13 +01:00
}
}
function undoRemove ( ) {
let data = undoBuffer . pop ( )
2023-03-28 13:43:37 +02:00
if ( ! data ) {
return
}
2023-03-20 22:53:13 +01:00
if ( data . next == null ) {
data . parent . appendChild ( data . element )
} else {
data . parent . insertBefore ( data . element , data . next )
}
if ( data . doubleUndo ) {
undoRemove ( )
}
if ( undoBuffer . length == 0 ) {
2023-04-27 19:56:56 +02:00
undoButton . classList . add ( "displayNone" )
2023-03-20 22:53:13 +01:00
}
updateInitialText ( )
}
2023-04-27 19:56:56 +02:00
undoButton . addEventListener ( "click" , ( ) => {
undoRemove ( )
} )
2023-03-20 22:53:13 +01:00
2023-04-27 19:56:56 +02:00
document . addEventListener ( "keydown" , function ( e ) {
if ( ( e . ctrlKey || e . metaKey ) && e . key === "z" && e . target == document . body ) {
2023-04-01 12:39:51 +02:00
undoRemove ( )
}
} )
2022-10-10 08:32:27 +02:00
function showImages ( reqBody , res , outputContainer , livePreview ) {
2023-04-27 19:56:56 +02:00
let imageItemElements = outputContainer . querySelectorAll ( ".imgItem" )
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 ) => {
2023-04-27 19:56:56 +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 ,
2023-04-27 19:56:56 +02:00
imageHeight = reqBody . height
2022-10-15 04:46:31 +02:00
2023-04-27 19:56:56 +02:00
if ( ! imageData . includes ( "/" ) ) {
2022-09-27 16:23:19 +02:00
// res contained no data for the image, stop execution
2023-04-27 19:56:56 +02:00
setStatus ( "request" , "invalid image" , "error" )
2022-09-29 09:38:42 +02:00
return
2022-09-25 01:55:11 +02:00
}
2023-04-27 19:56:56 +02:00
let imageItemElem = index < imageItemElements . length ? imageItemElements [ index ] : null
if ( ! imageItemElem ) {
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 >
2023-07-28 19:18:41 +02:00
< div class = "spinner displayNone" > < center > $ { spinnerPacmanHtml } < / c e n t e r > < d i v c l a s s = " s p i n n e r S t a t u s " > < / d i v > < / d i v >
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-04-27 19:56:56 +02:00
const imageRemoveBtn = imageItemElem . querySelector ( ".imgPreviewItemClearBtn" )
let parentTaskContainer = imageRemoveBtn . closest ( ".imageTaskContainer" )
imageRemoveBtn . addEventListener ( "click" , ( e ) => {
2023-03-20 22:53:13 +01:00
undoableRemove ( imageItemElem )
2023-04-27 19:56:56 +02:00
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
}
2023-03-20 22:53:13 +01:00
}
2023-04-27 19:56:56 +02:00
if ( allHidden === true ) {
2023-03-20 22:53:13 +01:00
const req = htmlTaskMap . get ( parentTaskContainer )
2023-04-27 19:56:56 +02:00
if ( ! req . isProcessing || req . batchesDone == req . batchCount ) {
undoableRemove ( parentTaskContainer , true )
}
2023-03-20 22:53:13 +01:00
}
2023-02-17 00:54:41 +01:00
} )
2022-10-10 08:32:27 +02:00
}
2023-04-27 19:56:56 +02:00
const imageElem = imageItemElem . querySelector ( "img" )
2022-10-10 08:32:27 +02:00
imageElem . src = imageData
imageElem . width = parseInt ( imageWidth )
imageElem . height = parseInt ( imageHeight )
2023-04-27 19:56:56 +02:00
imageElem . setAttribute ( "data-prompt" , imagePrompt )
imageElem . setAttribute ( "data-steps" , imageInferenceSteps )
imageElem . setAttribute ( "data-guidance" , imageGuidanceScale )
2022-10-15 04:46:31 +02:00
2023-04-27 19:56:56 +02:00
imageElem . addEventListener ( "load" , function ( ) {
imageItemElem . querySelector ( ".img_bottom_label" ) . innerText = ` ${ this . naturalWidth } x ${ this . naturalHeight } `
2023-03-01 10:52:38 +01:00
} )
2023-04-27 19:56:56 +02:00
const imageInfo = imageItemElem . querySelector ( ".imgItemInfo" )
imageInfo . style . visibility = livePreview ? "hidden" : "visible"
2022-10-10 08:32:27 +02:00
2023-04-27 19:56:56 +02:00
if ( "seed" in result && ! imageElem . hasAttribute ( "data-seed" ) ) {
const imageExpandBtn = imageItemElem . querySelector ( ".imgExpandBtn" )
imageExpandBtn . addEventListener ( "click" , function ( ) {
2023-04-04 01:35:37 +02:00
function previousImage ( img ) {
2023-04-27 19:56:56 +02:00
const allImages = Array . from ( outputContainer . parentNode . querySelectorAll ( ".imgItem img" ) )
2023-04-04 01:35:37 +02:00
const index = allImages . indexOf ( img )
return allImages . slice ( 0 , index ) . reverse ( ) [ 0 ]
}
function nextImage ( img ) {
2023-04-27 19:56:56 +02:00
const allImages = Array . from ( outputContainer . parentNode . querySelectorAll ( ".imgItem img" ) )
2023-04-04 01:35:37 +02:00
const index = allImages . indexOf ( img )
return allImages . slice ( index + 1 ) [ 0 ]
}
function imageModalParameter ( img ) {
const previousImg = previousImage ( img )
const nextImg = nextImage ( img )
return {
src : img . src ,
previous : previousImg ? ( ) => imageModalParameter ( previousImg ) : undefined ,
next : nextImg ? ( ) => imageModalParameter ( nextImg ) : undefined ,
}
}
imageModal ( imageModalParameter ( imageElem ) )
2023-03-16 00:08:24 +01:00
} )
2022-10-10 08:32:27 +02:00
const req = Object . assign ( { } , reqBody , {
2023-04-28 12:20:44 +02:00
seed : result ? . seed || reqBody . seed ,
2022-10-10 08:32:27 +02:00
} )
2023-04-27 19:56:56 +02:00
imageElem . setAttribute ( "data-seed" , req . seed )
imageElem . setAttribute ( "data-imagecounter" , ++ imageCounter )
2023-03-14 06:10:24 +01:00
imageRequest [ imageCounter ] = req
2023-04-27 19:56:56 +02:00
const imageSeedLabel = imageItemElem . querySelector ( ".imgSeedLabel" )
imageSeedLabel . innerText = "Seed: " + req . seed
2022-10-10 08:32:27 +02:00
2023-07-28 19:18:41 +02:00
const imageUndoBuffer = [ ]
2023-07-29 06:37:41 +02:00
const imageRedoBuffer = [ ]
2022-10-19 17:51:19 +02:00
let buttons = [
2023-04-27 19:56:56 +02:00
{ text : "Use as Input" , on _click : onUseAsInputClick } ,
2023-08-16 06:53:12 +02:00
{ text : "Use for Controlnet" , on _click : onUseForControlnetClick } ,
2023-03-15 15:11:19 +01:00
[
2023-04-27 19:56:56 +02:00
{
html : '<i class="fa-solid fa-download"></i> Download Image' ,
on _click : onDownloadImageClick ,
2023-04-28 12:20:44 +02:00
class : "download-img" ,
2023-04-27 19:56:56 +02:00
} ,
{
html : '<i class="fa-solid fa-download"></i> JSON' ,
on _click : onDownloadJSONClick ,
2023-04-28 12:20:44 +02:00
class : "download-json" ,
} ,
2023-03-15 15:11:19 +01:00
] ,
2023-04-27 19:56:56 +02:00
{ text : "Make Similar Images" , on _click : onMakeSimilarClick } ,
{ text : "Draw another 25 steps" , on _click : onContinueDrawingClick } ,
2023-03-15 15:11:19 +01:00
[
2023-07-29 06:37:41 +02:00
{ html : '<i class="fa-solid fa-undo"></i> Undo' , on _click : onUndoFilter } ,
{ html : '<i class="fa-solid fa-redo"></i> Redo' , on _click : onRedoFilter } ,
2023-07-28 19:18:41 +02:00
{ text : "Upscale" , on _click : onUpscaleClick } ,
{ text : "Fix Faces" , on _click : onFixFacesClick } ,
2023-04-28 12:20:44 +02:00
] ,
2023-10-12 06:28:29 +02:00
{
2023-08-21 07:34:13 +02:00
text : "Use as Thumbnail" ,
on _click : onUseAsThumbnailClick ,
2023-10-12 06:28:29 +02:00
filter : ( req , img ) => "use_embeddings_model" in req || "use_lora_model" in req
2023-08-21 07:34:13 +02:00
} ,
2022-10-19 17:51:19 +02:00
]
2022-10-19 16:20:05 +02:00
// include the plugins
2023-04-27 19:56:56 +02:00
buttons = buttons . concat ( PLUGINS [ "IMAGE_INFO_BUTTONS" ] )
2022-10-19 16:20:05 +02:00
2023-04-27 19:56:56 +02:00
const imgItemInfo = imageItemElem . querySelector ( ".imgItemInfo" )
const img = imageItemElem . querySelector ( "img" )
2023-07-28 19:18:41 +02:00
const spinner = imageItemElem . querySelector ( ".spinner" )
const spinnerStatus = imageItemElem . querySelector ( ".spinnerStatus" )
const tools = {
spinner : spinner ,
spinnerStatus : spinnerStatus ,
undoBuffer : imageUndoBuffer ,
2023-07-29 06:37:41 +02:00
redoBuffer : imageRedoBuffer ,
2023-07-28 19:18:41 +02:00
}
2022-10-19 17:51:19 +02:00
const createButton = function ( btnInfo ) {
2023-03-15 14:14:43 +01:00
if ( Array . isArray ( btnInfo ) ) {
2023-04-27 19:56:56 +02:00
const wrapper = document . createElement ( "div" )
btnInfo . map ( createButton ) . forEach ( ( buttonElement ) => wrapper . appendChild ( buttonElement ) )
2023-03-15 14:14:43 +01:00
return wrapper
}
2023-04-27 19:56:56 +02:00
const isLabel = btnInfo . type === "label"
2023-03-15 14:14:43 +01:00
2023-04-27 19:56:56 +02:00
const newButton = document . createElement ( isLabel ? "span" : "button" )
newButton . classList . add ( "tasksBtns" )
2023-03-15 14:14:43 +01:00
if ( btnInfo . html ) {
2023-04-27 19:56:56 +02:00
const html = typeof btnInfo . html === "function" ? btnInfo . html ( ) : btnInfo . html
2023-03-15 14:14:43 +01:00
if ( html instanceof HTMLElement ) {
newButton . appendChild ( html )
} else {
newButton . innerHTML = html
}
} else {
2023-04-27 19:56:56 +02:00
newButton . innerText = typeof btnInfo . text === "function" ? btnInfo . text ( ) : btnInfo . text
2023-03-15 14:14:43 +01:00
}
if ( btnInfo . on _click || ! isLabel ) {
2023-04-27 19:56:56 +02:00
newButton . addEventListener ( "click" , function ( event ) {
2023-07-28 19:18:41 +02:00
btnInfo . on _click . bind ( newButton ) ( req , img , event , tools )
2023-03-15 14:14:43 +01:00
} )
2023-07-28 19:18:41 +02:00
if ( btnInfo . on _click === onUndoFilter ) {
tools [ "undoButton" ] = newButton
newButton . classList . add ( "displayNone" )
}
2023-07-29 06:37:41 +02:00
if ( btnInfo . on _click === onRedoFilter ) {
tools [ "redoButton" ] = newButton
newButton . classList . add ( "displayNone" )
}
2023-03-15 14:14:43 +01:00
}
2023-04-27 19:56:56 +02:00
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
}
2023-04-27 19:56:56 +02:00
buttons . forEach ( ( btn ) => {
2023-03-15 14:14:43 +01:00
if ( Array . isArray ( btn ) ) {
2023-04-27 19:56:56 +02:00
btn = btn . filter ( ( btnInfo ) => ! btnInfo . filter || btnInfo . filter ( req , img ) === true )
2023-03-15 16:50:56 +01:00
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 ) {
2023-04-27 19:56:56 +02:00
console . error ( "Error creating image info button from plugin: " , btn , err )
2023-03-15 14:14:43 +01:00
}
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-08-16 06:53:12 +02:00
function onUseForControlnetClick ( req , img ) {
controlImagePreview . src = img . src
}
2023-03-14 06:10:24 +01:00
function getDownloadFilename ( img , suffix ) {
2023-04-27 19:56:56 +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 ) {
2023-04-27 19:56:56 +02:00
const name = getDownloadFilename ( img , "json" )
const blob = new Blob ( [ JSON . stringify ( req , null , 2 ) ] , { type : "text/plain" } )
2023-03-14 06:10:24 +01:00
saveAs ( blob , name )
}
function onDownloadImageClick ( req , img ) {
2023-04-27 19:56:56 +02:00
const name = getDownloadFilename ( img , req [ "output_format" ] )
2023-03-14 06:10:24 +01:00
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 , {
2023-04-28 12:20:44 +02:00
use _cpu : useCPUField . checked ,
2022-10-20 11:40:34 +02:00
} )
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 ,
2023-04-28 12:20:44 +02:00
seed : Math . floor ( Math . random ( ) * 10000000 ) ,
2022-10-19 13:56:35 +02:00
} )
newTaskRequest . numOutputsTotal = 5
newTaskRequest . batchCount = 5
delete newTaskRequest . reqBody . mask
createTask ( newTaskRequest )
}
2023-08-17 08:03:05 +02:00
// gets a flat list of all models of a certain type, ignoring directories
function getAllModelNames ( type ) {
function f ( tree ) {
if ( tree == undefined ) {
return [ ]
}
2023-08-17 12:39:47 +02:00
let result = [ ]
tree . forEach ( ( e ) => {
if ( typeof e == "object" ) {
result = result . concat ( f ( e [ 1 ] ) )
2023-08-17 08:03:05 +02:00
} else {
2023-08-17 12:39:47 +02:00
result . push ( e )
2023-08-17 08:03:05 +02:00
}
2023-08-17 12:39:47 +02:00
} )
2023-08-17 08:03:05 +02:00
return result
}
return f ( modelsOptions [ type ] )
}
2023-08-29 07:18:57 +02:00
// gets a flattened list of all models of a certain type. e.g. "path/subpath/modelname"
// use the filter to search for all models having a certain name.
2023-08-30 14:12:50 +02:00
function getAllModelPathes ( type , filter = "" ) {
2023-08-29 07:18:57 +02:00
function f ( tree , prefix ) {
if ( tree == undefined ) {
return [ ]
}
let result = [ ]
tree . forEach ( ( e ) => {
if ( typeof e == "object" ) {
result = result . concat ( f ( e [ 1 ] , prefix + e [ 0 ] + "/" ) )
} else {
2023-08-30 14:12:50 +02:00
if ( filter == "" || e == filter ) {
2023-08-29 07:18:57 +02:00
result . push ( prefix + e )
}
}
} )
return result
}
return f ( modelsOptions [ type ] , "" )
}
2023-08-17 08:03:05 +02:00
function onUseAsThumbnailClick ( req , img ) {
let scale = 1
let targetWidth = img . naturalWidth
let targetHeight = img . naturalHeight
let resize = false
onUseAsThumbnailClick . img = img
2023-08-17 12:39:47 +02:00
if ( typeof onUseAsThumbnailClick . croppr == "undefined" ) {
onUseAsThumbnailClick . croppr = new Croppr ( "#use-as-thumb-image" , {
aspectRatio : 1 ,
minSize : [ 384 , 384 , "px" ] ,
startSize : [ 512 , 512 , "px" ] ,
returnMode : "real" ,
} )
2023-08-17 08:03:05 +02:00
}
if ( img . naturalWidth > img . naturalHeight ) {
if ( img . naturalWidth > 768 ) {
2023-08-17 12:39:47 +02:00
scale = 768 / img . naturalWidth
targetWidth = 768
targetHeight = ( img . naturalHeight * scale ) >>> 0
resize = true
2023-08-17 08:03:05 +02:00
}
} else {
if ( img . naturalHeight > 768 ) {
scale = 768 / img . naturalHeight
targetHeight = 768
2023-08-17 12:39:47 +02:00
targetWidth = ( img . naturalWidth * scale ) >>> 0
2023-08-17 08:03:05 +02:00
resize = true
}
}
2023-08-17 12:39:47 +02:00
onUseAsThumbnailClick . croppr . options . minSize = { width : ( 384 * scale ) >>> 0 , height : ( 384 * scale ) >>> 0 }
onUseAsThumbnailClick . croppr . options . startSize = { width : ( 512 * scale ) >>> 0 , height : ( 512 * scale ) >>> 0 }
2023-08-17 08:03:05 +02:00
if ( resize ) {
2023-08-17 12:39:47 +02:00
const canvas = document . createElement ( "canvas" )
2023-08-17 08:03:05 +02:00
canvas . width = targetWidth
canvas . height = targetHeight
2023-08-17 12:39:47 +02:00
const ctx = canvas . getContext ( "2d" )
2023-08-17 08:03:05 +02:00
ctx . drawImage ( img , 0 , 0 , targetWidth , targetHeight )
2023-08-17 12:39:47 +02:00
onUseAsThumbnailClick . croppr . setImage ( canvas . toDataURL ( "image/png" ) )
2023-08-17 08:03:05 +02:00
} else {
onUseAsThumbnailClick . croppr . setImage ( img . src )
}
2023-10-12 06:28:29 +02:00
useAsThumbSelect . innerHTML = ""
2023-08-17 08:03:05 +02:00
2023-09-04 01:36:32 +02:00
if ( "use_embeddings_model" in req ) {
let embeddings = req . use _embeddings _model . map ( ( e ) => e . split ( "/" ) . pop ( ) )
let embOptions = document . createElement ( "optgroup" )
embOptions . label = "Embeddings"
embOptions . replaceChildren (
... embeddings . map ( ( e ) => {
let option = document . createElement ( "option" )
option . innerText = e
option . dataset [ "type" ] = "embeddings"
return option
} )
)
useAsThumbSelect . appendChild ( embOptions )
2023-08-17 08:03:05 +02:00
}
2023-10-12 06:28:29 +02:00
2023-09-04 01:36:32 +02:00
if ( "use_lora_model" in req ) {
let LORA = req . use _lora _model
if ( typeof LORA == "string" ) {
LORA = [ LORA ]
}
LORA = LORA . map ( ( e ) => e . split ( "/" ) . pop ( ) )
let loraOptions = document . createElement ( "optgroup" )
loraOptions . label = "LORA"
loraOptions . replaceChildren (
... LORA . map ( ( e ) => {
let option = document . createElement ( "option" )
option . innerText = e
option . dataset [ "type" ] = "lora"
return option
} )
)
useAsThumbSelect . appendChild ( loraOptions )
}
2023-08-17 08:03:05 +02:00
useAsThumbDialog . showModal ( )
onUseAsThumbnailClick . scale = scale
}
modalDialogCloseOnBackdropClick ( useAsThumbDialog )
makeDialogDraggable ( useAsThumbDialog )
useAsThumbDialogCloseBtn . addEventListener ( "click" , ( ) => {
useAsThumbDialog . close ( )
} )
useAsThumbCancelBtn . addEventListener ( "click" , ( ) => {
useAsThumbDialog . close ( )
} )
2023-09-04 01:36:32 +02:00
const Bucket = {
upload ( path , blob ) {
const formData = new FormData ( )
formData . append ( "file" , blob )
return fetch ( ` bucket/ ${ path } ` , {
method : "POST" ,
body : formData ,
} )
} ,
getImageAsDataURL ( path ) {
return fetch ( ` bucket/ ${ path } ` )
. then ( ( response ) => {
if ( response . status == 200 ) {
return response . blob ( )
} else {
throw new Error ( "Bucket error" )
}
} )
. then ( ( blob ) => {
return new Promise ( ( resolve , reject ) => {
const reader = new FileReader ( )
reader . onload = ( ) => resolve ( reader . result )
reader . onerror = reject
reader . readAsDataURL ( blob )
} )
} )
} ,
getList ( path ) {
2023-10-12 06:28:29 +02:00
return fetch ( ` bucket/ ${ path } ` )
. then ( ( response ) => ( response . status == 200 ? response . json ( ) : [ ] ) )
2023-09-04 01:36:32 +02:00
} ,
store ( path , data ) {
return Bucket . upload ( ` ${ path } .json ` , JSON . stringify ( data ) )
} ,
retrieve ( path ) {
2023-10-12 06:28:29 +02:00
return fetch ( ` bucket/ ${ path } .json ` )
. then ( ( response ) => ( response . status == 200 ? response . json ( ) : null ) )
2023-09-04 01:36:32 +02:00
} ,
}
2023-08-17 08:03:05 +02:00
useAsThumbSaveBtn . addEventListener ( "click" , ( e ) => {
2023-08-17 12:39:47 +02:00
let scale = 1 / onUseAsThumbnailClick . scale
2023-08-17 08:03:05 +02:00
let crop = onUseAsThumbnailClick . croppr . getValue ( )
2023-08-17 12:39:47 +02:00
let len = Math . max ( crop . width * scale , 384 )
2023-08-17 08:03:05 +02:00
let profileName = profileNameField . value
2023-08-17 12:39:47 +02:00
cropImageDataUrl ( onUseAsThumbnailClick . img . src , crop . x * scale , crop . y * scale , len , len )
. then ( ( thumb ) => fetch ( thumb ) )
. then ( ( response ) => response . blob ( ) )
2023-08-17 08:03:05 +02:00
. then ( async function ( blob ) {
let options = useAsThumbSelect . selectedOptions
let promises = [ ]
for ( let embedding of options ) {
2023-10-12 06:28:29 +02:00
promises . push (
Bucket . upload ( ` ${ profileName } / ${ embedding . dataset [ "type" ] } / ${ embedding . value } .png ` , blob )
)
2023-08-17 08:03:05 +02:00
}
return Promise . all ( promises )
2023-08-17 12:39:47 +02:00
} )
. then ( ( ) => {
2023-08-17 08:03:05 +02:00
useAsThumbDialog . close ( )
2023-09-17 22:36:50 +02:00
document . dispatchEvent ( new CustomEvent ( "saveThumb" , { detail : useAsThumbSelect . selectedOptions } ) )
2023-08-17 08:03:05 +02:00
} )
2023-08-17 12:39:47 +02:00
. catch ( ( error ) => {
2023-08-17 08:03:05 +02:00
console . error ( error )
2023-08-17 12:39:47 +02:00
showToast ( "Couldn't save thumbnail.<br>" + error )
2023-08-17 08:03:05 +02:00
} )
} )
2022-10-20 11:40:34 +02:00
function enqueueImageVariationTask ( req , img , reqDiff ) {
2023-04-27 19:56:56 +02:00
const imageSeed = img . getAttribute ( "data-seed" )
2022-10-19 18:28:51 +02:00
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
2023-04-28 12:20:44 +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
2023-04-27 19:56:56 +02:00
if ( ! ( "init_image" in req ) && ! ( "init_image" in reqDiff ) ) {
2022-12-05 06:16:10 +01:00
newRequestBody . init _image = undefined
newRequestBody . mask = undefined
2023-04-27 19:56:56 +02:00
} else if ( ! ( "mask" in req ) && ! ( "mask" in reqDiff ) ) {
2022-12-05 06:16:10 +01:00
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 )
}
2023-07-28 19:18:41 +02:00
function applyInlineFilter ( filterName , path , filterParams , img , statusText , tools ) {
const filterReq = {
image : img . src ,
filter : filterName ,
model _paths : { } ,
filter _params : filterParams ,
2023-07-30 10:36:31 +02:00
output _format : outputFormatField . value ,
output _quality : parseInt ( outputQualityField . value ) ,
output _lossless : outputLosslessField . checked ,
2023-07-28 19:18:41 +02:00
}
filterReq . model _paths [ filterName ] = path
2023-08-31 12:27:53 +02:00
if ( saveToDiskField . checked && diskPathField . value . trim ( ) !== "" ) {
filterReq . save _to _disk _path = diskPathField . value . trim ( )
}
2023-07-28 19:18:41 +02:00
tools . spinnerStatus . innerText = statusText
tools . spinner . classList . remove ( "displayNone" )
SD . filter ( filterReq , ( e ) => {
if ( e . status === "succeeded" ) {
let prevImg = img . src
img . src = e . output [ 0 ]
tools . spinner . classList . add ( "displayNone" )
if ( prevImg . length > 0 ) {
tools . undoBuffer . push ( prevImg )
2023-07-29 06:37:41 +02:00
tools . redoBuffer = [ ]
if ( tools . undoBuffer . length > MAX _IMG _UNDO _ENTRIES ) {
let n = tools . undoBuffer . length
tools . undoBuffer . splice ( 0 , n - MAX _IMG _UNDO _ENTRIES )
}
tools . undoButton . classList . remove ( "displayNone" )
tools . redoButton . classList . add ( "displayNone" )
2023-07-28 19:18:41 +02:00
}
} else if ( e . status == "failed" ) {
alert ( "Error running upscale: " + e . detail )
tools . spinner . classList . add ( "displayNone" )
}
2022-12-05 06:16:10 +01:00
} )
2022-10-20 11:40:34 +02:00
}
2022-10-19 18:32:59 +02:00
2023-07-29 06:37:41 +02:00
function moveImageBetweenBuffers ( img , fromBuffer , toBuffer , fromButton , toButton ) {
if ( fromBuffer . length === 0 ) {
2023-07-28 19:18:41 +02:00
return
}
2023-07-29 06:37:41 +02:00
let src = fromBuffer . pop ( )
2023-07-28 19:18:41 +02:00
if ( src . length > 0 ) {
2023-07-29 06:37:41 +02:00
toBuffer . push ( img . src )
2023-07-28 19:18:41 +02:00
img . src = src
}
2023-07-29 06:37:41 +02:00
if ( fromBuffer . length === 0 ) {
fromButton . classList . add ( "displayNone" )
2023-07-28 19:18:41 +02:00
}
2023-07-29 06:37:41 +02:00
if ( toBuffer . length > 0 ) {
toButton . classList . remove ( "displayNone" )
}
}
function onUndoFilter ( req , img , e , tools ) {
moveImageBetweenBuffers ( img , tools . undoBuffer , tools . redoBuffer , tools . undoButton , tools . redoButton )
}
function onRedoFilter ( req , img , e , tools ) {
moveImageBetweenBuffers ( img , tools . redoBuffer , tools . undoBuffer , tools . redoButton , tools . undoButton )
2023-07-28 19:18:41 +02:00
}
function onUpscaleClick ( req , img , e , tools ) {
let path = upscaleModelField . value
let scale = parseInt ( upscaleAmountField . value )
2024-10-01 07:24:58 +02:00
let filterName = null
const FILTERS = [ "realesrgan" , "latent_upscaler" , "esrgan_4x" , "lanczos" , "nearest" , "scunet" , "swinir" ]
for ( let idx in FILTERS ) {
let f = FILTERS [ idx ]
if ( path . toLowerCase ( ) . includes ( f ) ) {
filterName = f
break
}
}
if ( ! filterName ) {
return
}
2023-07-28 19:18:41 +02:00
let statusText = "Upscaling by " + scale + "x using " + filterName
applyInlineFilter ( filterName , path , { scale : scale } , img , statusText , tools )
}
function onFixFacesClick ( req , img , e , tools ) {
let path = gfpganModelField . value
let filterName = path . toLowerCase ( ) . includes ( "gfpgan" ) ? "gfpgan" : "codeformer"
let statusText = "Fixing faces with " + filterName
applyInlineFilter ( filterName , path , { } , img , statusText , tools )
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 , {
2023-04-28 12:20:44 +02:00
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 makeImage ( ) {
2023-01-09 15:03:23 +01:00
if ( typeof performance == "object" && performance . mark ) {
2023-04-27 19:56:56 +02:00
performance . mark ( "click-makeImage" )
2023-01-09 15:03:23 +01:00
}
2022-12-06 12:34:08 +01:00
if ( ! SD . isServerAvailable ( ) ) {
2023-04-27 19:56:56 +02:00
alert ( "The server is not available." )
2022-12-06 12:34:08 +01:00
return
}
2023-04-27 19:56:56 +02:00
if ( ! randomSeedField . checked && seedField . value == "" ) {
2022-12-06 12:34:08 +01:00
alert ( 'The "Seed" field must not be empty.' )
2023-08-17 07:48:00 +02:00
seedField . classList . add ( "validation-failed" )
2022-12-06 12:34:08 +01:00
return
}
2023-08-17 07:48:00 +02:00
seedField . classList . remove ( "validation-failed" )
2023-04-27 19:56:56 +02:00
if ( numInferenceStepsField . value == "" ) {
2022-12-06 12:34:08 +01:00
alert ( 'The "Inference Steps" field must not be empty.' )
2023-08-17 07:48:00 +02:00
numInferenceStepsField . classList . add ( "validation-failed" )
2022-12-06 12:34:08 +01:00
return
}
2023-08-17 07:48:00 +02:00
numInferenceStepsField . classList . remove ( "validation-failed" )
if ( controlnetModelField . value === "" && IMAGE _REGEX . test ( controlImagePreview . src ) ) {
alert ( "Please choose a ControlNet model, to use the ControlNet image." )
document . getElementById ( "controlnet_model" ) . classList . add ( "validation-failed" )
return
}
document . getElementById ( "controlnet_model" ) . classList . remove ( "validation-failed" )
2023-04-27 19:56:56 +02:00
if ( numOutputsTotalField . value == "" || numOutputsTotalField . value == 0 ) {
2022-12-06 12:34:08 +01:00
numOutputsTotalField . value = 1
}
2023-04-27 19:56:56 +02:00
if ( numOutputsParallelField . value == "" || numOutputsParallelField . value == 0 ) {
2022-12-06 12:34:08 +01:00
numOutputsParallelField . value = 1
}
2023-04-27 19:56:56 +02:00
if ( guidanceScaleField . value == "" ) {
2022-12-06 12:34:08 +01:00
guidanceScaleField . value = guidanceScaleSlider . value / 10
}
2024-10-09 07:42:28 +02:00
if ( distilledGuidanceScaleField . value == "" ) {
distilledGuidanceScaleField . value = distilledGuidanceScaleSlider . value / 10
}
2023-05-04 00:05:11 +02:00
if ( hypernetworkStrengthField . value == "" ) {
hypernetworkStrengthField . value = hypernetworkStrengthSlider . value / 100
}
2022-12-06 12:34:08 +01:00
const taskTemplate = getCurrentUserRequest ( )
2023-04-27 19:56:56 +02:00
const newTaskRequests = getPrompts ( ) . map ( ( prompt ) =>
Object . assign ( { } , taskTemplate , {
2023-04-28 12:20:44 +02:00
reqBody : Object . assign ( { prompt : prompt } , taskTemplate . reqBody ) ,
2023-04-27 19:56:56 +02:00
} )
)
2023-08-17 07:24:47 +02:00
newTaskRequests . forEach ( setEmbeddings )
2022-12-06 12:34:08 +01:00
newTaskRequests . forEach ( createTask )
2022-10-20 13:52:01 +02:00
2023-03-20 22:53:13 +01:00
updateInitialText ( )
2023-08-23 16:20:38 +02:00
2023-10-12 06:28:29 +02:00
const countBeforeBanner = localStorage . getItem ( "countBeforeBanner" ) || 1
if ( countBeforeBanner <= 0 ) {
// supportBanner.classList.remove("displayNone")
} else {
localStorage . setItem ( "countBeforeBanner" , countBeforeBanner - 1 )
}
2022-12-06 12:34:08 +01:00
}
2022-09-29 10:13:25 +02:00
2022-12-10 17:17:37 +01:00
/* Hover effect for the init image in the task list */
2023-08-18 12:54:17 +02:00
function createInitImageHover ( taskEntry , task ) {
2023-08-17 12:39:47 +02:00
taskEntry . querySelectorAll ( ".task-initimg" ) . forEach ( ( thumb ) => {
2023-08-17 07:48:47 +02:00
let thumbimg = thumb . querySelector ( "img" )
2023-08-17 12:39:47 +02:00
let img = createElement ( "img" , { src : thumbimg . src } )
2023-08-17 07:48:47 +02:00
thumb . querySelector ( ".task-fs-initimage" ) . appendChild ( img )
let div = createElement ( "div" , undefined , [ "top-right" ] )
div . innerHTML = `
< button class = "useAsInputBtn" > Use as Input < / b u t t o n >
< br >
2023-08-18 12:54:17 +02:00
< button class = "useForControlnetBtn" > Use for Controlnet < / b u t t o n >
< br >
< button class = "downloadPreviewImg" > Download < / b u t t o n > `
2023-08-17 12:39:47 +02:00
div . querySelector ( ".useAsInputBtn" ) . addEventListener ( "click" , ( e ) => {
2023-08-17 07:48:47 +02:00
e . preventDefault ( )
onUseAsInputClick ( null , img )
} )
2023-08-17 12:39:47 +02:00
div . querySelector ( ".useForControlnetBtn" ) . addEventListener ( "click" , ( e ) => {
2023-08-17 07:48:47 +02:00
e . preventDefault ( )
controlImagePreview . src = img . src
} )
2023-08-18 12:54:17 +02:00
div . querySelector ( ".downloadPreviewImg" ) . addEventListener ( "click" , ( e ) => {
e . preventDefault ( )
const name = "image." + task . reqBody [ "output_format" ]
const blob = dataURItoBlob ( img . src )
saveAs ( blob , name )
} )
2023-08-17 07:48:47 +02:00
thumb . querySelector ( ".task-fs-initimage" ) . appendChild ( div )
} )
return
2023-04-27 19:56:56 +02:00
var $tooltip = $ ( taskEntry . querySelector ( ".task-fs-initimage" ) )
var img = document . createElement ( "img" )
img . src = taskEntry . querySelector ( "div.task-initimg > img" ) . src
2022-12-17 01:30:30 +01:00
$tooltip . append ( img )
$tooltip . append ( ` <div class="top-right"><button>Use as Input</button></div> ` )
2023-04-27 19:56:56 +02:00
$tooltip . find ( "button" ) . on ( "click" , ( e ) => {
2023-02-08 03:02:42 +01:00
e . stopPropagation ( )
2023-04-27 19:56:56 +02:00
onUseAsInputClick ( null , img )
2023-02-08 03:02:42 +01:00
} )
2022-12-10 17:17:37 +01:00
}
2023-04-27 19:56:56 +02:00
let startX , startY
2022-12-19 20:34:06 +01:00
function onTaskEntryDragOver ( event ) {
2023-04-27 19:56:56 +02:00
imagePreview . querySelectorAll ( ".imageTaskContainer" ) . forEach ( ( itc ) => {
if ( itc != event . target . closest ( ".imageTaskContainer" ) ) {
itc . classList . remove ( "dropTargetBefore" , "dropTargetAfter" )
2022-12-19 20:34:06 +01:00
}
2023-04-27 19:56:56 +02: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-19 20:34:06 +01:00
}
}
}
2022-12-10 17:17:37 +01:00
}
2023-03-29 19:52:41 +02:00
function generateConfig ( { label , value , visible , cssKey } ) {
2023-04-27 19:56:56 +02:00
if ( ! visible ) return null
2023-03-29 19:52:41 +02:00
return ` <div class="taskConfigContainer task ${ cssKey } Container"><b> ${ label } :</b> <span class="task ${ cssKey } "> ${ value } `
}
function getVisibleConfig ( config , task ) {
const mergedTaskConfig = { ... config . taskConfig , ... config . pluginTaskConfig }
return Object . keys ( mergedTaskConfig )
. map ( ( key ) => {
const value = mergedTaskConfig ? . [ key ] ? . value ? . ( task ) ? ? task . reqBody [ key ]
const visible = mergedTaskConfig ? . [ key ] ? . visible ? . ( task ) ? ? value !== undefined ? ? true
const label = mergedTaskConfig ? . [ key ] ? . label ? ? mergedTaskConfig ? . [ key ]
const cssKey = config . getCSSKey ( key )
return { label , visible , value , cssKey }
} )
. map ( ( obj ) => generateConfig ( obj ) )
2023-04-27 19:56:56 +02:00
. filter ( ( obj ) => obj )
2023-03-29 19:52:41 +02:00
}
function createTaskConfig ( task ) {
2023-04-27 19:56:56 +02:00
return getVisibleConfig ( taskConfigSetup , task ) . join ( "</span>, </div>" )
2023-03-29 19:52:41 +02:00
}
2022-10-09 13:17:43 +02:00
function createTask ( task ) {
2023-04-27 19:56:56 +02:00
let taskConfig = ""
2022-12-10 17:17:37 +01:00
if ( task . reqBody . init _image !== undefined ) {
let h = 80
2023-04-27 19:56:56 +02:00
let w = ( ( task . reqBody . width * h ) / task . reqBody . height ) >> 0
2023-08-18 12:44:01 +02:00
taskConfig += ` <div class="task-initimg init-img-preview" 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-10 17:17:37 +01:00
}
2023-08-17 07:48:47 +02:00
if ( task . reqBody . control _image !== undefined ) {
let h = 80
let w = ( ( task . reqBody . width * h ) / task . reqBody . height ) >> 0
2023-08-18 12:44:01 +02:00
taskConfig += ` <div class="task-initimg controlnet-img-preview" style="float:left;"><img style="width: ${ w } px;height: ${ h } px;" src=" ${ task . reqBody . control _image } "><div class="task-fs-initimage"></div></div> `
2023-08-17 07:48:47 +02:00
}
2022-12-12 11:17:13 +01:00
2023-04-27 19:56:56 +02:00
taskConfig += ` <div class="taskConfigData"> ${ createTaskConfig ( task ) } </span></div></div> `
2022-09-23 16:18:48 +02:00
2023-04-27 19:56:56 +02:00
let taskEntry = document . createElement ( "div" )
2022-12-06 12:34:08 +01:00
taskEntry . id = ` imageTaskContainer- ${ Date . now ( ) } `
2023-04-27 19:56:56 +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 >
2023-08-24 12:37:58 +02:00
< button class = "secondaryButton stopTask" > < i class = "fa-solid fa-xmark" > < / i > C a n c e l < / 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 > `
2023-08-18 12:44:01 +02:00
if ( task . reqBody . init _image !== undefined || task . reqBody . control _image !== undefined ) {
2023-08-18 12:54:17 +02:00
createInitImageHover ( taskEntry , task )
2023-08-18 12:44:01 +02:00
}
if ( task . reqBody . control _image !== undefined && task . reqBody . control _filter _to _apply !== undefined ) {
let req = {
image : task . reqBody . control _image ,
filter : task . reqBody . control _filter _to _apply ,
model _paths : { } ,
filter _params : { } ,
}
req [ "model_paths" ] [ task . reqBody . control _filter _to _apply ] = task . reqBody . control _filter _to _apply
2023-08-21 07:34:13 +02:00
task [ "previewTaskReq" ] = req
2023-08-18 12:44:01 +02:00
}
2022-09-28 10:14:48 +02:00
createCollapsibles ( taskEntry )
2022-12-24 10:42:43 +01:00
2023-04-27 19:56:56 +02:00
let draghandle = taskEntry . querySelector ( ".drag-handle" )
draghandle . addEventListener ( "mousedown" , ( e ) => {
taskEntry . setAttribute ( "draggable" , true )
2022-12-24 10:42:43 +01:00
} )
// Add a debounce delay to allow mobile to bouble tap.
2023-04-27 19:56:56 +02:00
draghandle . addEventListener (
"mouseup" ,
debounce ( ( e ) => {
taskEntry . setAttribute ( "draggable" , false )
} , 2000 )
)
draghandle . addEventListener ( "click" , ( e ) => {
2022-12-24 10:55:28 +01:00
e . preventDefault ( ) // Don't allow the results to be collapsed...
} )
2023-04-27 19:56:56 +02:00
taskEntry . addEventListener ( "dragend" , ( e ) => {
taskEntry . setAttribute ( "draggable" , false )
imagePreview . querySelectorAll ( ".imageTaskContainer" ) . forEach ( ( itc ) => {
itc . classList . remove ( "dropTargetBefore" , "dropTargetAfter" )
} )
imagePreview . removeEventListener ( "dragover" , onTaskEntryDragOver )
2022-12-19 19:45:42 +01:00
} )
2023-04-27 19:56:56 +02:00
taskEntry . addEventListener ( "dragstart" , function ( e ) {
imagePreview . addEventListener ( "dragover" , onTaskEntryDragOver )
e . dataTransfer . setData ( "text/plain" , taskEntry . id )
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
2023-08-21 07:34:13 +02:00
task [ "taskConfig" ] = taskEntry . querySelector ( ".taskConfig" )
2023-04-27 19:56:56 +02:00
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 ( ".progress-bar" )
task [ "stopTask" ] = taskEntry . querySelector ( ".stopTask" )
2022-09-27 14:39:07 +02:00
2023-04-27 19:56:56 +02:00
task [ "stopTask" ] . addEventListener ( "click" , ( e ) => {
2022-12-12 15:26:27 +01:00
e . stopPropagation ( )
2023-04-27 19:56:56 +02:00
if ( task [ "isProcessing" ] ) {
2023-03-20 22:53:13 +01:00
shiftOrConfirm ( e , "Stop this task?" , async function ( e ) {
if ( task . batchesDone <= 0 || ! task . isProcessing ) {
removeTask ( taskEntry )
}
abortTask ( task )
} )
} else {
removeTask ( taskEntry )
}
2022-12-01 09:24:49 +01:00
} )
2022-09-23 16:18:48 +02:00
2023-04-27 19:56:56 +02:00
task [ "useSettings" ] = taskEntry . querySelector ( ".useSettings" )
task [ "useSettings" ] . addEventListener ( "click" , function ( e ) {
2022-11-18 11:08:17 +01:00
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-10-12 06:28:29 +02:00
taskEntry = imagePreviewContent . insertBefore ( taskEntry , supportBanner . 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
2023-04-27 19:56:56 +02:00
if ( task . previewPrompt . innerText . trim ( ) === "" ) {
task . previewPrompt . innerHTML = " " // allows the results to be collapsed
2022-10-18 15:32:34 +02:00
}
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 )
2023-08-01 08:19:30 +02:00
let numOutputsParallel = parseInt ( numOutputsParallelField . value )
2023-04-27 19:56:56 +02:00
const seed = randomSeedField . checked ? Math . floor ( Math . random ( ) * ( 2 * * 32 - 1 ) ) : parseInt ( seedField . value )
2022-12-06 12:34:08 +01:00
2023-08-01 17:54:22 +02:00
// if (
// testDiffusers.checked &&
// document.getElementById("toggle-tensorrt-install").innerHTML == "Uninstall" &&
// document.querySelector("#convert_to_tensorrt").checked
// ) {
// // TRT enabled
2023-08-01 08:19:30 +02:00
2023-08-01 17:54:22 +02:00
// numOutputsParallel = 1 // force 1 parallel
// }
2023-08-01 08:19:30 +02:00
2023-08-03 06:52:24 +02:00
// clamp to multiple of 8
let width = parseInt ( widthField . value )
let height = parseInt ( heightField . value )
2023-08-03 14:10:26 +02:00
width = width - ( width % IMAGE _STEP _SIZE )
height = height - ( height % IMAGE _STEP _SIZE )
2023-08-03 06:52:24 +02:00
2022-12-06 12:34:08 +01:00
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 ) ,
2023-08-03 06:52:24 +02:00
width : width ,
height : height ,
2022-12-06 12:34:08 +01:00
// allow_nsfw: allowNSFWField.checked,
2022-12-16 07:04:49 +01:00
vram _usage _level : vramUsageLevelField . value ,
2023-03-21 13:29:20 +01:00
sampler _name : samplerField . 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 ,
2023-05-18 13:55:45 +02:00
clip _skip : clipSkipField . checked ,
2022-12-06 12:34:08 +01:00
use _vae _model : vaeModelField . value ,
stream _progress _updates : true ,
2023-04-27 19:56:56 +02:00
stream _image _progress : numOutputsTotal > 50 ? false : streamImageProgressField . checked ,
2022-12-06 12:34:08 +01:00
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-03-25 03:46:03 +01:00
output _lossless : outputLosslessField . checked ,
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 ,
2023-04-27 19:56:56 +02:00
active _tags : activeTags . map ( ( x ) => x . name ) ,
2023-04-28 12:20:44 +02:00
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 ( )
2023-07-30 10:21:19 +02:00
newTask . reqBody . strict _mask _border = strictMaskBorderField . checked
2022-12-06 12:34:08 +01:00
}
2022-12-11 15:04:07 +01:00
newTask . reqBody . preserve _init _image _color _profile = applyColorCorrectionField . checked
2023-03-21 13:29:20 +01:00
if ( ! testDiffusers . checked ) {
2023-04-27 19:56:56 +02:00
newTask . reqBody . sampler _name = "ddim"
2023-03-21 13:29:20 +01:00
}
2022-12-06 12:34:08 +01:00
}
2023-04-27 19:56:56 +02:00
if ( saveToDiskField . checked && diskPathField . value . trim ( ) !== "" ) {
2022-12-06 12:34:08 +01:00
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
2023-06-01 13:20:01 +02:00
if ( gfpganModelField . value . includes ( "codeformer" ) ) {
newTask . reqBody . codeformer _upscale _faces = document . querySelector ( "#codeformer_upscale_faces" ) . checked
2023-06-06 12:46:21 +02:00
newTask . reqBody . codeformer _fidelity = 1 - parseFloat ( codeformerFidelityField . value )
2023-06-01 13:20:01 +02:00
}
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
2023-05-23 13:23:53 +02:00
if ( upscaleModelField . value === "latent_upscaler" ) {
newTask . reqBody . upscale _amount = "2"
newTask . reqBody . latent _upscaler _steps = latentUpscalerStepsField . 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 )
}
2023-07-15 16:03:49 +02:00
if ( testDiffusers . checked ) {
2023-08-18 09:57:00 +02:00
let loraModelData = loraModelField . value
let modelNames = loraModelData [ "modelNames" ]
let modelStrengths = loraModelData [ "modelWeights" ]
2023-07-15 16:03:49 +02:00
if ( modelNames . length > 0 ) {
modelNames = modelNames . length == 1 ? modelNames [ 0 ] : modelNames
modelStrengths = modelStrengths . length == 1 ? modelStrengths [ 0 ] : modelStrengths
newTask . reqBody . use _lora _model = modelNames
newTask . reqBody . lora _alpha = modelStrengths
}
2023-08-30 14:12:50 +02:00
if ( tilingField . value !== "none" ) {
newTask . reqBody . tiling = tilingField . value
}
2023-12-11 17:58:19 +01:00
newTask . reqBody . enable _vae _tiling = enableVAETilingField . checked
2023-03-21 16:15:12 +01:00
}
2023-07-29 17:39:27 +02:00
if ( testDiffusers . checked && document . getElementById ( "toggle-tensorrt-install" ) . innerHTML == "Uninstall" ) {
// TRT is installed
newTask . reqBody . convert _to _tensorrt = document . querySelector ( "#convert_to_tensorrt" ) . checked
2023-08-01 20:23:01 +02:00
let trtBuildConfig = {
batch _size _range : [
2023-08-02 10:33:59 +02:00
parseInt ( document . querySelector ( "#trt-build-min-batch" ) . value ) ,
parseInt ( document . querySelector ( "#trt-build-max-batch" ) . value ) ,
2023-08-01 20:23:01 +02:00
] ,
dimensions _range : [ ] ,
}
let sizes = [ 512 , 768 , 1024 , 1280 , 1536 ]
sizes . forEach ( ( i ) => {
let el = document . querySelector ( "#trt-build-res-" + i )
if ( el . checked ) {
trtBuildConfig [ "dimensions_range" ] . push ( [ i , i + 256 ] )
}
} )
newTask . reqBody . trt _build _config = trtBuildConfig
2023-07-29 17:39:27 +02:00
}
2023-08-01 12:09:15 +02:00
if ( controlnetModelField . value !== "" && IMAGE _REGEX . test ( controlImagePreview . src ) ) {
newTask . reqBody . use _controlnet _model = controlnetModelField . value
newTask . reqBody . control _image = controlImagePreview . src
2024-05-28 15:15:08 +02:00
newTask . reqBody . control _alpha = parseFloat ( controlAlphaField . value )
2023-08-01 12:09:15 +02:00
if ( controlImageFilterField . value !== "" ) {
newTask . reqBody . control _filter _to _apply = controlImageFilterField . value
}
}
2024-10-09 07:42:28 +02:00
if ( stableDiffusionModelField . value . toLowerCase ( ) . includes ( "flux" ) ) {
newTask . reqBody . distilled _guidance _scale = parseFloat ( distilledGuidanceScaleField . value )
}
2024-10-09 08:13:45 +02:00
if ( schedulerSelectionContainer . style . display !== "none" ) {
newTask . reqBody . scheduler _name = schedulerField . value
}
2023-07-29 17:39:27 +02:00
2022-12-06 12:34:08 +01:00
return newTask
2022-09-23 16:18:48 +02:00
}
2023-08-17 07:24:47 +02:00
function setEmbeddings ( task ) {
2023-08-29 06:53:05 +02:00
let prompt = task . reqBody . prompt
let negativePrompt = task . reqBody . negative _prompt
let overallPrompt = ( prompt + " " + negativePrompt ) . toLowerCase ( )
2023-08-31 18:49:04 +02:00
overallPrompt = overallPrompt . replaceAll ( /[^a-z0-9\-_\.]/g , " " ) // only allow alpha-numeric, dots and hyphens
2023-08-29 06:53:05 +02:00
overallPrompt = overallPrompt . split ( " " )
2023-08-17 07:24:47 +02:00
let embeddingsTree = modelsOptions [ "embeddings" ]
let embeddings = [ ]
function extract ( entries , basePath = "" ) {
entries . forEach ( ( e ) => {
if ( Array . isArray ( e ) ) {
let path = basePath === "" ? basePath + e [ 0 ] : basePath + "/" + e [ 0 ]
extract ( e [ 1 ] , path )
} else {
let path = basePath === "" ? basePath + e : basePath + "/" + e
embeddings . push ( [ e . toLowerCase ( ) . replace ( " " , "_" ) , path ] )
}
} )
}
extract ( embeddingsTree )
let embeddingPaths = [ ]
embeddings . forEach ( ( e ) => {
let token = e [ 0 ]
let path = e [ 1 ]
if ( overallPrompt . includes ( token ) ) {
embeddingPaths . push ( path )
}
} )
if ( embeddingPaths . length > 0 ) {
task . reqBody . use _embeddings _model = embeddingPaths
}
}
2022-12-06 12:34:08 +01:00
function getPrompts ( prompts ) {
2023-04-27 19:56:56 +02:00
if ( typeof prompts === "undefined" ) {
2022-12-06 12:34:08 +01:00
prompts = promptField . value
}
2023-04-27 19:56:56 +02:00
if ( prompts . trim ( ) === "" && activeTags . length === 0 ) {
return [ "" ]
2022-10-18 15:32:34 +02:00
}
2022-12-18 02:06:07 +01:00
let promptsToMake = [ ]
2023-04-27 19:56:56 +02:00
if ( prompts . trim ( ) !== "" ) {
prompts = prompts . split ( "\n" )
prompts = prompts . map ( ( prompt ) => prompt . trim ( ) )
prompts = prompts . filter ( ( prompt ) => prompt !== "" )
2022-12-18 02:06:07 +01:00
promptsToMake = applyPermuteOperator ( prompts )
promptsToMake = applySetOperator ( promptsToMake )
}
2023-04-27 19:56:56 +02:00
const newTags = activeTags . filter ( ( tag ) => tag . inactive === undefined || tag . inactive === false )
2022-12-01 10:40:36 +01:00
if ( newTags . length > 0 ) {
2023-04-27 19:56:56 +02:00
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 } ` )
2023-04-27 19:56:56 +02:00
} else {
2022-12-18 02:06:07 +01:00
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-04-27 19:56:56 +02:00
PLUGINS [ "GET_PROMPTS_HOOK" ] . forEach ( ( fn ) => {
promptsToMake = fn ( promptsToMake )
} )
2023-02-08 17:26:55 +01:00
2022-10-30 08:22:01 +01:00
return promptsToMake
2022-10-19 10:20:05 +02:00
}
2023-06-12 12:48:52 +02:00
function getPromptsNumber ( prompts ) {
if ( typeof prompts === "undefined" ) {
prompts = promptField . value
}
if ( prompts . trim ( ) === "" && activeTags . length === 0 ) {
return [ "" ]
}
let promptsToMake = [ ]
let numberOfPrompts = 0
2023-07-15 13:05:48 +02:00
if ( prompts . trim ( ) !== "" ) {
// this needs to stay sort of the same, as the prompts have to be passed through to the other functions
2023-06-12 12:48:52 +02:00
prompts = prompts . split ( "\n" )
prompts = prompts . map ( ( prompt ) => prompt . trim ( ) )
prompts = prompts . filter ( ( prompt ) => prompt !== "" )
2023-06-13 16:09:54 +02:00
// estimate number of prompts
let estimatedNumberOfPrompts = 0
prompts . forEach ( ( prompt ) => {
2023-07-15 13:05:48 +02:00
estimatedNumberOfPrompts +=
( prompt . match ( /{[^}]*}/g ) || [ ] )
. map ( ( e ) => ( e . match ( /,/g ) || [ ] ) . length + 1 )
. reduce ( ( p , a ) => p * a , 1 ) *
2 * * ( prompt . match ( /\|/g ) || [ ] ) . length
2023-06-13 16:09:54 +02:00
} )
if ( estimatedNumberOfPrompts >= 10000 ) {
return 10000
}
2023-06-12 12:48:52 +02:00
promptsToMake = applySetOperator ( prompts ) // switched those around as Set grows in a linear fashion and permute in 2^n, and one has to be computed for the other to be calculated
numberOfPrompts = applyPermuteOperatorNumber ( promptsToMake )
}
const newTags = activeTags . filter ( ( tag ) => tag . inactive === undefined || tag . inactive === false )
if ( newTags . length > 0 ) {
const promptTags = newTags . map ( ( x ) => x . name ) . join ( ", " )
if ( numberOfPrompts > 0 ) {
// promptsToMake = promptsToMake.map((prompt) => `${prompt}, ${promptTags}`)
// nothing changes, as all prompts just get modified
} else {
// promptsToMake.push(promptTags)
numberOfPrompts = 1
}
}
// Why is this applied twice? It does not do anything here, as everything should have already been done earlier
// promptsToMake = applyPermuteOperator(promptsToMake)
// promptsToMake = applySetOperator(promptsToMake)
return numberOfPrompts
}
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 ( )
2023-04-27 19:56: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
2023-07-15 13:05:48 +02:00
function applyPermuteOperator ( prompts ) {
// prompts is array of input, trimmed, filtered and split by \n
2022-10-19 10:20:05 +02:00
let promptsToMake = [ ]
2023-04-27 19:56:56 +02:00
prompts . forEach ( ( prompt ) => {
let promptMatrix = prompt . split ( "|" )
2022-10-08 12:26:56 +02:00
prompt = promptMatrix . shift ( ) . trim ( )
promptsToMake . push ( prompt )
2023-04-27 19:56:56 +02:00
promptMatrix = promptMatrix . map ( ( p ) => p . trim ( ) )
promptMatrix = promptMatrix . filter ( ( p ) => p !== "" )
2022-10-08 12:26:56 +02:00
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
}
2023-06-12 12:48:52 +02:00
// returns how many prompts would have to be made with the given prompts
2023-07-15 13:05:48 +02:00
function applyPermuteOperatorNumber ( prompts ) {
// prompts is array of input, trimmed, filtered and split by \n
2023-06-12 12:48:52 +02:00
let numberOfPrompts = 0
prompts . forEach ( ( prompt ) => {
let promptCounter = 1
let promptMatrix = prompt . split ( "|" )
promptMatrix . shift ( )
2023-07-15 13:05:48 +02:00
2023-06-12 12:48:52 +02:00
promptMatrix = promptMatrix . map ( ( p ) => p . trim ( ) )
promptMatrix = promptMatrix . filter ( ( p ) => p !== "" )
if ( promptMatrix . length > 0 ) {
2023-06-14 16:59:30 +02:00
promptCounter *= permuteNumber ( promptMatrix )
2023-06-12 12:48:52 +02:00
}
numberOfPrompts += promptCounter
} )
return numberOfPrompts
}
2022-10-08 12:26:56 +02:00
function permutePrompts ( promptBase , promptMatrix ) {
let prompts = [ ]
let permutations = permute ( promptMatrix )
2023-04-27 19:56:56 +02:00
permutations . forEach ( ( perm ) => {
2022-10-08 12:26:56 +02:00
let prompt = promptBase
if ( perm . length > 0 ) {
2023-04-27 19:56:56 +02:00
let promptAddition = perm . join ( ", " )
if ( promptAddition . trim ( ) === "" ) {
2022-10-08 12:26:56 +02:00
return
}
2023-04-27 19:56:56 +02:00
prompt += ", " + promptAddition
2022-10-08 12:26:56 +02:00
}
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
2023-04-27 19:56:56 +02:00
let underscoreName = prompt . replace ( /[^a-zA-Z0-9]/g , "_" )
2023-03-19 17:29:13 +01:00
underscoreName = underscoreName . substring ( 0 , 70 )
2022-09-23 16:18:48 +02:00
// name and the top level metadata
2023-03-19 17:29:13 +01:00
let fileName = ` ${ underscoreName } _S ${ seed } _St ${ steps } _G ${ guidance } . ${ outputFormat } `
2022-09-23 16:18:48 +02:00
return fileName
}
2023-03-20 22:53:13 +01:00
function updateInitialText ( ) {
2023-04-27 19:56:56 +02:00
if ( document . querySelector ( ".imageTaskContainer" ) === null ) {
2023-04-06 12:12:48 +02:00
if ( undoBuffer . length > 0 ) {
initialText . prepend ( undoButton )
2023-03-20 22:53:13 +01:00
}
2023-04-27 19:56:56 +02:00
previewTools . classList . add ( "displayNone" )
initialText . classList . remove ( "displayNone" )
2023-10-12 06:28:29 +02:00
supportBanner . classList . add ( "displayNone" )
2023-03-20 22:53:13 +01:00
} else {
2023-04-27 19:56:56 +02:00
initialText . classList . add ( "displayNone" )
previewTools . classList . remove ( "displayNone" )
document . querySelector ( "div.display-settings" ) . prepend ( undoButton )
2023-08-23 16:20:38 +02:00
2023-10-12 06:28:29 +02:00
const countBeforeBanner = localStorage . getItem ( "countBeforeBanner" ) || 1
if ( countBeforeBanner <= 0 ) {
supportBanner . classList . remove ( "displayNone" )
}
2022-11-27 21:25:46 +01:00
}
}
2023-03-20 22:53:13 +01:00
function removeTask ( taskToRemove ) {
undoableRemove ( taskToRemove )
updateInitialText ( )
}
2023-04-27 19:56:56 +02:00
clearAllPreviewsBtn . addEventListener ( "click" , ( e ) => {
shiftOrConfirm ( e , "Clear all the results and tasks in this window?" , async function ( ) {
await stopAllTasks ( )
2022-09-23 16:18:48 +02:00
2023-04-27 19:56:56 +02:00
let taskEntries = document . querySelectorAll ( ".imageTaskContainer" )
taskEntries . forEach ( removeTask )
} )
} )
2022-09-27 14:39:07 +02:00
2023-03-14 06:10:24 +01:00
/* Download images popup */
2023-07-15 13:05:48 +02:00
showDownloadDialogBtn . addEventListener ( "click" , ( e ) => {
saveAllImagesDialog . showModal ( )
} )
saveAllImagesCloseBtn . addEventListener ( "click" , ( e ) => {
saveAllImagesDialog . close ( )
} )
2023-06-28 19:11:37 +02:00
modalDialogCloseOnBackdropClick ( saveAllImagesDialog )
2023-06-28 22:12:32 +02:00
makeDialogDraggable ( saveAllImagesDialog )
2023-03-14 06:10:24 +01:00
2023-04-27 19:56:56 +02:00
saveAllZipToggle . addEventListener ( "change" , ( e ) => {
2023-03-14 06:10:24 +01:00
if ( saveAllZipToggle . checked ) {
2023-04-27 19:56:56 +02:00
saveAllFoldersOption . classList . remove ( "displayNone" )
2023-03-14 06:10:24 +01:00
} else {
2023-04-27 19:56:56 +02:00
saveAllFoldersOption . classList . add ( "displayNone" )
2023-03-14 06:10:24 +01:00
}
} )
// convert base64 to raw binary data held in a string
function dataURItoBlob ( dataURI ) {
2023-04-27 19:56:56 +02:00
var byteString = atob ( dataURI . split ( "," ) [ 1 ] )
2023-03-14 06:10:24 +01:00
// separate out the mime component
2023-04-27 19:56:56 +02:00
var mimeString = dataURI
. split ( "," ) [ 0 ]
. split ( ":" ) [ 1 ]
. split ( ";" ) [ 0 ]
2023-03-14 06:10:24 +01:00
// 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
2023-04-27 19:56:56 +02:00
return new Blob ( [ ab ] , { type : mimeString } )
2023-03-14 06:10:24 +01:00
}
function downloadAllImages ( ) {
2023-02-21 00:49:04 +01:00
let i = 0
2023-03-14 06:10:24 +01:00
2023-04-27 19:56:56 +02:00
let optZIP = saveAllZipToggle . checked
2023-03-14 06:10:24 +01:00
let optTree = optZIP && saveAllTreeToggle . checked
let optJSON = saveAllJSONToggle . checked
2023-04-27 19:56:56 +02:00
2023-03-14 06:10:24 +01:00
let zip = new JSZip ( )
let folder = zip
2023-04-27 19:56:56 +02:00
document . querySelectorAll ( ".imageTaskContainer" ) . forEach ( ( container ) => {
2023-03-14 06:10:24 +01:00
if ( optTree ) {
2023-04-27 19:56:56 +02:00
let name =
++ i +
"-" +
container
. querySelector ( ".preview-prompt" )
. textContent . replace ( /[^a-zA-Z0-9]/g , "_" )
. substring ( 0 , 25 )
2023-03-14 06:10:24 +01:00
folder = zip . folder ( name )
}
2023-04-27 19:56:56 +02:00
container . querySelectorAll ( ".imgContainer img" ) . forEach ( ( img ) => {
let imgItem = img . closest ( ".imgItem" )
2023-03-14 06:10:24 +01:00
2023-04-27 19:56:56 +02:00
if ( imgItem . style . display === "none" ) {
2023-02-14 15:03:25 +01:00
return
}
2023-03-14 06:10:24 +01:00
2023-04-27 19:56:56 +02:00
let req = imageRequest [ img . dataset [ "imagecounter" ] ]
2023-03-14 06:10:24 +01:00
if ( optZIP ) {
2023-04-27 19:56:56 +02:00
let suffix = img . dataset [ "imagecounter" ] + "." + req [ "output_format" ]
2023-03-14 06:10:24 +01:00
folder . file ( getDownloadFilename ( img , suffix ) , dataURItoBlob ( img . src ) )
if ( optJSON ) {
2023-04-27 19:56:56 +02:00
suffix = img . dataset [ "imagecounter" ] + ".json"
2023-03-14 06:10:24 +01:00
folder . file ( getDownloadFilename ( img , suffix ) , JSON . stringify ( req , null , 2 ) )
}
} else {
2023-04-27 19:56:56 +02:00
setTimeout ( ( ) => {
imgItem . querySelector ( ".download-img" ) . click ( )
} , i * 200 )
i = i + 1
2023-03-14 06:10:24 +01:00
if ( optJSON ) {
2023-04-27 19:56:56 +02:00
setTimeout ( ( ) => {
imgItem . querySelector ( ".download-json" ) . click ( )
} , i * 200 )
i = i + 1
2023-03-14 06:10:24 +01:00
}
}
2023-02-14 15:03:25 +01:00
} )
} )
2023-03-14 06:10:24 +01:00
if ( optZIP ) {
2023-04-27 19:56:56 +02:00
let now = Date . now ( )
. toString ( 36 )
. toUpperCase ( )
zip . generateAsync ( { type : "blob" } ) . then ( function ( blob ) {
saveAs ( blob , ` EasyDiffusion-Images- ${ now } .zip ` )
2023-03-14 06:10:24 +01:00
} )
}
2023-04-27 19:56:56 +02:00
}
2023-03-14 06:10:24 +01:00
2023-04-27 19:56:56 +02:00
saveAllImagesBtn . addEventListener ( "click" , ( e ) => {
downloadAllImages ( )
} )
2023-02-14 15:03:25 +01:00
2023-04-27 19:56:56 +02:00
stopImageBtn . addEventListener ( "click" , ( e ) => {
shiftOrConfirm ( e , "Stop all the tasks?" , async function ( e ) {
await stopAllTasks ( )
} )
} )
2022-09-23 16:18:48 +02:00
2023-04-27 19:56:56 +02:00
widthField . addEventListener ( "change" , onDimensionChange )
heightField . addEventListener ( "change" , onDimensionChange )
2022-10-18 18:39:11 +02:00
2022-10-21 11:48:05 +02:00
function renameMakeImageButton ( ) {
2023-04-27 19:56:56 +02:00
let totalImages =
2023-06-12 12:48:52 +02:00
Math . max ( parseInt ( numOutputsTotalField . value ) , parseInt ( numOutputsParallelField . value ) ) * getPromptsNumber ( )
2023-04-27 19:56:56 +02:00
let imageLabel = "Image"
2022-10-21 11:48:05 +02:00
if ( totalImages > 1 ) {
2023-04-27 19:56:56 +02:00
imageLabel = totalImages + " Images"
2022-10-21 11:48:05 +02:00
}
2022-12-06 12:34:08 +01:00
if ( SD . activeTasks . size == 0 ) {
2023-07-15 13:05:48 +02:00
if ( totalImages >= 10000 ) makeImageBtn . innerText = "Make 10000+ images"
else makeImageBtn . innerText = "Make " + imageLabel
2022-10-21 11:48:05 +02:00
} else {
2023-07-15 13:05:48 +02:00
if ( totalImages >= 10000 ) makeImageBtn . innerText = "Enqueue 10000+ images"
else makeImageBtn . innerText = "Enqueue Next " + imageLabel
2022-10-21 11:48:05 +02:00
}
}
2023-04-27 19:56:56 +02:00
numOutputsTotalField . addEventListener ( "change" , renameMakeImageButton )
numOutputsTotalField . addEventListener ( "keyup" , debounce ( renameMakeImageButton , 300 ) )
numOutputsParallelField . addEventListener ( "change" , renameMakeImageButton )
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 )
2023-04-27 19:56:56 +02:00
} else {
2022-12-01 11:31:09 +01:00
imageInpainter . setImage ( initImagePreview . src , widthValue , heightValue )
}
2023-04-27 19:56:56 +02:00
if ( widthValue < 512 && heightValue < 512 ) {
smallImageWarning . classList . remove ( "displayNone" )
2023-03-12 20:43:54 +01:00
} else {
2023-04-27 19:56:56 +02:00
smallImageWarning . classList . add ( "displayNone" )
2023-03-12 20:43:54 +01:00
}
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
2023-04-27 19:56:56 +02:00
useFaceCorrectionField . addEventListener ( "change" , function ( e ) {
2023-01-19 20:49:54 +01:00
gfpganModelField . disabled = ! this . checked
2023-06-01 13:20:01 +02:00
onFixFaceModelChange ( )
2023-01-19 20:49:54 +01:00
} )
2023-06-01 13:20:01 +02:00
function onFixFaceModelChange ( ) {
let codeformerSettings = document . querySelector ( "#codeformer_settings" )
if ( gfpganModelField . value === "codeformer" && ! gfpganModelField . disabled ) {
codeformerSettings . classList . remove ( "displayNone" )
2023-06-06 12:46:21 +02:00
codeformerSettings . classList . add ( "expandedSettingRow" )
2023-06-01 13:20:01 +02:00
} else {
codeformerSettings . classList . add ( "displayNone" )
2023-06-06 12:46:21 +02:00
codeformerSettings . classList . remove ( "expandedSettingRow" )
2023-06-01 13:20:01 +02:00
}
}
gfpganModelField . addEventListener ( "change" , onFixFaceModelChange )
onFixFaceModelChange ( )
2023-08-01 12:09:15 +02:00
function onControlnetModelChange ( ) {
let configBox = document . querySelector ( "#controlnet_config" )
if ( IMAGE _REGEX . test ( controlImagePreview . src ) ) {
configBox . classList . remove ( "displayNone" )
controlImageContainer . classList . remove ( "displayNone" )
} else {
configBox . classList . add ( "displayNone" )
controlImageContainer . classList . add ( "displayNone" )
}
}
controlImagePreview . addEventListener ( "load" , onControlnetModelChange )
controlImagePreview . addEventListener ( "unload" , onControlnetModelChange )
onControlnetModelChange ( )
2024-10-08 15:24:06 +02:00
// tip for Flux
let sdModelField = document . querySelector ( "#stable_diffusion_model" )
function checkGuidanceValue ( ) {
let guidance = parseFloat ( guidanceScaleField . value )
let guidanceWarning = document . querySelector ( "#guidanceWarning" )
let guidanceWarningText = document . querySelector ( "#guidanceWarningText" )
if ( sdModelField . value . toLowerCase ( ) . includes ( "flux" ) ) {
if ( guidance > 1.5 ) {
2024-10-09 07:42:28 +02:00
guidanceWarningText . innerText = "Flux recommends a 'Guidance Scale' of 1"
2024-10-08 15:24:06 +02:00
guidanceWarning . classList . remove ( "displayNone" )
} else {
guidanceWarning . classList . add ( "displayNone" )
}
} else {
if ( guidance < 2 ) {
2024-10-09 07:42:28 +02:00
guidanceWarningText . innerText = "A higher 'Guidance Scale' is recommended!"
2024-10-08 15:24:06 +02:00
guidanceWarning . classList . remove ( "displayNone" )
} else {
guidanceWarning . classList . add ( "displayNone" )
}
}
}
sdModelField . addEventListener ( "change" , checkGuidanceValue )
guidanceScaleField . addEventListener ( "change" , checkGuidanceValue )
guidanceScaleSlider . addEventListener ( "change" , checkGuidanceValue )
2024-10-09 07:42:28 +02:00
function checkGuidanceScaleVisibility ( ) {
let guidanceScaleContainer = document . querySelector ( "#distilled_guidance_scale_container" )
if ( sdModelField . value . toLowerCase ( ) . includes ( "flux" ) ) {
guidanceScaleContainer . classList . remove ( "displayNone" )
} else {
guidanceScaleContainer . classList . add ( "displayNone" )
}
}
sdModelField . addEventListener ( "change" , checkGuidanceScaleVisibility )
function checkFluxSampler ( ) {
let samplerWarning = document . querySelector ( "#fluxSamplerWarning" )
if ( sdModelField . value . toLowerCase ( ) . includes ( "flux" ) ) {
if ( samplerField . value == "euler_a" ) {
samplerWarning . classList . remove ( "displayNone" )
} else {
samplerWarning . classList . add ( "displayNone" )
}
} else {
samplerWarning . classList . add ( "displayNone" )
}
}
sdModelField . addEventListener ( "change" , checkFluxSampler )
samplerField . addEventListener ( "change" , checkFluxSampler )
document . addEventListener ( "refreshModels" , function ( ) {
checkGuidanceValue ( )
checkGuidanceScaleVisibility ( )
checkFluxSampler ( )
} )
2024-09-24 14:13:34 +02:00
// function onControlImageFilterChange() {
// let filterId = controlImageFilterField.value
// if (filterId.includes("openpose")) {
// controlnetModelField.value = "control_v11p_sd15_openpose"
// } else if (filterId === "canny") {
// controlnetModelField.value = "control_v11p_sd15_canny"
// } else if (filterId === "mlsd") {
// controlnetModelField.value = "control_v11p_sd15_mlsd"
// } else if (filterId === "mlsd") {
// controlnetModelField.value = "control_v11p_sd15_mlsd"
// } else if (filterId.includes("scribble")) {
// controlnetModelField.value = "control_v11p_sd15_scribble"
// } else if (filterId.includes("softedge")) {
// controlnetModelField.value = "control_v11p_sd15_softedge"
// } else if (filterId === "normal_bae") {
// controlnetModelField.value = "control_v11p_sd15_normalbae"
// } else if (filterId.includes("depth")) {
// controlnetModelField.value = "control_v11f1p_sd15_depth"
// } else if (filterId === "lineart_anime") {
// controlnetModelField.value = "control_v11p_sd15s2_lineart_anime"
// } else if (filterId.includes("lineart")) {
// controlnetModelField.value = "control_v11p_sd15_lineart"
// } else if (filterId === "shuffle") {
// controlnetModelField.value = "control_v11e_sd15_shuffle"
// } else if (filterId === "segment") {
// controlnetModelField.value = "control_v11p_sd15_seg"
// }
// }
// controlImageFilterField.addEventListener("change", onControlImageFilterChange)
// onControlImageFilterChange()
2023-08-01 14:09:04 +02:00
2022-10-29 03:25:54 +02:00
upscaleModelField . disabled = ! useUpscalingField . checked
2022-12-27 11:50:16 +01:00
upscaleAmountField . disabled = ! useUpscalingField . checked
2023-04-27 19:56:56 +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
2023-06-01 13:20:01 +02:00
onUpscaleModelChange ( )
2022-09-23 16:18:48 +02:00
} )
2023-05-23 13:23:53 +02:00
function onUpscaleModelChange ( ) {
let upscale4x = document . querySelector ( "#upscale_amount_4x" )
2023-06-01 13:20:01 +02:00
if ( upscaleModelField . value === "latent_upscaler" && ! upscaleModelField . disabled ) {
2023-05-23 13:23:53 +02:00
upscale4x . disabled = true
upscaleAmountField . value = "2"
latentUpscalerSettings . classList . remove ( "displayNone" )
2023-06-06 12:46:21 +02:00
latentUpscalerSettings . classList . add ( "expandedSettingRow" )
2023-05-23 13:23:53 +02:00
} else {
upscale4x . disabled = false
latentUpscalerSettings . classList . add ( "displayNone" )
2023-06-06 12:46:21 +02:00
latentUpscalerSettings . classList . remove ( "expandedSettingRow" )
2023-05-23 13:23:53 +02:00
}
}
upscaleModelField . addEventListener ( "change" , onUpscaleModelChange )
onUpscaleModelChange ( )
2023-04-27 19:56:56 +02:00
makeImageBtn . addEventListener ( "click" , makeImage )
2022-09-23 16:18:48 +02:00
2022-11-10 10:29:01 +01:00
document . onkeydown = function ( e ) {
2023-04-27 19:56:56 +02:00
if ( e . ctrlKey && e . code === "Enter" ) {
2022-11-10 10:29:01 +01:00
makeImage ( )
e . preventDefault ( )
}
}
2022-09-23 16:18:48 +02:00
2023-06-06 12:46:21 +02:00
/********************* CodeFormer Fidelity **************************/
function updateCodeformerFidelity ( ) {
codeformerFidelityField . value = codeformerFidelitySlider . value / 10
codeformerFidelityField . dispatchEvent ( new Event ( "change" ) )
}
function updateCodeformerFidelitySlider ( ) {
if ( codeformerFidelityField . value < 0 ) {
codeformerFidelityField . value = 0
} else if ( codeformerFidelityField . value > 1 ) {
codeformerFidelityField . value = 1
}
codeformerFidelitySlider . value = codeformerFidelityField . value * 10
codeformerFidelitySlider . dispatchEvent ( new Event ( "change" ) )
}
codeformerFidelitySlider . addEventListener ( "input" , updateCodeformerFidelity )
codeformerFidelityField . addEventListener ( "input" , updateCodeformerFidelitySlider )
updateCodeformerFidelity ( )
2023-05-23 13:23:53 +02:00
/********************* Latent Upscaler Steps **************************/
function updateLatentUpscalerSteps ( ) {
latentUpscalerStepsField . value = latentUpscalerStepsSlider . value
latentUpscalerStepsField . dispatchEvent ( new Event ( "change" ) )
}
function updateLatentUpscalerStepsSlider ( ) {
if ( latentUpscalerStepsField . value < 1 ) {
latentUpscalerStepsField . value = 1
} else if ( latentUpscalerStepsField . value > 50 ) {
latentUpscalerStepsField . value = 50
}
latentUpscalerStepsSlider . value = latentUpscalerStepsField . value
latentUpscalerStepsSlider . dispatchEvent ( new Event ( "change" ) )
}
latentUpscalerStepsSlider . addEventListener ( "input" , updateLatentUpscalerSteps )
latentUpscalerStepsField . addEventListener ( "input" , updateLatentUpscalerStepsSlider )
updateLatentUpscalerSteps ( )
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
}
2023-04-27 19:56:56 +02:00
guidanceScaleSlider . addEventListener ( "input" , updateGuidanceScale )
guidanceScaleField . addEventListener ( "input" , updateGuidanceScaleSlider )
2022-09-23 16:18:48 +02:00
updateGuidanceScale ( )
2024-10-09 07:42:28 +02:00
/********************* Distilled Guidance **************************/
function updateDistilledGuidanceScale ( ) {
distilledGuidanceScaleField . value = distilledGuidanceScaleSlider . value / 10
distilledGuidanceScaleField . dispatchEvent ( new Event ( "change" ) )
}
function updateDistilledGuidanceScaleSlider ( ) {
if ( distilledGuidanceScaleField . value < 0 ) {
distilledGuidanceScaleField . value = 0
} else if ( distilledGuidanceScaleField . value > 50 ) {
distilledGuidanceScaleField . value = 50
}
distilledGuidanceScaleSlider . value = distilledGuidanceScaleField . value * 10
distilledGuidanceScaleSlider . dispatchEvent ( new Event ( "change" ) )
}
distilledGuidanceScaleSlider . addEventListener ( "input" , updateDistilledGuidanceScale )
distilledGuidanceScaleField . addEventListener ( "input" , updateDistilledGuidanceScaleSlider )
updateDistilledGuidanceScale ( )
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
}
2023-04-27 19:56:56 +02:00
promptStrengthSlider . addEventListener ( "input" , updatePromptStrength )
promptStrengthField . addEventListener ( "input" , updatePromptStrengthSlider )
2022-09-23 16:18:48 +02:00
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" ) )
}
2023-04-27 19:56:56 +02:00
hypernetworkStrengthSlider . addEventListener ( "input" , updateHypernetworkStrength )
hypernetworkStrengthField . addEventListener ( "input" , updateHypernetworkStrengthSlider )
2022-12-07 06:54:16 +01:00
updateHypernetworkStrength ( )
2022-12-12 15:01:59 +01:00
function updateHypernetworkStrengthContainer ( ) {
2023-04-27 19:56:56 +02:00
document . querySelector ( "#hypernetwork_strength_container" ) . style . display =
hypernetworkModelField . value === "" ? "none" : ""
2022-12-12 15:01:59 +01:00
}
2023-04-27 19:56:56 +02:00
hypernetworkModelField . addEventListener ( "change" , updateHypernetworkStrengthContainer )
2022-12-12 15:01:59 +01:00
updateHypernetworkStrengthContainer ( )
2024-05-28 15:15:08 +02:00
/********************* Controlnet Alpha **************************/
function updateControlAlpha ( ) {
controlAlphaField . value = controlAlphaSlider . value / 10
controlAlphaField . dispatchEvent ( new Event ( "change" ) )
}
function updateControlAlphaSlider ( ) {
if ( controlAlphaField . value < 0 ) {
controlAlphaField . value = 0
} else if ( controlAlphaField . value > 10 ) {
controlAlphaField . value = 10
}
controlAlphaSlider . value = controlAlphaField . value * 10
controlAlphaSlider . dispatchEvent ( new Event ( "change" ) )
}
controlAlphaSlider . addEventListener ( "input" , updateControlAlpha )
controlAlphaField . addEventListener ( "input" , updateControlAlphaSlider )
updateControlAlpha ( )
2023-02-19 04:37:34 +01:00
/********************* JPEG/WEBP Quality **********************/
2022-12-05 06:32:33 +01:00
function updateOutputQuality ( ) {
2023-04-27 19:56:56 +02:00
outputQualityField . value = 0 | outputQualitySlider . value
2022-12-05 06:32:33 +01:00
outputQualityField . dispatchEvent ( new Event ( "change" ) )
}
function updateOutputQualitySlider ( ) {
if ( outputQualityField . value < 10 ) {
outputQualityField . value = 10
} else if ( outputQualityField . value > 95 ) {
outputQualityField . value = 95
}
2023-04-27 19:56:56 +02:00
outputQualitySlider . value = 0 | outputQualityField . value
2022-12-05 06:32:33 +01:00
outputQualitySlider . dispatchEvent ( new Event ( "change" ) )
}
2023-04-27 19:56:56 +02:00
outputQualitySlider . addEventListener ( "input" , updateOutputQuality )
outputQualityField . addEventListener ( "input" , debounce ( updateOutputQualitySlider , 1500 ) )
2022-12-05 06:32:33 +01:00
updateOutputQuality ( )
2023-03-25 03:46:03 +01:00
function updateOutputQualityVisibility ( ) {
2023-04-27 19:56:56 +02:00
if ( outputFormatField . value === "webp" ) {
outputLosslessContainer . classList . remove ( "displayNone" )
2023-03-25 03:46:03 +01:00
if ( outputLosslessField . checked ) {
2023-04-27 19:56:56 +02:00
outputQualityRow . classList . add ( "displayNone" )
2023-03-25 03:46:03 +01:00
} else {
2023-04-27 19:56:56 +02:00
outputQualityRow . classList . remove ( "displayNone" )
2023-03-25 03:46:03 +01:00
}
2023-04-27 19:56:56 +02:00
} else if ( outputFormatField . value === "png" ) {
outputQualityRow . classList . add ( "displayNone" )
outputLosslessContainer . classList . add ( "displayNone" )
2023-02-19 04:37:34 +01:00
} else {
2023-04-27 19:56:56 +02:00
outputQualityRow . classList . remove ( "displayNone" )
outputLosslessContainer . classList . add ( "displayNone" )
2022-12-05 06:32:33 +01:00
}
2023-03-25 03:46:03 +01:00
}
2023-04-27 19:56:56 +02:00
outputFormatField . addEventListener ( "change" , updateOutputQualityVisibility )
outputLosslessField . addEventListener ( "change" , updateOutputQualityVisibility )
2023-02-22 15:02:00 +01:00
/********************* Zoom Slider **********************/
2023-04-27 19:56:56 +02:00
thumbnailSizeField . addEventListener ( "change" , ( ) => {
; ( function ( s ) {
for ( var j = 0 ; j < document . styleSheets . length ; j ++ ) {
2023-02-22 15:02:00 +01:00
let cssSheet = document . styleSheets [ j ]
for ( var i = 0 ; i < cssSheet . cssRules . length ; i ++ ) {
2023-04-27 19:56:56 +02:00
var rule = cssSheet . cssRules [ i ]
2023-02-22 15:02:00 +01:00
if ( rule . selectorText == "div.img-preview img" ) {
2023-04-27 19:56:56 +02:00
rule . style [ "max-height" ] = s + "vh"
rule . style [ "max-width" ] = s + "vw"
return
2023-02-22 15:02:00 +01:00
}
}
}
} ) ( thumbnailSizeField . value )
} )
2023-03-01 13:57:48 +01:00
function onAutoScrollUpdate ( ) {
if ( autoScroll . checked ) {
2023-04-27 19:56:56 +02:00
autoscrollBtn . classList . add ( "pressed" )
2023-03-01 13:57:48 +01:00
} else {
2023-04-27 19:56:56 +02:00
autoscrollBtn . classList . remove ( "pressed" )
2023-03-01 13:57:48 +01:00
}
2023-04-27 19:56:56 +02:00
autoscrollBtn . querySelector ( ".state" ) . innerHTML = autoScroll . checked ? "ON" : "OFF"
2023-03-01 13:57:48 +01:00
}
2023-04-27 19:56:56 +02:00
autoscrollBtn . addEventListener ( "click" , function ( ) {
2023-03-01 13:57:48 +01:00
autoScroll . checked = ! autoScroll . checked
autoScroll . dispatchEvent ( new Event ( "change" ) )
onAutoScrollUpdate ( )
} )
2023-04-27 19:56:56 +02:00
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
}
}
2023-04-27 19:56:56 +02:00
randomSeedField . addEventListener ( "input" , checkRandomSeed )
2022-09-23 16:18:48 +02:00
checkRandomSeed ( )
2023-08-03 12:19:01 +02:00
// warning: the core plugin `image-editor-improvements.js:172` replaces loadImg2ImgFromFile() with a custom version
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 ]
2023-04-27 19:56:56 +02:00
reader . addEventListener ( "load" , function ( event ) {
2022-09-23 16:18:48 +02:00
initImagePreview . src = reader . result
} )
if ( file ) {
reader . readAsDataURL ( file )
}
}
2023-04-27 19:56:56 +02:00
initImageSelector . addEventListener ( "change" , loadImg2ImgFromFile )
2022-12-06 09:26:51 +01:00
loadImg2ImgFromFile ( )
2022-09-23 16:18:48 +02:00
2022-12-06 09:26:51 +01:00
function img2imgLoad ( ) {
2023-04-27 19:56:56 +02:00
promptStrengthContainer . style . display = "table-row"
2023-03-21 13:29:20 +01:00
if ( ! testDiffusers . checked ) {
samplerSelectionContainer . style . display = "none"
}
2022-12-01 11:31:09 +01:00
initImagePreviewContainer . classList . add ( "has-image" )
2023-04-27 19:56:56 +02:00
colorCorrectionSetting . style . display = ""
2023-07-30 10:21:19 +02:00
strictMaskBorderSetting . style . display = maskSetting . checked ? "" : "none"
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
2023-04-27 19:56:56 +02:00
initImagePreview . src = ""
2022-09-23 16:18:48 +02:00
maskSetting . checked = false
2022-12-06 09:26:51 +01:00
promptStrengthContainer . style . display = "none"
2023-03-21 13:29:20 +01:00
if ( ! testDiffusers . checked ) {
samplerSelectionContainer . style . display = ""
}
2022-12-01 11:31:09 +01:00
initImagePreviewContainer . classList . remove ( "has-image" )
2023-04-27 19:56:56 +02:00
colorCorrectionSetting . style . display = "none"
2023-07-30 10:21:19 +02:00
strictMaskBorderSetting . 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
}
2023-04-27 19:56:56 +02:00
initImagePreview . addEventListener ( "load" , img2imgLoad )
initImageClearBtn . addEventListener ( "click" , img2imgUnload )
2022-09-23 16:18:48 +02:00
2023-04-27 19:56:56 +02:00
maskSetting . addEventListener ( "click" , function ( ) {
2022-10-18 18:39:11 +02:00
onDimensionChange ( )
2022-09-23 16:18:48 +02:00
} )
2023-07-30 10:21:19 +02:00
maskSetting . addEventListener ( "change" , function ( ) {
strictMaskBorderSetting . style . display = this . checked ? "" : "none"
} )
2022-09-23 16:18:48 +02:00
2023-04-27 19:56:56 +02:00
promptsFromFileBtn . addEventListener ( "click" , function ( ) {
2022-10-07 20:16:56 +02:00
promptsFromFileSelector . click ( )
} )
2023-08-01 12:09:15 +02:00
function loadControlnetImageFromFile ( ) {
if ( controlImageSelector . files . length === 0 ) {
return
}
let reader = new FileReader ( )
let file = controlImageSelector . files [ 0 ]
reader . addEventListener ( "load" , function ( event ) {
controlImagePreview . src = reader . result
} )
if ( file ) {
reader . readAsDataURL ( file )
}
}
controlImageSelector . addEventListener ( "change" , loadControlnetImageFromFile )
function controlImageLoad ( ) {
let w = controlImagePreview . naturalWidth
let h = controlImagePreview . naturalHeight
2023-08-03 14:10:26 +02:00
w = w - ( w % IMAGE _STEP _SIZE )
h = h - ( h % IMAGE _STEP _SIZE )
2023-08-03 12:21:39 +02:00
2023-08-01 12:09:15 +02:00
addImageSizeOption ( w )
addImageSizeOption ( h )
widthField . value = w
heightField . value = h
widthField . dispatchEvent ( new Event ( "change" ) )
heightField . dispatchEvent ( new Event ( "change" ) )
}
controlImagePreview . addEventListener ( "load" , controlImageLoad )
function controlImageUnload ( ) {
controlImageSelector . value = null
controlImagePreview . src = ""
controlImagePreview . dispatchEvent ( new Event ( "unload" ) )
}
controlImageClearBtn . addEventListener ( "click" , controlImageUnload )
2023-04-27 19:56:56 +02:00
promptsFromFileSelector . addEventListener ( "change" , async function ( ) {
2022-10-07 20:16:56 +02:00
if ( promptsFromFileSelector . files . length === 0 ) {
return
}
let reader = new FileReader ( )
let file = promptsFromFileSelector . files [ 0 ]
2023-04-27 19:56:56 +02:00
reader . addEventListener ( "load" , async function ( ) {
2023-03-19 16:21:17 +01:00
await parseContent ( reader . result )
2022-10-07 20:16:56 +02:00
} )
if ( file ) {
reader . readAsText ( file )
}
} )
2022-10-29 01:48:32 +02:00
/* setup popup handlers */
2023-04-27 19:56:56 +02:00
document . querySelectorAll ( ".popup" ) . forEach ( ( popup ) => {
popup . addEventListener ( "click" , ( event ) => {
2022-10-29 01:48:32 +02:00
if ( event . target == popup ) {
popup . classList . remove ( "active" )
}
} )
var closeButton = popup . querySelector ( ".close-button" )
if ( closeButton ) {
2023-04-27 19:56:56 +02:00
closeButton . addEventListener ( "click" , ( ) => {
2022-10-29 01:48:32 +02:00
popup . classList . remove ( "active" )
} )
}
} )
2022-12-01 11:31:09 +01:00
var tabElements = [ ]
function selectTab ( tab _id ) {
2023-04-27 19:56:56 +02:00
let tabInfo = tabElements . find ( ( t ) => t . tab . id == tab _id )
2022-12-01 11:31:09 +01:00
if ( ! tabInfo . tab . classList . contains ( "active" ) ) {
2023-04-27 19:56:56 +02:00
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" )
}
2023-04-27 19:56:56 +02: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 ,
2023-04-28 12:20:44 +02:00
content : content ,
2022-11-09 04:54:41 +01:00
} )
2023-04-27 19:56:56 +02: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
2023-04-22 11:42:22 +02:00
function splashScreen ( force = false ) {
2023-07-15 13:05:48 +02:00
const splashVersion = splashScreenPopup . dataset [ "version" ]
2023-04-22 11:42:22 +02:00
const lastSplash = localStorage . getItem ( "lastSplashScreenVersion" ) || 0
if ( testDiffusers . checked ) {
if ( force || lastSplash < splashVersion ) {
splashScreenPopup . classList . add ( "active" )
localStorage . setItem ( "lastSplashScreenVersion" , splashVersion )
}
}
}
2023-07-15 13:05:48 +02:00
document . getElementById ( "logo_img" ) . addEventListener ( "click" , ( e ) => {
splashScreen ( true )
} )
2023-01-13 22:05:25 +01:00
2023-04-27 19:56:56 +02:00
promptField . addEventListener ( "input" , debounce ( renameMakeImageButton , 1000 ) )
2023-01-13 22:05:25 +01:00
2023-07-29 17:39:27 +02:00
function onPing ( event ) {
tunnelUpdate ( event )
packagesUpdate ( event )
}
2023-05-28 00:50:23 +02:00
function tunnelUpdate ( event ) {
if ( "cloudflare" in event ) {
2023-06-06 12:46:21 +02:00
document . getElementById ( "cloudflare-off" ) . classList . add ( "displayNone" )
document . getElementById ( "cloudflare-on" ) . classList . remove ( "displayNone" )
2023-07-01 00:15:18 +02:00
cloudflareAddressField . value = event . cloudflare
2023-06-06 12:46:21 +02:00
document . getElementById ( "toggle-cloudflare-tunnel" ) . innerHTML = "Stop"
2023-05-28 00:50:23 +02:00
} else {
2023-06-06 12:46:21 +02:00
document . getElementById ( "cloudflare-on" ) . classList . add ( "displayNone" )
document . getElementById ( "cloudflare-off" ) . classList . remove ( "displayNone" )
document . getElementById ( "toggle-cloudflare-tunnel" ) . innerHTML = "Start"
2023-05-28 00:50:23 +02:00
}
}
2023-07-29 17:39:27 +02:00
function packagesUpdate ( event ) {
let trtBtn = document . getElementById ( "toggle-tensorrt-install" )
let trtInstalled = "packages_installed" in event && "tensorrt" in event [ "packages_installed" ]
if ( "packages_installing" in event && event [ "packages_installing" ] . includes ( "tensorrt" ) ) {
trtBtn . innerHTML = "Installing.."
trtBtn . disabled = true
} else {
trtBtn . innerHTML = trtInstalled ? "Uninstall" : "Install"
trtBtn . disabled = false
}
if ( document . getElementById ( "toggle-tensorrt-install" ) . innerHTML == "Uninstall" ) {
document . querySelector ( "#enable_trt_config" ) . classList . remove ( "displayNone" )
2023-08-01 20:23:01 +02:00
document . querySelector ( "#trt-build-config" ) . classList . remove ( "displayNone" )
2023-07-29 17:39:27 +02:00
}
}
2023-06-06 12:46:21 +02:00
document . getElementById ( "toggle-cloudflare-tunnel" ) . addEventListener ( "click" , async function ( ) {
2023-05-28 00:50:23 +02:00
let command = "stop"
2023-06-06 12:46:21 +02:00
if ( document . getElementById ( "toggle-cloudflare-tunnel" ) . innerHTML == "Start" ) {
command = "start"
2023-05-28 00:50:23 +02:00
}
showToast ( ` Cloudflare tunnel ${ command } initiated. Please wait. ` )
2023-06-06 12:46:21 +02:00
let res = await fetch ( "/tunnel/cloudflare/" + command , {
2023-05-28 00:50:23 +02:00
method : "POST" ,
headers : {
"Content-Type" : "application/json" ,
} ,
body : JSON . stringify ( { } ) ,
} )
res = await res . json ( )
console . log ( ` Cloudflare tunnel ${ command } result: ` , res )
} )
2023-07-29 17:39:27 +02:00
document . getElementById ( "toggle-tensorrt-install" ) . addEventListener ( "click" , function ( e ) {
if ( this . disabled === true ) {
return
}
let command = this . innerHTML . toLowerCase ( )
let self = this
shiftOrConfirm (
e ,
"Are you sure you want to " + command + " TensorRT?" ,
async function ( ) {
showToast ( ` TensorRT ${ command } started. Please wait. ` )
self . disabled = true
if ( command === "install" ) {
self . innerHTML = "Installing.."
} else if ( command === "uninstall" ) {
self . innerHTML = "Uninstalling.."
}
if ( command === "installing.." ) {
alert ( "Already installing TensorRT!" )
return
}
if ( command !== "install" && command !== "uninstall" ) {
return
}
let res = await fetch ( "/package/tensorrt" , {
method : "POST" ,
headers : {
"Content-Type" : "application/json" ,
} ,
body : JSON . stringify ( {
command : command ,
} ) ,
} )
res = await res . json ( )
self . disabled = false
if ( res . status === "OK" ) {
alert ( "TensorRT " + command + "ed successfully!" )
self . innerHTML = command === "install" ? "Uninstall" : "Install"
} else if ( res . status _code === 500 ) {
alert ( "TensorselfRT failed to " + command + ": " + res . detail )
self . innerHTML = command === "install" ? "Install" : "Uninstall"
}
console . log ( ` Package ${ command } result: ` , res )
} ,
false
)
} )
2023-06-30 08:41:15 +02:00
/* Embeddings */
2023-08-21 07:34:13 +02:00
addEmbeddingsThumb . addEventListener ( "click" , ( e ) => addEmbeddingsThumbInput . click ( ) )
2023-08-19 06:20:40 +02:00
addEmbeddingsThumbInput . addEventListener ( "change" , loadThumbnailImageFromFile )
function loadThumbnailImageFromFile ( ) {
if ( addEmbeddingsThumbInput . files . length === 0 ) {
return
}
let reader = new FileReader ( )
let file = addEmbeddingsThumbInput . files [ 0 ]
reader . addEventListener ( "load" , function ( event ) {
let img = document . createElement ( "img" )
img . src = reader . result
onUseAsThumbnailClick (
{
2023-08-21 07:34:13 +02:00
use _embeddings _model : getAllModelNames ( "embeddings" ) . sort ( ( a , b ) =>
a . localeCompare ( b , undefined , { sensitivity : "base" } )
) ,
2023-08-19 06:20:40 +02:00
} ,
img
)
} )
if ( file ) {
reader . readAsDataURL ( file )
}
}
2023-07-15 13:05:48 +02:00
function updateEmbeddingsList ( filter = "" ) {
2023-09-04 01:36:32 +02:00
function html ( model , iconMap = { } , prefix = "" , filter = "" ) {
2023-06-30 08:41:15 +02:00
filter = filter . toLowerCase ( )
2023-08-17 08:03:05 +02:00
let toplevel = document . createElement ( "div" )
let folders = document . createElement ( "div" )
2023-07-15 13:05:48 +02:00
2023-08-17 08:03:05 +02:00
let profileName = profileNameField . value
2023-07-15 13:05:48 +02:00
model ? . forEach ( ( m ) => {
if ( typeof m == "string" ) {
2023-08-17 12:39:47 +02:00
let token = m . toLowerCase ( )
2023-08-17 08:03:05 +02:00
if ( token . search ( filter ) != - 1 ) {
let button
2023-08-17 12:39:47 +02:00
let img = "/media/images/noimg.png"
2023-09-04 01:36:32 +02:00
if ( token in iconMap ) {
img = ` /bucket/ ${ profileName } / ${ iconMap [ token ] } `
2023-08-17 08:03:05 +02:00
}
2023-08-17 12:39:47 +02:00
button = createModifierCard ( m , [ img , img ] , true )
2023-08-17 08:56:23 +02:00
// }
2023-08-17 08:03:05 +02:00
button . dataset [ "embedding" ] = m
button . addEventListener ( "click" , onButtonClick )
toplevel . appendChild ( button )
2023-06-30 08:41:15 +02:00
}
} else {
2023-09-04 01:36:32 +02:00
let subdir = html ( m [ 1 ] , iconMap , prefix + m [ 0 ] + "/" , filter )
2023-08-17 12:39:47 +02:00
if ( typeof subdir == "object" ) {
2023-08-17 08:03:05 +02:00
let div1 = document . createElement ( "div" )
let div2 = document . createElement ( "div" )
div1 . classList . add ( "collapsible-content" )
div1 . classList . add ( "embedding-category" )
div1 . appendChild ( subdir )
div2 . replaceChildren ( htmlToElement ( ` <h4 class="collapsible"> ${ prefix } ${ m [ 0 ] } </h4> ` ) , div1 )
folders . appendChild ( div2 )
2023-06-30 08:41:15 +02:00
}
}
} )
2023-08-17 09:53:13 +02:00
if ( toplevel . children . length == 0 && folders . children . length == 0 ) {
2023-08-17 12:39:47 +02:00
// Empty folder
return ""
2023-08-17 09:53:13 +02:00
}
2023-08-17 08:03:05 +02:00
let result = document . createElement ( "div" )
result . replaceChildren ( toplevel , htmlToElement ( '<br style="clear: both;">' ) , folders )
return result
2023-06-30 08:41:15 +02:00
}
function onButtonClick ( e ) {
2023-08-17 08:03:05 +02:00
let text = e . target . closest ( "[data-embedding]" ) . dataset [ "embedding" ]
2023-07-16 11:00:45 +02:00
const insertIntoNegative = e . shiftKey || positiveEmbeddingText . classList . contains ( "displayNone" )
2023-06-30 08:41:15 +02:00
if ( embeddingsModeField . value == "insert" ) {
2023-07-16 11:00:45 +02:00
if ( insertIntoNegative ) {
2023-06-30 08:41:15 +02:00
insertAtCursor ( negativePromptField , text )
} else {
insertAtCursor ( promptField , text )
}
} else {
2023-07-15 13:05:48 +02:00
let pad = ""
2023-07-16 11:00:45 +02:00
if ( insertIntoNegative ) {
2023-06-30 08:41:15 +02:00
if ( ! negativePromptField . value . endsWith ( " " ) ) {
pad = " "
}
2023-07-07 22:49:21 +02:00
negativePromptField . value += pad + text
2023-06-30 08:41:15 +02:00
} else {
if ( ! promptField . value . endsWith ( " " ) ) {
pad = " "
}
2023-07-07 22:49:21 +02:00
promptField . value += pad + text
2023-06-30 08:41:15 +02:00
}
}
}
2023-08-17 08:03:05 +02:00
// Usually the rendering of the Embeddings HTML takes less than a second. In case it takes longer, show a spinner
embeddingsList . innerHTML = `
< div class = "spinner-container" >
< div class = "spinner-block" > < / d i v > < d i v c l a s s = " s p i n n e r - b l o c k " > < / d i v > < d i v c l a s s = " s p i n n e r - b l o c k " > < / d i v > < d i v c l a s s = " s p i n n e r - b l o c k " > < / d i v >
< div class = "spinner-block" > < / d i v > < d i v c l a s s = " s p i n n e r - b l o c k " > < / d i v > < d i v c l a s s = " s p i n n e r - b l o c k " > < / d i v > < d i v c l a s s = " s p i n n e r - b l o c k " > < / d i v >
< div class = "spinner-block" > < / d i v > < d i v c l a s s = " s p i n n e r - b l o c k " > < / d i v > < d i v c l a s s = " s p i n n e r - b l o c k " > < / d i v > < d i v c l a s s = " s p i n n e r - b l o c k " > < / d i v >
< div class = "spinner-block" > < / d i v > < d i v c l a s s = " s p i n n e r - b l o c k " > < / d i v > < d i v c l a s s = " s p i n n e r - b l o c k " > < / d i v > < d i v c l a s s = " s p i n n e r - b l o c k " > < / d i v >
< / d i v >
`
2023-09-04 01:36:32 +02:00
let loraTokens = [ ]
2023-08-17 08:03:05 +02:00
let profileName = profileNameField . value
2023-09-04 01:36:32 +02:00
let iconMap = { }
Bucket . getList ( ` ${ profileName } /embeddings/ ` )
. then ( ( icons ) => {
iconMap = Object . assign (
{ } ,
... icons . map ( ( x ) => ( {
[ x
. toLowerCase ( )
. split ( "." )
. slice ( 0 , - 1 )
. join ( "." ) ] : ` embeddings/ ${ x } ` ,
} ) )
)
return Bucket . getList ( ` ${ profileName } /lora/ ` )
} )
2023-10-12 06:28:29 +02:00
. then ( async function ( icons ) {
2023-09-04 01:36:32 +02:00
for ( let lora of loraModelField . value . modelNames ) {
let keywords = await getLoraKeywords ( lora )
loraTokens = loraTokens . concat ( keywords )
let loraname = lora . split ( "/" ) . pop ( )
if ( icons . includes ( ` ${ loraname } .png ` ) ) {
keywords . forEach ( ( kw ) => {
iconMap [ kw . toLowerCase ( ) ] = ` lora/ ${ loraname } .png `
2023-10-12 06:28:29 +02:00
2023-09-04 01:36:32 +02:00
} )
}
}
let tokenList = [ ... modelsOptions . embeddings ]
if ( loraTokens . length != 0 ) {
2023-10-12 06:28:29 +02:00
tokenList . unshift ( [ 'LORA Keywords' , loraTokens ] )
2023-09-04 01:36:32 +02:00
}
embeddingsList . replaceChildren ( html ( tokenList , iconMap , "" , filter ) )
2023-08-17 12:39:47 +02:00
createCollapsibles ( embeddingsList )
if ( filter != "" ) {
embeddingsExpandAll ( )
}
resizeModifierCards ( embeddingsCardSizeSelector . value )
} )
2023-06-30 08:41:15 +02:00
}
2023-07-16 11:00:45 +02:00
function showEmbeddingDialog ( ) {
2023-06-30 08:41:15 +02:00
updateEmbeddingsList ( )
2023-07-15 13:05:48 +02:00
embeddingsSearchBox . value = ""
embeddingsDialog . showModal ( )
2023-07-16 11:00:45 +02:00
}
2023-08-17 08:03:05 +02:00
2023-07-16 11:00:45 +02:00
embeddingsButton . addEventListener ( "click" , ( ) => {
positiveEmbeddingText . classList . remove ( "displayNone" )
negativeEmbeddingText . classList . add ( "displayNone" )
showEmbeddingDialog ( )
} )
2023-08-17 08:03:05 +02:00
2023-07-16 11:00:45 +02:00
negativeEmbeddingsButton . addEventListener ( "click" , ( ) => {
positiveEmbeddingText . classList . add ( "displayNone" )
negativeEmbeddingText . classList . remove ( "displayNone" )
showEmbeddingDialog ( )
2023-06-30 08:41:15 +02:00
} )
2023-08-17 08:03:05 +02:00
2023-06-30 08:41:15 +02:00
embeddingsDialogCloseBtn . addEventListener ( "click" , ( e ) => {
embeddingsDialog . close ( )
} )
2023-08-17 08:03:05 +02:00
2023-06-30 08:41:15 +02:00
embeddingsSearchBox . addEventListener ( "input" , ( e ) => {
updateEmbeddingsList ( embeddingsSearchBox . value )
} )
2023-08-17 08:03:05 +02:00
embeddingsCardSizeSelector . addEventListener ( "change" , ( e ) => {
resizeModifierCards ( embeddingsCardSizeSelector . value )
} )
2023-06-30 23:28:24 +02:00
modalDialogCloseOnBackdropClick ( embeddingsDialog )
makeDialogDraggable ( embeddingsDialog )
2023-07-18 00:08:38 +02:00
const collapseText = "Collapse Categories"
const expandText = "Expand Categories"
const collapseIconClasses = [ "fa-solid" , "fa-square-minus" ]
const expandIconClasses = [ "fa-solid" , "fa-square-plus" ]
function embeddingsCollapseAll ( ) {
const btnElem = embeddingsCollapsiblesBtn
const iconElem = btnElem . querySelector ( ".embeddings-action-icon" )
const textElem = btnElem . querySelector ( ".embeddings-action-text" )
collapseAll ( "#embeddings-list .collapsible" )
collapsiblesBtnState = false
collapseIconClasses . forEach ( ( c ) => iconElem . classList . remove ( c ) )
expandIconClasses . forEach ( ( c ) => iconElem . classList . add ( c ) )
textElem . innerText = expandText
}
function embeddingsExpandAll ( ) {
const btnElem = embeddingsCollapsiblesBtn
const iconElem = btnElem . querySelector ( ".embeddings-action-icon" )
const textElem = btnElem . querySelector ( ".embeddings-action-text" )
expandAll ( "#embeddings-list .collapsible" )
collapsiblesBtnState = true
expandIconClasses . forEach ( ( c ) => iconElem . classList . remove ( c ) )
collapseIconClasses . forEach ( ( c ) => iconElem . classList . add ( c ) )
textElem . innerText = collapseText
}
embeddingsCollapsiblesBtn . addEventListener ( "click" , ( e ) => {
if ( collapsiblesBtnState ) {
embeddingsCollapseAll ( )
} else {
embeddingsExpandAll ( )
}
} )
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 ) {
2023-04-27 19:56:56 +02:00
const msg = "Unsaved pictures will be lost!"
2022-11-10 23:23:20 +01:00
2023-04-27 19:56:56 +02:00
let elementList = document . getElementsByClassName ( "imageTaskContainer" )
2022-11-10 23:23:20 +01:00
if ( elementList . length != 0 ) {
2023-04-27 19:56:56 +02:00
e . preventDefault ( )
; ( e || window . event ) . returnValue = msg
return msg
2022-11-10 23:23:20 +01:00
} else {
2023-04-27 19:56:56 +02:00
return true
2022-11-10 23:23:20 +01:00
}
2023-04-27 19:56:56 +02:00
} )
2022-11-10 23:23:20 +01:00
2023-08-22 13:29:40 +02:00
document . addEventListener ( "collapsibleClick" , function ( e ) {
let header = e . detail
if ( header === document . querySelector ( "#negative_prompt_handle" ) ) {
if ( header . classList . contains ( "active" ) ) {
negativeEmbeddingsButton . classList . remove ( "displayNone" )
} else {
negativeEmbeddingsButton . classList . add ( "displayNone" )
}
}
} )
2022-10-22 05:08:19 +02:00
createCollapsibles ( )
2023-04-27 19:56:56 +02: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-07-15 16:03:49 +02:00
2023-07-29 18:12:48 +02:00
////////////////////////////// Image Size Widget //////////////////////////////////////////
function roundToMultiple ( number , n ) {
2023-07-30 10:21:19 +02:00
if ( n == "" ) {
n = 1
}
return Math . round ( number / n ) * n
2023-07-29 18:12:48 +02:00
}
2023-07-30 06:46:04 +02:00
function addImageSizeOption ( size ) {
2023-07-30 10:21:19 +02:00
let sizes = Object . values ( widthField . options ) . map ( ( o ) => o . value )
2023-07-30 06:46:04 +02:00
if ( ! sizes . includes ( String ( size ) ) ) {
sizes . push ( String ( size ) )
2023-07-30 10:21:19 +02:00
sizes . sort ( ( a , b ) => Number ( a ) - Number ( b ) )
2023-07-30 06:46:04 +02:00
let option = document . createElement ( "option" )
option . value = size
option . text = ` ${ size } `
2023-07-30 10:21:19 +02:00
2023-07-30 06:46:04 +02:00
widthField . add ( option , sizes . indexOf ( String ( size ) ) )
heightField . add ( option . cloneNode ( true ) , sizes . indexOf ( String ( size ) ) )
}
2023-07-29 18:12:48 +02:00
}
2023-07-30 10:21:19 +02:00
function setImageWidthHeight ( w , h ) {
2023-07-30 06:46:04 +02:00
let step = customWidthField . step
w = roundToMultiple ( w , step )
h = roundToMultiple ( h , step )
2023-07-29 18:12:48 +02:00
2023-07-30 06:46:04 +02:00
addImageSizeOption ( w )
addImageSizeOption ( h )
2023-07-30 10:21:19 +02:00
widthField . value = w
heightField . value = h
2023-07-30 06:46:04 +02:00
widthField . dispatchEvent ( new Event ( "change" ) )
heightField . dispatchEvent ( new Event ( "change" ) )
}
function enlargeImageSize ( factor ) {
let step = customWidthField . step
2023-07-30 10:21:19 +02:00
let w = roundToMultiple ( widthField . value * factor , step )
let h = roundToMultiple ( heightField . value * factor , step )
2023-07-30 06:46:04 +02:00
customWidthField . value = w
customHeightField . value = h
2023-07-29 18:12:48 +02:00
}
let recentResolutionsValues = [ ]
2023-07-30 10:21:19 +02:00
; ( function ( ) {
///// Init resolutions dropdown
2023-08-14 14:56:33 +02:00
function makeResolutionButtons ( listElement , resolutionList ) {
listElement . innerHTML = ""
resolutionList . forEach ( ( el ) => {
2023-08-15 13:28:12 +02:00
let button = createElement ( "button" , { style : "width: 8em;" } , "tertiaryButton" , ` ${ el . w } × ${ el . h } ` )
2023-07-30 06:46:04 +02:00
button . addEventListener ( "click" , ( ) => {
2023-07-30 10:21:19 +02:00
customWidthField . value = el . w
customHeightField . value = el . h
2023-07-30 06:46:04 +02:00
hidePopup ( )
} )
2023-08-14 14:56:33 +02:00
listElement . appendChild ( button )
listElement . appendChild ( document . createElement ( "br" ) )
2023-07-30 06:46:04 +02:00
} )
}
2023-08-15 13:28:12 +02:00
enlargeButtons . querySelectorAll ( "button" ) . forEach ( ( button ) =>
button . addEventListener ( "click" , ( e ) => {
enlargeImageSize ( parseFloat ( button . dataset [ "factor" ] ) )
hidePopup ( )
} )
)
2023-07-29 18:12:48 +02:00
2023-07-30 06:46:04 +02:00
customWidthField . addEventListener ( "change" , ( ) => {
let w = customWidthField . value
customWidthField . value = roundToMultiple ( w , customWidthField . step )
2023-07-30 10:21:19 +02:00
if ( w != customWidthField . value ) {
2023-07-30 06:46:04 +02:00
showToast ( ` Rounded width to the closest multiple of ${ customWidthField . step } . ` )
2023-07-29 18:12:48 +02:00
}
} )
2023-07-30 06:46:04 +02:00
customHeightField . addEventListener ( "change" , ( ) => {
let h = customHeightField . value
customHeightField . value = roundToMultiple ( h , customHeightField . step )
2023-07-30 10:21:19 +02:00
if ( h != customHeightField . value ) {
2023-07-30 06:46:04 +02:00
showToast ( ` Rounded height to the closest multiple of ${ customHeightField . step } . ` )
2023-07-29 18:12:48 +02:00
}
} )
makeImageBtn . addEventListener ( "click" , ( ) => {
2023-07-30 06:46:04 +02:00
let w = widthField . value
let h = heightField . value
2023-07-29 18:12:48 +02:00
2023-07-30 10:21:19 +02:00
recentResolutionsValues = recentResolutionsValues . filter ( ( el ) => el . w != w || el . h != h )
recentResolutionsValues . unshift ( { w : w , h : h } )
recentResolutionsValues = recentResolutionsValues . slice ( 0 , 8 )
2023-07-29 18:12:48 +02:00
localStorage . recentResolutionsValues = JSON . stringify ( recentResolutionsValues )
2023-08-14 14:56:33 +02:00
makeResolutionButtons ( recentResolutionList , recentResolutionsValues )
2023-07-29 18:12:48 +02:00
} )
2023-08-14 14:56:33 +02:00
const defaultResolutionsValues = [
2023-08-15 13:28:12 +02:00
{ w : 512 , h : 512 } ,
{ w : 448 , h : 640 } ,
{ w : 512 , h : 768 } ,
{ w : 768 , h : 512 } ,
{ w : 1024 , h : 768 } ,
{ w : 768 , h : 1024 } ,
{ w : 1024 , h : 1024 } ,
{ w : 1920 , h : 1080 } ,
]
2023-08-14 14:56:33 +02:00
let _jsonstring = localStorage . recentResolutionsValues
if ( _jsonstring == undefined ) {
2023-08-15 13:28:12 +02:00
recentResolutionsValues = defaultResolutionsValues
2023-07-29 18:12:48 +02:00
localStorage . recentResolutionsValues = JSON . stringify ( recentResolutionsValues )
} else {
recentResolutionsValues = JSON . parse ( localStorage . recentResolutionsValues )
}
2023-08-14 14:56:33 +02:00
makeResolutionButtons ( recentResolutionList , recentResolutionsValues )
makeResolutionButtons ( commonResolutionList , defaultResolutionsValues )
2023-07-29 18:12:48 +02:00
2023-07-30 10:21:19 +02:00
recentResolutionsValues . forEach ( ( val ) => {
2023-07-30 06:46:04 +02:00
addImageSizeOption ( val . w )
addImageSizeOption ( val . h )
} )
2023-07-29 18:12:48 +02:00
function processClick ( e ) {
if ( ! recentResolutionsPopup . contains ( e . target ) ) {
2023-07-30 06:46:04 +02:00
hidePopup ( )
2023-07-29 18:12:48 +02:00
}
2023-07-30 06:46:04 +02:00
}
function showPopup ( ) {
customWidthField . value = widthField . value
customHeightField . value = heightField . value
recentResolutionsPopup . classList . remove ( "displayNone" )
2023-08-14 14:56:33 +02:00
resizeSlider . value = 1
resizeSlider . dataset [ "w" ] = widthField . value
resizeSlider . dataset [ "h" ] = heightField . value
2023-07-30 06:46:04 +02:00
document . addEventListener ( "click" , processClick )
}
function hidePopup ( ) {
recentResolutionsPopup . classList . add ( "displayNone" )
setImageWidthHeight ( customWidthField . value , customHeightField . value )
2023-07-29 18:12:48 +02:00
document . removeEventListener ( "click" , processClick )
}
recentResolutionsButton . addEventListener ( "click" , ( event ) => {
2023-07-30 06:46:04 +02:00
if ( recentResolutionsPopup . classList . contains ( "displayNone" ) ) {
showPopup ( )
event . stopPropagation ( )
} else {
hidePopup ( )
}
2023-07-29 18:12:48 +02:00
} )
2023-08-15 13:28:12 +02:00
resizeSlider . addEventListener ( "input" , ( e ) => {
2023-08-14 14:56:33 +02:00
let w = parseInt ( resizeSlider . dataset [ "w" ] )
let h = parseInt ( resizeSlider . dataset [ "h" ] )
let factor = parseFloat ( resizeSlider . value )
let step = customWidthField . step
2023-08-15 13:28:12 +02:00
customWidthField . value = roundToMultiple ( w * factor * factor , step )
customHeightField . value = roundToMultiple ( h * factor * factor , step )
2023-08-14 14:56:33 +02:00
} )
2023-08-15 13:28:12 +02:00
resizeSlider . addEventListener ( "change" , ( e ) => {
2023-08-14 14:56:33 +02:00
hidePopup ( )
} )
2023-07-29 18:12:48 +02:00
swapWidthHeightButton . addEventListener ( "click" , ( event ) => {
2023-07-30 06:46:04 +02:00
let temp = widthField . value
widthField . value = heightField . value
heightField . value = temp
2023-07-29 18:12:48 +02:00
} )
} ) ( )
2023-08-19 09:45:32 +02:00
2023-08-22 08:32:34 +02:00
document . addEventListener ( "before_task_start" , ( e ) => {
let task = e . detail . task
2023-08-19 09:45:32 +02:00
// Update the seed *before* starting the processing so it's retained if user stops the task
if ( randomSeedField . checked ) {
seedField . value = task . seed
}
} )
2023-08-22 08:32:34 +02:00
document . addEventListener ( "after_task_start" , ( e ) => {
2023-08-19 09:45:32 +02:00
renderButtons . style . display = "flex"
renameMakeImageButton ( )
updateInitialText ( )
} )
2023-08-22 08:32:34 +02:00
document . addEventListener ( "on_task_step" , ( e ) => {
showImages ( e . detail . reqBody , e . detail . stepUpdate , e . detail . outputContainer , true )
2023-08-19 09:45:32 +02:00
} )
2023-08-22 08:32:34 +02:00
document . addEventListener ( "on_render_task_success" , ( e ) => {
showImages ( e . detail . reqBody , e . detail . stepUpdate , e . detail . outputContainer , false )
2023-08-19 09:45:32 +02:00
} )
2023-08-22 08:32:34 +02:00
document . addEventListener ( "on_render_task_fail" , ( e ) => {
let task = e . detail . task
let stepUpdate = e . detail . stepUpdate
2023-08-19 09:45:32 +02:00
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 / >
2. Try picking a lower level in the '<em>GPU Memory Usage</em>' setting ( in the '<em>Settings</em>' tab ) . < br / >
3. Try generating a smaller image . < br / > `
} else if ( msg . includes ( "DefaultCPUAllocator: not enough memory" ) ) {
msg += ` <br/><br/>
Reason : Your computer is running out of system RAM !
< br / > < br / >
< b > Suggestions < / b > :
< br / >
1. Try closing unnecessary programs and browser tabs . < br / >
2. If that doesn 't help, please increase your computer' s virtual memory by following these steps for
< a href = "https://www.ibm.com/docs/en/opw/8.2.0?topic=tuning-optional-increasing-paging-file-size-windows-computers" target = "_blank" > Windows < / a > o r
< a href = "https://linuxhint.com/increase-swap-space-linux/" target = "_blank" > Linux < /a>.<br/ >
3. Try restarting your computer . < br / > `
} else if ( msg . includes ( "RuntimeError: output with shape [320, 320] doesn't match the broadcast shape" ) ) {
msg += ` <br/><br/>
< b > Reason < / b > : Y o u t r i e d t o u s e a L O R A t h a t w a s t r a i n e d f o r a d i f f e r e n t S t a b l e D i f f u s i o n m o d e l v e r s i o n !
< br / > < br / >
< b > Suggestions < / b > :
< br / >
Try to use a different model or a different LORA . `
} else if ( msg . includes ( "'ModuleList' object has no attribute '1'" ) ) {
msg += ` <br/><br/>
< b > Reason < / b > : S D X L m o d e l s n e e d a y a m l c o n f i g f i l e .
< br / > < br / >
< b > Suggestions < / b > :
< br / >
< ol >
< li > Download the < a href = "https://gist.githubusercontent.com/JeLuF/5dc56e7a3a6988265c423f464d3cbdd3/raw/4ba4c39b1c7329877ad7a39c8c8a077ea4b53d11/dreamshaperXL10_alpha2Xl10.yaml" target = "_blank" > config file < / a > < / l i >
< li > Save it in the same directory as the SDXL model file < / l i >
< li > Rename the config file so that it matches the filename of the model , with the extension of the model file replaced by < tt > yaml < / t t > .
For example , if the model file is called < tt > FantasySDXL _v2 . safetensors < / t t > , t h e c o n f i g f i l e m u s t b e c a l l e d < t t > F a n t a s y S D X L _ v 2 . y a m l < / t t > .
< / o l > `
}
} else {
msg = ` Unexpected Read Error:<br/><pre>StepUpdate: ${ JSON . stringify ( stepUpdate , undefined , 4 ) } </pre> `
}
logError ( msg , stepUpdate , outputMsg )
} )
2023-08-22 08:32:34 +02:00
document . addEventListener ( "on_all_tasks_complete" , ( e ) => {
2023-08-19 09:45:32 +02:00
renderButtons . style . display = "none"
renameMakeImageButton ( )
if ( isSoundEnabled ( ) ) {
playSound ( )
}
} )