mirror of
https://github.com/vladmandic/automatic
synced 2026-09-18 00:34:33 +02:00
refactor settings ui
This commit is contained in:
@@ -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'] });
|
||||
});
|
||||
});
|
||||
@@ -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 */
|
||||
|
||||
+5
-172
@@ -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) {
|
||||
|
||||
Reference in New Issue
Block a user