Merge branch 'beta' into installer_umamba

This commit is contained in:
cmdr2 2022-10-26 13:01:22 +05:30
commit 85de6dd52e
9 changed files with 438 additions and 262 deletions

View File

@ -91,7 +91,13 @@ Please use our [guide](https://github.com/cmdr2/stable-diffusion-ui/wiki/How-to-
# Bugs reports and code contributions welcome # Bugs reports and code contributions welcome
If there are any problems or suggestions, please feel free to ask on the [discord server](https://discord.com/invite/u9yhsFmEkB) or [file an issue](https://github.com/cmdr2/stable-diffusion-ui/issues). If there are any problems or suggestions, please feel free to ask on the [discord server](https://discord.com/invite/u9yhsFmEkB) or [file an issue](https://github.com/cmdr2/stable-diffusion-ui/issues).
Also, please feel free to submit a pull request, if you have any code contributions in mind. Join the [discord server](https://discord.com/invite/u9yhsFmEkB) for development-related discussions, and for helping other users. We could really use help on these aspects (click to view tasks that need your help):
* [User Interface](https://github.com/users/cmdr2/projects/1/views/1)
* [Engine](https://github.com/users/cmdr2/projects/3/views/1)
* [Installer](https://github.com/users/cmdr2/projects/4/views/1)
* [Documentation](https://github.com/users/cmdr2/projects/5/views/1)
If you have any code contributions in mind, please feel free to say Hi to us on the [discord server](https://discord.com/invite/u9yhsFmEkB). We use the Discord server for development-related discussions, and for helping users.
# Disclaimer # Disclaimer
The authors of this project are not responsible for any content generated using this interface. The authors of this project are not responsible for any content generated using this interface.

View File

@ -6,8 +6,8 @@
<link rel="icon" type="image/png" href="/media/images/favicon-32x32.png" sizes="32x32"> <link rel="icon" type="image/png" href="/media/images/favicon-32x32.png" sizes="32x32">
<link rel="stylesheet" href="/media/css/fonts.css?v=1"> <link rel="stylesheet" href="/media/css/fonts.css?v=1">
<link rel="stylesheet" href="/media/css/themes.css?v=1"> <link rel="stylesheet" href="/media/css/themes.css?v=1">
<link rel="stylesheet" href="/media/css/auto-save.css?v=1"> <link rel="stylesheet" href="/media/css/main.css?v=3">
<link rel="stylesheet" href="/media/css/main.css?v=1"> <link rel="stylesheet" href="/media/css/auto-save.css?v=2">
<link rel="stylesheet" href="/media/css/modifier-thumbnails.css?v=2"> <link rel="stylesheet" href="/media/css/modifier-thumbnails.css?v=2">
<link rel="stylesheet" href="/media/css/fontawesome-all.min.css?v=1"> <link rel="stylesheet" href="/media/css/fontawesome-all.min.css?v=1">
<link rel="stylesheet" href="/media/css/drawingboard.min.css"> <link rel="stylesheet" href="/media/css/drawingboard.min.css">
@ -43,11 +43,10 @@
<li><input id="use_cpu" name="use_cpu" type="checkbox"> <label for="use_cpu">Use CPU instead of GPU <small>(warning: this will be *very* slow)</small></label></li> <li><input id="use_cpu" name="use_cpu" type="checkbox"> <label for="use_cpu">Use CPU instead of GPU <small>(warning: this will be *very* slow)</small></label></li>
<li><input id="use_full_precision" name="use_full_precision" type="checkbox"> <label for="use_full_precision">Use full precision <small>(for GPU-only. warning: this will consume more VRAM)</small></label></li> <li><input id="use_full_precision" name="use_full_precision" type="checkbox"> <label for="use_full_precision">Use full precision <small>(for GPU-only. warning: this will consume more VRAM)</small></label></li>
<li> <li>
<input id="auto_save_settings" name="auto_save_settings" type="checkbox"> <input id="auto_save_settings" name="auto_save_settings" checked type="checkbox">
<label for="auto_save_settings">Automatically save settings <small>(settings restored on browser load)</small></label> <label for="auto_save_settings">Automatically save settings <small>(settings restored on browser load)</small></label>
<br/> <br/>
<button id="configureSettingsSaveBtn">Configure</button> <button id="configureSettingsSaveBtn">Configure</button>
<button id="restoreDefaultSettingsBtn">Restore Defaults</button>
</li> </li>
<!-- <li><input id="allow_nsfw" name="allow_nsfw" type="checkbox"> <label for="allow_nsfw">Allow NSFW Content (You confirm you are above 18 years of age)</label></li> --> <!-- <li><input id="allow_nsfw" name="allow_nsfw" type="checkbox"> <label for="allow_nsfw">Allow NSFW Content (You confirm you are above 18 years of age)</label></li> -->
<br/> <br/>
@ -104,7 +103,14 @@
<div class="line-separator">&nbsp;</div> <div class="line-separator">&nbsp;</div>
<div id="editor-settings" class="panel-box settings-box"> <div id="editor-settings" class="panel-box settings-box">
<h4 class="collapsible">Image Settings</h4> <h4 class="collapsible">
Image Settings
<i id="reset-image-settings" class="fa-solid fa-arrow-rotate-left">
<span class="simple-tooltip right">
Reset Image Settings
</span>
</i>
</h4>
<ul id="editor-settings-entries" class="collapsible-content"> <ul id="editor-settings-entries" class="collapsible-content">
<li><table> <li><table>
<tr><b class="settings-subheader">Image Settings</b></tr> <tr><b class="settings-subheader">Image Settings</b></tr>
@ -232,9 +238,8 @@
<div> <div>
<span id="save-settings-config-close-btn">X</span> <span id="save-settings-config-close-btn">X</span>
<h1>Save Settings Configuration</h1> <h1>Save Settings Configuration</h1>
<p>Select which settings should be saved and reloaded when restarting the browser</p> <p>Select which settings should be remembered when restarting the browser</p>
<table id="save-settings-config-table"> <table id="save-settings-config-table">
<tr><th>Setting</th><th></th><th>Default value</th></tr>
</table> </table>
</div> </div>
</div> </div>
@ -264,19 +269,19 @@
</body> </body>
<script src="media/js/plugins.js?v=1"></script> <script src="media/js/plugins.js?v=1"></script>
<script src="media/js/utils.js?v=3"></script> <script src="media/js/utils.js?v=4"></script>
<script src="media/js/inpainting-editor.js?v=1"></script> <script src="media/js/inpainting-editor.js?v=1"></script>
<script src="media/js/image-modifiers.js?v=3"></script> <script src="media/js/image-modifiers.js?v=3"></script>
<script src="media/js/auto-save.js?v=1"></script> <script src="media/js/auto-save.js?v=2"></script>
<script src="media/js/main.js?v=4"></script> <script src="media/js/main.js?v=5"></script>
<script src="media/js/themes.js?v=1"></script> <script src="media/js/themes.js?v=2"></script>
<script> <script>
async function init() { async function init() {
await loadModifiers() await initSettings()
await getModels()
await getDiskPath() await getDiskPath()
await getAppConfig() await getAppConfig()
await getModels() await loadModifiers()
await initSettings()
await loadUIPlugins() await loadUIPlugins()
setInterval(healthCheck, HEALTH_PING_INTERVAL * 1000) setInterval(healthCheck, HEALTH_PING_INTERVAL * 1000)

View File

@ -1,23 +1,26 @@
/* Auto-Settings Styling */ /* Auto-Settings Styling */
#auto_save_settings ~ button {
margin: 5px;
}
#auto_save_settings:not(:checked) ~ button { #auto_save_settings:not(:checked) ~ button {
display: none; display: none;
} }
#save-settings-config { #save-settings-config {
position: fixed; position: absolute;
background: rgba(32, 33, 36, 50%); background: rgba(32, 33, 36, 50%);
top: 0px; top: 0px;
left: 0px; left: 0px;
width: 100vw; right: 0px;
height: 100vh; bottom: 0px;
z-index: 1000; z-index: 1000;
} }
#save-settings-config > div { #save-settings-config > div {
background: var(--background-color2); background: var(--background-color3);
max-width: 600px; max-width: 600px;
margin: auto; margin: auto;
margin-top: 100px; margin-top: 50px;
border-radius: 6px; border-radius: 6px;
padding: 30px; padding: 30px;
text-align: center; text-align: center;
@ -27,6 +30,11 @@
margin: auto; margin: auto;
} }
#save-settings-config-table th {
padding-top: 15px;
padding-bottom: 5px;
}
#save-settings-config-table td:first-child, #save-settings-config-table td:first-child,
#save-settings-config-table th:first-child { #save-settings-config-table th:first-child {
float: right; float: right;
@ -47,3 +55,20 @@
padding: 10px; padding: 10px;
transform: translate(50%, -50%) scaleX(130%); transform: translate(50%, -50%) scaleX(130%);
} }
#reset-image-settings {
cursor: pointer;
float: right;
padding: 8px;
opacity: 1;
transition: opacity 0.5;
}
.collapsible:not(.active) #reset-image-settings {
display: none;
}
#reset-image-settings.hidden {
opacity: 0;
pointer-events: none;
}

View File

@ -1,7 +1,10 @@
* { * {
font-family: Work Sans, Verdana, Geneva, sans-serif; font-family: Work Sans, Verdana, Geneva, sans-serif;
box-sizing: border-box; box-sizing: border-box;
transition: background 0.5s, color 0.5s, background-color 0.5s; }
html {
position: relative;
} }
body { body {
@ -573,3 +576,68 @@ input::file-selector-button {
#promptsFromFileBtn { #promptsFromFileBtn {
font-size: 9pt; font-size: 9pt;
} }
#reset-image-settings {
position: relative;
transform: translateY(-13%);
}
.simple-tooltip {
border-radius: 3px;
font-weight: bold;
font-size: 16px;
background-color: var(--background-color3);
visibility: hidden;
opacity: 0;
position: absolute;
white-space: nowrap;
padding: 8px 12px;
transition: 0.3s all;
pointer-events: none;
}
@media (hover: hover) {
:hover > .simple-tooltip {
opacity: 1;
visibility: visible;
}
}
/* position specific */
.simple-tooltip.right {
right: 0px;
top: 50%;
transform: translate(calc(100% - 15%), -50%);
}
:hover > .simple-tooltip.right {
transform: translate(100%, -50%);
}
.simple-tooltip.top {
top: 0px;
left: 50%;
transform: translate(-50%, calc(-100% + 15%));
}
:hover > .simple-tooltip.top {
transform: translate(-50%, -100%);
}
.simple-tooltip.left {
left: 0px;
top: 50%;
transform: translate(calc(-100% + 15%), -50%);
}
:hover > .simple-tooltip.left {
transform: translate(-100%, -50%);
}
.simple-tooltip.bottom {
bottom: 0px;
left: 50%;
transform: translate(-50%, calc(100% - 15%));
}
:hover > .simple-tooltip.bottom {
transform: translate(-50%, 100%);
}

View File

@ -1,14 +1,13 @@
// Saving settings // Saving settings
let saveSettingsCheckbox = document.getElementById("auto_save_settings")
let saveSettingsConfigTable = document.getElementById("save-settings-config-table") let saveSettingsConfigTable = document.getElementById("save-settings-config-table")
let saveSettingsConfigOverlay = document.getElementById("save-settings-config") let saveSettingsConfigOverlay = document.getElementById("save-settings-config")
let resetImageSettingsButton = document.getElementById("reset-image-settings")
const SETTINGS_KEY = "user_settings" const SETTINGS_KEY = "user_settings_v2"
var SETTINGS_SHOULD_SAVE_MAP = {} // key=id. dict initialized in initSettings
var SETTINGS_VALUES = {} // key=id. dict initialized in initSettings const SETTINGS = {} // key=id. dict initialized in initSettings. { element, default, value, ignore }
var SETTINGS_DEFAULTS = {} // key=id. dict initialized in initSettings const SETTINGS_IDS_LIST = [
var SETTINGS_TO_SAVE = [] // list of elements initialized by initSettings "prompt",
var SETTINGS_IDS_LIST = [
"seed", "seed",
"random_seed", "random_seed",
"num_outputs_total", "num_outputs_total",
@ -18,8 +17,8 @@ var SETTINGS_IDS_LIST = [
"width", "width",
"height", "height",
"num_inference_steps", "num_inference_steps",
"guidance_scale_slider", "guidance_scale",
"prompt_strength_slider", "prompt_strength",
"output_format", "output_format",
"negative_prompt", "negative_prompt",
"stream_image_progress", "stream_image_progress",
@ -29,29 +28,70 @@ var SETTINGS_IDS_LIST = [
"upscale_model", "upscale_model",
"preview-image", "preview-image",
"modifier-card-size-slider", "modifier-card-size-slider",
"theme" "theme",
"save_to_disk",
"diskPath",
"sound_toggle",
"turbo",
"use_cpu",
"use_full_precision",
"auto_save_settings"
]
const IGNORE_BY_DEFAULT = [
"prompt"
]
const SETTINGS_SECTIONS = [ // gets the "keys" property filled in with an ordered list of settings in this section via initSettings
{ id: "editor-inputs", name: "Prompt" },
{ id: "editor-settings", name: "Image Settings" },
{ id: "system-settings", name: "System Settings" },
{ id: "container", name: "Other" }
] ]
async function initSettings() { async function initSettings() {
SETTINGS_IDS_LIST.forEach(id => SETTINGS_TO_SAVE.push(document.getElementById(id))) SETTINGS_IDS_LIST.forEach(id => {
SETTINGS_TO_SAVE.forEach(element => { var element = document.getElementById(id)
SETTINGS_SHOULD_SAVE_MAP[element.id] = true var label = document.querySelector(`label[for='${element.id}']`)
SETTINGS_DEFAULTS[element.id] = getSetting(element) SETTINGS[id] = {
SETTINGS_VALUES[element.id] = getSetting(element) key: id,
element: element,
label: getSettingLabel(element),
default: getSetting(element),
value: getSetting(element),
ignore: IGNORE_BY_DEFAULT.includes(id)
}
element.addEventListener("input", settingChangeHandler) element.addEventListener("input", settingChangeHandler)
element.addEventListener("change", settingChangeHandler) element.addEventListener("change", settingChangeHandler)
}) })
var unsorted_settings_ids = [...SETTINGS_IDS_LIST]
SETTINGS_SECTIONS.forEach(section => {
var name = section.name
var element = document.getElementById(section.id)
var children = Array.from(element.querySelectorAll(unsorted_settings_ids.map(id => `#${id}`).join(",")))
section.keys = []
children.forEach(e => {
section.keys.push(e.id)
})
unsorted_settings_ids = unsorted_settings_ids.filter(id => children.find(e => e.id == id) == undefined)
})
loadSettings() loadSettings()
fillSaveSettingsConfigTable()
} }
function getSetting(element) { function getSetting(element) {
if (typeof element === "string" || element instanceof String) {
element = SETTINGS[element].element
}
if (element.type == "checkbox") { if (element.type == "checkbox") {
return element.checked return element.checked
} }
return element.value return element.value
} }
function setSetting(element, value) { function setSetting(element, value) {
if (typeof element === "string" || element instanceof String) {
element = SETTINGS[element].element
}
SETTINGS[element.id].value = value
if (getSetting(element) == value) { if (getSetting(element) == value) {
return // no setting necessary return // no setting necessary
} }
@ -66,45 +106,51 @@ function setSetting(element, value) {
} }
function saveSettings() { function saveSettings() {
localStorage.setItem(SETTINGS_KEY, JSON.stringify({ var saved_settings = Object.values(SETTINGS).map(setting => {
values: SETTINGS_VALUES, return {
should_save: SETTINGS_SHOULD_SAVE_MAP key: setting.key,
})) value: setting.value,
ignore: setting.ignore
}
})
localStorage.setItem(SETTINGS_KEY, JSON.stringify(saved_settings))
} }
var CURRENTLY_LOADING_SETTINGS = false var CURRENTLY_LOADING_SETTINGS = false
function loadSettings() { function loadSettings() {
if (!saveSettingsCheckbox.checked) { var saved_settings_text = localStorage.getItem(SETTINGS_KEY)
if (saved_settings_text) {
var saved_settings = JSON.parse(saved_settings_text)
if (saved_settings.find(s => s.key == "auto_save_settings").value == false) {
setSetting("auto_save_settings", false)
return return
} }
var saved_settings = JSON.parse(localStorage.getItem(SETTINGS_KEY))
if (saved_settings) {
var values = saved_settings.values
var should_save = saved_settings.should_save
CURRENTLY_LOADING_SETTINGS = true CURRENTLY_LOADING_SETTINGS = true
SETTINGS_TO_SAVE.forEach(element => { saved_settings.map(saved_setting => {
if (element.id in values) { var setting = SETTINGS[saved_setting.key]
SETTINGS_SHOULD_SAVE_MAP[element.id] = should_save[element.id] setting.ignore = saved_setting.ignore
SETTINGS_VALUES[element.id] = values[element.id] if (!setting.ignore) {
if (SETTINGS_SHOULD_SAVE_MAP[element.id]) { setting.value = saved_setting.value
setSetting(element, SETTINGS_VALUES[element.id]) setSetting(setting.element, setting.value)
}
} }
}) })
CURRENTLY_LOADING_SETTINGS = false CURRENTLY_LOADING_SETTINGS = false
} }
else { else {
CURRENTLY_LOADING_SETTINGS = true
tryLoadOldSettings();
CURRENTLY_LOADING_SETTINGS = false
saveSettings() saveSettings()
} }
} }
document.querySelector('#restoreDefaultSettingsBtn').addEventListener('click', loadDefaultSettings) function loadDefaultSettingsSection(section_id) {
function loadDefaultSettings() {
CURRENTLY_LOADING_SETTINGS = true CURRENTLY_LOADING_SETTINGS = true
SETTINGS_TO_SAVE.forEach(element => { var section = SETTINGS_SECTIONS.find(s => s.id == section_id);
SETTINGS_VALUES[element.id] = SETTINGS_DEFAULTS[element.id] section.keys.forEach(key => {
setSetting(element, SETTINGS_VALUES[element.id]) var setting = SETTINGS[key];
setting.value = setting.default
setSetting(setting.element, setting.value)
}) })
CURRENTLY_LOADING_SETTINGS = false CURRENTLY_LOADING_SETTINGS = false
saveSettings() saveSettings()
@ -114,41 +160,59 @@ function settingChangeHandler(event) {
if (!CURRENTLY_LOADING_SETTINGS) { if (!CURRENTLY_LOADING_SETTINGS) {
var element = event.target var element = event.target
var value = getSetting(element) var value = getSetting(element)
if (value != SETTINGS_VALUES[element.id]) { if (value != SETTINGS[element.id].value) {
SETTINGS_VALUES[element.id] = value SETTINGS[element.id].value = value
saveSettings() saveSettings()
} }
} }
} }
function getSettingLabel(element) {
var labelElement = document.querySelector(`label[for='${element.id}']`)
var label = labelElement?.innerText || element.id
var truncate_length = 30
if (label.includes(" (")) {
label = label.substring(0, label.indexOf(" ("))
}
if (label.length > truncate_length) {
label = label.substring(0, truncate_length - 3) + "..."
}
label = label.replace("", "")
label = label.replace("", "")
return label
}
function fillSaveSettingsConfigTable() { function fillSaveSettingsConfigTable() {
SETTINGS_TO_SAVE.forEach(element => { saveSettingsConfigTable.textContent = ""
var caption = element.id SETTINGS_SECTIONS.forEach(section => {
var label = document.querySelector(`label[for='${element.id}']`) var section_row = `<tr><th>${section.name}</th><td></td></tr>`
if (label) { saveSettingsConfigTable.insertAdjacentHTML("beforeend", section_row)
caption = label.innerText section.keys.forEach(key => {
var truncate_length = 25 var setting = SETTINGS[key]
if (caption.length > truncate_length) { var element = setting.element
caption = caption.substring(0, truncate_length - 3) + "..."
}
}
var default_value = SETTINGS_DEFAULTS[element.id]
var checkbox_id = `shouldsave_${element.id}` var checkbox_id = `shouldsave_${element.id}`
var is_checked = SETTINGS_SHOULD_SAVE_MAP[element.id] ? "checked" : "" var is_checked = setting.ignore ? "" : "checked"
var newrow = `<tr><td><label for="${checkbox_id}">${caption}</label></td><td><input id="${checkbox_id}" name="${checkbox_id}" ${is_checked} type="checkbox" ></td><td><small>(${default_value})</small></td></tr>` var value = setting.value
var value_truncate_length = 30
if ((typeof value === "string" || value instanceof String) && value.length > value_truncate_length) {
value = value.substring(0, value_truncate_length - 3) + "..."
}
var newrow = `<tr><td><label for="${checkbox_id}">${setting.label}</label></td><td><input id="${checkbox_id}" name="${checkbox_id}" ${is_checked} type="checkbox" ></td><td><small>(${value})</small></td></tr>`
saveSettingsConfigTable.insertAdjacentHTML("beforeend", newrow) saveSettingsConfigTable.insertAdjacentHTML("beforeend", newrow)
var checkbox = document.getElementById(checkbox_id) var checkbox = document.getElementById(checkbox_id)
checkbox.addEventListener("input", event => { checkbox.addEventListener("input", event => {
SETTINGS_SHOULD_SAVE_MAP[element.id] = checkbox.checked setting.ignore = !checkbox.checked
saveSettings() saveSettings()
}) })
}) })
})
} }
document.getElementById("save-settings-config-close-btn").addEventListener('click', () => { document.getElementById("save-settings-config-close-btn").addEventListener('click', () => {
saveSettingsConfigOverlay.style.display = 'none' saveSettingsConfigOverlay.style.display = 'none'
}) })
document.getElementById("configureSettingsSaveBtn").addEventListener('click', () => { document.getElementById("configureSettingsSaveBtn").addEventListener('click', () => {
fillSaveSettingsConfigTable()
saveSettingsConfigOverlay.style.display = 'block' saveSettingsConfigOverlay.style.display = 'block'
}) })
saveSettingsConfigOverlay.addEventListener('click', (event) => { saveSettingsConfigOverlay.addEventListener('click', (event) => {
@ -156,3 +220,66 @@ saveSettingsConfigOverlay.addEventListener('click', (event) => {
saveSettingsConfigOverlay.style.display = 'none' saveSettingsConfigOverlay.style.display = 'none'
} }
}) })
document.getElementById("save-settings-config-close-btn").addEventListener('click', () => {
saveSettingsConfigOverlay.style.display = 'none'
})
resetImageSettingsButton.addEventListener('click', event => {
loadDefaultSettingsSection("editor-settings");
event.stopPropagation()
})
function tryLoadOldSettings() {
console.log("Loading old user settings")
// load v1 auto-save.js settings
var old_map = {
"guidance_scale_slider": "guidance_scale",
"prompt_strength_slider": "prompt_strength"
}
var settings_key_v1 = "user_settings"
var saved_settings_text = localStorage.getItem(settings_key_v1)
if (saved_settings_text) {
var saved_settings = JSON.parse(saved_settings_text)
Object.keys(saved_settings.should_save).forEach(key => {
key = key in old_map ? old_map[key] : key
SETTINGS[key].ignore = !saved_settings.should_save[key]
});
Object.keys(saved_settings.values).forEach(key => {
key = key in old_map ? old_map[key] : key
var setting = SETTINGS[key]
if (!setting.ignore) {
setting.value = saved_settings.values[key]
setSetting(setting.element, setting.value)
}
});
localStorage.removeItem(settings_key_v1)
}
// load old individually stored items
var individual_settings_map = { // maps old localStorage-key to new SETTINGS-key
"soundEnabled": "sound_toggle",
"saveToDisk": "save_to_disk",
"useCPU": "use_cpu",
"useFullPrecision": "use_full_precision",
"useTurboMode": "turbo",
"diskPath": "diskPath",
"useFaceCorrection": "use_face_correction",
"useUpscaling": "use_upscale",
"showOnlyFilteredImage": "show_only_filtered_image",
"streamImageProgress": "stream_image_progress",
"outputFormat": "output_format",
"autoSaveSettings": "auto_save_settings",
};
Object.keys(individual_settings_map).forEach(localStorageKey => {
var localStorageValue = localStorage.getItem(localStorageKey);
if (localStorageValue !== null) {
var setting = SETTINGS[individual_settings_map[localStorageKey]]
if (setting.element.type == "checkbox" && (typeof localStorageValue === "string" || localStorageValue instanceof String)) {
localStorageValue = localStorageValue == "true"
}
setting.value = localStorageValue
setSetting(setting.element, setting.value)
localStorage.removeItem(localStorageKey);
}
})
}

View File

@ -1,19 +1,4 @@
"use strict" // Opt in to a restricted variant of JavaScript "use strict" // Opt in to a restricted variant of JavaScript
const SOUND_ENABLED_KEY = "soundEnabled"
const SAVE_TO_DISK_KEY = "saveToDisk"
const USE_CPU_KEY = "useCPU"
const USE_FULL_PRECISION_KEY = "useFullPrecision"
const USE_TURBO_MODE_KEY = "useTurboMode"
const DISK_PATH_KEY = "diskPath"
const ADVANCED_PANEL_OPEN_KEY = "advancedPanelOpen"
const MODIFIERS_PANEL_OPEN_KEY = "modifiersPanelOpen"
const NEGATIVE_PROMPT_PANEL_OPEN_KEY = "negativePromptPanelOpen"
const USE_FACE_CORRECTION_KEY = "useFaceCorrection"
const USE_UPSCALING_KEY = "useUpscaling"
const SHOW_ONLY_FILTERED_IMAGE_KEY = "showOnlyFilteredImage"
const STREAM_IMAGE_PROGRESS_KEY = "streamImageProgress"
const OUTPUT_FORMAT_KEY = "outputFormat"
const AUTO_SAVE_SETTINGS_KEY = "autoSaveSettings"
const HEALTH_PING_INTERVAL = 5 // seconds const HEALTH_PING_INTERVAL = 5 // seconds
const MAX_INIT_IMAGE_DIMENSION = 768 const MAX_INIT_IMAGE_DIMENSION = 768
@ -44,8 +29,6 @@ let useCPUField = document.querySelector('#use_cpu')
let useFullPrecisionField = document.querySelector('#use_full_precision') let useFullPrecisionField = document.querySelector('#use_full_precision')
let saveToDiskField = document.querySelector('#save_to_disk') let saveToDiskField = document.querySelector('#save_to_disk')
let diskPathField = document.querySelector('#diskPath') let diskPathField = document.querySelector('#diskPath')
let autoSaveSettingsField = document.querySelector('#auto_save_settings')
let themeField = document.querySelector('#theme')
// let allowNSFWField = document.querySelector("#allow_nsfw") // let allowNSFWField = document.querySelector("#allow_nsfw")
let useBetaChannelField = document.querySelector("#use_beta_channel") let useBetaChannelField = document.querySelector("#use_beta_channel")
let promptStrengthSlider = document.querySelector('#prompt_strength_slider') let promptStrengthSlider = document.querySelector('#prompt_strength_slider')
@ -77,7 +60,6 @@ let clearAllPreviewsBtn = document.querySelector("#clear-all-previews")
// let maskImagePreviewContainer = document.querySelector('#mask_preview_container') // let maskImagePreviewContainer = document.querySelector('#mask_preview_container')
// let maskImageClearBtn = document.querySelector('#mask_clear') // let maskImageClearBtn = document.querySelector('#mask_clear')
let maskSetting = document.querySelector('#enable_mask') let maskSetting = document.querySelector('#enable_mask')
let negativePromptPanelHandle = document.querySelector('#negative_prompt_handle')
let imagePreview = document.querySelector("#preview") let imagePreview = document.querySelector("#preview")
@ -93,8 +75,6 @@ let soundToggle = document.querySelector('#sound_toggle')
let serverStatusColor = document.querySelector('#server-status-color') let serverStatusColor = document.querySelector('#server-status-color')
let serverStatusMsg = document.querySelector('#server-status-msg') let serverStatusMsg = document.querySelector('#server-status-msg')
let advancedPanelHandle = document.querySelector("#editor-settings .collapsible")
let modifiersPanelHandle = document.querySelector("#editor-modifiers .collapsible")
document.querySelector('.drawing-board-control-navigation-back').innerHTML = '<i class="fa-solid fa-rotate-left"></i>' document.querySelector('.drawing-board-control-navigation-back').innerHTML = '<i class="fa-solid fa-rotate-left"></i>'
document.querySelector('.drawing-board-control-navigation-forward').innerHTML = '<i class="fa-solid fa-rotate-right"></i>' document.querySelector('.drawing-board-control-navigation-forward').innerHTML = '<i class="fa-solid fa-rotate-right"></i>'
@ -111,15 +91,6 @@ let bellPending = false
let taskQueue = [] let taskQueue = []
let currentTask = null let currentTask = null
function getLocalStorageItem(key, fallback) {
let item = localStorage.getItem(key)
if (item === null) {
return fallback
}
return item
}
function getLocalStorageBoolItem(key, fallback) { function getLocalStorageBoolItem(key, fallback) {
let item = localStorage.getItem(key) let item = localStorage.getItem(key)
if (item === null) { if (item === null) {
@ -142,63 +113,11 @@ function handleStringSettingChange(key) {
} }
function isSoundEnabled() { function isSoundEnabled() {
return getLocalStorageBoolItem(SOUND_ENABLED_KEY, true) return getSetting("sound_toggle")
}
function isFaceCorrectionEnabled() {
return getLocalStorageBoolItem(USE_FACE_CORRECTION_KEY, false)
}
function isUpscalingEnabled() {
return getLocalStorageBoolItem(USE_UPSCALING_KEY, false)
}
function isShowOnlyFilteredImageEnabled() {
return getLocalStorageBoolItem(SHOW_ONLY_FILTERED_IMAGE_KEY, true)
}
function isSaveToDiskEnabled() {
return getLocalStorageBoolItem(SAVE_TO_DISK_KEY, false)
}
function isUseCPUEnabled() {
return getLocalStorageBoolItem(USE_CPU_KEY, false)
}
function isUseFullPrecisionEnabled() {
return getLocalStorageBoolItem(USE_FULL_PRECISION_KEY, false)
}
function isAutoSaveSettingsEnabled() {
return getLocalStorageBoolItem(AUTO_SAVE_SETTINGS_KEY, true)
}
function isUseTurboModeEnabled() {
return getLocalStorageBoolItem(USE_TURBO_MODE_KEY, true)
} }
function getSavedDiskPath() { function getSavedDiskPath() {
return getLocalStorageItem(DISK_PATH_KEY, '') return getSetting("diskPath")
}
function isAdvancedPanelOpenEnabled() {
return getLocalStorageBoolItem(ADVANCED_PANEL_OPEN_KEY, false)
}
function isModifiersPanelOpenEnabled() {
return getLocalStorageBoolItem(MODIFIERS_PANEL_OPEN_KEY, false)
}
function isNegativePromptPanelOpenEnabled() {
return getLocalStorageBoolItem(NEGATIVE_PROMPT_PANEL_OPEN_KEY, false)
}
function isStreamImageProgressEnabled() {
return getLocalStorageBoolItem(STREAM_IMAGE_PROGRESS_KEY, false)
}
function getOutputFormat() {
return getLocalStorageItem(OUTPUT_FORMAT_KEY, 'jpeg')
} }
function setStatus(statusType, msg, msgType) { function setStatus(statusType, msg, msgType) {
@ -261,11 +180,11 @@ function logError(msg, res, outputMsg) {
function playSound() { function playSound() {
const audio = new Audio('/media/ding.mp3') const audio = new Audio('/media/ding.mp3')
audio.volume = 0.2 audio.volume = 0.2
var promise = audio.play(); var promise = audio.play()
if (promise !== undefined) { if (promise !== undefined) {
promise.then(_ => {}).catch(error => { promise.then(_ => {}).catch(error => {
console.warn("browser blocked autoplay"); console.warn("browser blocked autoplay")
}); })
} }
} }
@ -522,7 +441,7 @@ async function doMakeImage(task) {
if (typeof renderRequest?.stream !== 'string') { if (typeof renderRequest?.stream !== 'string') {
console.log('Endpoint response: ', renderRequest) console.log('Endpoint response: ', renderRequest)
throw new Error(renderRequest.detail || 'Endpoint response does not contains a response stream url.') throw new Error(renderRequest?.detail || 'Endpoint response does not contains a response stream url.')
} }
task['taskStatusLabel'].innerText = "Waiting" task['taskStatusLabel'].innerText = "Waiting"
@ -759,7 +678,7 @@ async function checkTasks() {
const genSeeds = Boolean(typeof task.reqBody.seed !== 'number' || (task.reqBody.seed === task.seed && task.numOutputsTotal > 1)) const genSeeds = Boolean(typeof task.reqBody.seed !== 'number' || (task.reqBody.seed === task.seed && task.numOutputsTotal > 1))
const startSeed = task.reqBody.seed || task.seed const startSeed = task.reqBody.seed || task.seed
for (let i = 0; i < task.batchCount; i++) { for (let i = 0; i < task.batchCount; i++) {
let newTask = task; let newTask = task
if (task.batchCount > 1) { if (task.batchCount > 1) {
// Each output render batch needs it's own task instance to avoid altering the other runs after they are completed. // Each output render batch needs it's own task instance to avoid altering the other runs after they are completed.
newTask = Object.assign({}, task, { newTask = Object.assign({}, task, {
@ -1097,40 +1016,6 @@ stopImageBtn.addEventListener('click', async function() {
await stopAllTasks() await stopAllTasks()
}) })
soundToggle.addEventListener('click', handleBoolSettingChange(SOUND_ENABLED_KEY))
soundToggle.checked = isSoundEnabled()
saveToDiskField.checked = isSaveToDiskEnabled()
diskPathField.disabled = !saveToDiskField.checked
useFaceCorrectionField.addEventListener('click', handleBoolSettingChange(USE_FACE_CORRECTION_KEY))
useFaceCorrectionField.checked = isFaceCorrectionEnabled()
useUpscalingField.checked = isUpscalingEnabled()
upscaleModelField.disabled = !useUpscalingField.checked
showOnlyFilteredImageField.addEventListener('click', handleBoolSettingChange(SHOW_ONLY_FILTERED_IMAGE_KEY))
showOnlyFilteredImageField.checked = isShowOnlyFilteredImageEnabled()
useCPUField.addEventListener('click', handleBoolSettingChange(USE_CPU_KEY))
useCPUField.checked = isUseCPUEnabled()
useFullPrecisionField.addEventListener('click', handleBoolSettingChange(USE_FULL_PRECISION_KEY))
useFullPrecisionField.checked = isUseFullPrecisionEnabled()
autoSaveSettingsField.addEventListener('click', handleBoolSettingChange(AUTO_SAVE_SETTINGS_KEY))
autoSaveSettingsField.checked = isAutoSaveSettingsEnabled()
turboField.addEventListener('click', handleBoolSettingChange(USE_TURBO_MODE_KEY))
turboField.checked = isUseTurboModeEnabled()
streamImageProgressField.addEventListener('click', handleBoolSettingChange(STREAM_IMAGE_PROGRESS_KEY))
streamImageProgressField.checked = isStreamImageProgressEnabled()
outputFormatField.addEventListener('change', handleStringSettingChange(OUTPUT_FORMAT_KEY))
outputFormatField.value = getOutputFormat()
diskPathField.addEventListener('change', handleStringSettingChange(DISK_PATH_KEY))
widthField.addEventListener('change', onDimensionChange) widthField.addEventListener('change', onDimensionChange)
heightField.addEventListener('change', onDimensionChange) heightField.addEventListener('change', onDimensionChange)
@ -1161,37 +1046,18 @@ function onDimensionChange() {
saveToDiskField.addEventListener('click', function(e) { saveToDiskField.addEventListener('click', function(e) {
diskPathField.disabled = !this.checked diskPathField.disabled = !this.checked
handleBoolSettingChange(SAVE_TO_DISK_KEY)(e)
}) })
useUpscalingField.addEventListener('click', function(e) { useUpscalingField.addEventListener('click', function(e) {
upscaleModelField.disabled = !this.checked upscaleModelField.disabled = !this.checked
handleBoolSettingChange(USE_UPSCALING_KEY)(e)
}) })
function setPanelOpen(panelHandle) {
let panelContents = panelHandle.nextElementSibling
panelHandle.classList.add('active')
panelContents.style.display = 'block'
}
if (isAdvancedPanelOpenEnabled()) {
setPanelOpen(advancedPanelHandle)
}
if (isModifiersPanelOpenEnabled()) {
setPanelOpen(modifiersPanelHandle)
}
if (isNegativePromptPanelOpenEnabled()) {
setPanelOpen(negativePromptPanelHandle)
}
makeImageBtn.addEventListener('click', makeImage) makeImageBtn.addEventListener('click', makeImage)
function updateGuidanceScale() { function updateGuidanceScale() {
guidanceScaleField.value = guidanceScaleSlider.value / 10 guidanceScaleField.value = guidanceScaleSlider.value / 10
guidanceScaleField.dispatchEvent(new Event("change"))
} }
function updateGuidanceScaleSlider() { function updateGuidanceScaleSlider() {
@ -1211,6 +1077,7 @@ updateGuidanceScale()
function updatePromptStrength() { function updatePromptStrength() {
promptStrengthField.value = promptStrengthSlider.value / 100 promptStrengthField.value = promptStrengthSlider.value / 100
promptStrengthField.dispatchEvent(new Event("change"))
} }
function updatePromptStrengthSlider() { function updatePromptStrengthSlider() {
@ -1274,10 +1141,12 @@ async function getAppConfig() {
async function getModels() { async function getModels() {
try { try {
var model_setting_key = "stable_diffusion_model"
var selectedModel = SETTINGS[model_setting_key].value
let res = await fetch('/get/models') let res = await fetch('/get/models')
const models = await res.json() const models = await res.json()
let activeModel = models['active'] // let activeModel = models['active']
let modelOptions = models['options'] let modelOptions = models['options']
let stableDiffusionOptions = modelOptions['stable-diffusion'] let stableDiffusionOptions = modelOptions['stable-diffusion']
@ -1286,13 +1155,19 @@ async function getModels() {
modelOption.value = modelName modelOption.value = modelName
modelOption.innerText = modelName modelOption.innerText = modelName
if (modelName === activeModel['stable-diffusion']) { if (modelName === selectedModel) {
modelOption.selected = true modelOption.selected = true
} }
stableDiffusionModelField.appendChild(modelOption) stableDiffusionModelField.appendChild(modelOption)
}) })
// TODO: set default for model here too
SETTINGS[model_setting_key].default = stableDiffusionOptions[0]
if (getSetting(model_setting_key) == '' || SETTINGS[model_setting_key].value == '') {
setSetting(model_setting_key, stableDiffusionOptions[0])
}
console.log('get models response', models) console.log('get models response', models)
} catch (e) { } catch (e) {
console.log('get models error', e) console.log('get models error', e)
@ -1394,19 +1269,15 @@ promptsFromFileSelector.addEventListener('change', function() {
async function getDiskPath() { async function getDiskPath() {
try { try {
let diskPath = getSavedDiskPath() var diskPath = getSetting("diskPath")
if (diskPath == '' || diskPath == undefined || diskPath == "undefined") {
if (diskPath !== '') {
diskPathField.value = diskPath
return
}
let res = await fetch('/get/output_dir') let res = await fetch('/get/output_dir')
if (res.status === 200) { if (res.status === 200) {
res = await res.json() res = await res.json()
res = res.output_dir res = res.output_dir
document.querySelector('#diskPath').value = res setSetting("diskPath", res)
}
} }
} catch (e) { } catch (e) {
console.log('error fetching output dir path', e) console.log('error fetching output dir path', e)

View File

@ -1,3 +1,4 @@
const themeField = document.getElementById("theme");
var DEFAULT_THEME = {}; var DEFAULT_THEME = {};
var THEMES = []; // initialized in initTheme from data in css var THEMES = []; // initialized in initTheme from data in css
@ -36,6 +37,15 @@ function initTheme() {
new_option.innerText = theme.name; new_option.innerText = theme.name;
themeField.appendChild(new_option); themeField.appendChild(new_option);
}); });
// setup the style transitions a second after app initializes, so initial style is instant
setTimeout(() => {
var body = document.querySelector("body");
var style = document.createElement('style');
style.innerHTML = "* { transition: background 0.5s, color 0.5s, background-color 0.5s; }";
body.appendChild(style);
}, 1000);
} }
initTheme(); initTheme();
@ -46,6 +56,8 @@ function themeFieldChanged() {
body.classList.remove(...THEMES.map(theme => theme.key)); body.classList.remove(...THEMES.map(theme => theme.key));
body.classList.add(theme_key); body.classList.add(theme_key);
//
body.style = ""; body.style = "";
var theme = THEMES.find(t => t.key == theme_key); var theme = THEMES.find(t => t.key == theme_key);
if (theme) { if (theme) {

View File

@ -14,13 +14,54 @@ function getNextSibling(elem, selector) {
} }
} }
function createCollapsibles(node) {
if (!node) {
node = document /* Panel Stuff */
// true = open
var COLLAPSIBLES_INITIALIZED = false;
const COLLAPSIBLES_KEY = "collapsibles";
const COLLAPSIBLE_PANELS = []; // filled in by createCollapsibles with all the elements matching .collapsible
// on-init call this for any panels that are marked open
function toggleCollapsible(element) {
var collapsibleHeader = element.querySelector(".collapsible");
var handle = element.querySelector(".collapsible-handle");
collapsibleHeader.classList.toggle("active")
let content = getNextSibling(collapsibleHeader, '.collapsible-content')
if (content.style.display === "block") {
content.style.display = "none"
handle.innerHTML = '&#x2795;' // plus
} else {
content.style.display = "block"
handle.innerHTML = '&#x2796;' // minus
} }
if (COLLAPSIBLES_INITIALIZED && COLLAPSIBLE_PANELS.includes(element)) {
saveCollapsibles()
}
}
function saveCollapsibles() {
var values = {}
COLLAPSIBLE_PANELS.forEach(element => {
var value = element.querySelector(".collapsible").className.indexOf("active") !== -1
values[element.id] = value
})
localStorage.setItem(COLLAPSIBLES_KEY, JSON.stringify(values))
}
function createCollapsibles(node) {
var save = false
if (!node) {
node = document
save = true
}
let collapsibles = node.querySelectorAll(".collapsible") let collapsibles = node.querySelectorAll(".collapsible")
collapsibles.forEach(function(c) { collapsibles.forEach(function(c) {
if (save && c.parentElement.id) {
COLLAPSIBLE_PANELS.push(c.parentElement)
}
let handle = document.createElement('span') let handle = document.createElement('span')
handle.className = 'collapsible-handle' handle.className = 'collapsible-handle'
@ -32,28 +73,49 @@ function createCollapsibles(node) {
c.insertBefore(handle, c.firstChild) c.insertBefore(handle, c.firstChild)
c.addEventListener('click', function() { c.addEventListener('click', function() {
this.classList.toggle("active") toggleCollapsible(c.parentElement)
let content = getNextSibling(this, '.collapsible-content') })
if (content.style.display === "block") { })
content.style.display = "none" if (save) {
handle.innerHTML = '&#x2795;' // plus var saved = localStorage.getItem(COLLAPSIBLES_KEY)
} else { if (!saved) {
content.style.display = "block" saved = tryLoadOldCollapsibles();
handle.innerHTML = '&#x2796;' // minus
} }
if (!saved) {
saveCollapsibles()
saved = localStorage.getItem(COLLAPSIBLES_KEY)
}
var values = JSON.parse(saved)
COLLAPSIBLE_PANELS.forEach(element => {
var value = element.querySelector(".collapsible").className.indexOf("active") !== -1
if (values[element.id] != value) {
toggleCollapsible(element)
}
})
COLLAPSIBLES_INITIALIZED = true
}
}
if (this == advancedPanelHandle) { function tryLoadOldCollapsibles() {
let state = (content.style.display === 'block' ? 'true' : 'false') var old_map = {
localStorage.setItem(ADVANCED_PANEL_OPEN_KEY, state) "advancedPanelOpen": "editor-settings",
} else if (this == modifiersPanelHandle) { "modifiersPanelOpen": "editor-modifiers",
let state = (content.style.display === 'block' ? 'true' : 'false') "negativePromptPanelOpen": "editor-inputs-prompt"
localStorage.setItem(MODIFIERS_PANEL_OPEN_KEY, state) };
} else if (this == negativePromptPanelHandle) { if (localStorage.getItem(Object.keys(old_map)[0])) {
let state = (content.style.display === 'block' ? 'true' : 'false') var result = {};
localStorage.setItem(NEGATIVE_PROMPT_PANEL_OPEN_KEY, state) Object.keys(old_map).forEach(key => {
var value = localStorage.getItem(key);
if (value !== null) {
result[old_map[key]] = value == true || value == "true"
localStorage.removeItem(key)
} }
}) });
}) result = JSON.stringify(result)
localStorage.setItem(COLLAPSIBLES_KEY, result)
return result
}
return null;
} }
function permute(arr) { function permute(arr) {

View File

@ -85,9 +85,9 @@ def read_root():
@app.get('/ping') # Get server and optionally session status. @app.get('/ping') # Get server and optionally session status.
def ping(session_id:str=None): def ping(session_id:str=None):
if not task_manager.render_thread.is_alive(): # Render thread is dead. if not task_manager.render_thread.is_alive(): # Render thread is dead.
if task_manager.current_state_error: raise HTTPException(status_code=500, detail=str(current_state_error)) if task_manager.current_state_error: raise HTTPException(status_code=500, detail=str(task_manager.current_state_error))
raise HTTPException(status_code=500, detail='Render thread is dead.') raise HTTPException(status_code=500, detail='Render thread is dead.')
if task_manager.current_state_error and not isinstance(task_manager.current_state_error, StopAsyncIteration): raise HTTPException(status_code=500, detail=str(current_state_error)) if task_manager.current_state_error and not isinstance(task_manager.current_state_error, StopAsyncIteration): raise HTTPException(status_code=500, detail=str(task_manager.current_state_error))
# Alive # Alive
response = {'status': str(task_manager.current_state)} response = {'status': str(task_manager.current_state)}
if session_id: if session_id:
@ -223,7 +223,7 @@ def setConfig(config):
config_json_path = os.path.join(CONFIG_DIR, 'config.json') config_json_path = os.path.join(CONFIG_DIR, 'config.json')
with open(config_json_path, 'w', encoding='utf-8') as f: with open(config_json_path, 'w', encoding='utf-8') as f:
return json.dump(config, f) return json.dump(config, f)
except: except Exception as e:
print(str(e)) print(str(e))
print(traceback.format_exc()) print(traceback.format_exc())