diff --git a/CHANGELOG.md b/CHANGELOG.md
index 54c37fc1d..68a8c3e0c 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,11 +1,12 @@
# Change Log for SD.Next
-## Update for 2023-08-22
+## Update for 2023-08-24
- general:
- all system and image paths are now relative by default
- fix extra networks previews
- add settings validation when performing load/save
+ - settings tab in ui now shows settings that are changed from default values
- optimize model hashing
- add cli param `--skip-all` that skips all installer checks
use at personal discretion, but it can be useful for bulk deployments
diff --git a/extensions-builtin/sd-webui-agent-scheduler b/extensions-builtin/sd-webui-agent-scheduler
index 86857553e..e8d02c7e1 160000
--- a/extensions-builtin/sd-webui-agent-scheduler
+++ b/extensions-builtin/sd-webui-agent-scheduler
@@ -1 +1 @@
-Subproject commit 86857553e715c8aa50f503dc2da320da8964e701
+Subproject commit e8d02c7e119a890175ab2bf6649acdbcd743b861
diff --git a/extensions-builtin/sd-webui-controlnet b/extensions-builtin/sd-webui-controlnet
index fb000270b..9af9d6b94 160000
--- a/extensions-builtin/sd-webui-controlnet
+++ b/extensions-builtin/sd-webui-controlnet
@@ -1 +1 @@
-Subproject commit fb000270beef677d2caedbaecb0ef923eaeef2eb
+Subproject commit 9af9d6b94329092266af5be5c26b40d2e3fe96e9
diff --git a/extensions-builtin/stable-diffusion-webui-images-browser b/extensions-builtin/stable-diffusion-webui-images-browser
index 15c337914..a48f65350 160000
--- a/extensions-builtin/stable-diffusion-webui-images-browser
+++ b/extensions-builtin/stable-diffusion-webui-images-browser
@@ -1 +1 @@
-Subproject commit 15c337914d86040d50864a37488f447cd233b2bc
+Subproject commit a48f65350dcf33f367cb5a79eee95999f5d692c0
diff --git a/javascript/settings.js b/javascript/settings.js
new file mode 100644
index 000000000..33411dc0d
--- /dev/null
+++ b/javascript/settings.js
@@ -0,0 +1,168 @@
+const monitoredOpts = [
+ { sd_model_checkpoint: null },
+ {
+ sd_backend: () => {
+ gradioApp().getElementById('refresh_sd_model_checkpoint')?.click();
+ },
+ },
+];
+
+function updateOpts(json_string) {
+ const settings_data = JSON.parse(json_string);
+ for (const op of monitoredOpts) {
+ const key = Object.keys(op)[0];
+ const callback = op[key];
+ if (opts[key] && opts[key] !== settings_data.values[key]) {
+ console.log('updateOpts', key, opts[key], settings_data.values[key]);
+ if (callback) callback();
+ }
+ }
+ opts = settings_data.values;
+ opts_metadata = settings_data.metadata;
+ Object.entries(opts_metadata).forEach(([opt, meta]) => {
+ if (!opts_tabs[meta.tab_name]) opts_tabs[meta.tab_name] = {};
+ if (!opts_tabs[meta.tab_name].unsaved_keys) opts_tabs[meta.tab_name].unsaved_keys = new Set();
+ if (!opts_tabs[meta.tab_name].saved_keys) opts_tabs[meta.tab_name].saved_keys = new Set();
+ if (!meta.is_stored) opts_tabs[meta.tab_name].unsaved_keys.add(opt);
+ else opts_tabs[meta.tab_name].saved_keys.add(opt);
+ });
+}
+
+function showAllSettings() {
+ // Try to ensure that the show all settings tab is opened by clicking on its tab button
+ const tab_dirty_indicator = gradioApp().getElementById('modification_indicator_show_all_pages');
+ if (tab_dirty_indicator && tab_dirty_indicator.nextSibling) {
+ tab_dirty_indicator.nextSibling.click();
+ }
+ gradioApp().querySelectorAll('#settings > .tab-content > .tabitem').forEach((elem) => {
+ if (elem.id === 'settings_tab_licenses' || elem.id === 'settings_show_all_pages') return;
+ elem.style.display = 'block';
+ });
+}
+
+function markIfModified(setting_name, value) {
+ const elem = gradioApp().getElementById(`modification_indicator_${setting_name}`);
+ if (elem == null) return;
+ // Use JSON.stringify to compare nested objects (e.g. arrays for checkbox-groups)
+ const previous_value_json = JSON.stringify(opts[setting_name]);
+ const changed_value = JSON.stringify(value) !== previous_value_json;
+ if (changed_value) elem.title = `click to revert to previous value: ${previous_value_json}`;
+
+ const is_unsaved = opts_metadata[setting_name].is_stored;
+ if (is_unsaved) elem.title = 'custom value';
+ elem.disabled = !(is_unsaved || changed_value);
+ elem.classList.toggle('changed', changed_value);
+ elem.classList.toggle('unsaved', is_unsaved);
+
+ const { tab_name } = opts_metadata[setting_name];
+ if (!opts_tabs[tab_name].changed) opts_tabs[tab_name].changed = new Set();
+ const changed_items = opts_tabs[tab_name].changed;
+ if (changed_value) changed_items.add(setting_name);
+ else changed_items.delete(setting_name);
+ const unsaved = opts_tabs[tab_name].unsaved_keys;
+ const saved = opts_tabs[tab_name].saved_keys;
+
+ // Set the indicator on the tab nav element
+ const tab_nav_indicator = gradioApp().getElementById(`modification_indicator_${tab_name}`);
+ tab_nav_indicator.disabled = (changed_items.size === 0) && (unsaved.size === 0);
+ tab_nav_indicator.title = '';
+ tab_nav_indicator.classList.toggle('changed', changed_items.size > 0);
+ tab_nav_indicator.classList.toggle('unsaved', saved.size > 0);
+ if (changed_items.size > 0) tab_nav_indicator.title += `click to reset ${changed_items.size} unapplied changes in this tab\n`;
+ if (saved.size > 0) tab_nav_indicator.title += `${saved.size} custom values\n${unsaved.size} default values}`;
+}
+
+onAfterUiUpdate(async () => {
+ if (Object.keys(opts).length !== 0) return;
+ const json_elem = gradioApp().getElementById('settings_json');
+ if (!json_elem) return;
+ json_elem.parentElement.style.display = 'none';
+ const textarea = json_elem.querySelector('textarea');
+ const jsdata = textarea.value;
+ updateOpts(jsdata);
+ executeCallbacks(optionsChangedCallbacks);
+ register_drag_drop();
+
+ Object.defineProperty(textarea, 'value', {
+ set(newValue) {
+ const valueProp = Object.getOwnPropertyDescriptor(HTMLTextAreaElement.prototype, 'value');
+ const oldValue = valueProp.get.call(textarea);
+ valueProp.set.call(textarea, newValue);
+ if (oldValue !== newValue) updateOpts(textarea.value);
+ executeCallbacks(optionsChangedCallbacks);
+ },
+ get() {
+ const valueProp = Object.getOwnPropertyDescriptor(HTMLTextAreaElement.prototype, 'value');
+ return valueProp.get.call(textarea);
+ },
+ });
+
+ const settings_search = gradioApp().querySelectorAll('#settings_search > label > textarea')[0];
+ settings_search.oninput = (e) => {
+ setTimeout(() => {
+ showAllSettings();
+ gradioApp().querySelectorAll('#tab_settings .tabitem').forEach((section) => {
+ section.querySelectorAll('.dirtyable').forEach((setting) => {
+ const visible = setting.innerText.toLowerCase().includes(e.target.value.toLowerCase()) || setting.id.toLowerCase().includes(e.target.value.toLowerCase());
+ if (!visible) {
+ setting.style.display = 'none';
+ } else {
+ setting.style.removeProperty('display');
+ }
+ });
+ });
+ }, 50);
+ };
+});
+
+onOptionsChanged(() => {
+ const elem = gradioApp().getElementById('sd_checkpoint_hash');
+ const sd_checkpoint_hash = opts.sd_checkpoint_hash || '';
+ const shorthash = sd_checkpoint_hash.substring(0, 10);
+
+ if (elem && elem.textContent !== shorthash) {
+ elem.textContent = shorthash;
+ elem.title = sd_checkpoint_hash;
+ elem.href = `https://google.com/search?q=${sd_checkpoint_hash}`;
+ }
+});
+
+onOptionsChanged(() => {
+ const setting_elems = gradioApp().querySelectorAll('#settings [id^="setting_"]');
+ setting_elems.forEach((elem) => {
+ const setting_name = elem.id.replace('setting_', '');
+ markIfModified(setting_name, opts[setting_name]);
+ });
+});
+
+onUiLoaded(() => {
+ const tab_nav_element = gradioApp().querySelector('#settings > .tab-nav');
+ const tab_nav_buttons = gradioApp().querySelectorAll('#settings > .tab-nav > button');
+ const tab_elements = gradioApp().querySelectorAll('#settings > div:not(.tab-nav)');
+ const observer = new MutationObserver((mutations) => {
+ const show_all_pages_dummy = gradioApp().getElementById('settings_show_all_pages');
+ if (show_all_pages_dummy.style.display === 'none') { return; }
+ function mutation_on_style(mut) {
+ return mut.type === 'attributes' && mut.attributeName === 'style';
+ }
+ if (mutations.some(mutation_on_style)) {
+ showAllSettings();
+ }
+ });
+ const tab_content_wrapper = document.createElement('div');
+ tab_content_wrapper.className = 'tab-content';
+ tab_nav_element.parentElement.insertBefore(tab_content_wrapper, tab_nav_element.nextSibling);
+
+ tab_elements.forEach((elem, index) => {
+ // Move the modification indicator to the toplevel tab button
+ const tab_name = elem.id.replace('settings_', '');
+ const indicator = gradioApp().getElementById(`modification_indicator_${tab_name}`);
+ tab_nav_element.insertBefore(indicator, tab_nav_buttons[index]);
+
+ // Add the tab content to the wrapper
+ tab_content_wrapper.appendChild(elem);
+
+ // Add the mutation observer to the tab element
+ observer.observe(elem, { attributes: true, attributeFilter: ['style'] });
+ });
+});
diff --git a/javascript/style.css b/javascript/style.css
index 37ae7d136..ae8f529b3 100644
--- a/javascript/style.css
+++ b/javascript/style.css
@@ -128,11 +128,11 @@ div#extras_scale_to_tab div.form{ flex-direction: row; }
#settings .block.gradio-checkbox { margin: 0; width: auto; }
#settings .dirtyable { display: grid; grid-template-columns: .3em auto; gap: .5em; }
#settings .dirtyable.hidden { display: none; }
-#settings .modification-indicator { width: 100%; height: 100%; border-radius: 1em !important; padding: 0; }
+#settings .modification-indicator { width: 100%; height: 100%; border-radius: 1em !important; padding: 0; width: 0 }
#settings .modification-indicator:disabled { visibility: hidden; }
-#settings .modification-indicator.unsaved { background: var(--color-accent-soft); }
-#settings .modification-indicator.changed { background: var(--color-accent); }
-#settings .modification-indicator.changed.unsaved { background-image: linear-gradient(var(--color-accent) 25%, var(--color-accent-soft) 75%); }
+#settings .modification-indicator.unsaved { background: var(--color-accent-soft); width: 4px; }
+#settings .modification-indicator.changed { background: var(--color-accent); width: 4px; }
+#settings .modification-indicator.changed.unsaved { background-image: linear-gradient(var(--color-accent) 25%, var(--color-accent-soft) 75%); width: 4px; }
#settings_result { margin: 0 1.2em; }
/* live preview */
diff --git a/javascript/ui.js b/javascript/ui.js
index 7e3870ec7..5100125e9 100644
--- a/javascript/ui.js
+++ b/javascript/ui.js
@@ -232,46 +232,6 @@ function register_drag_drop() {
});
}
-const monitoredOpts = [
- { sd_model_checkpoint: null },
- {
- sd_backend: () => {
- gradioApp().getElementById('refresh_sd_model_checkpoint')?.click();
- },
- },
-];
-
-function updateOpts(json_string) {
- const settings_data = JSON.parse(json_string);
- for (const op of monitoredOpts) {
- const key = Object.keys(op)[0];
- const callback = op[key];
- if (opts[key] && opts[key] !== settings_data.values[key]) {
- console.log('updateOpts', key, opts[key], settings_data.values[key]);
- if (callback) callback();
- }
- }
- opts = settings_data.values;
- opts_metadata = settings_data.metadata;
- Object.entries(opts_metadata).forEach(([opt, meta]) => {
- if (!opts_tabs[meta.tab_name]) opts_tabs[meta.tab_name] = {};
- if (!opts_tabs[meta.tab_name].unsaved_keys) opts_tabs[meta.tab_name].unsaved_keys = new Set();
- if (!meta.is_stored) opts_tabs[meta.tab_name].unsaved_keys.add(opt);
- });
-}
-
-function showAllSettings() {
- // Try to ensure that the show all settings tab is opened by clicking on its tab button
- const tab_dirty_indicator = gradioApp().getElementById('modification_indicator_show_all_pages');
- if (tab_dirty_indicator && tab_dirty_indicator.nextSibling) {
- tab_dirty_indicator.nextSibling.click();
- }
- gradioApp().querySelectorAll('#settings > .tab-content > .tabitem').forEach((elem) => {
- if (elem.id === 'settings_tab_licenses' || elem.id === 'settings_show_all_pages') return;
- elem.style.display = 'block';
- });
-}
-
function sortUIElements() {
// sort top-level tabs
const currSelected = gradioApp()?.querySelector('.tab-nav > .selected')?.innerText;
@@ -302,69 +262,12 @@ function sortUIElements() {
console.log('sortUIElements');
}
-function markIfModified(setting_name, value) {
- const elem = gradioApp().getElementById(`modification_indicator_${setting_name}`);
- if (elem == null) return;
- // Use JSON.stringify to compare nested objects (e.g. arrays for checkbox-groups)
- const previous_value_json = JSON.stringify(opts[setting_name]);
- const changed_value = JSON.stringify(value) !== previous_value_json;
- if (changed_value) {
- elem.title = `Click to revert to previous value: ${previous_value_json}`;
- }
-
- const is_unsaved = !opts_metadata[setting_name].is_stored;
- if (is_unsaved) {
- elem.title = 'Default value (not saved to config)';
- }
- elem.disabled = !(is_unsaved || changed_value);
- elem.classList.toggle('changed', changed_value);
- elem.classList.toggle('unsaved', is_unsaved);
-
- const { tab_name } = opts_metadata[setting_name];
- if (!opts_tabs[tab_name].changed) opts_tabs[tab_name].changed = new Set();
- const changed_items = opts_tabs[tab_name].changed;
- if (changed_value) changed_items.add(setting_name);
- else changed_items.delete(setting_name);
- const unsaved = opts_tabs[tab_name].unsaved_keys;
-
- // Set the indicator on the tab nav element
- const tab_nav_indicator = gradioApp().getElementById(`modification_indicator_${tab_name}`);
- tab_nav_indicator.disabled = (changed_items.size === 0) && (unsaved.size === 0);
- tab_nav_indicator.title = '';
- tab_nav_indicator.classList.toggle('changed', changed_items.size > 0);
- tab_nav_indicator.classList.toggle('unsaved', unsaved.size > 0);
- if (changed_items.size > 0) { tab_nav_indicator.title += `Click to reset ${changed_items.size} unapplied change${changed_items.size > 1 ? 's' : ''} in this tab.\n`; }
- if (unsaved.size > 0) { tab_nav_indicator.title += `${unsaved.size} new default value${unsaved.size > 1 ? 's' : ''} (not yet saved).`; }
-}
-
onAfterUiUpdate(async () => {
- sortUIElements();
- if (Object.keys(opts).length !== 0) return;
- const json_elem = gradioApp().getElementById('settings_json');
- if (!json_elem) return;
- json_elem.parentElement.style.display = 'none';
- const textarea = json_elem.querySelector('textarea');
- const jsdata = textarea.value;
- updateOpts(jsdata);
- executeCallbacks(optionsChangedCallbacks);
- register_drag_drop();
-
- Object.defineProperty(textarea, 'value', {
- set(newValue) {
- const valueProp = Object.getOwnPropertyDescriptor(HTMLTextAreaElement.prototype, 'value');
- const oldValue = valueProp.get.call(textarea);
- valueProp.set.call(textarea, newValue);
- if (oldValue !== newValue) updateOpts(textarea.value);
- executeCallbacks(optionsChangedCallbacks);
- },
- get() {
- const valueProp = Object.getOwnPropertyDescriptor(HTMLTextAreaElement.prototype, 'value');
- return valueProp.get.call(textarea);
- },
- });
+ let promptsInitialized = false;
function registerTextarea(id, id_counter, id_button) {
const prompt = gradioApp().getElementById(id);
+ if (!prompt) return;
const counter = gradioApp().getElementById(id_counter);
const localTextarea = gradioApp().querySelector(`#${id} > label > textarea`);
if (counter.parentElement === prompt.parentElement) return;
@@ -372,85 +275,15 @@ onAfterUiUpdate(async () => {
prompt.parentElement.style.position = 'relative';
promptTokecountUpdateFuncs[id] = () => { update_token_counter(id_button); };
localTextarea.addEventListener('input', promptTokecountUpdateFuncs[id]);
+ if (!promptsInitialized) console.log('initPrompts')
+ promptsInitialized = true;
}
+ sortUIElements();
registerTextarea('txt2img_prompt', 'txt2img_token_counter', 'txt2img_token_button');
registerTextarea('txt2img_neg_prompt', 'txt2img_negative_token_counter', 'txt2img_negative_token_button');
registerTextarea('img2img_prompt', 'img2img_token_counter', 'img2img_token_button');
registerTextarea('img2img_neg_prompt', 'img2img_negative_token_counter', 'img2img_negative_token_button');
-
- const settings_search = gradioApp().querySelectorAll('#settings_search > label > textarea')[0];
- settings_search.oninput = (e) => {
- setTimeout(() => {
- showAllSettings();
- gradioApp().querySelectorAll('#tab_settings .tabitem').forEach((section) => {
- section.querySelectorAll('.dirtyable').forEach((setting) => {
- const visible = setting.innerText.toLowerCase().includes(e.target.value.toLowerCase()) || setting.id.toLowerCase().includes(e.target.value.toLowerCase());
- if (!visible) {
- setting.style.display = 'none';
- } else {
- setting.style.removeProperty('display');
- }
- });
- });
- }, 50);
- };
-});
-
-onOptionsChanged(() => {
- const elem = gradioApp().getElementById('sd_checkpoint_hash');
- const sd_checkpoint_hash = opts.sd_checkpoint_hash || '';
- const shorthash = sd_checkpoint_hash.substring(0, 10);
-
- if (elem && elem.textContent !== shorthash) {
- elem.textContent = shorthash;
- elem.title = sd_checkpoint_hash;
- elem.href = `https://google.com/search?q=${sd_checkpoint_hash}`;
- }
-});
-
-onOptionsChanged(() => {
- const setting_elems = gradioApp().querySelectorAll('#settings [id^="setting_"]');
- setting_elems.forEach((elem) => {
- const setting_name = elem.id.replace('setting_', '');
- markIfModified(setting_name, opts[setting_name]);
- });
-});
-
-onUiLoaded(() => {
- const tab_nav_element = gradioApp().querySelector('#settings > .tab-nav');
- const tab_nav_buttons = gradioApp().querySelectorAll('#settings > .tab-nav > button');
- const tab_elements = gradioApp().querySelectorAll('#settings > div:not(.tab-nav)');
-
- // HACK Add mutation observer to keep gradio from closing setting tabs when showing all pages
- const observer = new MutationObserver((mutations) => {
- const show_all_pages_dummy = gradioApp().getElementById('settings_show_all_pages');
- if (show_all_pages_dummy.style.display === 'none') { return; }
- function mutation_on_style(mut) {
- return mut.type === 'attributes' && mut.attributeName === 'style';
- }
- if (mutations.some(mutation_on_style)) {
- showAllSettings();
- }
- });
-
- // Add a wrapper for the tab content (everything but the tab nav)
- const tab_content_wrapper = document.createElement('div');
- tab_content_wrapper.className = 'tab-content';
- tab_nav_element.parentElement.insertBefore(tab_content_wrapper, tab_nav_element.nextSibling);
-
- tab_elements.forEach((elem, index) => {
- // Move the modification indicator to the toplevel tab button
- const tab_name = elem.id.replace('settings_', '');
- const indicator = gradioApp().getElementById(`modification_indicator_${tab_name}`);
- tab_nav_element.insertBefore(indicator, tab_nav_buttons[index]);
-
- // Add the tab content to the wrapper
- tab_content_wrapper.appendChild(elem);
-
- // Add the mutation observer to the tab element
- observer.observe(elem, { attributes: true, attributeFilter: ['style'] });
- });
});
function update_txt2img_tokens(...args) {
diff --git a/modules/processing.py b/modules/processing.py
index 8142fd717..2b0df158c 100644
--- a/modules/processing.py
+++ b/modules/processing.py
@@ -812,11 +812,12 @@ def process_images_inner(p: StableDiffusionProcessing) -> Processed:
if not p.disable_extra_networks and extra_network_data:
extra_networks.deactivate(p, extra_network_data)
+
res = Processed(
p,
images_list=output_images,
seed=p.all_seeds[0],
- info=infotext(),
+ info=infotext() if 'infotext' in globals() else '',
comments="\n".join(comments),
subseed=p.all_subseeds[0],
index_of_first_image=index_of_first_image,
diff --git a/modules/sd_models.py b/modules/sd_models.py
index a4b99268e..40af872c0 100644
--- a/modules/sd_models.py
+++ b/modules/sd_models.py
@@ -188,9 +188,12 @@ def update_model_hashes():
shared.log.info(f'Models list: full hash missing for {len(lst)} out of {len(checkpoints_list)} models')
for ckpt in lst:
ckpt.sha256 = hashes.sha256(ckpt.filename, f"checkpoint/{ckpt.name}")
- ckpt.shorthash = ckpt.sha256[0:10]
- txt.append(f'Calculated full hash: {ckpt.title} {ckpt.shorthash}')
- txt.append(f'Updated full hashes for {len(lst)} out of {len(checkpoints_list)} models')
+ ckpt.shorthash = ckpt.sha256[0:10] if ckpt.sha256 is not None else None
+ if ckpt.sha256 is not None:
+ txt.append(f'Calculated full hash: {ckpt.title} {ckpt.shorthash}')
+ else:
+ txt.append(f'Skipped hash calculation: {ckpt.title}')
+ txt.append(f'Updated hashes for {len(lst)} out of {len(checkpoints_list)} models')
txt = '
'.join(txt)
return txt
diff --git a/modules/shared.py b/modules/shared.py
index b4320e7af..b2a27a2b7 100644
--- a/modules/shared.py
+++ b/modules/shared.py
@@ -702,9 +702,7 @@ class Options:
def get_default(self, key):
"""returns the default value for the key"""
data_label = self.data_labels.get(key)
- if data_label is None:
- return None
- return data_label.default
+ return data_label.default if data_label is not None else None
def save(self, filename):
if cmd_opts.freeze:
@@ -753,7 +751,7 @@ class Options:
if info is None and k not in compatibility_opts:
unknown_settings.append(k)
if len(unknown_settings) > 0:
- log.warning(f"Unknown settings: {unknown_settings}")
+ log.debug(f"Unknown settings: {unknown_settings}")
def onchange(self, key, func, call=True):
item = self.data_labels.get(key)
diff --git a/modules/ui.py b/modules/ui.py
index dfd3b9292..1f031abe8 100644
--- a/modules/ui.py
+++ b/modules/ui.py
@@ -910,11 +910,7 @@ def create_ui(startup_timer = None):
if not is_quicksettings:
dirtyable_setting = gr.Group(elem_classes="dirtyable", visible=(args or {}).get("visible", True))
dirtyable_setting.__enter__()
- dirty_indicator = gr.Button(
- "",
- elem_classes="modification-indicator",
- elem_id="modification_indicator_" + key
- )
+ dirty_indicator = gr.Button("", elem_classes="modification-indicator", elem_id="modification_indicator_" + key)
if info.refresh is not None:
if is_quicksettings: