/** * Enum of parameter types * @readonly * @enum {string} */ var ParameterType = { checkbox: "checkbox", select: "select", select_multiple: "select_multiple", custom: "custom", }; /** * JSDoc style * @typedef {object} Parameter * @property {string} id * @property {ParameterType} type * @property {string} label * @property {?string} note * @property {number|boolean|string} default */ /** @type {Array.} */ var PARAMETERS = [ { id: "theme", type: ParameterType.select, label: "Theme", default: "theme-default", note: "customize the look and feel of the ui", options: [ // Note: options expanded dynamically { value: "theme-default", label: "Default" } ], icon: "fa-palette" }, { id: "save_to_disk", type: ParameterType.checkbox, label: "Auto-Save Images", note: "automatically saves images to the specified location", icon: "fa-download", default: false, }, { id: "diskPath", type: ParameterType.custom, label: "Save Location", render: (parameter) => { return `` } }, { id: "sound_toggle", type: ParameterType.checkbox, label: "Enable Sound", note: "plays a sound on task completion", icon: "fa-volume-low", default: true, }, { id: "process_order_toggle", type: ParameterType.checkbox, label: "Process newest jobs first", note: "reverse the normal processing order", default: false, }, { id: "ui_open_browser_on_start", type: ParameterType.checkbox, label: "Open browser on startup", note: "starts the default browser on startup", icon: "fa-window-restore", default: true, }, { id: "turbo", type: ParameterType.checkbox, label: "Turbo Mode", note: "generates images faster, but uses an additional 1 GB of GPU memory", icon: "fa-forward", default: true, }, { id: "use_cpu", type: ParameterType.checkbox, label: "Use CPU (not GPU)", note: "warning: this will be *very* slow", icon: "fa-microchip", default: false, }, { id: "auto_pick_gpus", type: ParameterType.checkbox, label: "Automatically pick the GPUs (experimental)", default: false, }, { id: "use_gpus", type: ParameterType.select_multiple, label: "GPUs to use (experimental)", note: "to process in parallel", default: false, }, { id: "use_full_precision", type: ParameterType.checkbox, label: "Use Full Precision", note: "for GPU-only. warning: this will consume more VRAM", icon: "fa-crosshairs", default: false, }, { id: "auto_save_settings", type: ParameterType.checkbox, label: "Auto-Save Settings", note: "restores settings on browser load", icon: "fa-gear", default: true, }, { id: "confirm_dangerous_actions", type: ParameterType.checkbox, label: "Confirm dangerous actions", note: "Actions that might lead to data loss must either be clicked with the shift key pressed, or confirmed in an 'Are you sure?' dialog", icon: "fa-check-double", default: true, }, { id: "listen_to_network", type: ParameterType.checkbox, label: "Make Stable Diffusion available on your network", note: "Other devices on your network can access this web page", icon: "fa-network-wired", default: true, }, { id: "listen_port", type: ParameterType.custom, label: "Network port", note: "Port that this server listens to. The '9000' part in 'http://localhost:9000'", icon: "fa-anchor", render: (parameter) => { return `` } }, { id: "test_sd2", type: ParameterType.checkbox, label: "Test SD 2.0", note: "Experimental! High memory usage! GPU-only! Not the final version! Please restart the program after changing this.", icon: "fa-fire", default: false, }, { id: "use_beta_channel", type: ParameterType.checkbox, label: "Beta channel", note: "Get the latest features immediately (but could be less stable). Please restart the program after changing this.", icon: "fa-fire", default: false, }, ]; function getParameterSettingsEntry(id) { let parameter = PARAMETERS.filter(p => p.id === id) if (parameter.length === 0) { return } return parameter[0].settingsEntry } function getParameterElement(parameter) { switch (parameter.type) { case ParameterType.checkbox: var is_checked = parameter.default ? " checked" : ""; return `` case ParameterType.select: case ParameterType.select_multiple: var options = (parameter.options || []).map(option => ``).join("") var multiple = (parameter.type == ParameterType.select_multiple ? 'multiple' : '') return `` case ParameterType.custom: return parameter.render(parameter) default: console.error(`Invalid type for parameter ${parameter.id}`); return "ERROR: Invalid Type" } } let parametersTable = document.querySelector("#system-settings .parameters-table") /* fill in the system settings popup table */ function initParameters() { PARAMETERS.forEach(parameter => { var element = getParameterElement(parameter) var note = parameter.note ? `${parameter.note}` : ""; var icon = parameter.icon ? `` : ""; var newrow = document.createElement('div') newrow.innerHTML = `
${icon}
${note}
${element}
` parametersTable.appendChild(newrow) parameter.settingsEntry = newrow }) } initParameters() let turboField = document.querySelector('#turbo') let useCPUField = document.querySelector('#use_cpu') let autoPickGPUsField = document.querySelector('#auto_pick_gpus') let useGPUsField = document.querySelector('#use_gpus') let useFullPrecisionField = document.querySelector('#use_full_precision') let saveToDiskField = document.querySelector('#save_to_disk') let diskPathField = document.querySelector('#diskPath') let listenToNetworkField = document.querySelector("#listen_to_network") let listenPortField = document.querySelector("#listen_port") let testSD2Field = document.querySelector("#test_sd2") let useBetaChannelField = document.querySelector("#use_beta_channel") let uiOpenBrowserOnStartField = document.querySelector("#ui_open_browser_on_start") let confirmDangerousActionsField = document.querySelector("#confirm_dangerous_actions") let saveSettingsBtn = document.querySelector('#save-system-settings-btn') async function changeAppConfig(configDelta) { try { let res = await fetch('/app_config', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(configDelta) }) res = await res.json() console.log('set config status response', res) } catch (e) { console.log('set config status error', e) } } async function getAppConfig() { try { let res = await fetch('/get/app_config') const config = await res.json() if (config.update_branch === 'beta') { useBetaChannelField.checked = true document.querySelector("#updateBranchLabel").innerText = "(beta)" } if (config.ui && config.ui.open_browser_on_start === false) { uiOpenBrowserOnStartField.checked = false } if ('test_sd2' in config) { testSD2Field.checked = config['test_sd2'] } let testSD2SettingEntry = getParameterSettingsEntry('test_sd2') testSD2SettingEntry.style.display = (config.update_branch === 'beta' ? '' : 'none') if (config.net && config.net.listen_to_network === false) { listenToNetworkField.checked = false } if (config.net && config.net.listen_port !== undefined) { listenPortField.value = config.net.listen_port } console.log('get config status response', config) } catch (e) { console.log('get config status error', e) } } saveToDiskField.addEventListener('change', function(e) { diskPathField.disabled = !this.checked }) function getCurrentRenderDeviceSelection() { let selectedGPUs = $('#use_gpus').val() if (useCPUField.checked && !autoPickGPUsField.checked) { return 'cpu' } if (autoPickGPUsField.checked || selectedGPUs.length == 0) { return 'auto' } return selectedGPUs.join(',') } useCPUField.addEventListener('click', function() { let gpuSettingEntry = getParameterSettingsEntry('use_gpus') let autoPickGPUSettingEntry = getParameterSettingsEntry('auto_pick_gpus') if (this.checked) { gpuSettingEntry.style.display = 'none' autoPickGPUSettingEntry.style.display = 'none' autoPickGPUsField.setAttribute('data-old-value', autoPickGPUsField.checked) autoPickGPUsField.checked = false } else if (useGPUsField.options.length >= MIN_GPUS_TO_SHOW_SELECTION) { gpuSettingEntry.style.display = '' autoPickGPUSettingEntry.style.display = '' let oldVal = autoPickGPUsField.getAttribute('data-old-value') if (oldVal === null || oldVal === undefined) { // the UI started with CPU selected by default autoPickGPUsField.checked = true } else { autoPickGPUsField.checked = (oldVal === 'true') } gpuSettingEntry.style.display = (autoPickGPUsField.checked ? 'none' : '') } }) useGPUsField.addEventListener('click', function() { let selectedGPUs = $('#use_gpus').val() autoPickGPUsField.checked = (selectedGPUs.length === 0) }) autoPickGPUsField.addEventListener('click', function() { if (this.checked) { $('#use_gpus').val([]) } let gpuSettingEntry = getParameterSettingsEntry('use_gpus') gpuSettingEntry.style.display = (this.checked ? 'none' : '') }) async function getDiskPath() { try { var diskPath = getSetting("diskPath") if (diskPath == '' || diskPath == undefined || diskPath == "undefined") { let res = await fetch('/get/output_dir') if (res.status === 200) { res = await res.json() res = res.output_dir setSetting("diskPath", res) } } } catch (e) { console.log('error fetching output dir path', e) } } function setDeviceInfo(devices) { let cpu = devices.all.cpu.name let allGPUs = Object.keys(devices.all).filter(d => d != 'cpu') let activeGPUs = Object.keys(devices.active) function ID_TO_TEXT(d) { let info = devices.all[d] if ("mem_free" in info && "mem_total" in info) { return `${info.name} (${d}) (${info.mem_free.toFixed(1)}Gb free / ${info.mem_total.toFixed(1)} Gb total)` } else { return `${info.name} (${d}) (no memory info)` } } allGPUs = allGPUs.map(ID_TO_TEXT) activeGPUs = activeGPUs.map(ID_TO_TEXT) let systemInfoEl = document.querySelector('#system-info') systemInfoEl.querySelector('#system-info-cpu').innerText = cpu systemInfoEl.querySelector('#system-info-gpus-all').innerHTML = allGPUs.join('
') systemInfoEl.querySelector('#system-info-rendering-devices').innerHTML = activeGPUs.join('
') } function setHostInfo(hosts) { let port = listenPortField.value hosts = hosts.map(addr => `http://${addr}:${port}/`).map(url => `
${url}
`) document.querySelector('#system-info-server-hosts').innerHTML = hosts.join('') } async function getSystemInfo() { try { const res = await SD.getSystemInfo() let devices = res['devices'] let allDeviceIds = Object.keys(devices['all']).filter(d => d !== 'cpu') let activeDeviceIds = Object.keys(devices['active']).filter(d => d !== 'cpu') if (activeDeviceIds.length === 0) { useCPUField.checked = true } if (allDeviceIds.length < MIN_GPUS_TO_SHOW_SELECTION || useCPUField.checked) { let gpuSettingEntry = getParameterSettingsEntry('use_gpus') gpuSettingEntry.style.display = 'none' let autoPickGPUSettingEntry = getParameterSettingsEntry('auto_pick_gpus') autoPickGPUSettingEntry.style.display = 'none' } if (allDeviceIds.length === 0) { useCPUField.checked = true useCPUField.disabled = true // no compatible GPUs, so make the CPU mandatory } autoPickGPUsField.checked = (devices['config'] === 'auto') useGPUsField.innerHTML = '' allDeviceIds.forEach(device => { let deviceName = devices['all'][device]['name'] let deviceOption = `` useGPUsField.insertAdjacentHTML('beforeend', deviceOption) }) if (autoPickGPUsField.checked) { let gpuSettingEntry = getParameterSettingsEntry('use_gpus') gpuSettingEntry.style.display = 'none' } else { $('#use_gpus').val(activeDeviceIds) } setDeviceInfo(devices) setHostInfo(res['hosts']) } catch (e) { console.log('error fetching devices', e) } } saveSettingsBtn.addEventListener('click', function() { if (listenPortField.value == '') { alert('The network port field must not be empty.') return } if (listenPortField.value < 1 || listenPortField.value > 65535) { alert('The network port must be a number from 1 to 65535') return } let updateBranch = (useBetaChannelField.checked ? 'beta' : 'main') changeAppConfig({ 'render_devices': getCurrentRenderDeviceSelection(), 'update_branch': updateBranch, 'ui_open_browser_on_start': uiOpenBrowserOnStartField.checked, 'listen_to_network': listenToNetworkField.checked, 'listen_port': listenPortField.value, 'test_sd2': testSD2Field.checked }) saveSettingsBtn.classList.add('active') asyncDelay(300).then(() => saveSettingsBtn.classList.remove('active')) })