2022-09-23 16:18:48 +02:00
const SOUND _ENABLED _KEY = "soundEnabled"
const SAVE _TO _DISK _KEY = "saveToDisk"
const USE _CPU _KEY = "useCPU"
const USE _FULL _PRECISION _KEY = "useFullPrecision"
const USE _TURBO _MODE _KEY = "useTurboMode"
const DISK _PATH _KEY = "diskPath"
const ADVANCED _PANEL _OPEN _KEY = "advancedPanelOpen"
const MODIFIERS _PANEL _OPEN _KEY = "modifiersPanelOpen"
const USE _FACE _CORRECTION _KEY = "useFaceCorrection"
const USE _UPSCALING _KEY = "useUpscaling"
const SHOW _ONLY _FILTERED _IMAGE _KEY = "showOnlyFilteredImage"
const STREAM _IMAGE _PROGRESS _KEY = "streamImageProgress"
const HEALTH _PING _INTERVAL = 5 // seconds
const MAX _INIT _IMAGE _DIMENSION = 768
const IMAGE _REGEX = new RegExp ( 'data:image/[A-Za-z]+;base64' )
let sessionId = new Date ( ) . getTime ( )
let promptField = document . querySelector ( '#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' )
let randomSeedField = document . querySelector ( "#random_seed" )
let seedField = document . querySelector ( '#seed' )
let widthField = document . querySelector ( '#width' )
let heightField = document . querySelector ( '#height' )
let initImageSelector = document . querySelector ( "#init_image" )
let initImagePreview = document . querySelector ( "#init_image_preview" )
let maskImageSelector = document . querySelector ( "#mask" )
let maskImagePreview = document . querySelector ( "#mask_preview" )
let turboField = document . querySelector ( '#turbo' )
let useCPUField = document . querySelector ( '#use_cpu' )
let useFullPrecisionField = document . querySelector ( '#use_full_precision' )
let saveToDiskField = document . querySelector ( '#save_to_disk' )
let diskPathField = document . querySelector ( '#diskPath' )
// let allowNSFWField = document.querySelector("#allow_nsfw")
let useBetaChannelField = document . querySelector ( "#use_beta_channel" )
let promptStrengthSlider = document . querySelector ( '#prompt_strength_slider' )
let promptStrengthField = document . querySelector ( '#prompt_strength' )
let samplerField = document . querySelector ( '#sampler' )
let samplerSelectionContainer = document . querySelector ( "#samplerSelection" )
let useFaceCorrectionField = document . querySelector ( "#use_face_correction" )
let useUpscalingField = document . querySelector ( "#use_upscale" )
let upscaleModelField = document . querySelector ( "#upscale_model" )
let showOnlyFilteredImageField = document . querySelector ( "#show_only_filtered_image" )
let updateBranchLabel = document . querySelector ( "#updateBranchLabel" )
let streamImageProgressField = document . querySelector ( "#stream_image_progress" )
let makeImageBtn = document . querySelector ( '#makeImage' )
let stopImageBtn = document . querySelector ( '#stopImage' )
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' )
// let maskSetting = document.querySelector('#editor-inputs-mask_setting')
// let maskImagePreviewContainer = document.querySelector('#mask_preview_container')
// let maskImageClearBtn = document.querySelector('#mask_clear')
let maskSetting = document . querySelector ( '#enable_mask' )
let editorModifierEntries = document . querySelector ( '#editor-modifiers-entries' )
let editorModifierTagsList = document . querySelector ( '#editor-inputs-tags-list' )
let editorTagsContainer = document . querySelector ( '#editor-inputs-tags-container' )
let previewImageField = document . querySelector ( '#preview-image' )
previewImageField . onchange = ( ) => changePreviewImages ( previewImageField . value ) ;
let modifierCardSizeSlider = document . querySelector ( '#modifier-card-size-slider' )
modifierCardSizeSlider . onchange = ( ) => resizeModifierCards ( modifierCardSizeSlider . value ) ;
let previewPrompt = document . querySelector ( '#preview-prompt' )
let showConfigToggle = document . querySelector ( '#configToggleBtn' )
// let configBox = document.querySelector('#config')
let outputMsg = document . querySelector ( '#outputMsg' )
let progressBar = document . querySelector ( "#progressBar" )
let soundToggle = document . querySelector ( '#sound_toggle' )
let serverStatusColor = document . querySelector ( '#server-status-color' )
let serverStatusMsg = document . querySelector ( '#server-status-msg' )
let advancedPanelHandle = document . querySelector ( "#editor-settings .collapsible" )
let modifiersPanelHandle = document . querySelector ( "#editor-modifiers .collapsible" )
let inpaintingEditorContainer = document . querySelector ( '#inpaintingEditor' )
let inpaintingEditor = new DrawingBoard . Board ( 'inpaintingEditor' , {
color : "#ffffff" ,
background : false ,
size : 30 ,
webStorage : false ,
controls : [ { 'DrawingMode' : { 'filler' : false } } , 'Size' , 'Navigation' ]
} )
let inpaintingEditorCanvasBackground = document . querySelector ( '.drawing-board-canvas-wrapper' )
2022-09-24 14:01:36 +02:00
document . querySelector ( '.drawing-board-control-navigation-back' ) . innerHTML = '<i class="fa-solid fa-rotate-left"></i>'
document . querySelector ( '.drawing-board-control-navigation-forward' ) . innerHTML = '<i class="fa-solid fa-rotate-right"></i>'
2022-09-23 16:18:48 +02:00
let maskResetButton = document . querySelector ( '.drawing-board-control-navigation-reset' )
maskResetButton . innerHTML = 'Clear'
maskResetButton . style . fontWeight = 'normal'
maskResetButton . style . fontSize = '10pt'
let serverStatus = 'offline'
let activeTags = [ ]
let modifiers = [ ]
let lastPromptUsed = ''
let taskStopped = true
let batchesDone = 0
const modifierThumbnailPath = 'media/modifier-thumbnails' ;
const activeCardClass = 'modifier-card-active' ;
function getLocalStorageItem ( key , fallback ) {
let item = localStorage . getItem ( key )
if ( item === null ) {
return fallback
}
return item
}
function getLocalStorageBoolItem ( key , fallback ) {
let item = localStorage . getItem ( key )
if ( item === null ) {
return fallback
}
return ( item === 'true' ? true : false )
}
function handleBoolSettingChange ( key ) {
return function ( e ) {
localStorage . setItem ( key , e . target . checked . toString ( ) )
}
}
function handleStringSettingChange ( key ) {
return function ( e ) {
localStorage . setItem ( key , e . target . value . toString ( ) )
}
}
function isSoundEnabled ( ) {
return getLocalStorageBoolItem ( SOUND _ENABLED _KEY , true )
}
function isFaceCorrectionEnabled ( ) {
return getLocalStorageBoolItem ( USE _FACE _CORRECTION _KEY , false )
}
function isUpscalingEnabled ( ) {
return getLocalStorageBoolItem ( USE _UPSCALING _KEY , false )
}
function isShowOnlyFilteredImageEnabled ( ) {
return getLocalStorageBoolItem ( SHOW _ONLY _FILTERED _IMAGE _KEY , true )
}
function isSaveToDiskEnabled ( ) {
return getLocalStorageBoolItem ( SAVE _TO _DISK _KEY , false )
}
function isUseCPUEnabled ( ) {
return getLocalStorageBoolItem ( USE _CPU _KEY , false )
}
function isUseFullPrecisionEnabled ( ) {
return getLocalStorageBoolItem ( USE _FULL _PRECISION _KEY , false )
}
function isUseTurboModeEnabled ( ) {
return getLocalStorageBoolItem ( USE _TURBO _MODE _KEY , true )
}
function getSavedDiskPath ( ) {
return getLocalStorageItem ( DISK _PATH _KEY , '' )
}
function isAdvancedPanelOpenEnabled ( ) {
return getLocalStorageBoolItem ( ADVANCED _PANEL _OPEN _KEY , false )
}
function isModifiersPanelOpenEnabled ( ) {
return getLocalStorageBoolItem ( MODIFIERS _PANEL _OPEN _KEY , false )
}
function isStreamImageProgressEnabled ( ) {
return getLocalStorageBoolItem ( STREAM _IMAGE _PROGRESS _KEY , false )
}
function setStatus ( statusType , msg , msgType ) {
if ( statusType !== 'server' ) {
return ;
}
if ( msgType == 'error' ) {
// msg = '<span style="color: red">' + msg + '<span>'
serverStatusColor . style . color = 'red'
serverStatusMsg . style . color = 'red'
serverStatusMsg . innerText = 'Stable Diffusion has stopped'
} else if ( msgType == 'success' ) {
// msg = '<span style="color: green">' + msg + '<span>'
serverStatusColor . style . color = 'green'
serverStatusMsg . style . color = 'green'
serverStatusMsg . innerText = 'Stable Diffusion is ready'
serverStatus = 'online'
}
}
function logMsg ( msg , level ) {
if ( level === 'error' ) {
outputMsg . innerHTML = '<span style="color: red">Error: ' + msg + '</span>'
} else if ( level === 'warn' ) {
outputMsg . innerHTML = '<span style="color: orange">Warning: ' + msg + '</span>'
} else {
outputMsg . innerText = msg
}
console . log ( level , msg )
}
function logError ( msg , res ) {
logMsg ( msg , 'error' )
console . log ( 'request error' , res )
setStatus ( 'request' , 'error' , 'error' )
}
function playSound ( ) {
const audio = new Audio ( '/media/ding.mp3' )
audio . volume = 0.2
audio . play ( )
}
async function healthCheck ( ) {
try {
let res = await fetch ( '/ping' )
res = await res . json ( )
if ( res [ 0 ] == 'OK' ) {
setStatus ( 'server' , 'online' , 'success' )
} else {
setStatus ( 'server' , 'offline' , 'error' )
}
} catch ( e ) {
setStatus ( 'server' , 'offline' , 'error' )
}
}
2022-09-27 16:23:19 +02:00
function showImages ( req , res , livePreview ) {
res . output . forEach ( ( result , index ) => {
2022-09-25 01:55:11 +02:00
if ( typeof res != 'object' ) return ;
2022-09-27 23:05:34 +02:00
const imageData = result ? . data || result ? . path + '?t=' + new Date ( ) . getTime ( ) ,
imageSeed = req . seed ,
2022-09-25 01:55:11 +02:00
imageWidth = req . width ,
2022-09-27 16:23:19 +02:00
imageHeight = req . height ,
2022-09-27 23:05:34 +02:00
imageIdentifier = 'IMG_ID_' + ( imageSeed + '' ) . replace ( /\d/g , c => 'SUOMIPERKL' [ c ] ) + 'X' . repeat ( index ) ;
2022-09-27 16:23:19 +02:00
2022-09-27 23:05:34 +02:00
if ( ! imageData . includes ( '/' ) ) {
2022-09-27 16:23:19 +02:00
// res contained no data for the image, stop execution
2022-09-25 01:55:11 +02:00
setStatus ( 'request' , 'invalid image' , 'error' ) ;
return ;
}
2022-09-27 16:23:19 +02:00
let imageItemElem = document . querySelector ( '#' + imageIdentifier ) ;
if ( ! imageItemElem ) {
imageItemElem = document . createElement ( 'div' ) ;
imageItemElem . className = 'imgItem' ;
imageItemElem . id = imageIdentifier ;
imageItemElem . innerHTML = `
< div class = "imgContainer" >
< img / >
< div class = "imgItemInfo" >
< span class = "imgSeedLabel" > < / s p a n >
< button class = "imgUseBtn" > Use as Input < / b u t t o n >
< button class = "imgSaveBtn" > Download < / b u t t o n >
< / d i v >
2022-09-25 01:55:11 +02:00
< / d i v >
2022-09-27 16:23:19 +02:00
` ;
2022-09-27 23:05:34 +02:00
const useAsInputBtn = imageItemElem . querySelector ( '.imgUseBtn' ) ,
saveImageBtn = imageItemElem . querySelector ( '.imgSaveBtn' ) ;
useAsInputBtn . addEventListener ( 'click' , e => {
const imgData = e . path . find ( x => x == imageItemElem ) . querySelector ( 'img' ) . src ;
2022-09-27 16:23:19 +02:00
initImageSelector . value = null ;
2022-09-27 23:05:34 +02:00
initImagePreview . src = imgData ;
2022-09-27 16:23:19 +02:00
initImagePreviewContainer . style . display = 'block' ;
inpaintingEditorContainer . style . display = 'none' ;
promptStrengthContainer . style . display = 'block' ;
maskSetting . checked = false ;
// maskSetting.style.display = 'block';
randomSeedField . checked = false ;
seedField . value = imageSeed ;
seedField . disabled = false ;
} ) ;
2022-09-27 23:05:34 +02:00
saveImageBtn . addEventListener ( 'click' , e => {
const imgData = e . path . find ( x => x == imageItemElem ) . querySelector ( 'img' ) . src ;
2022-09-27 16:23:19 +02:00
const imgDownload = document . createElement ( 'a' ) ;
imgDownload . download = createFileName ( imageSeed ) ;
2022-09-27 23:05:34 +02:00
imgDownload . href = imgData ;
2022-09-27 16:23:19 +02:00
imgDownload . click ( ) ;
} ) ;
imagesContainer . appendChild ( imageItemElem ) ;
}
2022-09-27 23:05:34 +02:00
const imageElem = imageItemElem . querySelector ( 'img' ) ,
imageSeedLabel = imageItemElem . querySelector ( '.imgSeedLabel' ) ;
2022-09-27 16:23:19 +02:00
2022-09-27 23:05:34 +02:00
imageElem . src = imageData ;
2022-09-25 01:55:11 +02:00
imageElem . width = parseInt ( imageWidth ) ;
imageElem . height = parseInt ( imageHeight ) ;
2022-09-23 16:18:48 +02:00
2022-09-27 16:23:19 +02:00
imageSeedLabel . innerText = livePreview
? '(Live Preview)'
: 'Seed: ' + imageSeed ;
2022-09-25 01:55:11 +02:00
} ) ;
2022-09-23 16:18:48 +02:00
}
// makes a single image. don't call this directly, use makeImage() instead
async function doMakeImage ( reqBody , batchCount ) {
if ( taskStopped ) {
return
}
let res = ''
let seed = reqBody [ 'seed' ]
let numOutputs = parseInt ( reqBody [ 'num_outputs' ] )
try {
res = await fetch ( '/image' , {
method : 'POST' ,
headers : {
'Content-Type' : 'application/json'
} ,
body : JSON . stringify ( reqBody )
} )
let reader = res . body . getReader ( )
let textDecoder = new TextDecoder ( )
let finalJSON = ''
let prevTime = - 1
while ( true ) {
try {
let t = new Date ( ) . getTime ( )
const { value , done } = await reader . read ( )
if ( done ) {
break
}
let timeTaken = ( prevTime === - 1 ? - 1 : t - prevTime )
let jsonStr = textDecoder . decode ( value )
try {
let stepUpdate = JSON . parse ( jsonStr )
if ( stepUpdate . step === undefined ) {
finalJSON += jsonStr
} else {
let batchSize = stepUpdate . total _steps
let overallStepCount = stepUpdate . step + batchesDone * batchSize
let totalSteps = batchCount * batchSize
let percent = 100 * ( overallStepCount / totalSteps )
percent = ( percent > 100 ? 100 : percent )
percent = percent . toFixed ( 0 )
stepsRemaining = totalSteps - overallStepCount
stepsRemaining = ( stepsRemaining < 0 ? 0 : stepsRemaining )
timeRemaining = ( timeTaken === - 1 ? '' : stepsRemaining * timeTaken ) // ms
outputMsg . innerHTML = ` Batch ${ batchesDone + 1 } of ${ batchCount } `
progressBar . innerHTML = ` Generating image(s): ${ percent } % `
if ( timeTaken !== - 1 ) {
progressBar . innerHTML += ` <br>Time remaining (approx): ${ millisecondsToStr ( timeRemaining ) } `
}
progressBar . style . display = 'block'
if ( stepUpdate . output !== undefined ) {
2022-09-27 16:23:19 +02:00
showImages ( reqBody , stepUpdate , true ) ;
2022-09-23 16:18:48 +02:00
}
}
} catch ( e ) {
finalJSON += jsonStr
}
prevTime = t
} catch ( e ) {
logError ( 'Stable Diffusion had an error. Please check the logs in the command-line window.' , res )
res = undefined
throw e
}
}
if ( res . status != 200 ) {
if ( serverStatus === 'online' ) {
logError ( 'Stable Diffusion had an error: ' + await res . text ( ) , res )
} else {
logError ( "Stable Diffusion is still starting up, please wait. If this goes on beyond a few minutes, Stable Diffusion has probably crashed. Please check the error message in the command-line window." , res )
}
res = undefined
progressBar . style . display = 'none'
} else {
2022-09-24 18:59:31 +02:00
if ( finalJSON !== undefined && finalJSON . indexOf ( '}{' ) !== - 1 ) {
// hack for a middleman buffering all the streaming updates, and unleashing them
// on the poor browser in one shot.
// this results in having to parse JSON like {"step": 1}{"step": 2}...{"status": "succeeded"..}
// which is obviously invalid.
// So we need to just extract the last {} section, starting from "status" to the end of the response
let lastChunkIdx = finalJSON . lastIndexOf ( '}{' )
if ( lastChunkIdx !== - 1 ) {
let remaining = finalJSON . substring ( lastChunkIdx )
finalJSON = remaining . substring ( 1 )
}
}
2022-09-23 16:18:48 +02:00
res = JSON . parse ( finalJSON )
progressBar . style . display = 'none'
if ( res . status !== 'succeeded' ) {
let msg = ''
if ( res . detail !== undefined ) {
msg = res . 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 disabling the '<em>Turbo mode</em>' under '<em>Advanced Settings</em>' . < br / >
3. Try generating a smaller image . < br / > `
}
} else {
msg = res
}
logError ( msg , res )
res = undefined
}
}
} catch ( e ) {
console . log ( 'request error' , e )
logError ( 'Stable Diffusion had an error. Please check the logs in the command-line window. <br/><br/>' + e + '<br/><pre>' + e . stack + '</pre>' , res )
setStatus ( 'request' , 'error' , 'error' )
progressBar . style . display = 'none'
res = undefined
}
2022-09-25 01:55:11 +02:00
if ( ! res ) return false ;
2022-09-23 16:18:48 +02:00
2022-09-25 01:55:11 +02:00
lastPromptUsed = reqBody [ 'prompt' ] ;
2022-09-23 16:18:48 +02:00
2022-09-27 16:23:19 +02:00
showImages ( reqBody , res , false ) ;
2022-09-23 16:18:48 +02:00
2022-09-25 01:55:11 +02:00
return true ;
2022-09-23 16:18:48 +02:00
}
function validateInput ( ) {
let width = parseInt ( widthField . value )
let height = parseInt ( heightField . value )
if ( IMAGE _REGEX . test ( initImagePreview . src ) ) {
if ( initImagePreview . naturalWidth > MAX _INIT _IMAGE _DIMENSION || initImagePreview . naturalHeight > MAX _INIT _IMAGE _DIMENSION ) {
return { 'isValid' : false , 'warning' : ` The dimensions of your initial image are very large, and can cause 'Out of Memory' errors! Please ensure that its dimensions are equal (or smaller) than the desired output image.
< br / > < br / >
Your initial image size is $ { initImagePreview . naturalWidth } x$ { initImagePreview . naturalHeight } pixels . Please try to keep it smaller than $ { MAX _INIT _IMAGE _DIMENSION } x$ { MAX _INIT _IMAGE _DIMENSION } . ` }
}
}
return { 'isValid' : true }
}
async function makeImage ( ) {
if ( serverStatus !== 'online' ) {
logError ( 'The server is still starting up..' )
return
}
let validation = validateInput ( )
if ( validation [ 'isValid' ] ) {
outputMsg . innerHTML = 'Starting..'
} else {
if ( validation [ 'error' ] ) {
logError ( validation [ 'error' ] )
return
} else if ( validation [ 'warning' ] ) {
logMsg ( validation [ 'warning' ] , 'warn' )
}
}
setStatus ( 'request' , 'fetching..' )
makeImageBtn . innerHTML = 'Processing..'
makeImageBtn . disabled = true
makeImageBtn . style . display = 'none'
stopImageBtn . style . display = 'block'
taskStopped = false
batchesDone = 0
let seed = ( randomSeedField . checked ? Math . floor ( Math . random ( ) * 10000000 ) : parseInt ( seedField . value ) )
let numOutputsTotal = parseInt ( numOutputsTotalField . value )
let numOutputsParallel = parseInt ( numOutputsParallelField . value )
let batchCount = Math . ceil ( numOutputsTotal / numOutputsParallel )
let batchSize = numOutputsParallel
let streamImageProgress = ( numOutputsTotal > 50 ? false : streamImageProgressField . checked )
let prompt = promptField . value
if ( activeTags . length > 0 ) {
let promptTags = activeTags . map ( x => x . name ) . join ( ", " ) ;
prompt += ", " + promptTags ;
}
previewPrompt . innerText = prompt
let reqBody = {
session _id : sessionId ,
prompt : prompt ,
num _outputs : batchSize ,
num _inference _steps : numInferenceStepsField . value ,
guidance _scale : guidanceScaleField . value ,
width : widthField . value ,
height : heightField . value ,
// allow_nsfw: allowNSFWField.checked,
turbo : turboField . checked ,
use _cpu : useCPUField . checked ,
use _full _precision : useFullPrecisionField . checked ,
stream _progress _updates : true ,
stream _image _progress : streamImageProgress ,
show _only _filtered _image : showOnlyFilteredImageField . checked
}
if ( IMAGE _REGEX . test ( initImagePreview . src ) ) {
reqBody [ 'init_image' ] = initImagePreview . src
reqBody [ 'prompt_strength' ] = promptStrengthField . value
// if (IMAGE_REGEX.test(maskImagePreview.src)) {
// reqBody['mask'] = maskImagePreview.src
// }
if ( maskSetting . checked ) {
reqBody [ 'mask' ] = inpaintingEditor . getImg ( )
}
reqBody [ 'sampler' ] = 'ddim'
} else {
reqBody [ 'sampler' ] = samplerField . value
}
if ( saveToDiskField . checked && diskPathField . value . trim ( ) !== '' ) {
reqBody [ 'save_to_disk_path' ] = diskPathField . value . trim ( )
}
if ( useFaceCorrectionField . checked ) {
reqBody [ 'use_face_correction' ] = 'GFPGANv1.3'
}
if ( useUpscalingField . checked ) {
reqBody [ 'use_upscale' ] = upscaleModelField . value
}
let time = new Date ( ) . getTime ( )
imagesContainer . innerHTML = ''
let successCount = 0
for ( let i = 0 ; i < batchCount ; i ++ ) {
reqBody [ 'seed' ] = seed + ( i * batchSize )
let success = await doMakeImage ( reqBody , batchCount )
batchesDone ++
if ( success ) {
outputMsg . innerText = 'Processed batch ' + ( i + 1 ) + '/' + batchCount
successCount ++
}
}
makeImageBtn . innerText = 'Make Image'
makeImageBtn . disabled = false
makeImageBtn . style . display = 'block'
stopImageBtn . style . display = 'none'
if ( isSoundEnabled ( ) ) {
playSound ( )
}
time = new Date ( ) . getTime ( ) - time
time /= 1000
if ( successCount === batchCount ) {
outputMsg . innerText = 'Processed ' + numOutputsTotal + ' images in ' + time + ' seconds'
setStatus ( 'request' , 'done' , 'success' )
}
if ( randomSeedField . checked ) {
seedField . value = seed
}
}
// create a file name with embedded prompt and metadata
// for easier cateloging and comparison
2022-09-25 01:55:11 +02:00
function createFileName ( seed ) {
2022-09-23 16:18:48 +02:00
// Most important information is the prompt
let underscoreName = lastPromptUsed . replace ( /[^a-zA-Z0-9]/g , '_' )
underscoreName = underscoreName . substring ( 0 , 100 )
const steps = numInferenceStepsField . value
const guidance = guidanceScaleField . value
// name and the top level metadata
let fileName = ` ${ underscoreName } _Seed- ${ seed } _Steps- ${ steps } _Guidance- ${ guidance } `
// add the tags
// let tags = [];
// let tagString = '';
// document.querySelectorAll(modifyTagsSelector).forEach(function(tag) {
// tags.push(tag.innerHTML);
// })
// join the tags with a pipe
// if (activeTags.length > 0) {
// tagString = '_Tags-';
// tagString += tags.join('|');
// }
// // append empty or populated tags
// fileName += `${tagString}`;
// add the file extension
fileName += ` .png `
return fileName
}
stopImageBtn . addEventListener ( 'click' , async function ( ) {
try {
let res = await fetch ( '/image/stop' )
} catch ( e ) {
console . log ( e )
}
stopImageBtn . style . display = 'none'
makeImageBtn . style . display = 'block'
taskStopped = true
} )
soundToggle . addEventListener ( 'click' , handleBoolSettingChange ( SOUND _ENABLED _KEY ) )
soundToggle . checked = isSoundEnabled ( )
saveToDiskField . checked = isSaveToDiskEnabled ( )
diskPathField . disabled = ! saveToDiskField . checked
useFaceCorrectionField . addEventListener ( 'click' , handleBoolSettingChange ( USE _FACE _CORRECTION _KEY ) )
useFaceCorrectionField . checked = isFaceCorrectionEnabled ( )
useUpscalingField . checked = isUpscalingEnabled ( )
upscaleModelField . disabled = ! useUpscalingField . checked
showOnlyFilteredImageField . addEventListener ( 'click' , handleBoolSettingChange ( SHOW _ONLY _FILTERED _IMAGE _KEY ) )
showOnlyFilteredImageField . checked = isShowOnlyFilteredImageEnabled ( )
useCPUField . addEventListener ( 'click' , handleBoolSettingChange ( USE _CPU _KEY ) )
useCPUField . checked = isUseCPUEnabled ( )
useFullPrecisionField . addEventListener ( 'click' , handleBoolSettingChange ( USE _FULL _PRECISION _KEY ) )
useFullPrecisionField . checked = isUseFullPrecisionEnabled ( )
turboField . addEventListener ( 'click' , handleBoolSettingChange ( USE _TURBO _MODE _KEY ) )
turboField . checked = isUseTurboModeEnabled ( )
streamImageProgressField . addEventListener ( 'click' , handleBoolSettingChange ( STREAM _IMAGE _PROGRESS _KEY ) )
streamImageProgressField . checked = isStreamImageProgressEnabled ( )
diskPathField . addEventListener ( 'change' , handleStringSettingChange ( DISK _PATH _KEY ) )
saveToDiskField . addEventListener ( 'click' , function ( e ) {
diskPathField . disabled = ! this . checked
handleBoolSettingChange ( SAVE _TO _DISK _KEY ) ( e )
} )
useUpscalingField . addEventListener ( 'click' , function ( e ) {
upscaleModelField . disabled = ! this . checked
handleBoolSettingChange ( USE _UPSCALING _KEY ) ( e )
} )
function setPanelOpen ( panelHandle ) {
let panelContents = panelHandle . nextElementSibling
panelHandle . classList . add ( 'active' )
panelContents . style . display = 'block'
}
if ( isAdvancedPanelOpenEnabled ( ) ) {
setPanelOpen ( advancedPanelHandle )
}
if ( isModifiersPanelOpenEnabled ( ) ) {
setPanelOpen ( modifiersPanelHandle )
}
makeImageBtn . addEventListener ( 'click' , makeImage )
function updateGuidanceScale ( ) {
guidanceScaleField . value = guidanceScaleSlider . value / 10
}
function updateGuidanceScaleSlider ( ) {
if ( guidanceScaleField . value < 0 ) {
guidanceScaleField . value = 0
} else if ( guidanceScaleField . value > 50 ) {
guidanceScaleField . value = 50
}
guidanceScaleSlider . value = guidanceScaleField . value * 10
}
guidanceScaleSlider . addEventListener ( 'input' , updateGuidanceScale )
guidanceScaleField . addEventListener ( 'input' , updateGuidanceScaleSlider )
updateGuidanceScale ( )
function updatePromptStrength ( ) {
promptStrengthField . value = promptStrengthSlider . value / 100
}
function updatePromptStrengthSlider ( ) {
if ( promptStrengthField . value < 0 ) {
promptStrengthField . value = 0
} else if ( promptStrengthField . value > 0.99 ) {
promptStrengthField . value = 0.99
}
promptStrengthSlider . value = promptStrengthField . value * 100
}
promptStrengthSlider . addEventListener ( 'input' , updatePromptStrength )
promptStrengthField . addEventListener ( 'input' , updatePromptStrengthSlider )
updatePromptStrength ( )
useBetaChannelField . addEventListener ( 'click' , async function ( e ) {
if ( serverStatus !== 'online' ) {
logError ( 'The server is still starting up..' )
alert ( 'The server is still starting up..' )
e . preventDefault ( )
return false
}
let updateBranch = ( this . checked ? 'beta' : 'main' )
try {
let res = await fetch ( '/app_config' , {
method : 'POST' ,
headers : {
'Content-Type' : 'application/json'
} ,
body : JSON . stringify ( {
'update_branch' : updateBranch
} )
} )
res = await res . json ( )
console . log ( 'set config status response' , res )
} catch ( e ) {
console . log ( 'set config status error' , e )
}
} )
async function getAppConfig ( ) {
try {
let res = await fetch ( '/app_config' )
config = await res . json ( )
if ( config . update _branch === 'beta' ) {
useBetaChannelField . checked = true
updateBranchLabel . innerText = "(beta)"
}
console . log ( 'get config status response' , config )
} catch ( e ) {
console . log ( 'get config status error' , e )
}
}
function checkRandomSeed ( ) {
if ( randomSeedField . checked ) {
seedField . disabled = true
seedField . value = "0"
} else {
seedField . disabled = false
}
}
randomSeedField . addEventListener ( 'input' , checkRandomSeed )
checkRandomSeed ( )
function showInitImagePreview ( ) {
if ( initImageSelector . files . length === 0 ) {
initImagePreviewContainer . style . display = 'none'
// inpaintingEditorContainer.style.display = 'none'
promptStrengthContainer . style . display = 'none'
// maskSetting.style.display = 'none'
return
}
let reader = new FileReader ( )
let file = initImageSelector . files [ 0 ]
reader . addEventListener ( 'load' , function ( ) {
// console.log(file.name, reader.result)
initImagePreview . src = reader . result
initImagePreviewContainer . style . display = 'block'
inpaintingEditorContainer . style . display = 'none'
promptStrengthContainer . style . display = 'block'
samplerSelectionContainer . style . display = 'none'
// maskSetting.checked = false
} )
if ( file ) {
reader . readAsDataURL ( file )
}
}
initImageSelector . addEventListener ( 'change' , showInitImagePreview )
showInitImagePreview ( )
initImagePreview . addEventListener ( 'load' , function ( ) {
inpaintingEditorCanvasBackground . style . backgroundImage = "url('" + this . src + "')"
// maskSetting.style.display = 'block'
// inpaintingEditorContainer.style.display = 'block'
} )
initImageClearBtn . addEventListener ( 'click' , function ( ) {
initImageSelector . value = null
// maskImageSelector.value = null
initImagePreview . src = ''
// maskImagePreview.src = ''
maskSetting . checked = false
initImagePreviewContainer . style . display = 'none'
// inpaintingEditorContainer.style.display = 'none'
// maskImagePreviewContainer.style.display = 'none'
// maskSetting.style.display = 'none'
promptStrengthContainer . style . display = 'none'
samplerSelectionContainer . style . display = 'block'
} )
maskSetting . addEventListener ( 'click' , function ( ) {
inpaintingEditorContainer . style . display = ( this . checked ? 'block' : 'none' )
} )
// function showMaskImagePreview() {
// if (maskImageSelector.files.length === 0) {
// // maskImagePreviewContainer.style.display = 'none'
// return
// }
// let reader = new FileReader()
// let file = maskImageSelector.files[0]
// reader.addEventListener('load', function() {
// // maskImagePreview.src = reader.result
// // maskImagePreviewContainer.style.display = 'block'
// })
// if (file) {
// reader.readAsDataURL(file)
// }
// }
// maskImageSelector.addEventListener('change', showMaskImagePreview)
// showMaskImagePreview()
// maskImageClearBtn.addEventListener('click', function() {
// maskImageSelector.value = null
// maskImagePreview.src = ''
// // maskImagePreviewContainer.style.display = 'none'
// })
// https://stackoverflow.com/a/8212878
function millisecondsToStr ( milliseconds ) {
function numberEnding ( number ) {
return ( number > 1 ) ? 's' : '' ;
}
var temp = Math . floor ( milliseconds / 1000 ) ;
var hours = Math . floor ( ( temp %= 86400 ) / 3600 ) ;
var s = ''
if ( hours ) {
s += hours + ' hour' + numberEnding ( hours ) + ' ' ;
}
var minutes = Math . floor ( ( temp %= 3600 ) / 60 ) ;
if ( minutes ) {
s += minutes + ' minute' + numberEnding ( minutes ) + ' ' ;
}
var seconds = temp % 60 ;
if ( ! hours && minutes < 4 && seconds ) {
s += seconds + ' second' + numberEnding ( seconds ) ;
}
return s ;
}
function createCollapsibles ( node ) {
if ( ! node ) {
node = document
}
let collapsibles = node . querySelectorAll ( ".collapsible" )
collapsibles . forEach ( function ( c ) {
let handle = document . createElement ( 'span' )
handle . className = 'collapsible-handle'
if ( c . className . indexOf ( 'active' ) !== - 1 ) {
handle . innerHTML = '➖' // minus
} else {
handle . innerHTML = '➕' // plus
}
c . insertBefore ( handle , c . firstChild )
c . addEventListener ( 'click' , function ( ) {
this . classList . toggle ( "active" )
let content = this . nextElementSibling
if ( content . style . display === "block" ) {
content . style . display = "none"
handle . innerHTML = '➕' // plus
} else {
content . style . display = "block"
handle . innerHTML = '➖' // minus
}
if ( this == advancedPanelHandle ) {
let state = ( content . style . display === 'block' ? 'true' : 'false' )
localStorage . setItem ( ADVANCED _PANEL _OPEN _KEY , state )
} else if ( this == modifiersPanelHandle ) {
let state = ( content . style . display === 'block' ? 'true' : 'false' )
localStorage . setItem ( MODIFIERS _PANEL _OPEN _KEY , state )
}
} )
} )
}
createCollapsibles ( )
function refreshTagsList ( ) {
editorModifierTagsList . innerHTML = '' ;
if ( activeTags . length == 0 ) {
editorTagsContainer . style . display = 'none' ;
return ;
} else {
editorTagsContainer . style . display = 'block' ;
}
activeTags . forEach ( ( tag , index ) => {
tag . element . querySelector ( '.modifier-card-image-overlay' ) . innerText = '-' ;
tag . element . classList . add ( 'modifier-card-tiny' ) ;
editorModifierTagsList . appendChild ( tag . element ) ;
tag . element . addEventListener ( 'click' , ( ) => {
let idx = activeTags . indexOf ( tag ) ;
if ( idx !== - 1 ) {
activeTags [ idx ] . originElement . classList . remove ( activeCardClass ) ;
activeTags [ idx ] . originElement . querySelector ( '.modifier-card-image-overlay' ) . innerText = '+' ;
activeTags . splice ( idx , 1 ) ;
refreshTagsList ( ) ;
}
} ) ;
} ) ;
let brk = document . createElement ( 'br' )
brk . style . clear = 'both'
editorModifierTagsList . appendChild ( brk )
}
async function getDiskPath ( ) {
try {
let diskPath = getSavedDiskPath ( )
if ( diskPath !== '' ) {
diskPathField . value = diskPath
return
}
let res = await fetch ( '/output_dir' )
if ( res . status === 200 ) {
res = await res . json ( )
res = res [ 0 ]
document . querySelector ( '#diskPath' ) . value = res
}
} catch ( e ) {
console . log ( 'error fetching output dir path' , e )
}
}
function createModifierCard ( name , previews ) {
const modifierCard = document . createElement ( 'div' ) ;
modifierCard . className = 'modifier-card' ;
modifierCard . innerHTML = `
< div class = "modifier-card-overlay" > < / d i v >
< div class = "modifier-card-image-container" >
< div class = "modifier-card-image-overlay" > + < / d i v >
< p class = "modifier-card-error-label" > < / p >
< img onerror = "this.remove()" alt = "Modifier Image" class = "modifier-card-image" >
< / d i v >
< div class = "modifier-card-container" >
< div class = "modifier-card-label" > < p > < / p > < / d i v >
< / d i v > ` ;
const image = modifierCard . querySelector ( '.modifier-card-image' ) ;
const errorText = modifierCard . querySelector ( '.modifier-card-error-label' ) ;
const label = modifierCard . querySelector ( '.modifier-card-label' ) ;
errorText . innerText = 'No Image' ;
if ( typeof previews == 'object' ) {
image . src = previews [ 0 ] ; // portrait
image . setAttribute ( 'preview-type' , 'portrait' ) ;
} else {
image . remove ( ) ;
}
const maxLabelLength = 30 ;
const nameWithoutBy = name . replace ( 'by ' , '' ) ;
if ( nameWithoutBy . length <= maxLabelLength ) {
label . querySelector ( 'p' ) . innerText = nameWithoutBy ;
} else {
const tooltipText = document . createElement ( 'span' ) ;
tooltipText . className = 'tooltip-text' ;
tooltipText . innerText = name ;
label . classList . add ( 'tooltip' ) ;
label . appendChild ( tooltipText ) ;
label . querySelector ( 'p' ) . innerText = nameWithoutBy . substring ( 0 , maxLabelLength ) + '...' ;
}
return modifierCard ;
}
function changePreviewImages ( val ) {
const previewImages = document . querySelectorAll ( '.modifier-card-image-container img' ) ;
let previewArr = [ ] ;
modifiers . map ( x => x . modifiers ) . forEach ( x => previewArr . push ( ... x . map ( m => m . previews ) ) ) ;
previewArr = previewArr . map ( x => {
let obj = { } ;
x . forEach ( preview => {
obj [ preview . name ] = preview . path ;
} ) ;
return obj ;
} ) ;
previewImages . forEach ( previewImage => {
const currentPreviewType = previewImage . getAttribute ( 'preview-type' ) ;
const relativePreviewPath = previewImage . src . split ( modifierThumbnailPath + '/' ) . pop ( ) ;
const previews = previewArr . find ( preview => relativePreviewPath == preview [ currentPreviewType ] ) ;
if ( typeof previews == 'object' ) {
let preview = null ;
if ( val == 'portrait' ) {
preview = previews . portrait ;
}
else if ( val == 'landscape' ) {
preview = previews . landscape ;
}
if ( preview != null ) {
previewImage . src = ` ${ modifierThumbnailPath } / ${ preview } ` ;
previewImage . setAttribute ( 'preview-type' , val ) ;
}
}
} ) ;
}
function resizeModifierCards ( val ) {
const cardSizePrefix = 'modifier-card-size_' ;
const modifierCardClass = 'modifier-card' ;
const modifierCards = document . querySelectorAll ( ` . ${ modifierCardClass } ` ) ;
const cardSize = n => ` ${ cardSizePrefix } ${ n } ` ;
modifierCards . forEach ( card => {
// remove existing size classes
const classes = card . className . split ( ' ' ) . filter ( c => ! c . startsWith ( cardSizePrefix ) ) ;
card . className = classes . join ( ' ' ) . trim ( ) ;
if ( val != 0 )
card . classList . add ( cardSize ( val ) ) ;
} ) ;
}
async function loadModifiers ( ) {
try {
2022-09-24 06:17:34 +02:00
let res = await fetch ( '/modifiers.json?v=2' )
2022-09-23 16:18:48 +02:00
if ( res . status === 200 ) {
res = await res . json ( )
modifiers = res ; // update global variable
2022-09-24 12:09:48 +02:00
res . forEach ( ( modifierGroup , idx ) => {
2022-09-23 16:18:48 +02:00
const title = modifierGroup . category ;
const modifiers = modifierGroup . modifiers ;
const titleEl = document . createElement ( 'h5' ) ;
titleEl . className = 'collapsible' ;
titleEl . innerText = title ;
const modifiersEl = document . createElement ( 'div' ) ;
modifiersEl . classList . add ( 'collapsible-content' , 'editor-modifiers-leaf' ) ;
2022-09-24 12:09:48 +02:00
if ( idx == 0 ) {
titleEl . className += ' active'
modifiersEl . style . display = 'block'
}
2022-09-23 16:18:48 +02:00
modifiers . forEach ( modObj => {
const modifierName = modObj . modifier ;
const modifierPreviews = modObj ? . previews ? . map ( preview => ` ${ modifierThumbnailPath } / ${ preview . path } ` ) ;
const modifierCard = createModifierCard ( modifierName , modifierPreviews ) ;
if ( typeof modifierCard == 'object' ) {
modifiersEl . appendChild ( modifierCard ) ;
modifierCard . addEventListener ( 'click' , ( ) => {
if ( activeTags . map ( x => x . name ) . includes ( modifierName ) ) {
// remove modifier from active array
activeTags = activeTags . filter ( x => x . name != modifierName ) ;
modifierCard . classList . remove ( activeCardClass ) ;
modifierCard . querySelector ( '.modifier-card-image-overlay' ) . innerText = '+' ;
} else {
// add modifier to active array
activeTags . push ( {
'name' : modifierName ,
'element' : modifierCard . cloneNode ( true ) ,
'originElement' : modifierCard ,
'previews' : modifierPreviews
} ) ;
modifierCard . classList . add ( activeCardClass ) ;
modifierCard . querySelector ( '.modifier-card-image-overlay' ) . innerText = '-' ;
}
refreshTagsList ( ) ;
} ) ;
}
} ) ;
let brk = document . createElement ( 'br' )
brk . style . clear = 'both'
modifiersEl . appendChild ( brk )
let e = document . createElement ( 'div' )
e . appendChild ( titleEl )
e . appendChild ( modifiersEl )
editorModifierEntries . appendChild ( e )
} )
createCollapsibles ( editorModifierEntries )
}
} catch ( e ) {
console . log ( 'error fetching modifiers' , e )
}
2022-09-25 01:55:11 +02:00
}