2022-10-29 01:48:32 +02:00
/ * *
* Enum of parameter types
* @ readonly
* @ enum { string }
* /
var ParameterType = {
checkbox : "checkbox" ,
2022-11-30 07:48:34 +01:00
select : "select" ,
select _multiple : "select_multiple" ,
2023-02-22 15:02:00 +01:00
slider : "slider" ,
2022-11-30 07:48:34 +01:00
custom : "custom" ,
2022-10-29 01:48:32 +02:00
} ;
/ * *
* JSDoc style
* @ typedef { object } Parameter
* @ property { string } id
* @ property { ParameterType } type
* @ property { string } label
* @ property { ? string } note
* @ property { number | boolean | string } default
* /
/** @type {Array.<Parameter>} */
var PARAMETERS = [
2022-11-30 07:48:34 +01:00
{
id : "theme" ,
type : ParameterType . select ,
label : "Theme" ,
default : "theme-default" ,
note : "customize the look and feel of the ui" ,
options : [ // Note: options expanded dynamically
{
value : "theme-default" ,
label : "Default"
}
] ,
icon : "fa-palette"
} ,
{
id : "save_to_disk" ,
type : ParameterType . checkbox ,
label : "Auto-Save Images" ,
note : "automatically saves images to the specified location" ,
icon : "fa-download" ,
default : false ,
} ,
{
id : "diskPath" ,
type : ParameterType . custom ,
label : "Save Location" ,
render : ( parameter ) => {
return ` <input id=" ${ parameter . id } " name=" ${ parameter . id } " size="30" disabled> `
}
} ,
2022-12-12 09:36:20 +01:00
{
id : "metadata_output_format" ,
type : ParameterType . select ,
label : "Metadata format" ,
2022-12-14 05:48:01 +01:00
note : "will be saved to disk in this format" ,
2023-02-10 13:43:08 +01:00
default : "txt" ,
2022-12-12 09:36:20 +01:00
options : [
2023-01-24 10:47:48 +01:00
{
value : "none" ,
label : "none"
} ,
2022-12-12 09:36:20 +01:00
{
value : "txt" ,
2022-12-14 05:48:01 +01:00
label : "txt"
} ,
{
value : "json" ,
2022-12-12 09:36:20 +01:00
label : "json"
2023-01-24 10:47:48 +01:00
} ,
{
value : "embed" ,
label : "embed"
2023-03-29 05:49:05 +02:00
} ,
{
value : "embed,txt" ,
label : "embed & txt" ,
} ,
{
value : "embed,json" ,
label : "embed & json" ,
} ,
2022-12-12 09:36:20 +01:00
] ,
} ,
2023-02-18 10:31:13 +01:00
{
id : "block_nsfw" ,
type : ParameterType . checkbox ,
label : "Block NSFW images" ,
note : "blurs out NSFW images" ,
icon : "fa-land-mine-on" ,
default : false ,
} ,
2022-11-30 07:48:34 +01:00
{
id : "sound_toggle" ,
type : ParameterType . checkbox ,
label : "Enable Sound" ,
note : "plays a sound on task completion" ,
icon : "fa-volume-low" ,
default : true ,
} ,
2022-12-06 12:34:08 +01:00
{
id : "process_order_toggle" ,
type : ParameterType . checkbox ,
label : "Process newest jobs first" ,
note : "reverse the normal processing order" ,
2022-12-27 04:10:37 +01:00
icon : "fa-arrow-down-short-wide" ,
2022-12-06 12:34:08 +01:00
default : false ,
} ,
2022-11-30 07:48:34 +01:00
{
id : "ui_open_browser_on_start" ,
type : ParameterType . checkbox ,
label : "Open browser on startup" ,
note : "starts the default browser on startup" ,
icon : "fa-window-restore" ,
default : true ,
} ,
{
2022-12-16 07:04:49 +01:00
id : "vram_usage_level" ,
2022-12-15 19:00:06 +01:00
type : ParameterType . select ,
2022-12-16 07:04:49 +01:00
label : "GPU Memory Usage" ,
2022-12-16 09:41:55 +01:00
note : "Faster performance requires more GPU memory (VRAM)<br/><br/>" +
"<b>Balanced:</b> nearly as fast as High, much lower VRAM usage<br/>" +
2022-12-15 19:00:06 +01:00
"<b>High:</b> fastest, maximum GPU memory usage</br>" +
2023-02-08 15:11:55 +01:00
"<b>Low:</b> slowest, recommended for GPUs with 3 to 4 GB memory" ,
2022-11-30 07:48:34 +01:00
icon : "fa-forward" ,
2022-12-16 07:04:49 +01:00
default : "balanced" ,
2022-12-15 19:00:06 +01:00
options : [
2022-12-16 07:04:49 +01:00
{ value : "balanced" , label : "Balanced" } ,
2022-12-15 19:00:06 +01:00
{ value : "high" , label : "High" } ,
{ value : "low" , label : "Low" }
] ,
2022-11-30 07:48:34 +01:00
} ,
{
id : "use_cpu" ,
type : ParameterType . checkbox ,
label : "Use CPU (not GPU)" ,
note : "warning: this will be *very* slow" ,
icon : "fa-microchip" ,
default : false ,
} ,
{
id : "auto_pick_gpus" ,
type : ParameterType . checkbox ,
label : "Automatically pick the GPUs (experimental)" ,
default : false ,
} ,
{
id : "use_gpus" ,
type : ParameterType . select _multiple ,
label : "GPUs to use (experimental)" ,
note : "to process in parallel" ,
default : false ,
} ,
{
id : "auto_save_settings" ,
type : ParameterType . checkbox ,
label : "Auto-Save Settings" ,
note : "restores settings on browser load" ,
icon : "fa-gear" ,
default : true ,
} ,
2022-11-30 09:17:08 +01:00
{
id : "confirm_dangerous_actions" ,
type : ParameterType . checkbox ,
label : "Confirm dangerous actions" ,
note : "Actions that might lead to data loss must either be clicked with the shift key pressed, or confirmed in an 'Are you sure?' dialog" ,
icon : "fa-check-double" ,
default : true ,
} ,
2022-11-30 07:48:34 +01:00
{
id : "listen_to_network" ,
type : ParameterType . checkbox ,
label : "Make Stable Diffusion available on your network" ,
note : "Other devices on your network can access this web page" ,
icon : "fa-network-wired" ,
default : true ,
} ,
{
id : "listen_port" ,
type : ParameterType . custom ,
label : "Network port" ,
note : "Port that this server listens to. The '9000' part in 'http://localhost:9000'" ,
icon : "fa-anchor" ,
render : ( parameter ) => {
return ` <input id=" ${ parameter . id } " name=" ${ parameter . id } " size="6" value="9000" onkeypress="preventNonNumericalInput(event)"> `
}
} ,
{
id : "use_beta_channel" ,
type : ParameterType . checkbox ,
label : "Beta channel" ,
note : "Get the latest features immediately (but could be less stable). Please restart the program after changing this." ,
icon : "fa-fire" ,
default : false ,
} ,
2023-03-21 13:29:20 +01:00
{
id : "test_diffusers" ,
type : ParameterType . checkbox ,
label : "Test Diffusers" ,
note : "<b>Experimental! Can have bugs!</b> Use upcoming features (like LoRA) in our new engine. Please press Save, then restart the program after changing this." ,
icon : "fa-bolt" ,
default : false ,
} ,
2022-10-29 03:25:54 +02:00
] ;
2022-11-09 14:47:44 +01:00
function getParameterSettingsEntry ( id ) {
2022-11-30 07:48:34 +01:00
let parameter = PARAMETERS . filter ( p => p . id === id )
if ( parameter . length === 0 ) {
return
}
return parameter [ 0 ] . settingsEntry
2022-11-09 14:47:44 +01:00
}
2022-10-29 03:25:54 +02:00
2023-02-22 15:02:00 +01:00
function sliderUpdate ( event ) {
if ( event . srcElement . id . endsWith ( '-input' ) ) {
2023-02-22 15:26:45 +01:00
let slider = document . getElementById ( event . srcElement . id . slice ( 0 , - 6 ) )
slider . value = event . srcElement . value
slider . dispatchEvent ( new Event ( "change" ) )
2023-02-22 15:02:00 +01:00
} else {
2023-02-22 15:26:45 +01:00
let field = document . getElementById ( event . srcElement . id + '-input' )
field . value = event . srcElement . value
field . dispatchEvent ( new Event ( "change" ) )
2023-02-22 15:02:00 +01:00
}
}
2022-10-29 03:25:54 +02:00
function getParameterElement ( parameter ) {
2022-11-30 07:48:34 +01:00
switch ( parameter . type ) {
case ParameterType . checkbox :
var is _checked = parameter . default ? " checked" : "" ;
return ` <input id=" ${ parameter . id } " name=" ${ parameter . id } " ${ is _checked } type="checkbox"> `
case ParameterType . select :
case ParameterType . select _multiple :
var options = ( parameter . options || [ ] ) . map ( option => ` <option value=" ${ option . value } "> ${ option . label } </option> ` ) . join ( "" )
var multiple = ( parameter . type == ParameterType . select _multiple ? 'multiple' : '' )
return ` <select id=" ${ parameter . id } " name=" ${ parameter . id } " ${ multiple } > ${ options } </select> `
2023-02-22 15:02:00 +01:00
case ParameterType . slider :
return ` <input id=" ${ parameter . id } " name=" ${ parameter . id } " class="editor-slider" type="range" value=" ${ parameter . default } " min=" ${ parameter . slider _min } " max=" ${ parameter . slider _max } " oninput="sliderUpdate(event)"> <input id=" ${ parameter . id } -input" name=" ${ parameter . id } -input" size="4" value=" ${ parameter . default } " pattern="^[0-9 \. ]+ $ " onkeypress="preventNonNumericalInput(event)" oninput="sliderUpdate(event)"> ${ parameter . slider _unit } `
2022-11-30 07:48:34 +01:00
case ParameterType . custom :
return parameter . render ( parameter )
default :
console . error ( ` Invalid type for parameter ${ parameter . id } ` ) ;
return "ERROR: Invalid Type"
}
2022-10-29 03:25:54 +02:00
}
2022-11-18 02:58:09 +01:00
let parametersTable = document . querySelector ( "#system-settings .parameters-table" )
2022-10-29 03:25:54 +02:00
/* fill in the system settings popup table */
function initParameters ( ) {
2022-11-30 07:48:34 +01:00
PARAMETERS . forEach ( parameter => {
var element = getParameterElement ( parameter )
var note = parameter . note ? ` <small> ${ parameter . note } </small> ` : "" ;
var icon = parameter . icon ? ` <i class="fa ${ parameter . icon } "></i> ` : "" ;
var newrow = document . createElement ( 'div' )
newrow . innerHTML = `
< div > $ { icon } < / d i v >
< div > < label for = "${parameter.id}" > $ { parameter . label } < / l a b e l > $ { n o t e } < / d i v >
< div > $ { element } < / d i v > `
parametersTable . appendChild ( newrow )
parameter . settingsEntry = newrow
} )
2022-10-29 03:25:54 +02:00
}
2022-11-15 07:52:55 +01:00
initParameters ( )
2022-12-16 07:04:49 +01:00
let vramUsageLevelField = document . querySelector ( '#vram_usage_level' )
2022-11-15 07:52:55 +01:00
let useCPUField = document . querySelector ( '#use_cpu' )
let autoPickGPUsField = document . querySelector ( '#auto_pick_gpus' )
let useGPUsField = document . querySelector ( '#use_gpus' )
let saveToDiskField = document . querySelector ( '#save_to_disk' )
let diskPathField = document . querySelector ( '#diskPath' )
2023-01-24 10:47:48 +01:00
let metadataOutputFormatField = document . querySelector ( '#metadata_output_format' )
2022-11-19 17:10:45 +01:00
let listenToNetworkField = document . querySelector ( "#listen_to_network" )
let listenPortField = document . querySelector ( "#listen_port" )
2022-11-15 07:52:55 +01:00
let useBetaChannelField = document . querySelector ( "#use_beta_channel" )
2022-11-16 08:13:46 +01:00
let uiOpenBrowserOnStartField = document . querySelector ( "#ui_open_browser_on_start" )
2022-11-23 23:05:30 +01:00
let confirmDangerousActionsField = document . querySelector ( "#confirm_dangerous_actions" )
2023-03-21 13:29:20 +01:00
let testDiffusers = document . querySelector ( "#test_diffusers" )
2022-11-15 07:52:55 +01:00
let saveSettingsBtn = document . querySelector ( '#save-system-settings-btn' )
2022-11-23 11:25:36 +01:00
2022-11-15 07:52:55 +01:00
async function changeAppConfig ( configDelta ) {
try {
let res = await fetch ( '/app_config' , {
method : 'POST' ,
headers : {
'Content-Type' : 'application/json'
} ,
body : JSON . stringify ( configDelta )
} )
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 ( '/get/app_config' )
const config = await res . json ( )
if ( config . update _branch === 'beta' ) {
useBetaChannelField . checked = true
2022-11-19 17:10:45 +01:00
document . querySelector ( "#updateBranchLabel" ) . innerText = "(beta)"
2023-04-01 13:12:06 +02:00
} else {
getParameterSettingsEntry ( "test_diffusers" ) . style . display = "none"
2022-11-15 07:52:55 +01:00
}
2022-11-16 08:13:46 +01:00
if ( config . ui && config . ui . open _browser _on _start === false ) {
uiOpenBrowserOnStartField . checked = false
}
2022-11-30 07:48:34 +01:00
if ( config . net && config . net . listen _to _network === false ) {
listenToNetworkField . checked = false
}
if ( config . net && config . net . listen _port !== undefined ) {
listenPortField . value = config . net . listen _port
}
2023-03-21 13:29:20 +01:00
if ( config . test _diffusers !== undefined ) {
2023-04-01 13:12:06 +02:00
testDiffusers . checked = config . test _diffusers && config . update _branch === 'beta'
2023-03-21 13:29:20 +01:00
document . querySelector ( "#lora_model_container" ) . style . display = ( testDiffusers . checked ? '' : 'none' )
2023-04-01 13:14:36 +02:00
document . querySelector ( "#lora_alpha_container" ) . style . display = ( testDiffusers . checked ? '' : 'none' )
2023-03-21 13:29:20 +01:00
}
2022-11-15 07:52:55 +01:00
console . log ( 'get config status response' , config )
} catch ( e ) {
console . log ( 'get config status error' , e )
}
}
saveToDiskField . addEventListener ( 'change' , function ( e ) {
diskPathField . disabled = ! this . checked
2023-01-24 10:47:48 +01:00
metadataOutputFormatField . disabled = ! this . checked
2022-11-15 07:52:55 +01:00
} )
function getCurrentRenderDeviceSelection ( ) {
let selectedGPUs = $ ( '#use_gpus' ) . val ( )
if ( useCPUField . checked && ! autoPickGPUsField . checked ) {
return 'cpu'
}
if ( autoPickGPUsField . checked || selectedGPUs . length == 0 ) {
return 'auto'
}
return selectedGPUs . join ( ',' )
}
useCPUField . addEventListener ( 'click' , function ( ) {
let gpuSettingEntry = getParameterSettingsEntry ( 'use_gpus' )
let autoPickGPUSettingEntry = getParameterSettingsEntry ( 'auto_pick_gpus' )
if ( this . checked ) {
gpuSettingEntry . style . display = 'none'
autoPickGPUSettingEntry . style . display = 'none'
autoPickGPUsField . setAttribute ( 'data-old-value' , autoPickGPUsField . checked )
autoPickGPUsField . checked = false
} else if ( useGPUsField . options . length >= MIN _GPUS _TO _SHOW _SELECTION ) {
gpuSettingEntry . style . display = ''
autoPickGPUSettingEntry . style . display = ''
let oldVal = autoPickGPUsField . getAttribute ( 'data-old-value' )
if ( oldVal === null || oldVal === undefined ) { // the UI started with CPU selected by default
autoPickGPUsField . checked = true
} else {
autoPickGPUsField . checked = ( oldVal === 'true' )
}
gpuSettingEntry . style . display = ( autoPickGPUsField . checked ? 'none' : '' )
}
} )
useGPUsField . addEventListener ( 'click' , function ( ) {
let selectedGPUs = $ ( '#use_gpus' ) . val ( )
autoPickGPUsField . checked = ( selectedGPUs . length === 0 )
} )
autoPickGPUsField . addEventListener ( 'click' , function ( ) {
if ( this . checked ) {
$ ( '#use_gpus' ) . val ( [ ] )
}
let gpuSettingEntry = getParameterSettingsEntry ( 'use_gpus' )
gpuSettingEntry . style . display = ( this . checked ? 'none' : '' )
} )
2022-12-30 21:05:25 +01:00
async function setDiskPath ( defaultDiskPath , force = false ) {
2022-12-08 17:09:09 +01:00
var diskPath = getSetting ( "diskPath" )
2022-12-30 21:05:25 +01:00
if ( force || diskPath == '' || diskPath == undefined || diskPath == "undefined" ) {
2022-12-08 17:09:09 +01:00
setSetting ( "diskPath" , defaultDiskPath )
2022-11-15 07:52:55 +01:00
}
}
2022-11-30 10:04:24 +01:00
function setDeviceInfo ( devices ) {
let cpu = devices . all . cpu . name
let allGPUs = Object . keys ( devices . all ) . filter ( d => d != 'cpu' )
let activeGPUs = Object . keys ( devices . active )
function ID _TO _TEXT ( d ) {
let info = devices . all [ d ]
if ( "mem_free" in info && "mem_total" in info ) {
return ` ${ info . name } <small>( ${ d } ) ( ${ info . mem _free . toFixed ( 1 ) } Gb free / ${ info . mem _total . toFixed ( 1 ) } Gb total)</small> `
} else {
return ` ${ info . name } <small>( ${ d } ) (no memory info)</small> `
}
}
allGPUs = allGPUs . map ( ID _TO _TEXT )
activeGPUs = activeGPUs . map ( ID _TO _TEXT )
let systemInfoEl = document . querySelector ( '#system-info' )
systemInfoEl . querySelector ( '#system-info-cpu' ) . innerText = cpu
systemInfoEl . querySelector ( '#system-info-gpus-all' ) . innerHTML = allGPUs . join ( '</br>' )
systemInfoEl . querySelector ( '#system-info-rendering-devices' ) . innerHTML = activeGPUs . join ( '</br>' )
}
function setHostInfo ( hosts ) {
let port = listenPortField . value
hosts = hosts . map ( addr => ` http:// ${ addr } : ${ port } / ` ) . map ( url => ` <div><a href=" ${ url } "> ${ url } </a></div> ` )
document . querySelector ( '#system-info-server-hosts' ) . innerHTML = hosts . join ( '' )
}
async function getSystemInfo ( ) {
2022-11-15 07:52:55 +01:00
try {
2022-12-06 12:34:08 +01:00
const res = await SD . getSystemInfo ( )
let devices = res [ 'devices' ]
2022-11-15 07:52:55 +01:00
2022-12-06 12:34:08 +01:00
let allDeviceIds = Object . keys ( devices [ 'all' ] ) . filter ( d => d !== 'cpu' )
let activeDeviceIds = Object . keys ( devices [ 'active' ] ) . filter ( d => d !== 'cpu' )
2022-11-15 07:52:55 +01:00
2022-12-06 12:34:08 +01:00
if ( activeDeviceIds . length === 0 ) {
useCPUField . checked = true
}
2022-11-15 07:52:55 +01:00
2022-12-06 12:34:08 +01:00
if ( allDeviceIds . length < MIN _GPUS _TO _SHOW _SELECTION || useCPUField . checked ) {
let gpuSettingEntry = getParameterSettingsEntry ( 'use_gpus' )
gpuSettingEntry . style . display = 'none'
let autoPickGPUSettingEntry = getParameterSettingsEntry ( 'auto_pick_gpus' )
autoPickGPUSettingEntry . style . display = 'none'
}
2022-11-15 07:52:55 +01:00
2022-12-06 12:34:08 +01:00
if ( allDeviceIds . length === 0 ) {
useCPUField . checked = true
useCPUField . disabled = true // no compatible GPUs, so make the CPU mandatory
}
2022-11-15 07:52:55 +01:00
2022-12-06 12:34:08 +01:00
autoPickGPUsField . checked = ( devices [ 'config' ] === 'auto' )
2022-11-15 07:52:55 +01:00
2022-12-06 12:34:08 +01:00
useGPUsField . innerHTML = ''
allDeviceIds . forEach ( device => {
let deviceName = devices [ 'all' ] [ device ] [ 'name' ]
let deviceOption = ` <option value=" ${ device } "> ${ deviceName } ( ${ device } )</option> `
useGPUsField . insertAdjacentHTML ( 'beforeend' , deviceOption )
} )
2022-11-30 10:04:24 +01:00
2022-12-06 12:34:08 +01:00
if ( autoPickGPUsField . checked ) {
let gpuSettingEntry = getParameterSettingsEntry ( 'use_gpus' )
gpuSettingEntry . style . display = 'none'
} else {
$ ( '#use_gpus' ) . val ( activeDeviceIds )
2022-11-15 07:52:55 +01:00
}
2022-12-06 12:34:08 +01:00
setDeviceInfo ( devices )
setHostInfo ( res [ 'hosts' ] )
2022-12-30 21:05:25 +01:00
let force = false
if ( res [ 'enforce_output_dir' ] !== undefined ) {
force = res [ 'enforce_output_dir' ]
2023-02-21 04:09:16 +01:00
if ( force == true ) {
saveToDiskField . checked = true
metadataOutputFormatField . disabled = false
}
2022-12-30 21:05:25 +01:00
saveToDiskField . disabled = force
diskPathField . disabled = force
}
setDiskPath ( res [ 'default_output_dir' ] , force )
2022-11-15 07:52:55 +01:00
} catch ( e ) {
console . log ( 'error fetching devices' , e )
}
}
saveSettingsBtn . addEventListener ( 'click' , function ( ) {
2022-11-30 07:48:34 +01:00
if ( listenPortField . value == '' ) {
alert ( 'The network port field must not be empty.' )
2022-12-06 12:34:08 +01:00
return
}
if ( listenPortField . value < 1 || listenPortField . value > 65535 ) {
2022-11-30 07:48:34 +01:00
alert ( 'The network port must be a number from 1 to 65535' )
2022-12-06 12:34:08 +01:00
return
2022-11-30 07:48:34 +01:00
}
2022-12-06 12:34:08 +01:00
let updateBranch = ( useBetaChannelField . checked ? 'beta' : 'main' )
changeAppConfig ( {
'render_devices' : getCurrentRenderDeviceSelection ( ) ,
'update_branch' : updateBranch ,
'ui_open_browser_on_start' : uiOpenBrowserOnStartField . checked ,
'listen_to_network' : listenToNetworkField . checked ,
2023-03-21 13:29:20 +01:00
'listen_port' : listenPortField . value ,
'test_diffusers' : testDiffusers . checked
2022-12-06 12:34:08 +01:00
} )
2022-11-30 07:48:34 +01:00
saveSettingsBtn . classList . add ( 'active' )
asyncDelay ( 300 ) . then ( ( ) => saveSettingsBtn . classList . remove ( 'active' ) )
2022-11-15 07:52:55 +01:00
} )
2023-02-22 15:02:00 +01:00