stable-diffusion-webui-state/javascript/state.core.js

366 lines
12 KiB
JavaScript

window.state = window.state || {};
state = window.state;
state.core = (function () {
const TABS = ['txt2img', 'img2img'];
const ELEMENTS = {
'prompt': 'prompt',
'negative_prompt': 'neg_prompt',
'sampling_steps': 'steps',
'restore_faces': 'restore_faces',
'tiling': 'tiling',
'hires_fix': 'enable_hr',
'hires_steps': 'hires_steps',
'hires_scale': 'hr_scale',
'hires_resize_x': 'hr_resize_x',
'hires_resize_y': 'hr_resize_y',
'hires_denoising_strength': 'denoising_strength',
'width': 'width',
'height': 'height',
'batch_count': 'batch_count',
'batch_size': 'batch_size',
'cfg_scale': 'cfg_scale',
'denoising_strength': 'denoising_strength',
'seed': 'seed'
};
const ELEMENTS_WITHOUT_PREFIX = {
'resize_mode': 'resize_mode',
};
const SELECTS = {
'sampling': 'sampling',
'hires_upscaler': 'hr_upscaler',
};
const MULTI_SELECTS = {
'styles': 'styles'
};
const TOGGLE_BUTTONS = {
'extra_networks': 'extra_networks',
};
let store = null;
function hasSetting(id, tab) {
const suffix = tab ? `_${tab}` : '';
return this[`state${suffix}`] && this[`state${suffix}`].indexOf(id) > -1;
}
function init() {
fetch('/state/config.json?_=' + (+new Date()))
.then(response => response.json())
.then(config => {
try {
config.hasSetting = hasSetting
load(config);
} catch (error) {
console.error('[state]: Error:', error);
}
})
.catch(error => console.error('[state]: Error getting JSON file:', error));
}
function forEachElement(list, config, action) {
for (const [settingId, element] of Object.entries(list)) {
TABS.forEach(tab => {
if (config.hasSetting(settingId, tab)) {
action(element, tab);
}
});
}
}
function load(config) {
store = new state.Store();
loadUI();
restoreTabs(config);
forEachElement(ELEMENTS, config, (element, tab) => {
handleSavedInput(`${tab}_${element}`);
});
forEachElement(ELEMENTS_WITHOUT_PREFIX, config, (element, tab) => {
handleSavedInput(`${element}`);
});
forEachElement(SELECTS, config, (element, tab) => {
handleSavedSelects(`${tab}_${element}`);
});
forEachElement(MULTI_SELECTS, config, (element, tab) => {
handleSavedMultiSelects(`${tab}_${element}`);
});
forEachElement(TOGGLE_BUTTONS, config, (element, tab) => {
handleToggleButton(`${tab}_${element}`);
});
handleExtensions(config);
handleSettingsPage();
}
function createHeaderButton(title, text, className, style, action) {
const button = state.utils.html.create('button', {
title: title,
innerHTML: text,
className: className,
}, style);
if (action) {
button.addEventListener('click', action);
}
return button;
}
function createHeaderFileInput(title, text, className) {
let inputId = 'state-import-file-inline';
let importBtn = createHeaderButton(title,text, className, {
display: 'none'
}, () => {
actions.importState(inputId);
});
let label = state.utils.html.create('label', {}, { cursor: 'pointer' });
label.appendChild(state.utils.html.create('input', {
type: 'file',
id: inputId,
accept: 'application/json',
}, {
display: 'none'
}));
label.appendChild(document.createTextNode(text));
label.addEventListener('change', () => {
importBtn.dispatchEvent(new Event('click'));
});
let button = createHeaderButton(title, '', className, {});
button.appendChild(label);
return {
hiddenButton: importBtn,
button: button
};
}
function loadUI() {
let quickSettings = gradioApp().getElementById("quicksettings");
let className = quickSettings.querySelector('button').className;
quickSettings.appendChild(createHeaderButton('State: Reset', "*️⃣", className, {}, actions.resetAll));
quickSettings.appendChild(createHeaderButton('State: Export',"📤", className, {}, actions.exportState));
let fileInput = createHeaderFileInput('State: Import',"📥", className);
quickSettings.appendChild(fileInput.hiddenButton);
quickSettings.appendChild(fileInput.button);
}
function restoreTabs(config) {
if (! config.hasSetting('tabs')) {
return;
}
const tabs = gradioApp().querySelectorAll('#tabs > div:first-child button');
const value = store.get('tab');
if (value) {
for (var i = 0; i < tabs.length; i++) {
if (tabs[i].textContent === value) {
state.utils.triggerEvent(tabs[i], 'click');
break;
}
}
}
// Use this when onUiTabChange is fixed
// onUiTabChange(function () {
// store.set('tab', gradioApp().querySelector('#tabs .tab-nav button.selected').textContent);
// });
bindTabClickEvents();
}
function bindTabClickEvents() {
Array.from(gradioApp().querySelectorAll('#tabs .tab-nav button')).forEach(tab => {
tab.removeEventListener('click', storeTab);
tab.addEventListener('click', storeTab);
});
}
function storeTab() {
store.set('tab', gradioApp().querySelector('#tabs .tab-nav button.selected').textContent);
bindTabClickEvents(); // dirty hack here...
}
function handleSavedInput(id) {
const elements = gradioApp().querySelectorAll(`#${id} textarea, #${id} input`);
const events = ['change', 'input'];
if (! elements || ! elements.length) {
return;
}
let forEach = function (action) {
events.forEach(function(event) {
elements.forEach(function (element) {
action.call(element, event);
});
});
};
forEach(function (event) {
this.addEventListener(event, function () {
let value = this.value;
if (this.type && this.type === 'checkbox') {
value = this.checked;
}
store.set(id, value);
});
});
TABS.forEach(tab => {
const seedInput = gradioApp().querySelector(`#${tab}_seed input`);
['random_seed', 'reuse_seed'].forEach(id => {
const btn = gradioApp().querySelector(`#${tab}_${id}`);
btn.addEventListener('click', () => {
setTimeout(() => {
state.utils.triggerEvent(seedInput, 'change');
}, 100);
});
});
});
let value = store.get(id);
if (! value) {
return;
}
forEach(function (event) {
state.utils.setValue(this, value, event);
});
}
function handleSavedSelects(id) {
const select = gradioApp().getElementById(`${id}`);
state.utils.handleSelect(select, id, store);
}
function handleSavedMultiSelects(id) {
const select = gradioApp().getElementById(`${id}`);
state.utils.handleMultipleSelect(select, id, store);
}
function handleToggleButton(id) {
const btn = gradioApp().querySelector(`button#${id}`);
if (! btn) { return; }
if (store.get(id) === 'true') {
state.utils.triggerMouseEvent(btn);
}
btn.addEventListener('click', function () {
store.set(id, Array.from(this.classList).indexOf('secondary-down') === -1);
});
}
function handleExtensions(config) {
if (config['state_extensions']) {
config['state_extensions'].forEach(function (ext) {
if (ext in state.extensions) {
state.extensions[ext].init();
}
});
}
}
function handleSettingsPage() {
const page = gradioApp().querySelector('#settings_state');
state.utils.html.setStyle(page.querySelectorAll('fieldset'), {
'marginTop': '20px',
'marginBottom': '10px'
});
let buttonsContainer = gradioApp().querySelector('#settings_state_buttons');
if (buttonsContainer) {
buttonsContainer.parentNode.removeChild(buttonsContainer);
}
buttonsContainer = document.createElement('div');
buttonsContainer.id = 'settings_state_buttons';
let setCheckboxes = function (value, checkFunc) {
checkFunc = checkFunc || function () { return true; };
Array.from(page.querySelectorAll('input[type="checkbox"]')).forEach(function (el) {
if (checkFunc(el)) {
if (el.checked !== value) {
el.checked = value;
state.utils.triggerEvent(el, 'change');
}
} else if (el.checked === value) {
el.checked = !value;
state.utils.triggerEvent(el, 'change');
}
});
};
buttonsContainer.appendChild(state.utils.html.createButton('Select All', function () {
setCheckboxes(true);
}));
buttonsContainer.appendChild(state.utils.html.createButton('Select All Except Seeds', function () {
setCheckboxes(true, function (el) {
return el.nextElementSibling.textContent.indexOf('seed') === -1;
});
}));
buttonsContainer.appendChild(state.utils.html.createButton('Unselect All', function () {
setCheckboxes(false);
}));
state.utils.html.setStyle(buttonsContainer, {
'marginTop': '20px',
'marginBottom': '10px'
});
buttonsContainer.appendChild(state.utils.html.create('hr'));
buttonsContainer.appendChild(state.utils.html.create('div', { innerHTML: 'Actions' }, { marginBottom: '10px' }));
buttonsContainer.appendChild(state.utils.html.createButton('Reset All', actions.resetAll));
buttonsContainer.appendChild(state.utils.html.createButton('Export State', actions.exportState));
buttonsContainer.appendChild(state.utils.html.createButton('Import State', actions.importState));
buttonsContainer.appendChild(state.utils.html.create('input', {
id: 'state-import-file', type: 'file', accept: 'application/json'
}));
page.appendChild(buttonsContainer);
}
let actions = {
resetAll: function () {
let confirmed = confirm('Reset all state values?');
if (confirmed) {
store.clearAll();
alert('All state values deleted!');
}
},
exportState: function () {
state.utils.saveFile('sd-webui-state', store.getAll());
},
importState: function (id) {
const fileInput = gradioApp().getElementById(id || 'state-import-file');
if (! fileInput.files || ! fileInput.files[0]) {
alert('Please select a JSON file!');
return;
}
const file = fileInput.files[0];
const reader = new FileReader();
reader.onload = function (event) {
store.load(JSON.parse(event.target.result));
window.location.reload();
};
reader.readAsText(file);
}
};
return { init };
}());