2022-11-29 20:08:43 -06:00
//TODO FIND OUT WHY I HAVE TO RESIZE A TEXTBOX AND THEN START USING IT TO AVOID THE 1px WHITE LINE ON LEFT EDGES DURING IMG2IMG
//...lmao did setting min width 200 on info div fix that accidentally? once the canvas is infinite and the menu bar is hideable it'll probably be a problem again
window . onload = startup ;
var stableDiffusionData = {
//includes img2img data but works for txt2img just fine
prompt : "" ,
negative _prompt : "" ,
seed : - 1 ,
cfg _scale : null ,
sampler _index : "DDIM" ,
steps : null ,
denoising _strength : 1 ,
mask _blur : 0 ,
batch _size : null ,
width : 512 ,
height : 512 ,
n _iter : null , // batch count
mask : "" ,
init _images : [ ] ,
inpaint _full _res : false ,
inpainting _fill : 2 ,
enable _hr : false ,
firstphase _width : 0 ,
firstphase _height : 0 ,
2022-12-02 11:31:49 -06:00
styles : [ ] ,
2022-11-29 20:08:43 -06:00
// here's some more fields that might be useful
// ---txt2img specific:
// "enable_hr": false, // hires fix
// "denoising_strength": 0, // ok this is in both txt and img2img but txt2img only applies it if enable_hr == true
// "firstphase_width": 0, // hires fp w
// "firstphase_height": 0, // see above s/w/h/
// ---img2img specific
// "init_images": [ // imageS!??!? wtf maybe for batch img2img?? i just dump one base64 in here
// "string"
// ],
// "resize_mode": 0,
// "denoising_strength": 0.75, // yeah see
// "mask": "string", // string is just a base64 image
// "mask_blur": 4,
// "inpainting_fill": 0, // 0- fill, 1- orig, 2- latent noise, 3- latent nothing
// "inpaint_full_res": true,
// "inpaint_full_res_padding": 0, // px
// "inpainting_mask_invert": 0, // bool??????? wtf
// "include_init_images": false // ??????
} ;
// stuff things use
2022-12-01 15:10:30 -06:00
let debug = false ;
2022-11-29 20:08:43 -06:00
var returnedImages ;
var imageIndex = 0 ;
var tmpImgXYWH = { } ;
var host = "" ;
var url = "/sdapi/v1/" ;
var endpoint = "txt2img" ;
var frameX = 512 ;
var frameY = 512 ;
var drawThis = { } ;
const basePixelCount = 64 ; //64 px - ALWAYS 64 PX
var snapToGrid = true ;
var backupMaskPaintCanvas ; //???
var backupMaskPaintCtx ; //...? look i am bad at this
var backupMaskChunk = null ;
var backupMaskX = null ;
var backupMaskY = null ;
var totalImagesReturned ;
var overMaskPx = 0 ;
var drawTargets = [ ] ; // is this needed? i only draw the last one anyway...
var dropTargets = [ ] ; // uhhh yeah similar to the above but for arbitrary dropped images
var arbitraryImage ;
var arbitraryImageData ;
var arbitraryImageBitmap ;
var arbitraryImageBase64 ; // seriously js cmon work with me here
var placingArbitraryImage = false ; // for when the user has loaded an existing image from their computer
var marchOffset = 0 ;
var inProgress = false ;
var marchCoords = { } ;
2022-11-29 20:54:09 -06:00
//
2022-11-29 20:08:43 -06:00
function startup ( ) {
testHostConfiguration ( ) ;
loadSettings ( ) ;
const hostEl = document . getElementById ( "host" ) ;
2022-11-30 15:47:30 -06:00
testHostConnection ( ) . then ( ( checkConnection ) => {
hostEl . onchange = ( ) => {
host = hostEl . value . endsWith ( "/" )
? hostEl . value . substring ( 0 , hostEl . value . length - 1 )
: hostEl . value ;
hostEl . value = host ;
localStorage . setItem ( "host" , host ) ;
checkConnection ( ) ;
} ;
} ) ;
2022-11-29 20:08:43 -06:00
const promptEl = document . getElementById ( "prompt" ) ;
promptEl . oninput = ( ) => {
stableDiffusionData . prompt = promptEl . value ;
promptEl . title = promptEl . value ;
localStorage . setItem ( "prompt" , stableDiffusionData . prompt ) ;
} ;
const negPromptEl = document . getElementById ( "negPrompt" ) ;
negPromptEl . oninput = ( ) => {
stableDiffusionData . negative _prompt = negPromptEl . value ;
negPromptEl . title = negPromptEl . value ;
localStorage . setItem ( "neg_prompt" , stableDiffusionData . negative _prompt ) ;
} ;
drawBackground ( ) ;
changeMaskBlur ( ) ;
2022-12-06 15:22:22 -06:00
changeSmoothRendering ( ) ;
2022-11-29 20:08:43 -06:00
changeSeed ( ) ;
changeHiResFix ( ) ;
}
/ * *
* Initial connection checks
* /
function testHostConfiguration ( ) {
/ * *
* Check host configuration
* /
const hostEl = document . getElementById ( "host" ) ;
2022-12-01 15:10:30 -06:00
hostEl . value = localStorage . getItem ( "host" ) ;
2022-11-29 20:08:43 -06:00
const requestHost = ( prompt , def = "http://127.0.0.1:7860" ) => {
let value = window . prompt ( prompt , def ) ;
if ( value === null ) value = "http://127.0.0.1:7860" ;
value = value . endsWith ( "/" ) ? value . substring ( 0 , value . length - 1 ) : value ;
host = value ;
hostEl . value = host ;
localStorage . setItem ( "host" , host ) ;
testHostConfiguration ( ) ;
} ;
const current = localStorage . getItem ( "host" ) ;
if ( current ) {
if ( ! current . match ( /^https?:\/\/[a-z0-9][a-z0-9.]+[a-z0-9](:[0-9]+)?$/i ) )
requestHost (
"Host seems to be invalid! Please fix your host here:" ,
current
) ;
2022-11-29 20:54:09 -06:00
else
host = current . endsWith ( "/" )
? current . substring ( 0 , current . length - 1 )
: current ;
2022-11-29 20:08:43 -06:00
} else {
requestHost (
"This seems to be the first time you are using openOutpaint! Please set your host here:"
) ;
}
}
2022-11-30 15:47:30 -06:00
async function testHostConnection ( ) {
2022-11-29 20:54:09 -06:00
class CheckInProgressError extends Error { }
2022-11-29 20:08:43 -06:00
const connectionIndicator = document . getElementById (
"connection-status-indicator"
) ;
let connectionStatus = false ;
let firstTimeOnline = true ;
const setConnectionStatus = ( status ) => {
2022-12-02 11:31:49 -06:00
const connectionIndicatorText = document . getElementById (
"connection-status-indicator-text"
) ;
2022-11-29 20:08:43 -06:00
const statuses = {
online : ( ) => {
connectionIndicator . classList . add ( "online" ) ;
connectionIndicator . classList . remove (
"cors-issue" ,
"offline" ,
2022-11-30 15:47:30 -06:00
"before" ,
2022-11-29 20:08:43 -06:00
"server-error"
) ;
2022-12-02 11:31:49 -06:00
connectionIndicatorText . textContent = connectionIndicator . title =
"Connected" ;
2022-11-29 20:08:43 -06:00
connectionStatus = true ;
} ,
error : ( ) => {
connectionIndicator . classList . add ( "server-error" ) ;
2022-11-30 15:47:30 -06:00
connectionIndicator . classList . remove (
"online" ,
"offline" ,
"before" ,
"cors-issue"
) ;
2022-12-02 11:31:49 -06:00
connectionIndicatorText . textContent = "Error" ;
2022-11-29 20:08:43 -06:00
connectionIndicator . title =
"Server is online, but is returning an error response" ;
connectionStatus = false ;
} ,
corsissue : ( ) => {
connectionIndicator . classList . add ( "cors-issue" ) ;
connectionIndicator . classList . remove (
"online" ,
"offline" ,
2022-11-30 15:47:30 -06:00
"before" ,
2022-11-29 20:08:43 -06:00
"server-error"
) ;
2022-12-02 11:31:49 -06:00
connectionIndicatorText . textContent = "CORS" ;
2022-11-29 20:08:43 -06:00
connectionIndicator . title =
"Server is online, but CORS is blocking our requests" ;
connectionStatus = false ;
} ,
offline : ( ) => {
connectionIndicator . classList . add ( "offline" ) ;
connectionIndicator . classList . remove (
"cors-issue" ,
"online" ,
2022-11-30 15:47:30 -06:00
"before" ,
2022-11-29 20:08:43 -06:00
"server-error"
) ;
2022-12-02 11:31:49 -06:00
connectionIndicatorText . textContent = "Offline" ;
2022-11-29 20:08:43 -06:00
connectionIndicator . title =
"Server seems to be offline. Please check the console for more information." ;
connectionStatus = false ;
} ,
2022-11-30 15:47:30 -06:00
before : ( ) => {
connectionIndicator . classList . add ( "before" ) ;
connectionIndicator . classList . remove (
"cors-issue" ,
"online" ,
"offline" ,
"server-error"
) ;
2022-12-02 11:31:49 -06:00
connectionIndicatorText . textContent = "Waiting" ;
2022-11-30 15:47:30 -06:00
connectionIndicator . title = "Waiting for check to complete." ;
connectionStatus = false ;
} ,
2022-11-29 20:08:43 -06:00
} ;
statuses [ status ] && statuses [ status ] ( ) ;
} ;
2022-11-30 15:47:30 -06:00
setConnectionStatus ( "before" ) ;
2022-11-29 20:08:43 -06:00
let checkInProgress = false ;
const checkConnection = async ( notify = false ) => {
if ( checkInProgress )
throw new CheckInProgressError (
"Check is currently in progress, please try again"
) ;
checkInProgress = true ;
var url = document . getElementById ( "host" ) . value + "/startup-events" ;
// Attempt normal request
try {
2022-11-30 15:47:30 -06:00
/** @type {Response} */
const response = await fetch ( url , {
signal : AbortSignal . timeout ( 5000 ) ,
} ) ;
2022-11-29 20:08:43 -06:00
if ( response . status === 200 ) {
setConnectionStatus ( "online" ) ;
// Load data as soon as connection is first stablished
if ( firstTimeOnline ) {
2022-12-07 15:55:41 -06:00
getConfig ( ) ;
2022-12-02 11:31:49 -06:00
getStyles ( ) ;
2022-11-29 20:08:43 -06:00
getSamplers ( ) ;
getUpscalers ( ) ;
getModels ( ) ;
firstTimeOnline = false ;
}
} else {
setConnectionStatus ( "error" ) ;
const message = ` Server responded with ${ response . status } - ${ response . statusText } . Try running the webui with the flag '--api' ` ;
console . error ( message ) ;
if ( notify ) alert ( message ) ;
}
} catch ( e ) {
try {
2022-11-30 15:47:30 -06:00
if ( e instanceof DOMException ) throw "offline" ;
2022-11-29 20:08:43 -06:00
// Tests if problem is CORS
await fetch ( url , { mode : "no-cors" } ) ;
setConnectionStatus ( "corsissue" ) ;
2022-12-02 11:31:49 -06:00
const message = ` CORS is blocking our requests. Try running the webui with the flag '--cors-allow-origins= ${ window . location . protocol } // ${ window . location . host } /' ` ;
2022-11-29 20:08:43 -06:00
console . error ( message ) ;
if ( notify ) alert ( message ) ;
} catch ( e ) {
setConnectionStatus ( "offline" ) ;
const message = ` The server seems to be offline. Is host ' ${
document . getElementById ( "host" ) . value
} ' correct ? ` ;
console . error ( message ) ;
if ( notify ) alert ( message ) ;
}
}
checkInProgress = false ;
return status ;
} ;
2022-11-30 15:47:30 -06:00
await checkConnection ( true ) ;
2022-11-29 20:08:43 -06:00
// On click, attempt to refresh
connectionIndicator . onclick = async ( ) => {
try {
await checkConnection ( true ) ;
checked = true ;
} catch ( e ) {
console . debug ( "Already refreshing" ) ;
}
} ;
// Checks every 5 seconds if offline, 30 seconds if online
const checkAgain = ( ) => {
setTimeout (
2022-11-30 15:47:30 -06:00
async ( ) => {
await checkConnection ( ) ;
2022-11-29 20:08:43 -06:00
checkAgain ( ) ;
} ,
connectionStatus ? 30000 : 5000
) ;
} ;
checkAgain ( ) ;
2022-11-30 15:47:30 -06:00
return ( ) => {
checkConnection ( ) . catch ( ( ) => { } ) ;
} ;
2022-11-29 20:08:43 -06:00
}
function newImage ( evt ) {
clearPaintedMask ( ) ;
2022-12-04 13:22:35 -06:00
uil . layers . forEach ( ( { layer } ) => {
commands . runCommand ( "eraseImage" , "Clear Canvas" , {
x : 0 ,
y : 0 ,
w : layer . canvas . width ,
h : layer . canvas . height ,
ctx : layer . ctx ,
} ) ;
2022-11-29 20:08:43 -06:00
} ) ;
}
function clearPaintedMask ( ) {
maskPaintCtx . clearRect ( 0 , 0 , maskPaintCanvas . width , maskPaintCanvas . height ) ;
}
2022-12-05 09:26:52 -06:00
function march ( bb , options = { } ) {
defaultOpt ( options , {
style : "#FFFF" ,
width : "2px" ,
filter : null ,
} ) ;
2022-11-29 20:54:09 -06:00
const expanded = { ... bb } ;
expanded . x -- ;
expanded . y -- ;
expanded . w += 2 ;
expanded . h += 2 ;
// Get temporary layer to draw marching ants
const layer = imageCollection . registerLayer ( null , {
bb : expanded ,
} ) ;
layer . canvas . style . imageRendering = "pixelated" ;
2022-11-29 20:08:43 -06:00
let offset = 0 ;
const interval = setInterval ( ( ) => {
2022-12-05 09:26:52 -06:00
drawMarchingAnts ( layer . ctx , bb , offset ++ , options ) ;
2022-11-29 20:54:09 -06:00
offset %= 12 ;
2022-11-29 20:08:43 -06:00
} , 20 ) ;
2022-11-29 20:54:09 -06:00
return ( ) => {
clearInterval ( interval ) ;
imageCollection . deleteLayer ( layer ) ;
} ;
2022-11-29 20:08:43 -06:00
}
2022-12-05 09:26:52 -06:00
function drawMarchingAnts ( ctx , bb , offset , options ) {
ctx . save ( ) ;
2022-11-29 20:54:09 -06:00
ctx . clearRect ( 0 , 0 , bb . w + 2 , bb . h + 2 ) ;
2022-12-05 09:26:52 -06:00
ctx . strokeStyle = options . style ;
ctx . strokeWidth = options . width ;
ctx . filter = options . filter ;
2022-11-29 20:54:09 -06:00
ctx . setLineDash ( [ 4 , 2 ] ) ;
ctx . lineDashOffset = - offset ;
ctx . strokeRect ( 1 , 1 , bb . w , bb . h ) ;
2022-12-05 09:26:52 -06:00
ctx . restore ( ) ;
2022-11-29 20:08:43 -06:00
}
const makeSlider = (
label ,
el ,
lsKey ,
min ,
max ,
step ,
defaultValue ,
2022-12-03 04:50:23 -06:00
textStep = null ,
2022-11-29 20:08:43 -06:00
valuecb = null
) => {
2022-12-03 06:53:12 -06:00
const local = lsKey && localStorage . getItem ( lsKey ) ;
2022-11-29 20:08:43 -06:00
const def = parseFloat ( local === null ? defaultValue : local ) ;
2022-12-03 06:53:12 -06:00
let cb = ( v ) => {
stableDiffusionData [ lsKey ] = v ;
if ( lsKey ) localStorage . setItem ( lsKey , v ) ;
} ;
if ( valuecb ) {
cb = ( v ) => {
valuecb ( v ) ;
localStorage . setItem ( lsKey , v ) ;
} ;
}
2022-11-29 20:08:43 -06:00
return createSlider ( label , el , {
2022-12-03 06:53:12 -06:00
valuecb : cb ,
2022-11-29 20:08:43 -06:00
min ,
max ,
step ,
defaultValue : def ,
2022-12-03 04:50:23 -06:00
textStep ,
2022-11-29 20:08:43 -06:00
} ) ;
} ;
2022-12-08 15:23:17 -06:00
const modelAutoComplete = createAutoComplete (
"Model" ,
document . getElementById ( "models-ac-select" )
) ;
const samplerAutoComplete = createAutoComplete (
"Sampler" ,
document . getElementById ( "sampler-ac-select" )
) ;
const upscalerAutoComplete = createAutoComplete (
"Upscaler" ,
document . getElementById ( "upscaler-ac-select" )
) ;
2022-12-03 06:53:12 -06:00
makeSlider (
"Resolution" ,
document . getElementById ( "resolution" ) ,
"resolution" ,
64 ,
1024 ,
64 ,
512 ,
2 ,
( v ) => {
stableDiffusionData . width = stableDiffusionData . height = v ;
stableDiffusionData . firstphase _width =
stableDiffusionData . firstphase _height = v / 2 ;
}
) ;
2022-11-29 20:08:43 -06:00
makeSlider (
"CFG Scale" ,
document . getElementById ( "cfgScale" ) ,
"cfg_scale" ,
- 1 ,
25 ,
0.5 ,
2022-12-03 04:50:23 -06:00
7.0 ,
0.1
2022-11-29 20:08:43 -06:00
) ;
makeSlider (
"Batch Size" ,
document . getElementById ( "batchSize" ) ,
"batch_size" ,
1 ,
8 ,
1 ,
2
) ;
makeSlider (
"Iterations" ,
document . getElementById ( "batchCount" ) ,
"n_iter" ,
1 ,
8 ,
1 ,
2
) ;
2022-12-06 21:12:10 -06:00
makeSlider (
"Upscale X" ,
document . getElementById ( "upscaleX" ) ,
"upscale_x" ,
1.0 ,
4.0 ,
0.1 ,
2.0 ,
0.1
) ;
2022-11-29 20:08:43 -06:00
2022-12-03 04:50:23 -06:00
makeSlider ( "Steps" , document . getElementById ( "steps" ) , "steps" , 1 , 70 , 5 , 30 , 1 ) ;
2022-11-29 20:08:43 -06:00
function changeMaskBlur ( ) {
2022-12-01 15:10:30 -06:00
stableDiffusionData . mask _blur = parseInt (
document . getElementById ( "maskBlur" ) . value
) ;
2022-11-29 20:08:43 -06:00
localStorage . setItem ( "mask_blur" , stableDiffusionData . mask _blur ) ;
}
function changeSeed ( ) {
stableDiffusionData . seed = document . getElementById ( "seed" ) . value ;
localStorage . setItem ( "seed" , stableDiffusionData . seed ) ;
}
function changeHiResFix ( ) {
stableDiffusionData . enable _hr = Boolean (
document . getElementById ( "cbxHRFix" ) . checked
) ;
localStorage . setItem ( "enable_hr" , stableDiffusionData . enable _hr ) ;
}
2022-12-04 13:22:35 -06:00
function changeSmoothRendering ( ) {
const layers = document . getElementById ( "layer-render" ) ;
if ( document . getElementById ( "cbxSmooth" ) . checked ) {
layers . classList . remove ( "pixelated" ) ;
} else {
layers . classList . add ( "pixelated" ) ;
}
}
2022-11-29 20:08:43 -06:00
2022-12-04 13:22:35 -06:00
function isCanvasBlank ( x , y , w , h , canvas ) {
2022-11-29 20:08:43 -06:00
return ! canvas
. getContext ( "2d" )
. getImageData ( x , y , w , h )
. data . some ( ( channel ) => channel !== 0 ) ;
}
function drawBackground ( ) {
// Checkerboard
let darkTileColor = "#333" ;
let lightTileColor = "#555" ;
2022-11-29 20:54:09 -06:00
for ( var x = 0 ; x < bgLayer . canvas . width ; x += 64 ) {
for ( var y = 0 ; y < bgLayer . canvas . height ; y += 64 ) {
bgLayer . ctx . fillStyle =
( x + y ) % 128 === 0 ? lightTileColor : darkTileColor ;
bgLayer . ctx . fillRect ( x , y , 64 , 64 ) ;
2022-11-29 20:08:43 -06:00
}
}
}
2022-12-08 15:23:17 -06:00
async function getUpscalers ( ) {
2022-11-29 20:08:43 -06:00
/ *
so for some reason when upscalers request returns upscalers , the real - esrgan model names are incorrect , and need to be fetched from / sdapi / v1 / realesrgan - models
also the realesrgan models returned are not all correct , extra fun !
LDSR seems to have problems so we dont add that either - > RuntimeError : Number of dimensions of repeat dims can not be smaller than number of dimensions of tensor
need to figure out why that is , if you dont get this error then you can add it back in
Hacky way to get the correct list all in one go is to purposefully make an incorrect request , which then returns
{ detail : "Invalid upscaler, needs to be on of these: None , Lanczos , Nearest , LDSR , BSRGAN , R-ESRGAN General 4xV3 , R-ESRGAN 4x+ Anime6B , ScuNET , ScuNET PSNR , SwinIR_4x" }
from which we can extract the correct list of upscalers
* /
// hacky way to get the correct list of upscalers
var extras _url =
document . getElementById ( "host" ) . value + "/sdapi/v1/extra-single-image/" ; // endpoint for upscaling, needed for the hacky way to get the correct list of upscalers
2022-12-08 15:23:17 -06:00
var empty _image = new Image ( 1 , 1 ) ;
2022-11-29 20:08:43 -06:00
var purposefully _incorrect _data = {
"resize-mode" : 0 , // 0 = just resize, 1 = crop and resize, 2 = resize and fill i assume based on theimg2img tabs options
upscaling _resize : 2 ,
upscaler _1 : "fake_upscaler" ,
image : empty _image . src ,
} ;
2022-12-08 15:23:17 -06:00
try {
const response = await fetch ( extras _url , {
method : "POST" ,
headers : {
Accept : "application/json" ,
"Content-Type" : "application/json" ,
} ,
body : JSON . stringify ( purposefully _incorrect _data ) ,
} ) ;
const data = await response . json ( ) ;
console . log (
"[index] purposefully_incorrect_data response, ignore above error"
) ;
// result = purposefully_incorrect_data response: Invalid upscaler, needs to be on of these: None , Lanczos , Nearest , LDSR , BSRGAN , R-ESRGAN General 4xV3 , R-ESRGAN 4x+ Anime6B , ScuNET , ScuNET PSNR , SwinIR_4x
const upscalers = data . detail
. split ( ": " ) [ 1 ]
. split ( "," )
. map ( ( v ) => v . trim ( ) )
. filter ( ( v ) => v !== "None" ) ; // converting the result to a list of upscalers
upscalerAutoComplete . options = upscalers . map ( ( u ) => {
return { name : u , value : u } ;
2022-11-29 20:08:43 -06:00
} ) ;
2022-12-08 15:23:17 -06:00
upscalerAutoComplete . value = upscalers [ 0 ] ;
} catch ( e ) {
console . warn ( "[index] Failed to fetch upscalers:" ) ;
console . warn ( e ) ;
}
2022-11-29 20:08:43 -06:00
/ * T H E N O N H A C K Y W A Y T H A T I S I M P L Y C O U L D N O T G E T T O P R O D U C E A L I S T W I T H O U T N O N W O R K I N G U P S C A L E R S , F E E L F R E E T O T R Y A N D F I G U R E I T O U T
var url = document . getElementById ( "host" ) . value + "/sdapi/v1/upscalers" ;
var realesrgan _url = document . getElementById ( "host" ) . value + "/sdapi/v1/realesrgan-models" ;
// get upscalers
fetch ( url )
. then ( ( response ) => response . json ( ) )
. then ( ( data ) => {
console . log ( data ) ;
for ( var i = 0 ; i < data . length ; i ++ ) {
var option = document . createElement ( "option" ) ;
if ( data [ i ] . name . includes ( "ESRGAN" ) || data [ i ] . name . includes ( "LDSR" ) ) {
continue ;
}
option . text = data [ i ] . name ;
upscalerSelect . add ( option ) ;
}
} )
. catch ( ( error ) => {
alert (
"Error getting upscalers, please check console for additional info\n" +
error
) ;
} ) ;
// fetch realesrgan models separately
fetch ( realesrgan _url )
. then ( ( response ) => response . json ( ) )
. then ( ( data ) => {
var model = data ;
for ( var i = 0 ; i < model . length ; i ++ ) {
let option = document . createElement ( "option" ) ;
option . text = model [ i ] . name ;
option . value = model [ i ] . name ;
upscalerSelect . add ( option ) ;
}
} )
* /
}
async function getModels ( ) {
var url = document . getElementById ( "host" ) . value + "/sdapi/v1/sd-models" ;
2022-12-08 16:11:08 -06:00
try {
const response = await fetch ( url ) ;
const data = await response . json ( ) ;
2022-11-29 20:08:43 -06:00
2022-12-08 16:11:08 -06:00
modelAutoComplete . options = data . map ( ( option ) => ( {
name : option . title ,
value : option . title ,
} ) ) ;
2022-11-29 20:08:43 -06:00
2022-12-08 16:11:08 -06:00
try {
const optResponse = await fetch (
document . getElementById ( "host" ) . value + "/sdapi/v1/options"
) ;
const optData = await optResponse . json ( ) ;
const model = optData . sd _model _checkpoint ;
2022-11-29 20:08:43 -06:00
console . log ( "Current model: " + model ) ;
2022-12-08 16:11:08 -06:00
modelAutoComplete . value = model ;
} catch ( e ) {
console . warn ( "[index] Failed to fetch current model:" ) ;
console . warn ( e ) ;
}
} catch ( e ) {
console . warn ( "[index] Failed to fetch models:" ) ;
console . warn ( e ) ;
}
2022-11-29 20:08:43 -06:00
2022-12-08 15:23:17 -06:00
modelAutoComplete . onchange . on ( async ( { value } ) => {
console . log ( ` [index] Changing model to [ ${ value } ] ` ) ;
var payload = {
sd _model _checkpoint : value ,
} ;
var url = document . getElementById ( "host" ) . value + "/sdapi/v1/options/" ;
try {
await fetch ( url , {
method : "POST" ,
headers : {
"Content-Type" : "application/json" ,
} ,
body : JSON . stringify ( payload ) ,
} ) ;
alert ( ` Model changed to [ ${ value } ] ` ) ;
} catch ( e ) {
console . warn ( "[index] Error changing model" ) ;
console . warn ( e ) ;
2022-11-29 20:08:43 -06:00
alert (
2022-12-08 15:23:17 -06:00
"Error changing model, please check console for additional information"
2022-11-29 20:08:43 -06:00
) ;
2022-12-08 15:23:17 -06:00
}
} ) ;
2022-11-29 20:08:43 -06:00
}
2022-12-07 15:55:41 -06:00
async function getConfig ( ) {
var url = document . getElementById ( "host" ) . value + "/sdapi/v1/options" ;
let message =
"The following options for the AUTOMATIC1111's webui are not recommended to use with this software:" ;
try {
const response = await fetch ( url ) ;
const data = await response . json ( ) ;
let wrong = false ;
// Check if img2img color correction is disabled and inpainting mask weight is set to one
// TODO: API Seems bugged for retrieving inpainting mask weight - returning 0 for all values different than 1.0
if ( data . img2img _color _correction ) {
message += "\n - Image to Image Color Correction: false recommended" ;
wrong = true ;
}
if ( data . inpainting _mask _weight < 1.0 ) {
message += ` \n - Inpainting Conditioning Mask Strength: 1.0 recommended ` ;
wrong = true ;
}
message += "\n\nShould these values be changed to the recommended ones?" ;
if ( ! wrong ) {
console . info ( "[index] WebUI Settings set as recommended." ) ;
return ;
}
console . info (
"[index] WebUI Settings not set as recommended. Prompting for changing settings automatically."
) ;
if ( ! confirm ( message ) ) return ;
try {
await fetch ( url , {
method : "POST" ,
headers : {
"Content-Type" : "application/json" ,
} ,
body : JSON . stringify ( {
img2img _color _correction : false ,
inpainting _mask _weight : 1.0 ,
} ) ,
} ) ;
} catch ( e ) {
console . warn ( "[index] Failed to fetch WebUI Configuration" ) ;
console . warn ( e ) ;
}
} catch ( e ) {
console . warn ( "[index] Failed to fetch WebUI Configuration" ) ;
console . warn ( e ) ;
}
}
2022-12-02 11:31:49 -06:00
async function getStyles ( ) {
/** @type {HTMLSelectElement} */
var styleSelect = document . getElementById ( "styleSelect" ) ;
var url = document . getElementById ( "host" ) . value + "/sdapi/v1/prompt-styles" ;
try {
const response = await fetch ( url ) ;
/** @type {{name: string, prompt: string, negative_prompt: string}[]} */
const data = await response . json ( ) ;
/** @type {string[]} */
let stored = null ;
try {
stored = JSON . parse ( localStorage . getItem ( "promptStyle" ) ) ;
2022-12-02 16:23:55 -06:00
// doesn't seem to throw a syntaxerror if the localstorage item simply doesn't exist?
if ( stored == null ) stored = [ ] ;
2022-12-02 11:31:49 -06:00
} catch ( e ) {
stored = [ ] ;
}
data . forEach ( ( style ) => {
const option = document . createElement ( "option" ) ;
option . classList . add ( "style-select-option" ) ;
option . text = style . name ;
2022-12-02 18:20:07 -06:00
option . value = style . name ;
2022-12-02 11:31:49 -06:00
option . title = ` prompt: ${ style . prompt } \n negative: ${ style . negative _prompt } ` ;
2022-12-02 23:47:47 -06:00
if ( stored . length === 0 ) option . selected = style . name === "None" ;
else
option . selected = ! ! stored . find (
( styleName ) => style . name === styleName
) ;
2022-12-02 11:31:49 -06:00
styleSelect . add ( option ) ;
} ) ;
changeStyles ( ) ;
stored . forEach ( ( styleName , index ) => {
if ( ! data . findIndex ( ( style ) => style . name === styleName ) ) {
stored . splice ( index , 1 ) ;
}
} ) ;
localStorage . setItem ( "promptStyle" , JSON . stringify ( stored ) ) ;
} catch ( e ) {
console . warn ( "[index] Failed to fetch prompt styles" ) ;
console . warn ( e ) ;
}
}
function changeStyles ( ) {
/** @type {HTMLSelectElement} */
const styleSelectEl = document . getElementById ( "styleSelect" ) ;
const selected = Array . from ( styleSelectEl . options ) . filter (
( option ) => option . selected
) ;
2022-12-02 23:47:47 -06:00
let selectedString = selected . map ( ( option ) => option . value ) ;
if ( selectedString . find ( ( selected ) => selected === "None" ) ) {
selectedString = [ ] ;
Array . from ( styleSelectEl . options ) . forEach ( ( option ) => {
if ( option . value !== "None" ) option . selected = false ;
} ) ;
}
2022-12-02 11:31:49 -06:00
2022-12-02 23:47:47 -06:00
localStorage . setItem ( "promptStyle" , JSON . stringify ( selectedString ) ) ;
2022-12-02 11:31:49 -06:00
// change the model
2022-12-02 23:47:47 -06:00
if ( selectedString . length > 0 )
console . log ( ` [index] Changing styles to ${ selectedString . join ( ", " ) } ` ) ;
else console . log ( ` [index] Clearing styles ` ) ;
2022-12-02 11:31:49 -06:00
stableDiffusionData . styles = selectedString ;
}
2022-12-08 15:23:17 -06:00
async function getSamplers ( ) {
2022-11-29 20:08:43 -06:00
var url = document . getElementById ( "host" ) . value + "/sdapi/v1/samplers" ;
2022-12-08 15:23:17 -06:00
try {
const response = await fetch ( url ) ;
const data = await response . json ( ) ;
samplerAutoComplete . options = data . map ( ( sampler ) => ( {
name : sampler . name ,
value : sampler . name ,
} ) ) ;
// Initial sampler
if ( localStorage . getItem ( "sampler" ) != null ) {
samplerAutoComplete . value = localStorage . getItem ( "sampler" ) ;
} else {
samplerAutoComplete . value = data [ 0 ] . name ;
localStorage . setItem ( "sampler" , samplerAutoComplete . value ) ;
}
samplerAutoComplete . onchange . on ( ( { value } ) => {
stableDiffusionData . sampler _index = value ;
localStorage . setItem ( "sampler" , value ) ;
2022-11-29 20:08:43 -06:00
} ) ;
2022-12-08 15:23:17 -06:00
} catch ( e ) {
console . warn ( "[index] Failed to fetch samplers" ) ;
console . warn ( e ) ;
}
2022-11-29 20:08:43 -06:00
}
async function upscaleAndDownload ( ) {
// Future improvements: some upscalers take a while to upscale, so we should show a loading bar or something, also a slider for the upscale amount
// get cropped canvas, send it to upscaler, download result
2022-12-06 21:12:10 -06:00
var upscale _factor = localStorage . getItem ( "upscale_x" )
? localStorage . getItem ( "upscale_x" )
: 2 ;
2022-12-08 15:23:17 -06:00
var upscaler = upscalerAutoComplete . value ;
2022-12-04 20:19:34 -06:00
var croppedCanvas = cropCanvas (
uil . getVisible ( {
x : 0 ,
y : 0 ,
w : imageCollection . size . w ,
h : imageCollection . size . h ,
} )
) ;
2022-11-29 20:08:43 -06:00
if ( croppedCanvas != null ) {
var url =
document . getElementById ( "host" ) . value + "/sdapi/v1/extra-single-image/" ;
2022-12-01 20:00:33 -06:00
var imgdata = croppedCanvas . canvas . toDataURL ( "image/png" ) ;
2022-11-29 20:08:43 -06:00
var data = {
"resize-mode" : 0 , // 0 = just resize, 1 = crop and resize, 2 = resize and fill i assume based on theimg2img tabs options
upscaling _resize : upscale _factor ,
upscaler _1 : upscaler ,
image : imgdata ,
} ;
console . log ( data ) ;
await fetch ( url , {
method : "POST" ,
headers : {
Accept : "application/json" ,
"Content-Type" : "application/json" ,
} ,
body : JSON . stringify ( data ) ,
} )
. then ( ( response ) => response . json ( ) )
. then ( ( data ) => {
console . log ( data ) ;
var link = document . createElement ( "a" ) ;
link . download =
new Date ( )
. toISOString ( )
. slice ( 0 , 19 )
. replace ( "T" , " " )
. replace ( ":" , " " ) +
" openOutpaint image upscaler_" +
upscaler +
2022-12-06 21:12:10 -06:00
"_x" +
2022-12-06 21:17:02 -06:00
upscale _factor +
2022-11-29 20:08:43 -06:00
".png" ;
link . href = "data:image/png;base64," + data [ "image" ] ;
link . click ( ) ;
} ) ;
}
}
function loadSettings ( ) {
// set default values if not set
var _prompt =
localStorage . getItem ( "prompt" ) == null
? "ocean floor scientific expedition, underwater wildlife"
: localStorage . getItem ( "prompt" ) ;
var _negprompt =
localStorage . getItem ( "neg_prompt" ) == null
? "people, person, humans, human, divers, diver, glitch, error, text, watermark, bad quality, blurry"
: localStorage . getItem ( "neg_prompt" ) ;
var _mask _blur =
localStorage . getItem ( "mask_blur" ) == null
? 0
: localStorage . getItem ( "mask_blur" ) ;
var _seed =
localStorage . getItem ( "seed" ) == null ? - 1 : localStorage . getItem ( "seed" ) ;
var _enable _hr = Boolean (
localStorage . getItem ( "enable_hr" ) == ( null || "false" )
? false
: localStorage . getItem ( "enable_hr" )
) ;
// set the values into the UI
document . getElementById ( "prompt" ) . value = String ( _prompt ) ;
document . getElementById ( "prompt" ) . title = String ( _prompt ) ;
document . getElementById ( "negPrompt" ) . value = String ( _negprompt ) ;
document . getElementById ( "negPrompt" ) . title = String ( _negprompt ) ;
document . getElementById ( "maskBlur" ) . value = Number ( _mask _blur ) ;
document . getElementById ( "seed" ) . value = Number ( _seed ) ;
document . getElementById ( "cbxHRFix" ) . checked = Boolean ( _enable _hr ) ;
}
2022-11-29 20:54:09 -06:00
imageCollection . element . addEventListener (
"wheel" ,
( evn ) => {
evn . preventDefault ( ) ;
} ,
{ passive : false }
) ;
imageCollection . element . addEventListener (
"contextmenu" ,
( evn ) => {
evn . preventDefault ( ) ;
} ,
{ passive : false }
) ;
2022-12-01 20:31:49 -06:00
function resetToDefaults ( ) {
if ( confirm ( "Are you sure you want to clear your settings?" ) ) {
localStorage . clear ( ) ;
}
}