SDAtom-WebUi-client-queue-ext/javascript/SDAtom-WebUi-us.user.js

1643 lines
97 KiB
JavaScript

// ==UserScript==
// @name SDAtom-WebUi-us
// @namespace SDAtom-WebUi-us
// @version 1.2.2
// @description Queue for AUTOMATIC1111 WebUi and an option to saving settings
// @author Kryptortio
// @homepage https://github.com/Kryptortio/SDAtom-WebUi-us
// @match http://127.0.0.1:7860/
// @updateURL https://raw.githubusercontent.com/Kryptortio/SDAtom-WebUi-us/main/SDAtom-WebUi-us.user.js
// @downloadURL https://raw.githubusercontent.com/Kryptortio/SDAtom-WebUi-us/main/SDAtom-WebUi-us.user.js
// @grant none
// ==/UserScript==
(function() {
'use strict';
// ----------------------------------------------------------------------------- Config
let conf = {
shadowDOM:{sel:"gradio-app"},
commonData: {
t2iContainer:{sel:"#tab_txt2img"},
i2iContainer:{sel:"#tab_img2img"},
extContainer:{sel:"#tab_extras"},
sdModelCheckpoint:{grad:"setting_sd_model_checkpoint"},
versionContainer:{sel:"#footer .versions"},
working:false,
processing:false,
waiting:false,
},
t2i: {
controls:{
tabButton: {sel:"#tabs > div:nth-child(1) > button:nth-child(1)"},
genrateButton: {sel:"#txt2img_generate"},
skipButton: {sel:"#txt2img_skip"},
},
prompt: {sel:"#txt2img_prompt textarea"},
negPrompt: {sel:"#txt2img_neg_prompt textarea"},
sample: {sel:"#txt2img_steps [id^=range_id]",sel2:"#txt2img_steps input"},
sampleMethod: {grad:"txt2img_sampling"},
width: {sel:"#txt2img_width [id^=range_id]",sel2:"#txt2img_width input"},
height: {sel:"#txt2img_height [id^=range_id]",sel2:"#txt2img_height input"},
restoreFace: {sel:"#txt2img_restore_faces input"},
tiling: {sel:"#txt2img_tiling input"},
highresFix: {sel:"#txt2img_enable_hr input"},
hrFixUpscaler: {grad:"txt2img_hr_upscaler"},
hrFixSteps: {sel:"#txt2img_hires_steps [id^=range_id]",sel2:"#txt2img_hires_steps input"},
hrFixdenoise: {sel:"#txt2img_denoising_strength [id^=range_id]",sel2:"#txt2img_denoising_strength input"},
hrFixUpscaleBy: {sel:"#txt2img_hr_scale [id^=range_id]",sel2:"#txt2img_hr_scale input"},
hrFixWidth: {sel:"#txt2img_hr_resize_x [id^=range_id]",sel2:"#txt2img_hr_resize_x input"},
hrFixHeight: {sel:"#txt2img_hr_resize_y [id^=range_id]",sel2:"#txt2img_hr_resize_y input"},
batchCount: {sel:"#txt2img_batch_count [id^=range_id]",sel2:"#txt2img_batch_count input"},
batchSize: {sel:"#txt2img_batch_size [id^=range_id]",sel2:"#txt2img_batch_size input"},
cfg: {sel:"#txt2img_cfg_scale [id^=range_id]",se2:"#txt2img_cfg_scale input"},
seed: {sel:"#txt2img_seed input"},
extra: {sel:"#txt2img_subseed_show input"},
varSeed: {sel:"#txt2img_subseed input"},
varStr: {sel:"#txt2img_subseed_strength [id^=range_id]",sel2:"#txt2img_subseed_strength input"},
varRSFWidth: {sel:"#txt2img_seed_resize_from_w [id^=range_id]",sel2:"#txt2img_seed_resize_from_w input"},
varRSFHeight: {sel:"#txt2img_seed_resize_from_h [id^=range_id]",sel2:"#txt2img_seed_resize_from_h input"},
script: {grad:"script_list",gradIndex:0},
scriptPromptMatrixPutVar: {sel:"#script_txt2txt_prompt_matrix_put_at_start input"},
scriptPromptMatrixUseDiff: {sel:"#script_txt2txt_prompt_matrix_different_seeds input"},
scriptXYZXtype:{grad:"script_txt2txt_xyz_plot_x_type"},
scriptXYZXVals:{sel:"#script_txt2txt_xyz_plot_x_values textarea"},
scriptXYZYtype:{grad:"script_txt2txt_xyz_plot_y_type"},
scriptXYZYVals:{sel:"#script_txt2txt_xyz_plot_y_values textarea"},
scriptXYZZtype:{grad:"script_txt2txt_xyz_plot_z_type"},
scriptXYZZVals:{sel:"#script_txt2txt_xyz_plot_z_values textarea"},
scriptXYZDrawLeg:{sel:"#script_txt2txt_xyz_plot_draw_legend input"},
scriptXYZIncludeSubImg:{sel:"#script_txt2txt_xyz_plot_include_lone_images input"},
scriptXYZIncludeSubGrid:{sel:"#script_txt2txt_xyz_plot_include_sub_grids input"},
scriptXYZKeepMOne:{sel:"#script_txt2txt_xyz_plot_no_fixed_seeds input"},
scriptXYZGridMargin: {sel:"#script_txt2txt_xyz_plot_margin_size [id^=range_id]",sel2:"#script_txt2txt_xyz_plot_margin_size input"},
},
i2i:{
controls:{
tabButton: {sel:"#tabs > div:nth-child(1) > button:nth-child(2)"},
genrateButton: {sel:"#img2img_generate"},
skipButton: {sel:"#img2img_skip"},
i2iMode:[
{name:"i2i", buttonSel:"#mode_img2img button:nth-child(1)", containerSel:"#img2img_img2img_tab"},
{name:"sketch", buttonSel:"#mode_img2img button:nth-child(2)", containerSel:"#img2img_img2img_sketch_tab"},
{name:"inpaint", buttonSel:"#mode_img2img button:nth-child(3)", containerSel:"#img2img_inpaint_tab"},
{name:"inpaintSketch", buttonSel:"#mode_img2img button:nth-child(4)", containerSel:"#img2img_inpaint_sketch_tab"},
{name:"inpaintUpload", buttonSel:"#mode_img2img button:nth-child(5)", containerSel:"#img2img_inpaint_upload_tab"},
{name:"batch", buttonSel:"#mode_img2img button:nth-child(6)", containerSel:"#img2img_batch_tab"},
],
},
prompt: {sel:"#img2img_prompt textarea"},
negPrompt: {sel:"#img2img_neg_prompt textarea"},
resizeMode: {sel:"#resize_mode"},
inpaintBlur: {sel:"#img2img_mask_blur [id^=range_id]",sel2:"#img2img_mask_blur"},
inpaintMaskMode: {sel:"#img2img_mask_mode"},
inpaintMaskContent: {sel:"#img2img_inpainting_fill"},
inpaintArea: {sel:"#img2img_inpaint_full_res"},
inpaintPadding: {sel:"#img2img_inpaint_full_res_padding [id^=range_id]",sel2:"#img2img_inpaint_full_res_padding"},
i2iBatchInputDir: {sel:"#img2img_batch_input_dir textarea"},
i2iBatchOutputDir: {sel:"#img2img_batch_output_dir textarea"},
i2iBatchMaskDir: {sel:"#img2img_batch_inpaint_mask_dir textarea"},
sample: {sel:"#img2img_steps [id^=range_id]",sel2:"#img2img_steps input"},
sampleMethod: {grad:"img2img_sampling"},
width: {sel:"#img2img_width [id^=range_id]",sel2:"#img2img_width input"},
height: {sel:"#img2img_height [id^=range_id]",sel2:"#img2img_height input"},
restoreFace: {sel:"#img2img_restore_faces input"},
tiling: {sel:"#img2img_tiling input"},
batchCount: {sel:"#img2img_batch_count [id^=range_id]",sel2:"#img2img_batch_count input"},
batchSize: {sel:"#img2img_batch_size [id^=range_id]",sel2:"#img2img_batch_size input"},
cfg: {sel:"#img2img_cfg_scale [id^=range_id]",se2:"#img2img_cfg_scale input"},
denoise: {sel:"#img2img_denoising_strength [id^=range_id]",se2:"#img2img_denoising_strength input"},
seed: {sel:"#img2img_seed input"},
extra: {sel:"#txt2img_subseed_show input"},
varSeed: {sel:"#img2img_subseed input"},
varStr: {sel:"#img2img_subseed_strength input",sel2:"#img2img_subseed_strength [id^=range_id]"},
varRSFWidth: {sel:"#img2img_seed_resize_from_w input",sel2:"#img2img_seed_resize_from_w [id^=range_id]"},
varRSFHeight: {sel:"#img2img_seed_resize_from_h input",sel2:"#img2img_seed_resize_from_h [id^=range_id]"},
script: {grad:"script_list",gradIndex:1},
scriptPromptMatrixPutVar: {sel:"#script_img2img_prompt_matrix_put_at_start input"},
scriptPromptMatrixUseDiff: {sel:"#script_img2img_prompt_matrix_different_seeds input"},
scriptXYZXtype:{grad:"script_img2img_xyz_plot_x_type"},
scriptXYZXVals:{sel:"#script_img2img_xyz_plot_x_values textarea"},
scriptXYZYtype:{grad:"script_img2img_xyz_plot_y_type"},
scriptXYZYVals:{sel:"#script_img2img_xyz_plot_y_values textarea"},
scriptXYZZtype:{grad:"script_img2img_xyz_plot_z_type"},
scriptXYZZVals:{sel:"#script_img2img_xyz_plot_z_values textarea"},
scriptXYZDrawLeg:{sel:"#script_img2img_xyz_plot_draw_legend input"},
scriptXYZIncludeSubImg:{sel:"#script_img2img_xyz_plot_include_lone_images input"},
scriptXYZIncludeSubGrid:{sel:"#script_img2img_xyz_plot_include_sub_grids input"},
scriptXYZKeepMOne:{sel:"#script_img2img_xyz_plot_no_fixed_seeds input"},
scriptXYZGridMargin: {sel:"#script_img2img_xyz_plot_margin_size [id^=range_id]",sel2:"#script_img2img_xyz_plot_margin_size input"},
scripti2iAltTestOverrideSampM:{sel:"#script_img2img_alternative_test_override_sampler input"},
scripti2iAltTestOverrideProm:{sel:"#script_img2img_alternative_test_override_prompt input"},
scripti2iAltTestOrigProm:{sel:"#script_img2img_alternative_test_original_prompt textarea"},
scripti2iAltTestOrigNProm:{sel:"#script_img2img_alternative_test_original_negative_prompt textarea"},
scripti2iAltTestOverrideSampS:{sel:"#script_img2img_alternative_test_override_steps input"},
scripti2iAltTestDecStep:{sel:"#script_img2img_alternative_test_st input",sel2:"#script_img2img_alternative_test_st [id^=range_id]"},
scripti2iAltTestOverrideDenoi:{sel:"#script_img2img_alternative_test_override_strength input"},
scripti2iAltTestDecCFG:{sel:"#script_img2img_alternative_test_cfg input",sel2:"#script_img2img_alternative_test_cfg [id^=range_id]"},
scripti2iAltTestRand:{sel:"#script_img2img_alternative_test_randomness input",sel2:"#script_img2img_alternative_test_randomness [id^=range_id]"},
scripti2iAltTestSigma:{sel:"#script_img2img_alternative_test_sigma_adjustment input"},
scriptLoopbackLoops:{sel:"#script_loopback_loops input",sel2:"#script_loopback_loops [id^=range_id]"},
scriptLoopbackDenoStr:{sel:"#script_loopback_final_denoising_strength input",sel2:"#script_loopback_final_denoising_strength [id^=range_id]"},
scriptLoopbackDenoStrCurve:{gradLab:"Denoising strength curve"},
scriptLoopbackAppend:{gradLab:"Append interrogated prompt at each iteration"},
scriptOutPMK2Pixels:{sel:"#script_outpainting_mk2_pixels input",sel2:"#script_outpainting_mk2_pixels [id^=range_id]"},
scriptOutPMK2MaskBlur:{sel:"#script_outpainting_mk2_mask_blur input",sel2:"#script_outpainting_mk2_mask_blur [id^=range_id]"},
scriptOutPMK2Left:{sel:"#script_outpainting_mk2_direction label:nth-child(1) input"},
scriptOutPMK2Right:{sel:"#script_outpainting_mk2_direction label:nth-child(2) input"},
scriptOutPMK2Up:{sel:"#script_outpainting_mk2_direction label:nth-child(3) input"},
scriptOutPMK2Down:{sel:"#script_outpainting_mk2_direction label:nth-child(4) input"},
scriptOutPMK2FallOff:{sel:"#script_outpainting_mk2_noise_q input",sel2:"#script_outpainting_mk2_noise_q [id^=range_id]"},
scriptOutPMK2ColorVar:{sel:"#script_outpainting_mk2_color_variation input",sel2:"#script_outpainting_mk2_color_variation [id^=range_id]"},
scriptPoorManPixels:{sel:"#script_poor_mans_outpainting_pixels input",sel2:"#script_poor_mans_outpainting_pixels [id^=range_id]"},
scriptPoorManMaskBlur:{sel:"#script_poor_mans_outpainting_mask_blur input",sel2:"#script_poor_mans_outpainting_mask_blur [id^=range_id]"},
scriptPoorManMaskCont:{sel:"#script_poor_mans_outpainting_inpainting_fill"},
scriptPoorManLeft:{sel:"#script_poor_mans_outpainting_direction label:nth-child(1) input"},
scriptPoorManRight:{sel:"#script_poor_mans_outpainting_direction label:nth-child(2) input"},
scriptPoorManUp:{sel:"#script_poor_mans_outpainting_direction label:nth-child(3) input"},
scriptPoorManDown:{sel:"#script_poor_mans_outpainting_direction label:nth-child(4) input"},
scriptSDUpTile:{sel:"#script_sd_upscale_overlap input",sel2:"#script_sd_upscale_overlap [id^=range_id]"},
scriptSDUpScale:{sel:"#script_sd_upscale_scale_factor input",sel2:"#script_sd_upscale_scale_factor [id^=range_id]"},
scriptSDUpUpcaler:{sel:"#script_sd_upscale_upscaler_index"},
},
ext:{
controls:{
tabButton: {sel:"#tabs > div:nth-child(1) > button:nth-child(3)"},
genrateButton: {sel:"#extras_generate"},
loadingElement:{sel:"#html_info_x_extras .wrap"},
extrasResizeMode:[
{name:"scaleBy",buttonSel:"#extras_resize_mode button:nth-child(1)",containerSel:"#extras_scale_by_tab"},
{name:"scaleTo",buttonSel:"#extras_resize_mode button:nth-child(2)",containerSel:"#extras_scale_to_tab"},
],
extrasMode:[
{name:"singleImg",buttonSel:"#mode_extras button:nth-child(1)",containerSel:"#extras_single_tab"},
{name:"batchProcess",buttonSel:"#mode_extras button:nth-child(2)",containerSel:"#extras_batch_process_tab"},
{name:"batchDir",buttonSel:"#mode_extras button:nth-child(3)",containerSel:"#extras_batch_directory_tab"},
],
},
scaleByResize:{sel:"#extras_upscaling_resize input",sel2:"#extras_upscaling_resize [id^=range_id]"},
scaleToWidth:{sel:"#extras_upscaling_resize_w input"},
scaleToHeight:{sel:"#extras_upscaling_resize_h input"},
scaleToCropToFit:{sel:"#extras_upscaling_crop input"},
batchDirInput:{sel:"#extras_batch_input_dir textarea"},
batchDirOutput:{sel:"#extras_batch_output_dir textarea"},
batchDirShowImg:{sel:"#extras_show_extras_results input"},
upscaler1:{grad:"extras_upscaler_1"},
upscaler2:{grad:"extras_upscaler_2"},
upscale2Vis:{sel:"#extras_upscaler_2_visibility input",sel2:"#extras_upscaler_2_visibility [id^=range_id]"},
GFPGANVis:{sel:"#extras_gfpgan_visibility input",sel2:"#extras_gfpgan_visibility [id^=range_id]"},
CodeFormVis:{sel:"#extras_codeformer_visibility input",sel2:"#extras_codeformer_visibility [id^=range_id]"},
CodeFormWeight:{sel:"#extras_codeformer_weight input",sel2:"#extras_codeformer_weight [id^=range_id]"},
},
extensions: {
iBrowser: {
name:"stable-diffusion-webui-images-browser",
existCheck:{sel:"#tab_image_browser"},
guiElems: {
iBrowserContainer:{sel:"#tab_image_browser"},
generationInfo: {sel: "#image_browser_tab_txt2img_image_browser_file_info textarea"},
txt2img:{sel:'#image_browser_tab_txt2img_image_browser_file_info textarea'},
img2img:{sel:'#image_browser_tab_img2img_image_browser_file_info textarea'},
txt2imgG:{sel:'#image_browser_tab_txt2img-grids_image_browser_file_info textarea'},
img2imgG:{sel:'#image_browser_tab_img2img-grids_image_browser_file_info textarea'},
extras:{sel:'#image_browser_tab_extras_image_browser_file_info textarea'},
favorites:{sel:'#image_browser_tab_favorites_image_browser_file_info textarea'},
},
ui:{},
text:{
queueVariationsButtonText:'Add 5 variations',
queueHiResVersionButtonText:'Add HiRes version',
},
functions: {
getValueJSON: function () {
awqLog('iBrowser.getValueJSON: parsing data');
let valueJSON = {type:'t2i'};
let currentTab = document.querySelector('#image_browser_tabs_container button.selected').innerHTML;
currentTab = currentTab.replace(/\s/g,'');
currentTab = currentTab.replace('-grids','G').toLowerCase();
let generationInfoValue = conf.extensions.iBrowser.guiElems[currentTab].el.value;
//Used when loading prompt from image browser
/*
Kind of using the logic from generation_parameters_copypaste.py/parse_generation_parameters (but not really, because that doesn't account for Template/Negative Template)
Structure
<prompt>
Negative prompt: <negative prompt>
a1: b1, a2: b2, etc
Template: <template>
Negative Template: <negative template>
<prompt>, <negative prompt>, <template> and <negative template> can all be multiline, or missing.
Maybe assume that <prompt> is never missing?
*/
let lines = generationInfoValue.split(/\r?\n/);
let whichLine=0; //0=prompt, 1=negPrompt, 2=template, 3=negTemplate, 4: dictionary
valueJSON['prompt']='';
valueJSON['negPrompt']='';
for (let l of lines) {
if (l.startsWith("Negative prompt: ")) {
whichLine=1;
l=l.substring(17);
}
else if (l.startsWith("Template: ")) {
whichLine=2;
l=l.substring(10);
}
else if (l.startsWith("Negative Template: ")) {
whichLine=3;
l=l.substring(19);
}
else if (l.startsWith("Steps: ")) {
whichLine=4;
}
switch (whichLine) {
case 0:
valueJSON['prompt']+=l;
break;
case 1:
valueJSON['negPrompt']+=l;
break;
case 2:
//ignore template
break;
case 3:
//ignore neg template
break;
case 4:
for (let v of l.split(/, /)) {
let kv=v.split(/: /);
let key=kv[0];
switch (kv[0]){
case "Steps":
valueJSON['sample']=kv[1];
break;
case "Sampler":
valueJSON['sampleMethod']=kv[1];
break;
case "CFG scale":
valueJSON['cfg']=kv[1];
break;
case "Seed":
valueJSON['seed']=kv[1];
break;
case "Size":
let wh=kv[1].split(/x/)
valueJSON['width']=wh[0];
valueJSON['height']=wh[1];
break;
case "Model":
valueJSON['sdModelCheckpoint']=kv[1];
break;
case "Denoising strength":
valueJSON['hrFixdenoise']=kv[1];
break;
case "Hires upscale":
valueJSON['hrFixUpscaleBy']=kv[1];
valueJSON['highresFix']=true;
break;
case "Hires upscaler":
valueJSON['hrFixUpscaler']=kv[1];
break;
}
}
break;
} // End of switch
} // End of for
return JSON.stringify(valueJSON);
},
}, // End of functions
}, // End of iBrowser
},
ui:{},
scriptSettings: {
defaultQuantity:{name:"Default queue quantity", description:"Default number of times to execute each queue item", type:"numeric",value:"1"},
rememberQueue:{name:"Remember queue", description:"Remember the queue if you reload the page", type:"boolean",value:true},
notificationSound:{name:"Notification sound", description:"Sound to be played when processing of queue items stops", type:"boolean",value:true},
extensionScript:{name:"Extension script(s)", description:"https://github.com/Kryptortio/SDAtom-WebUi-us#script-extensions", type:"text",value:""},
promptFilter:{name:"Prompt filter(s)", description:"https://github.com/Kryptortio/SDAtom-WebUi-us#prompt-filter", type:"text",value:""},
promptFilterNegative:{name:"Filter negative prompt", description:"Apply the prompt filter to the negative filter as well", type:"boolean",value:false},
autoscrollOutput:{name:"Autoscroll console", description:"Scroll console automatically when new lines appear", type:"boolean",value:true},
verboseLog:{name:"Verbose console", description:"Log as much as possible to the console", type:"boolean",value:false},
maxOutputLines:{name:"Max console lines", description:"The maximum number of lines that can be shown in the console box", type:"numeric",value:"500"},
overwriteQueueSettings1:{name:"Alt 1 overwrite", description:"Add settings you want to overwrite the current settings with when you click the Alt 1 button to add to queue (same format as in the queue)", type:"text",value:'{"width":"768","height":"768"}'},
overwriteQueueSettings2:{name:"Alt 2 overwrite", description:"Add settings you want to overwrite the current settings with when you click the Alt 2 button to add to queue (same format as in the queue)", type:"text",value:'{"width":"1024","height":"1024"}'},
overwriteQueueSettings3:{name:"Alt 3 overwrite", description:"Add settings you want to overwrite the current settings with when you click the Alt 3 button to add to queue (same format as in the queue)", type:"text",value:'{"sample":"20","sampleMethod":"Euler a","width":"512","height":"512","restoreFace": false,"tiling": false,"batchCount": "1","batchSize": "1","cfg": "7","seed": "-1","extra": false, "varSeed": "-1","varStr": "0"}'},
},
savedSetting: JSON.parse(localStorage.awqSavedSetting || '{}'),
currentQueue: JSON.parse(localStorage.awqCurrentQueue || '[]'),
};
if(localStorage.hasOwnProperty("awqNotificationSound") &&
!localStorage.hasOwnProperty("awqScriptSettings")) { // Tmp settings migration
awqLog('Copying settings from old storage');
if (localStorage.hasOwnProperty("awqNotificationSound"))
conf.scriptSettings.notificationSound.value = localStorage.awqNotificationSound == 1 ? true : false;
if (localStorage.hasOwnProperty("awqAutoscrollOutput"))
conf.scriptSettings.autoscrollOutput.value = localStorage.awqAutoscrollOutput == 1 ? true : false;
if (localStorage.hasOwnProperty("awqVerboseLog"))
conf.scriptSettings.verboseLog.value = localStorage.awqVerboseLog == 1 ? true : false;
if (localStorage.hasOwnProperty("awqMaxOutputLines"))
conf.scriptSettings.maxOutputLines.value = localStorage.awqMaxOutputLines;
if (localStorage.hasOwnProperty("awqPromptFilter"))
conf.scriptSettings.promptFilter.value = localStorage.awqPromptFilter;
if (localStorage.hasOwnProperty("awqExtensionScript"))
conf.scriptSettings.extensionScript.value = localStorage.awqExtensionScript;
}
const c_emptyQueueString = 'Queue is empty';
const c_addToQueueButtonText = 'Add to queue';
const c_processButtonText = 'Process queue';
const c_defaultTextStoredSettings = "Stored settings";
const c_innerUIWidth = 'calc(100vw - 20px)';
const c_uiElemntHeight = '25px';
const c_uiElemntHeightSmall = '18px';
const c_audio_base64 = new Audio('data:audio/mpeg;base64,//PkZAAAAAGkAAAAAAAAA0gAAAAATEFNRTMuMTAwVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVTEFNRTMuMTAwVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVV//PkZAAAAAGkAAAAAAAAA0gAAAAATEFNRTMuMTAwVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVTEFNRTMuMTAwVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVR9DU02wxI2HBY0xzzTPHVjsaEggIYbBQymbZ5et+lSLLDAPL2LcegkDNlkiLYgIBgMoIRxodnFVNMu2pmYwSfDSC2CxTFDKowcoycCimGKGFhYRCx50EjDS05dCEXJaIRTRHMTQfkO/U/sjuSEa5wroVM0a2hmXr0oA7JasD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V///+YGDIYDB4HGBRAYjCABAQMAOrVXeNb/////QYBwSBIETHQhBgBRAMGgq1lV3jW13f//////ogl+UTWeJbqBIMiQBa6X2STL/Y1tdq75lrtXf///////44AURAYAVhE+lMmFrcRNYQqJhyhq4FhWjY1tdx/mWu475lrv//////////rTVMnql4XBZApWX+aWuRL1jSmZf5pbEEvXQWDS+Z+yzHfK2u1d8y12rtFKTRUxBTUUzLjEwMFVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVV//PkZAAAAAGkAOAAAAAAA0gBwAAATEFNRTMuMTAwVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVTEFNRTMuMTAwVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVV');
const c_defaultOtputConsoleText = "Here you will see some information about what the script is doing";
const c_defaultOtputConsoleTextHTML = `<span class="console-description" style="color:darkgray">${c_defaultOtputConsoleText}</span>`;
const c_extraResizeModeScaleByType = 1;
const c_extraResizeModeScaleToType = 2;
const c_wait_tick_duration = 200;
// ----------------------------------------------------------------------------- Logging
const c_scriptVersion = typeof GM_info == 'undefined' ? new Date().toUTCString() : GM_info.script.version;
const c_scriptHandeler = typeof GM_info == 'undefined' ? '(not user script)' : GM_info.scriptHandler;
function preAwqLog(p_message) {
console.log(`SDAtom-WebUi-us: ${p_message}`);
}
preAwqLog(`Running SDAtom-WebUi-us version ${c_scriptVersion} using ${c_scriptHandeler} with browser ${window.navigator.userAgent}`);
function awqLog(p_message) {
if(conf.scriptSettings.verboseLog.value) {
awqLogPublishMsg(p_message, 'lightgray');
}
}
function awqLogPublishMsg(p_message, p_color) {
if(!conf.ui.outputConsole) return;
if(conf.ui.outputConsole.innerHTML.match('console-description')) {
let ASDWUIVerText = '<span style="font-size: 0.9em;">' + conf.commonData.versionContainer.el.textContent + '</span>';
conf.ui.outputConsole.innerHTML = `* Running SDAtom-WebUi-us version ${c_scriptVersion} using ${c_scriptHandeler} with browser ${window.navigator.userAgent} stable-diffusion-webui version ${ASDWUIVerText}`;
}
let lines = conf.ui.outputConsole.querySelectorAll('div');
let line = document.createElement('div');
const timestamp = (new Date()).toLocaleTimeString([], {hour: '2-digit',minute: '2-digit',second: '2-digit', hour12: false});
line.innerHTML = '<span style="' + (p_color ? 'color:'+p_color : '') + '">' + timestamp + ': ' + p_message + '</span>';
conf.ui.outputConsole.appendChild(line);
if(lines.length >= conf.scriptSettings.maxOutputLines.value) {
lines[0].parentNode.removeChild(lines[0]);
}
if(conf.scriptSettings.autoscrollOutput.value) conf.ui.outputConsole.scrollTo(0, conf.ui.outputConsole.scrollHeight);
}
function awqLogPublishError(p_message) { awqLogPublishMsg(p_message, 'red') }
window.addEventListener("error", (event) => {
if(conf.scriptSettings.verboseLog.value) {
awqLogPublishMsg(
`Javascript error (can be caused by something other than this script): ${event.message} source:${event.filename} line:${event.lineno} col:${event.colno} Error:${event.error ? JSON.stringify(event.error) : '' }`,
'darkorange'
);
}
},true);
window.addEventListener("unhandledrejection", (event) => {
if(conf.scriptSettings.verboseLog.value) {
awqLogPublishMsg(
`Javascript promise error (can be caused by something other than this script): ${event.reason}`,
'darkorange'
);
}
},true);
let oldWarn = window.console.warn,oldInfo = window.console.info,oldLog = window.console.log,oldError = window.console.error;
window.console.warn = function(p_msg) {
if(conf.scriptSettings.verboseLog.value) awqLogPublishMsg('log (warn) message (can be caused by something other than this script):' + p_msg +`<br>Call stack:<pre>${getCallStack()}</pre>`, 'lightgray'); oldWarn(p_msg);
}
window.console.info = function(p_msg) {
if(conf.scriptSettings.verboseLog.value) awqLogPublishMsg('log (info) message (can be caused by something other than this script):' + p_msg +`<br>Call stack:<pre>${getCallStack()}</pre>`, 'lightgray'); oldInfo(p_msg);
}
window.console.log = function(p_msg) {
if(conf.scriptSettings.verboseLog.value) awqLogPublishMsg('log (log) message (can be caused by something other than this script):' + p_msg +`<br>Call stack:<pre>${getCallStack()}</pre>`, 'lightgray'); oldLog(p_msg);
}
// ----------------------------------------------------------------------------- Wait for content to load
let waitForLoadInterval = setInterval(initAWQ, c_wait_tick_duration);
function initAWQ() {
window.awqConf = conf;
conf.shadowDOM.root = document.querySelector(conf.shadowDOM.sel);
if(!conf.shadowDOM.root || !conf.shadowDOM.root.querySelector('#txt2img_prompt')) return;
clearInterval(waitForLoadInterval);
conf.commonData.versionContainer.el = conf.shadowDOM.root.querySelector('#footer .versions');
// Check for extensions
for(let ext in conf.extensions) {
if(!document.querySelector(conf.extensions[ext].existCheck.sel)) {
preAwqLog(`Extension ${conf.extensions[ext].name} not found, disabling`);
conf.extensions[ext] = false;
} else {
preAwqLog(`Extension ${conf.extensions[ext].name} found`);
}
}
loadScriptSettings();
generateMainUI();
try { eval(conf.scriptSettings.extensionScript.value);} catch(e) { awqLogPublishMsg(`Failed to load extension script, error: <pre>${e.message} l:${e.lineNumber} c:${e.columnNumber}\n${e.stack}</pre>`,'darkorange')}
function mapElementsToConf(p_object, p_info) {
for (let prop in p_object) {
if(p_object[prop].sel) {
p_object[prop].el = conf.shadowDOM.root.querySelector(p_object[prop].sel);
if(!p_object[prop].el) awqLogPublishError(`Failed to find the ${p_info} ${prop}`);
}
if(p_object[prop].sel2) {
p_object[prop].el2 = conf.shadowDOM.root.querySelector(p_object[prop].sel2);
if(!p_object[prop].el2) awqLogPublishError(`Failed to find the secondary ${p_info} ${prop}`);
}
if(p_object[prop].grad) {
let gradIndex = p_object[prop].gradIndex ? p_object[prop].gradIndex : 0;
p_object[prop].gradEl = findGradioComponentState(p_object[prop].grad)[gradIndex];
if(!p_object[prop].gradEl) awqLogPublishError(`Failed to find the gradio element ${p_info} ${prop}`);
}
if(p_object[prop].gradLab) {
p_object[prop].gradEl = findGradioComponentStateByLabel(p_object[prop].gradLab)[0];
if(!p_object[prop].gradEl) awqLogPublishError(`Failed to find the gradio element ${p_info} ${prop}`);
}
}
}
mapElementsToConf(conf.commonData, 'main object');
mapElementsToConf(conf.t2i, 't2i object');
mapElementsToConf(conf.t2i.controls, 't2i control');
mapElementsToConf(conf.i2i, 'i2i object');
mapElementsToConf(conf.i2i.controls, 'i2i control');
mapElementsToConf(conf.ext, 'ext object');
mapElementsToConf(conf.ext.controls, 'ext control');
if(conf.extensions.iBrowser) waitForElm(conf.extensions.iBrowser.guiElems.txt2img.sel).then(function() {mapElementsToConf(conf.extensions.iBrowser.guiElems, 'iBrowser objects')});
setInterval(updateStatus, c_wait_tick_duration);
}
function appendAddToQueueButton(container, top, innerHTML, onclick, title, right) {
let button = document.createElement('button');
button.innerHTML = innerHTML;
button.style.height = c_uiElemntHeight;
button.style.color = 'black';
button.style.position = 'fixed';
button.style.top = top + 'px';
button.style.right = right ? right + 'px' : 0;
button.style.opacity = 0.2;
button.onclick = onclick;
button.style.cursor = "pointer";
button.title = title;
button.style.display = 'none';
container.appendChild(button);
return button;
}
function generateMainUI() {
let container = document.createElement('div');
container.style.width = c_innerUIWidth;
container.style.border = "1px solid white";
container.style.position = "relative";
document.body.appendChild(container);
let addToQueueButton = appendAddToQueueButton(container, 0, c_addToQueueButtonText, appendQueueItem, "Add an item to the queue according to current tab and settings");
let addToQueueButtonA1 = appendAddToQueueButton(container, 25, 'A1', () => {appendQueueItem(null, null, null, JSON.parse(conf.scriptSettings.overwriteQueueSettings1.value)) }, "Add an item to the queue according to current tab and settings and overwrite with Alt 1",63);
let addToQueueButtonA2 = appendAddToQueueButton(container, 25, 'A2', () => {appendQueueItem(null, null, null, JSON.parse(conf.scriptSettings.overwriteQueueSettings2.value)) }, "Add an item to the queue according to current tab and settings and overwrite with Alt 2",32);
let addToQueueButtonA3 = appendAddToQueueButton(container, 25, 'A3', () => {appendQueueItem(null, null, null, JSON.parse(conf.scriptSettings.overwriteQueueSettings3.value)) }, "Add an item to the queue according to current tab and settings and overwrite with Alt 3",1);
let unsupportedButton = appendAddToQueueButton(container, 0, 'Tab not supported', function(){}, "Not supported");
unsupportedButton.disabled = true;
unsupportedButton.innerHTML = 'Tab not supported';
unsupportedButton.style.cursor = 'not-allowed';
let defaultQueueQuantity = document.createElement('input');
defaultQueueQuantity.placeholder = 'Def #';
defaultQueueQuantity.style.height = c_uiElemntHeightSmall;
defaultQueueQuantity.style.width = '50px';
defaultQueueQuantity.type = 'number';
defaultQueueQuantity.title = "How many items of each will be added to the queue (default is 1)";
defaultQueueQuantity.value = conf.scriptSettings.defaultQuantity.value;
defaultQueueQuantity.onchange = function() {conf.scriptSettings.defaultQuantity.value = this.value;};
defaultQueueQuantity.onfocus = function() {this.select();};
container.appendChild(defaultQueueQuantity);
let assignDefaultToAll = document.createElement('button');
assignDefaultToAll.innerHTML = ' ⤵';
assignDefaultToAll.style.cursor = "pointer";
assignDefaultToAll.title = "Assign the default value to all queue items";
assignDefaultToAll.style.height = c_uiElemntHeight;
assignDefaultToAll.style.marginRight = '10px';
assignDefaultToAll.onclick = function() {
if(conf.scriptSettings.defaultQuantity.value >= 0) {
document.querySelectorAll('.AWQ-item-quantity').forEach((inp) => {inp.value = conf.scriptSettings.defaultQuantity.value});
updateQueueState();
}
};
container.appendChild(assignDefaultToAll);
let processButton = document.createElement('button');
processButton.innerHTML = c_processButtonText;
processButton.style.height = c_uiElemntHeight;
processButton.style.cursor = "pointer";
processButton.title = "Start processing the queue, click again to stop";
processButton.onclick = function() {
toggleProcessButton();
}
conf.ui.processButton = processButton;
container.appendChild(processButton);
let clearButton = document.createElement('button');
clearButton.innerHTML = "Clear queue";
clearButton.style.marginLeft = "5px";
clearButton.style.height = c_uiElemntHeight;
clearButton.style.cursor = "pointer";
clearButton.title = "Empty the queue completely";
clearButton.onclick = function() {
if(!confirm('Are you sure you want to remove everything in your queue?')) return;
queueContainer.innerHTML = c_emptyQueueString;
let oldQueueLength = conf.currentQueue.length;
conf.currentQueue = [];
updateQueueState();
awqLogPublishMsg(`Queue has been cleared, ${oldQueueLength} items removed`);
}
container.appendChild(clearButton);
let scriptSettingsButton = document.createElement('button');
scriptSettingsButton.innerHTML = "Script settings";
scriptSettingsButton.style.float = 'right';
scriptSettingsButton.style.height = c_uiElemntHeight;
scriptSettingsButton.style.cursor = "pointer";
scriptSettingsButton.title = "Open the settings popup for the script";
scriptSettingsButton.onclick = openScriptSettingsPopup;
container.appendChild(scriptSettingsButton);
let queueContainer = document.createElement('div');
queueContainer.style.width = c_innerUIWidth;
queueContainer.style.border = "1px solid white";
queueContainer.style.color = "gray";
queueContainer.style.marginBottom = "5px";
queueContainer.innerHTML = c_emptyQueueString;
container.appendChild(queueContainer);
let clearSettingButton = document.createElement('button');
clearSettingButton.innerHTML = "❌";
clearSettingButton.style.height = c_uiElemntHeight;
clearSettingButton.style.background = 'none';
clearSettingButton.onclick = function() {clearSetting(); }
clearSettingButton.style.cursor = "pointer";
clearSettingButton.title = "Remove the currently selected setting";
container.appendChild(clearSettingButton);
let settingsStorage = document.createElement('select');
settingsStorage.style.height = c_uiElemntHeight;
settingsStorage.title = "List of stored settings (template of all settings)";
container.appendChild(settingsStorage);
let editSettingButton = document.createElement('button');
editSettingButton.innerHTML = "✏️";
editSettingButton.style.height = c_uiElemntHeight;
editSettingButton.onclick = function() {editSetting(); }
editSettingButton.style.cursor = "pointer";
editSettingButton.title = "Edit the currently selected setting (a property can be removed to not change it when loading)";
container.appendChild(editSettingButton);
let loadSettingButton = document.createElement('button');
loadSettingButton.innerHTML = "Load";
loadSettingButton.style.height = c_uiElemntHeight;
loadSettingButton.onclick = function() {loadSetting(); }
loadSettingButton.style.cursor = "pointer";
loadSettingButton.title = "Load the currently selected setting (replacing current settings)";
container.appendChild(loadSettingButton);
let settingName =document.createElement('input');
settingName.placeholder = "Setting name";
settingName.style.height = c_uiElemntHeightSmall;
settingName.title = "Name to use when saving a new setting (duplicates not allowed)";
settingName.onfocus = function() {this.select();};
container.appendChild(settingName);
let saveSettingButton = document.createElement('button');
saveSettingButton.innerHTML = "Save";
saveSettingButton.style.height = c_uiElemntHeight;
saveSettingButton.onclick = function() {saveSettings(); }
saveSettingButton.style.cursor = "pointer";
saveSettingButton.title = "Save currently selected settings so that you can load them again later";
container.appendChild(saveSettingButton);
let outputConsole = document.createElement('div');
outputConsole.title = c_defaultOtputConsoleText;
outputConsole.innerHTML = c_defaultOtputConsoleTextHTML;
outputConsole.style.width = 'calc(100vw - 50px)';
outputConsole.style.border = "1px solid black";
outputConsole.style.color = "gray";
outputConsole.style.padding = "2px";
outputConsole.style.marginTop = "5px";
outputConsole.style.marginBottom = "5px";
outputConsole.style.marginLeft = "15px";
outputConsole.style.marginRight = "15px";
outputConsole.style.height = (16*10)+"px";
outputConsole.style.overflow = "auto";
outputConsole.style.backgroundColor = "white";
outputConsole.style.boxShadow = 'inset 0px 1px 4px #666';
container.appendChild(outputConsole);
let outputConsoleClearButton = document.createElement('button');
outputConsoleClearButton.innerHTML = "Clear";
outputConsoleClearButton.style.height = c_uiElemntHeight;
outputConsoleClearButton.onclick = function() {conf.ui.outputConsole.innerHTML = c_defaultOtputConsoleTextHTML; }
outputConsoleClearButton.style.cursor = "pointer";
outputConsoleClearButton.style.marginLeft = "10px";
outputConsoleClearButton.title = "Clear the console above";
container.appendChild(outputConsoleClearButton);
conf.ui.addToQueueButton = addToQueueButton;
conf.ui.addToQueueButtonA1 = addToQueueButtonA1;
conf.ui.addToQueueButtonA2 = addToQueueButtonA2;
conf.ui.addToQueueButtonA3 = addToQueueButtonA3;
conf.ui.unsupportedButton = unsupportedButton;
conf.ui.queueContainer = queueContainer;
conf.ui.clearButton = clearButton;
conf.ui.loadSettingButton = loadSettingButton;
conf.ui.settingName = settingName;
conf.ui.settingsStorage = settingsStorage;
conf.ui.defaultQueueQuantity = defaultQueueQuantity;
conf.ui.outputConsole = outputConsole;
document.querySelector('.gradio-container').style.overflow = 'visible'; // Fix so that a dropdown menu can overlap the queue
refreshSettings();
if(conf.currentQueue.length > 0) {
awqLog('Loaded saved queue:'+conf.currentQueue.length);
for(let i = 0; i < conf.currentQueue.length; i++) {
appendQueueItem(conf.currentQueue[i].quantity, conf.currentQueue[i].value, conf.currentQueue[i].type);
}
updateQueueState();
}
awqLog('generateMainUI: Completed');
}
function appendQueueItem(p_quantity, p_value, p_type, p_overwrite_data) {
awqLog('appendQueueItem: quantity:' + p_quantity + ' type:' + p_type);
let quantity = isNaN(p_quantity) || p_quantity == null ?
(parseInt(conf.ui.defaultQueueQuantity.value) > 0 ?
parseInt(conf.ui.defaultQueueQuantity.value) : 1) : p_quantity;
let queueItem = document.createElement('div');
queueItem.style.width = c_innerUIWidth;
let itemType =document.createElement('input');
itemType.classList = 'AWQ-item-type';
itemType.style.display = "50px";
itemType.style.height = c_uiElemntHeightSmall;
itemType.style.width = "20px";
itemType.value = p_type || conf.commonData.activeType;
itemType.title = "This is the type/tab for the queue item";
itemType.disabled = true;
let itemQuantity = document.createElement('input');
function updateItemQuantityBG() {
if(itemQuantity.value.length == 0) { itemQuantity.style.backgroundColor = 'red'; }
else if(itemQuantity.value < 1) { itemQuantity.style.backgroundColor = '#c2ffc2'; }
else if(itemQuantity.value > 0) { itemQuantity.style.backgroundColor = 'white'; }
}
itemQuantity.classList = 'AWQ-item-quantity';
itemQuantity.value = quantity;
itemQuantity.style.width = "50px";
itemQuantity.type = 'number';
itemQuantity.style.height = c_uiElemntHeightSmall;
itemQuantity.onchange = function() {
updateItemQuantityBG();
updateQueueState();
};
updateItemQuantityBG();
itemQuantity.onfocus = function() {this.select();};
itemQuantity.title = "This is how many times this item should be executed";
let itemJSON =document.createElement('input');
itemJSON.classList = 'AWQ-item-JSON';
itemJSON.value = p_value || getValueJSON(p_type);
if(p_overwrite_data) {
awqLog('appendQueueItem: Adding to queue with Overwrite button');
let jsonData = JSON.parse(itemJSON.value);
for(let setKey in jsonData) {
if(p_overwrite_data.hasOwnProperty(setKey)) jsonData[setKey] = p_overwrite_data[setKey];
}
itemJSON.value = JSON.stringify(jsonData);
}
itemJSON.style.width = "calc(100vw - 290px)";
itemJSON.style.height = "18px";
itemJSON.onchange = function() {
updateQueueState;
// Update itemType if needed
let newType = itemJSON.value.match(/"type":"([^"]+)"/);
newType = newType ? newType[1] : null;
if(newType != itemType.value) itemType.value = newType;
}
itemJSON.title = "This is a JSON string with all the settings to be used for this item. Can be changed while processing the queue but will fail if you enter invalid values.";
let removeItem =document.createElement('button');
removeItem.innerHTML = '❌';
removeItem.style.height = c_uiElemntHeight;
removeItem.style.background = 'none';
removeItem.style.cursor = "pointer";
removeItem.title = "Remove this item from the queue";
removeItem.onclick = function() {
this.parentNode.parentNode.removeChild(this.parentNode);
updateQueueState();
awqLogPublishMsg(`Removed queue item`);
};
let moveItemUp =document.createElement('button');
moveItemUp.innerHTML = '⇧';
moveItemUp.style.height = c_uiElemntHeight;
moveItemUp.style.cursor = "pointer";
moveItemUp.title = "Move this item up in the queue";
moveItemUp.onclick = function() {
let tar = this.parentNode;
if(tar.previousSibling) {
tar.parentNode.insertBefore(tar, tar.previousSibling);
awqLogPublishMsg(`Rearranged queue`);
updateQueueState();
}
};
let moveItemDown =document.createElement('button');
moveItemDown.innerHTML = '⇩';
moveItemDown.style.height = c_uiElemntHeight;
moveItemDown.style.cursor = "pointer";
moveItemDown.title = "Move this item down in the queue";
moveItemDown.onclick = function() {
let tar = this.parentNode;
if(tar.nextSibling) {
tar.parentNode.insertBefore(tar.nextSibling, tar);
awqLogPublishMsg(`Rearranged queue`);
updateQueueState();
}
};
let moveItemBottom =document.createElement('button');
moveItemBottom.innerHTML = '⤓';
moveItemBottom.style.height = c_uiElemntHeight;
moveItemBottom.style.cursor = "pointer";
moveItemBottom.title = "Move this item to the bottom of the queue";
moveItemBottom.onclick = function() {
let tar = this.parentNode;
if(tar.parentNode.lastChild !== tar) {
tar.parentNode.appendChild(tar);
updateQueueState();
}
};
let moveItemTop =document.createElement('button');
moveItemTop.innerHTML = '⤒';
moveItemTop.style.height = c_uiElemntHeight;
moveItemTop.style.cursor = "pointer";
moveItemTop.title = "Move this item to the top of the queue";
moveItemTop.onclick = function() {
let tar = this.parentNode;
let parentFirstChild = tar.parentNode.firstChild;
if(parentFirstChild && parentFirstChild !== tar) {
tar.parentNode.insertBefore(tar, parentFirstChild);
awqLogPublishMsg(`Rearranged queue`);
updateQueueState();
}
};
let loadItem =document.createElement('button');
loadItem.innerHTML = 'Load';
loadItem.style.height = c_uiElemntHeight;
loadItem.style.cursor = "pointer";
loadItem.title = "Load the settings from this item";
loadItem.onclick = async function() {
let itemRow = this.parentNode;
await loadJson(itemRow.querySelector('.AWQ-item-JSON').value);
};
queueItem.appendChild(removeItem);
queueItem.appendChild(moveItemUp);
queueItem.appendChild(moveItemDown);
queueItem.appendChild(moveItemBottom);
queueItem.appendChild(moveItemTop);
queueItem.appendChild(loadItem);
queueItem.appendChild(itemType);
queueItem.appendChild(itemQuantity);
queueItem.appendChild(itemJSON);
if(conf.ui.queueContainer.innerHTML == c_emptyQueueString) conf.ui.queueContainer.innerHTML = "";
conf.ui.queueContainer.appendChild(queueItem);
awqLogPublishMsg(`Added new ${itemType.value} queue item (${quantity}x)`);
// Wait with updating state while loading a predefined queue
if(isNaN(p_quantity)) updateQueueState();
}
function saveScriptSettings() {
awqLog('Saving script settings');
let scriptSettingsCopy = structuredClone(conf.scriptSettings);
// Delete data that does not need to be saved
for(let ssk in scriptSettingsCopy) {
for(let ssk2 in scriptSettingsCopy[ssk]) {
if(ssk2 != 'value') delete scriptSettingsCopy[ssk][ssk2];
}
}
conf.ui.defaultQueueQuantity.value = conf.scriptSettings.defaultQuantity.value; // Update beacuse this one is in two places
localStorage.awqScriptSettings = JSON.stringify(scriptSettingsCopy);
}
function loadScriptSettings(p_scriptSettings) {
if(!localStorage.hasOwnProperty("awqScriptSettings")) return;
awqLog('Loding saved script settings');
let savedSettings = p_scriptSettings || JSON.parse(localStorage.awqScriptSettings);
for(let ssk in conf.scriptSettings) {
if(savedSettings.hasOwnProperty(ssk)) conf.scriptSettings[ssk].value = savedSettings[ssk].value;
}
}
function openScriptSettingsPopup() {
let dialog = document.createElement('span');
dialog.style.minWidth = '90%';
dialog.style.minHeight = '90%';
dialog.style.marginLeft = '5%';
dialog.style.marginTop = '5%';
dialog.style.top = '0';
dialog.style.position = 'fixed';
dialog.style.backgroundColor = 'white';
dialog.style.zIndex = '1000';
dialog.style.borderRadius = '15px';
dialog.style.boxShadow = '3px 3px 100px black,3px 3px 500px black, 3px 3px 25px black, inset 0 0 10px black';
let titleText = document.createElement('span');
titleText.innerHTML = '<b>Script settings</b> - <i>hold your mouse over an item for a description</i>';
titleText.style.top = '5px';
titleText.style.left = '10px';
titleText.style.position = 'absolute';
let closeButton = document.createElement('span');
closeButton.style.position = 'absolute';
closeButton.style.top = '5px';
closeButton.style.right = '10px';
closeButton.style.textShadow = '#292929 2px 3px 5px';
closeButton.style.cursor = 'pointer';
closeButton.onclick = function() {document.body.removeChild(dialog); saveScriptSettings();};
closeButton.innerHTML = '⛌';
let dialogBody = document.createElement('span');
dialogBody.style.width = '90%';
dialogBody.style.height = '90%';
dialogBody.style.marginLeft = '5%';
dialogBody.style.marginTop = 'max(5%,45px)';
dialogBody.style.top = '0';
dialogBody.style.position = 'absolute';
dialogBody.style.overflow = 'auto';
dialogBody.style.display = 'grid';
dialogBody.style.gridAutoFlow = 'row';
dialogBody.style.gridTemplateColumns = 'repeat(auto-fit, minmax(200px, 1fr))';
dialog.appendChild(titleText);
dialog.appendChild(closeButton);
dialog.appendChild(dialogBody);
// Create input for each script setting
for(let ssKey in conf.scriptSettings) {
let ssObj = conf.scriptSettings[ssKey];
let ssElem = document.createElement(ssObj.type == 'text' ? 'textarea' : 'input');
ssElem.id = 'ss-'+ssKey;
ssElem.placeholder = ssObj.name;
ssElem.value = ssObj.value;
ssElem.style.height = ssObj.type == 'text' ? '40px' : '20px';
ssElem.style.marginRight = '20px';
ssElem.onchange = function() {
conf.scriptSettings[ssKey].value = ssObj.type == 'boolean' ? this.checked : this.value;
saveScriptSettings();
};
if(ssObj.type == 'boolean') ssElem.type = 'checkbox';
if(ssObj.type == 'numeric') {
ssElem.type = 'number' ;
ssElem.inputmode = 'numeric';
ssElem.onkeypress = e => { if (e.key.match(/\D/g)) { e.preventDefault();} };
}
if(ssObj.type == 'boolean') {
ssElem.type = 'checkbox';
ssElem.checked = ssObj.value;
}
let cbLabel = document.createElement('label');
cbLabel.for = ssElem.id;
cbLabel.innerHTML = ssObj.name;
cbLabel.title = ssObj.description;
let ssElemContainer = document.createElement('span');
ssElemContainer.appendChild(cbLabel);
if(ssObj.description.match("http")) {
let helpLink = document.createElement('a');
helpLink.innerHTML = '❓';
helpLink.target = '_blank';
helpLink.href = ssObj.description;
helpLink.style.textDecoration = 'none';
ssElem.title = ssObj.name;
ssElemContainer.appendChild(helpLink);
} else {
ssElem.title = ssObj.description;
}
ssElemContainer.appendChild(ssElem);
dialogBody.appendChild(ssElemContainer);
}
let importExportButton = document.createElement('button');
importExportButton.innerHTML = "Import/export";
importExportButton.style.height = c_uiElemntHeight;
importExportButton.style.cursor = "pointer";
importExportButton.title = "Import or export all the data for this script (to import add previoiusly exported data to the right, to export leave it empty). Importing data will reload the page!";
importExportButton.onclick = exportImport;
let importExportData = document.createElement('input');
importExportData.id = 'import-export-data-input';
importExportData.placeholder = 'Import/export data';
importExportData.style.height = c_uiElemntHeightSmall;
importExportData.style.width = '125px';
importExportData.title = "Exported data will be show here, add data here to import it. Importing data will reload the page!";
importExportData.onfocus = function() {this.select();};
let importExportContainer = document.createElement('span');
importExportContainer.appendChild(importExportButton);
importExportContainer.appendChild(importExportData);
dialogBody.appendChild(importExportContainer);
document.body.appendChild(dialog);
}
function toggleProcessButton(p_set_processing) {
awqLog('toggleProcessButton:' + p_set_processing);
let pb = conf.ui.processButton;
let undefinedInput = typeof p_set_processing == 'undefined';
if(undefinedInput) {
toggleProcessButton(!conf.commonData.processing);
} else if (p_set_processing) {
awqLogPublishMsg('Processing <b>started</b>');
conf.commonData.processing = true;
pb.style.background = 'green';
pb.innerHTML = '⏸︎ ';
let cogElem = document.createElement('div')
cogElem.innerHTML = '⚙️';
cogElem.style.display = 'inline-block';
pb.appendChild(cogElem);
//cogElem.animate([{ transform: 'rotate(0)' },{transform: 'rotate(360deg)'}], {duration: 1000,iterations: Infinity});
executeAllNewTasks();
} else {
awqLogPublishMsg('Processing <b>ended</b>');
conf.commonData.processing = false;
conf.commonData.previousTaskStartTime = null;
pb.style.background = 'buttonface';
pb.innerHTML = c_processButtonText;
}
}
function updateQueueState() {
let queueItems = conf.ui.queueContainer.getElementsByTagName('div');
awqLog('updateQueueState: old length:'+conf.currentQueue.length + ' new length:'+queueItems.length);
let newArray = [];
for(let i = 0; i < queueItems.length; i++) {
let newRowObject = {};
newRowObject.rowid = i;
newRowObject.type = queueItems[i].querySelector('.AWQ-item-type').value;
newRowObject.quantity = queueItems[i].querySelector('.AWQ-item-quantity').value;
newRowObject.value = queueItems[i].querySelector('.AWQ-item-JSON').value;
newArray.push(newRowObject);
}
conf.currentQueue = newArray;
if(conf.scriptSettings.rememberQueue.value) {
awqLog('updateQueueState: Saving current queue state '+conf.currentQueue.length);
localStorage.awqCurrentQueue = JSON.stringify(conf.currentQueue);
} else {
awqLog('updateQueueState: Cleared current queue state');
localStorage.removeItem("awqCurrentQueue");
}
}
let stuckProcessingCounter = 0;
function updateStatus() {
let previousType = conf.commonData.activeType;
let previousWorking = conf.commonData.working;
let workingOnI2I = conf.i2i.controls.skipButton.el.getAttribute('style') == 'display: block;';
let workingOnT2I = conf.t2i.controls.skipButton.el.getAttribute('style') == 'display: block;';
let workingOnExt = conf.ext.controls.loadingElement.el.querySelectorAll('.z-20').length > 0;
// Hide all buttons by default, and then show the right one
conf.ui.addToQueueButton.style.display = 'none'
conf.ui.addToQueueButtonA1.style.display = 'none'
conf.ui.addToQueueButtonA2.style.display = 'none'
conf.ui.addToQueueButtonA3.style.display = 'none'
conf.ui.unsupportedButton.style.display = 'none'
if(conf.commonData.i2iContainer.el.style.display !== 'none') {
conf.commonData.activeType = 'i2i';
} else if(conf.commonData.t2iContainer.el.style.display !== 'none') {
conf.commonData.activeType = 't2i';
} else if(conf.commonData.extContainer.el.style.display !== 'none') {
conf.commonData.activeType = 'ext';
} else if(conf.extensions.iBrowser && conf.extensions.iBrowser.guiElems.iBrowserContainer.el.style.display !== 'none') {
conf.commonData.activeType = 'iBrowser';
} else {
conf.commonData.activeType = 'other';
conf.ui.unsupportedButton.style.display = 'block';
}
if(conf.commonData.activeType != 'other') {
conf.ui.addToQueueButton.style.display = 'block';
conf.ui.addToQueueButtonA1.style.display = 'block';
conf.ui.addToQueueButtonA2.style.display = 'block';
conf.ui.addToQueueButtonA3.style.display = 'block';
}
let typeChanged = conf.commonData.activeType !== previousType ? true : false;
let workingChanged = conf.commonData.working !== previousWorking ? true : false;
if(typeChanged) awqLog('updateStatus: active type changed to:' + conf.commonData.activeType);
if(workingChanged) awqLog('updateStatus: Work status changed to:' + conf.commonData.working);
// If no work is being done for a while disable queue
stuckProcessingCounter = !conf.commonData.waiting && !workingChanged && !conf.commonData.working && conf.commonData.processing ? stuckProcessingCounter + 1 : 0;
if(stuckProcessingCounter > 30) {
awqLog('updateStatus: stuck in processing queue status? Disabling queue processing');
toggleProcessButton(false);
stuckProcessingCounter = 0;
playWorkCompleteSound();
}
}
async function executeNewTask() {
awqLog('executeNewTask: working='+conf.commonData.working + ' processing=' + conf.commonData.processing);
if(conf.commonData.working) return; // Already working on task
if(!conf.commonData.processing) return; // Not proicessing queue
if(conf.commonData.previousTaskStartTime) {
let timeSpent = Date.now() - conf.commonData.previousTaskStartTime;
awqLogPublishMsg(`Completed work on queue item after ${Math.floor(timeSpent/1000/60)} minutes ${Math.round((timeSpent-Math.floor(timeSpent/60000)*60000)/1000)} seconds `);
}
let queueItems = conf.ui.queueContainer.getElementsByTagName('div');
for(let i = 0; i < queueItems.length; i++) {
let itemQuantity = queueItems[i].querySelector('.AWQ-item-quantity');
let itemType = queueItems[i].querySelector('.AWQ-item-type').value;
if(itemQuantity.value > 0) {
awqLog('executeNewTask: found next work item with index ' + i + ', quantity ' + itemQuantity.value + ' and type ' + itemType);
await loadJson(queueItems[i].querySelector('.AWQ-item-JSON').value);
await clickStartButton(itemType);
conf.commonData.working = true;
itemQuantity.value = itemQuantity.value - 1;
itemQuantity.onchange();
awqLogPublishMsg(`Started working on ${itemType} queue item ${i+1} (${itemQuantity.value} more to go) `);
conf.commonData.previousTaskStartTime = Date.now();
await waitForTaskToComplete(itemType);
return;
}
}
conf.commonData.previousTaskStartTime = null;
awqLog('executeNewTask: No more tasks found');
toggleProcessButton(false); // No more tasks to process
playWorkCompleteSound();
}
async function executeAllNewTasks() {
while(conf.commonData.processing) {
await executeNewTask();
}
}
function playWorkCompleteSound() { if(conf.scriptSettings.notificationSound.value) c_audio_base64.play();}
function editSetting() {
let settingStorage = conf.ui.settingsStorage;
let settingIndex = settingStorage.selectedIndex;
let settingOption = settingStorage.options[settingIndex];
let settingKey = settingOption.innerHTML;
if(settingKey == c_defaultTextStoredSettings) return;
awqLog('editSettings: index'+settingIndex);
let editContainer = document.createElement('div');
editContainer.style.cssText = 'position: fixed;bottom: 0;left: 0;width: 100vw;height: 100vh;background:black;z-index: 9999;';
let txtInput = document.createElement('input');
txtInput.style.cssText = 'width: 100vw;height: 10vh;';
txtInput.title = "Name of the settins set (do not remove the prefix)";
editContainer.appendChild(txtInput);
let txtArea = document.createElement('textarea');
txtArea.style.cssText = 'width: 100vw;height: 80vh;';
txtArea.title = 'The set of settings i JSON format (Edit the value inside the "" but leave structure intact, an entire propertey: "id":"value" can also be removed if you do not want this setting set to make any changes to that setting)';
editContainer.appendChild(txtArea);
let editButton = document.createElement('button');
editButton.style.cssText = 'width: 50vw;height: 10vh;';
editButton.innerHTML = 'OK';
editButton.title = "Save changes";
editContainer.appendChild(editButton);
let resetButton = document.createElement('button');
resetButton.style.cssText = 'width: 50vw;height: 10vh;';
resetButton.innerHTML = 'Reset';
resetButton.title = "Revert changes";
resetButton.onclick = function() {
txtArea.value = conf.ui.settingsStorage.options[settingIndex].value;
txtInput.value = conf.ui.settingsStorage.options[settingIndex].text;
}
editContainer.appendChild(resetButton);
resetButton.onclick();
document.body.style.overflow = 'hidden';
document.body.appendChild(editContainer);
editButton.onclick = function() {
// Validate
if(txtInput.value.length < 1) {
alert('Name is missing');
return;
}
if(!isJsonString(txtArea.value)) {
alert('Value is invalid JSON');
return;
}
if(!['t2i-','i2i-','ext-'].includes(txtInput.value.substr(0,4))) {
alert('Name does not have valid prefix (t2i-, i2i-, ext-)');
return;
}
// Remove overlay
document.body.style.overflow = 'scroll';
document.body.removeChild(editContainer);
// Update data and refresh UI
awqLog('editSettings: updating '+ settingKey + (settingKey == txtInput.value ? '' : ' to ' + txtInput.value));
delete conf.savedSetting[settingKey];
conf.savedSetting[txtInput.value] = txtArea.value;
localStorage.awqSavedSetting = JSON.stringify(conf.savedSetting);
refreshSettings();
// Select option again
let optionToSelect = Array.from(settingStorage.options).find(item => item.text ===txtInput.value);
optionToSelect.selected = true;
};
}
function saveSettings() {
if(conf.ui.settingName.value.length < 1) {alert('Missing name'); return;}
if(conf.savedSetting.hasOwnProperty(conf.ui.settingName.value)) {alert('Duplicate name'); return;}
let settingSetName = conf.commonData.activeType + '-'+ conf.ui.settingName.value;
conf.savedSetting[settingSetName] = getValueJSON();
localStorage.awqSavedSetting = JSON.stringify(conf.savedSetting);
awqLogPublishMsg(`Saved new setting set ` + settingSetName);
refreshSettings();
}
function refreshSettings() {
awqLog('refreshSettings: saved settings:'+Object.keys(conf.savedSetting).length);
conf.ui.settingName.value = "";
conf.ui.settingsStorage.innerHTML = "";
for (let prop in conf.savedSetting) {
let newOption = document.createElement('option');
newOption.innerHTML = prop;
newOption.value = conf.savedSetting[prop]
conf.ui.settingsStorage.appendChild(newOption);
}
if(Object.keys(conf.savedSetting).length < 1) {
let blankOption = document.createElement('option');
blankOption.innerHTML = c_defaultTextStoredSettings;
blankOption.value = "";
conf.ui.settingsStorage.appendChild(blankOption);
}
}
async function loadSetting() {
if(conf.ui.settingsStorage.value.length < 1) return;
let itemName = conf.ui.settingsStorage.options[conf.ui.settingsStorage.selectedIndex].text;
let itemType = itemName.split('-')[0];
awqLog('loadSetting: ' + itemName);
await loadJson(conf.ui.settingsStorage.value);
}
function clearSetting() {
let ss = conf.ui.settingsStorage;
if(ss.value.length < 1) return;
awqLogPublishMsg(`Removed setting ` + ss.options[ss.selectedIndex].innerHTML);
delete conf.savedSetting[ss.options[ss.selectedIndex].innerHTML];
ss.removeChild(ss.options[ss.selectedIndex]);
localStorage.awqSavedSetting = JSON.stringify(conf.savedSetting);
if(ss.value.length < 1) refreshSettings();
}
function clickStartButton(p_type) {
const c_max_time_to_wait = 100;
let targetButton = conf[conf.commonData.activeType].controls.genrateButton.el;
awqLog(`clickStartButton: working ${conf.commonData.working} waiting ${conf.commonData.working} type ${p_type}`);
if(conf.commonData.working || conf.commonData.waiting) return;
targetButton.click();
conf.commonData.waiting = true;
return new Promise(resolve => {
let retryCount = 0;
let waitForSwitchInterval = setInterval(function() {
retryCount++;
if(retryCount >= c_max_time_to_wait) {
targetButton.click(); retryCount = 0;
awqLog(`Work has not started after ${c_max_time_to_wait/10} seconds, clicked again`);
}
if(!webUICurrentyWorkingOn(p_type)) return;
conf.commonData.waiting = false;
awqLog('clickStartButton: work has started');
clearInterval(waitForSwitchInterval);
resolve();
},c_wait_tick_duration);
});
}
function switchTabAndWait(p_type) {
if(p_type == conf.commonData.activeType) return;
awqLog('switchTabAndWait: ' + p_type);
conf.shadowDOM.root.querySelector(conf[p_type].controls.tabButton.sel).click(); // Using .el doesn't work
conf.commonData.waiting = true;
return new Promise(resolve => {
let startingTab = conf.commonData.activeType;
let waitForSwitchInterval = setInterval(function() {
if(conf.commonData.activeType !== p_type) return;
conf.commonData.waiting = false;
awqLogPublishMsg(`Switched active tab from ${startingTab} to ${conf.commonData.activeType}`);
clearInterval(waitForSwitchInterval);
resolve();
},c_wait_tick_duration);
});
}
function switchTabAndWaitUntilSwitched(p_targetTabName, p_tabConfig) {
awqLog('switchTabAndWaitUntilSwitched: p_target=' + p_targetTabName + ' p_config=' + p_tabConfig);
let targetTabConf = p_tabConfig.filter( (elem) => { return elem.name == p_targetTabName })[0];
function correctTabVisible() {
return conf.shadowDOM.root.querySelector(targetTabConf.containerSel).style.display == 'none' ? false : true;
}
if(correctTabVisible()) return;
conf.shadowDOM.root.querySelector(targetTabConf.buttonSel).click();
conf.commonData.waiting = true;
return new Promise(resolve => {
let waitForSwitchInterval = setInterval(function() {
if(!correctTabVisible()) return;
conf.commonData.waiting = false;
awqLog('switchTabAndWaitUntilSwitched: switch complete');
clearInterval(waitForSwitchInterval);
resolve();
},c_wait_tick_duration);
});
}
function forceGradioUIUpdate() {
const someCheckboxInputSelector = '#txt2img_subseed_show input';
document.querySelector(someCheckboxInputSelector).click();
document.querySelector(someCheckboxInputSelector).click();
}
function webUICurrentyWorkingOn(p_itemType) {
if(p_itemType == 'i2i') {
return conf.i2i.controls.skipButton.el.getAttribute('style') == 'display: block;';
} else if (p_itemType == 't2i') {
return conf.t2i.controls.skipButton.el.getAttribute('style') == 'display: block;';
} else {
return conf.ext.controls.loadingElement.el.querySelectorAll('.z-20').length > 0;
}
}
function waitForTaskToComplete(p_itemType) {
awqLog(`waitForTaskToComplete: Waiting to complete work for ${p_itemType}`);
conf.commonData.waiting = true;
return new Promise(resolve => {
let waitForCompleteInterval = setInterval(function() {
if(webUICurrentyWorkingOn(p_itemType)) return;
clearInterval(waitForCompleteInterval);
awqLog(`Work is complete for ${p_itemType}`);
conf.commonData.waiting = false;
conf.commonData.working = false;
resolve();
},c_wait_tick_duration);
});
}
function filterPrompt(p_prompt_text, p_neg) {
let newPromptText = p_prompt_text;
let promptFilter = conf.scriptSettings.promptFilter.value.length > 0 ?
JSON.parse(conf.scriptSettings.promptFilter.value) : [];
for(let i=0; i < promptFilter.length; i++) {
if(!promptFilter[i].hasOwnProperty('pattern') ||
!promptFilter[i].hasOwnProperty('flags') ||
!promptFilter[i].hasOwnProperty('replace')) continue;
let regEx = new RegExp(promptFilter[i].pattern, promptFilter[i].flags);
let tmpNewPromptText = newPromptText.replace(regEx, promptFilter[i].replace);
if(tmpNewPromptText !== newPromptText) {
let changesCount = levenshteinDist(newPromptText, tmpNewPromptText);
awqLogPublishMsg(`Filtered ${p_neg ? '(neg)' : ''}prompt with filter (${promptFilter[i].desc}), ${changesCount} char changes`);
awqLog(`Filtered from:<pre>${newPromptText}</pre>to:<pre>${tmpNewPromptText}</pre>`);
newPromptText = tmpNewPromptText;
}
}
return newPromptText;
}
function exportImport() {
let exportJSON = JSON.stringify({
savedSetting: conf.savedSetting,
currentQueue: conf.currentQueue,
scriptSettings: JSON.parse(localStorage.awqScriptSettings), // Use localstorage since it has filtered everything except values
});
let exportImportInput = document.getElementById('import-export-data-input');
let importJSON = exportImportInput.value;
if(importJSON.length < 1) {
awqLogPublishMsg(`Exported script data`);
exportImportInput.value = exportJSON;
exportImportInput.focus();
exportImportInput.select();
return;
}
if(!isJsonString(importJSON)) {
alert(`There is something wrong with the import data provided`);
return;
} else if(exportJSON == importJSON) {
alert(`The input data is the same as the current script data`);
} else {
awqLog('Data has changed');
let parsedImportJSON = JSON.parse(importJSON);
conf.savedSetting = parsedImportJSON.savedSetting;
conf.currentQueue = parsedImportJSON.currentQueue;
loadScriptSettings(parsedImportJSON.scriptSettings); // Load with loadScriptSettings to only replace values
localStorage.awqScriptSettings = JSON.stringify(parsedImportJSON.scriptSettings);
localStorage.awqSavedSetting = JSON.stringify(conf.savedSetting);
localStorage.awqCurrentQueue = JSON.stringify(conf.currentQueue);
location.reload();
}
}
function isJsonString(str) {
try {
JSON.parse(str);
} catch (e) {
return false;
}
return true;
}
function sleep(ms) {
return new Promise(resolve => setTimeout(resolve, ms));
}
function stringDiffCount(p_str1, p_str2) {
let count = 0;
for(let i = 0; i < p_str1.length; i++){
if(p_str1[i] === p_str2[i]){
continue;
};
count++;
};
return count;
};
function getCallStack() {
try {
throw new Error();
} catch (err) {
return err.stack.replace(/^getCallStack.*\n/, '');
}
}
function levenshteinDist(s1, s2) {
if (s1 === s2) {
return 0;
} else {
var s1_len = s1.length, s2_len = s2.length;
if (s1_len && s2_len) {
var i1 = 0, i2 = 0, a, b, c, c2, row = [];
while (i1 < s1_len) {
row[i1] = ++i1;
}
while (i2 < s2_len) {
c2 = s2.charCodeAt(i2);
a = i2;
++i2;
b = i2;
for (i1 = 0; i1 < s1_len; ++i1) {
c = a + (s1.charCodeAt(i1) === c2 ? 0 : 1);
a = row[i1];
b = b < a ? (b < c ? b + 1 : c) : (a < c ? a + 1 : c);
row[i1] = b;
}
}
return b;
} else {
return s1_len + s2_len;
}
}
};
function getValueJSON(p_type) {
let type = p_type || conf.commonData.activeType;
awqLog('getValueJSON: type=' + type);
let valueJSON = {type:type};
if(type == 'ext') { // Needs special saving since it's not an input but a tab switch
valueJSON.extrasMode = conf.ext.controls.extrasMode.filter((elem) => {
return conf.shadowDOM.root.querySelector(elem.containerSel).style.display == 'none' ? false : true
})[0].name;
valueJSON.extrasResizeMode = conf.ext.controls.extrasResizeMode.filter((elem) => {
return conf.shadowDOM.root.querySelector(elem.containerSel).style.display == 'none' ? false : true
})[0].name;
} else if(type == 'i2i') { // Needs special saving since it's not an input but a tab switch
valueJSON.i2iMode = conf.i2i.controls.i2iMode.filter((elem) => {
return conf.shadowDOM.root.querySelector(elem.containerSel).style.display == 'none' ? false : true
})[0].name;
} else if(type == 'iBrowser') {
return conf.extensions.iBrowser.functions.getValueJSON();
}
for (let prop in conf[type]) {
if(prop !== 'controls') {
try {
if(conf[type][prop].gradEl) {
valueJSON[prop] = getGradVal(conf[type][prop].gradEl);
} else if(conf[type][prop].el.type == 'fieldset') { // Radio buttons
valueJSON[prop] = conf[type][prop].el.querySelector('input:checked').value;
} else if(conf[type][prop].el.type == 'checkbox') {
valueJSON[prop] = conf[type][prop].el.checked;
} else { // Inputs, Textarea
valueJSON[prop] = conf[type][prop].el.value;
if(prop == 'prompt') valueJSON[prop] = filterPrompt(valueJSON[prop]);
if(prop == 'negPrompt' && conf.scriptSettings.promptFilterNegative.value) valueJSON[prop] = filterPrompt(valueJSON[prop], true);
}
} catch(e) {
awqLogPublishError(`Failed to retrieve settings for ${type} item ${prop} with error ${e.message}: <pre style="margin: 0;">${e.stack}</pre>`);
}
}
}
valueJSON.sdModelCheckpoint = getGradVal(conf.commonData.sdModelCheckpoint.gradEl);
return JSON.stringify(valueJSON);
}
async function loadJson(p_json) {
let inputJSONObject = JSON.parse(p_json);
let type = inputJSONObject.type ? inputJSONObject.type : conf.commonData.activeType;
let oldData = JSON.parse(getValueJSON(type));
let waitForThisContainer;
awqLog('loadJson: ' + type);
setGradVal(conf.commonData.sdModelCheckpoint.gradEl, inputJSONObject.sdModelCheckpoint);
if(conf.commonData.activeType != inputJSONObject.type) await switchTabAndWait(inputJSONObject.type); // Switch tab?
if(inputJSONObject.extrasResizeMode) await switchTabAndWaitUntilSwitched(inputJSONObject.extrasResizeMode, conf.ext.controls.extrasResizeMode); // Needs special loading since it's not an input but a tab switch
if(inputJSONObject.extrasMode) await switchTabAndWaitUntilSwitched(inputJSONObject.extrasMode, conf.ext.controls.extrasMode); // Needs special loading since it's not an input but a tab switch
if(inputJSONObject.i2iMode) await switchTabAndWaitUntilSwitched(inputJSONObject.i2iMode, conf.i2i.controls.i2iMode); // Needs special loading since it's not an input but a tab switch
let loadOutput = 'loadJson: loaded: ';
for (let prop in inputJSONObject) {
let triggerOnBaseElem = true;
if(['type','extrasMode','extrasResizeMode','sdModelCheckpoint', 'i2iMode'].includes(prop)) continue;
try {
if(oldData[prop] != inputJSONObject[prop]) loadOutput += `${prop}:${oldData[prop]}-->${inputJSONObject[prop]} | `;
if(conf[type][prop].el) {
if(conf[type][prop].el.type == 'fieldset') {
triggerOnBaseElem = false; // No need to trigger this on base element
conf[type][prop].el.querySelector('[value="' + inputJSONObject[prop] + '"]').checked = true;
triggerChange(conf[type][prop].el.querySelector('[value="' + inputJSONObject[prop] + '"]'));
} else if(conf[type][prop].el.type == 'select-one') { // Select
if(conf[type][prop].el.checked == inputJSONObject[prop]) triggerOnBaseElem = false; // Not needed
conf[type][prop].el.value = inputJSONObject[prop];
} else if(conf[type][prop].el.type == 'checkbox') {
if(conf[type][prop].el.checked == inputJSONObject[prop]) triggerOnBaseElem = false; // Prevent checbox getting toggled
conf[type][prop].el.checked = inputJSONObject[prop];
} else { // Input, Textarea
if(conf[type][prop].el.value == inputJSONObject[prop]) triggerOnBaseElem = false; // Fixes svelte error
conf[type][prop].el.value = inputJSONObject[prop];
}
if(conf[type][prop].el2) {
let triggerForSel2 = conf[type][prop].sel2.value != inputJSONObject[prop];
conf[type][prop].el2.value = inputJSONObject[prop];
if(triggerForSel2) triggerChange(conf[type][prop].el2);
}
}
if(conf[type][prop].gradEl) {
setGradVal(conf[type][prop].gradEl, inputJSONObject[prop]);
triggerOnBaseElem = false;
}
if(triggerOnBaseElem) triggerChange(conf[type][prop].el);
} catch(e) {
awqLogPublishError(`Failed to load settings for ${type} item ${prop} with error ${e.message}: <pre style="margin: 0;">${e.stack}</pre>`);
}
}
awqLog(loadOutput.replace(/\|\s$/, ''));
forceGradioUIUpdate();
}
function waitForElm(selector) {
return new Promise(resolve => {
if (document.querySelector(selector)) {
return resolve(document.querySelector(selector));
}
const observer = new MutationObserver(mutations => {
if (document.querySelector(selector)) {
resolve(document.querySelector(selector));
observer.disconnect();
}
});
observer.observe(document.body, {
childList: true,
subtree: true
});
});
}
function triggerChange(p_elem) {
let evt = document.createEvent("HTMLEvents");
evt.initEvent("change", false, true); // Needed for script to update subsection
p_elem.dispatchEvent(evt);
evt = document.createEvent("HTMLEvents");
evt.initEvent("input", false, true); // Needded for webui to register changed settings
p_elem.dispatchEvent(evt);
}
function findGradioComponentState(p_elem_id) {
return window.gradio_config.components.filter(comp => comp.props.elem_id == p_elem_id);
}
function findGradioComponentStateByLabel(p_elem_label) {
return window.gradio_config.components.filter(comp => comp.props.label == p_elem_label);
}
function getGradVal(p_grad_comp) {
return p_grad_comp.props.value;
}
function setGradVal(p_grad_comp,p_val) {
p_grad_comp.props.value = p_val;
}
})();