ac374e0b97
* ini * remove shit * Create control_model.py * i * i * Update controlnet_supported.py * Update controlnet_supported.py * Update controlnet_supported.py * i * i * Update controlnet_supported.py * i * Update controlnet_supported.py * remove shits * remove shit * Update global_state.py * i * i * Update legacy_preprocessors.py * Update legacy_preprocessors.py * remove shit * Update batch_hijack.py * remove shit * remove shit * i * i * i * Update external_code.py * Update global_state.py * Update infotext.py * Update utils.py * Update external_code.py * i * i * i * Update controlnet_ui_group.py * remove shit * remove shit * i * Update controlnet.py * Update controlnet.py * Update controlnet.py * Update controlnet.py * Update controlnet.py * i * Update global_state.py * Update global_state.py * i * Update global_state.py * Update global_state.py * Update global_state.py * Update global_state.py * Update controlnet_ui_group.py * i * Update global_state.py * Update controlnet_ui_group.py * Update controlnet_ui_group.py * i * Update controlnet_ui_group.py * Update controlnet_ui_group.py * Update controlnet_ui_group.py * Update controlnet_ui_group.py
311 lines
13 KiB
JavaScript
311 lines
13 KiB
JavaScript
/**
|
|
* Give a badge on ControlNet Accordion indicating total number of active
|
|
* units.
|
|
* Make active unit's tab name green.
|
|
* Append control type to tab name.
|
|
* Disable resize mode selection when A1111 img2img input is used.
|
|
*/
|
|
(function () {
|
|
const cnetAllAccordions = new Set();
|
|
onUiUpdate(() => {
|
|
const ImgChangeType = {
|
|
NO_CHANGE: 0,
|
|
REMOVE: 1,
|
|
ADD: 2,
|
|
SRC_CHANGE: 3,
|
|
};
|
|
|
|
function imgChangeObserved(mutationsList) {
|
|
// Iterate over all mutations that just occured
|
|
for (let mutation of mutationsList) {
|
|
// Check if the mutation is an addition or removal of a node
|
|
if (mutation.type === 'childList') {
|
|
// Check if nodes were added
|
|
if (mutation.addedNodes.length > 0) {
|
|
for (const node of mutation.addedNodes) {
|
|
if (node.tagName === 'IMG') {
|
|
return ImgChangeType.ADD;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Check if nodes were removed
|
|
if (mutation.removedNodes.length > 0) {
|
|
for (const node of mutation.removedNodes) {
|
|
if (node.tagName === 'IMG') {
|
|
return ImgChangeType.REMOVE;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
// Check if the mutation is a change of an attribute
|
|
else if (mutation.type === 'attributes') {
|
|
if (mutation.target.tagName === 'IMG' && mutation.attributeName === 'src') {
|
|
return ImgChangeType.SRC_CHANGE;
|
|
}
|
|
}
|
|
}
|
|
return ImgChangeType.NO_CHANGE;
|
|
}
|
|
|
|
function childIndex(element) {
|
|
// Get all child nodes of the parent
|
|
let children = Array.from(element.parentNode.childNodes);
|
|
|
|
// Filter out non-element nodes (like text nodes and comments)
|
|
children = children.filter(child => child.nodeType === Node.ELEMENT_NODE);
|
|
|
|
return children.indexOf(element);
|
|
}
|
|
|
|
function imageInputDisabledAlert() {
|
|
alert('Inpaint control type must use a1111 input in img2img mode.');
|
|
}
|
|
|
|
class ControlNetUnitTab {
|
|
constructor(tab, accordion) {
|
|
this.tab = tab;
|
|
this.accordion = accordion;
|
|
this.isImg2Img = tab.querySelector('.cnet-unit-enabled').id.includes('img2img');
|
|
|
|
this.enabledCheckbox = tab.querySelector('.cnet-unit-enabled input');
|
|
this.inputImage = tab.querySelector('.cnet-input-image-group .cnet-image input[type="file"]');
|
|
this.inputImageContainer = tab.querySelector('.cnet-input-image-group .cnet-image');
|
|
this.controlTypeRadios = tab.querySelectorAll('.controlnet_control_type_filter_group input[type="radio"]');
|
|
this.resizeModeRadios = tab.querySelectorAll('.controlnet_resize_mode_radio input[type="radio"]');
|
|
this.runPreprocessorButton = tab.querySelector('.cnet-run-preprocessor');
|
|
|
|
const tabs = tab.parentNode;
|
|
this.tabNav = tabs.querySelector('.tab-nav');
|
|
this.tabIndex = childIndex(tab) - 1; // -1 because tab-nav is also at the same level.
|
|
|
|
this.attachEnabledButtonListener();
|
|
this.attachControlTypeRadioListener();
|
|
this.attachTabNavChangeObserver();
|
|
this.attachImageUploadListener();
|
|
this.attachImageStateChangeObserver();
|
|
this.attachA1111SendInfoObserver();
|
|
this.attachPresetDropdownObserver();
|
|
}
|
|
|
|
getTabNavButton() {
|
|
return this.tabNav.querySelector(`:nth-child(${this.tabIndex + 1})`);
|
|
}
|
|
|
|
getActiveControlType() {
|
|
for (let radio of this.controlTypeRadios) {
|
|
if (radio.checked) {
|
|
return radio.value;
|
|
}
|
|
}
|
|
return undefined;
|
|
}
|
|
|
|
updateActiveState() {
|
|
const tabNavButton = this.getTabNavButton();
|
|
if (!tabNavButton) return;
|
|
|
|
if (this.enabledCheckbox.checked) {
|
|
tabNavButton.classList.add('cnet-unit-active');
|
|
} else {
|
|
tabNavButton.classList.remove('cnet-unit-active');
|
|
}
|
|
}
|
|
|
|
updateActiveUnitCount() {
|
|
function getActiveUnitCount(checkboxes) {
|
|
let activeUnitCount = 0;
|
|
for (const checkbox of checkboxes) {
|
|
if (checkbox.checked)
|
|
activeUnitCount++;
|
|
}
|
|
return activeUnitCount;
|
|
}
|
|
|
|
const checkboxes = this.accordion.querySelectorAll('.cnet-unit-enabled input');
|
|
const span = this.accordion.querySelector('.label-wrap span');
|
|
|
|
// Remove existing badge.
|
|
if (span.childNodes.length !== 1) {
|
|
span.removeChild(span.lastChild);
|
|
}
|
|
// Add new badge if necessary.
|
|
const activeUnitCount = getActiveUnitCount(checkboxes);
|
|
if (activeUnitCount > 0) {
|
|
const div = document.createElement('div');
|
|
div.classList.add('cnet-badge');
|
|
div.classList.add('primary');
|
|
div.innerHTML = `${activeUnitCount} unit${activeUnitCount > 1 ? 's' : ''}`;
|
|
span.appendChild(div);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Add the active control type to tab displayed text.
|
|
*/
|
|
updateActiveControlType() {
|
|
const tabNavButton = this.getTabNavButton();
|
|
if (!tabNavButton) return;
|
|
|
|
// Remove the control if exists
|
|
const controlTypeSuffix = tabNavButton.querySelector('.control-type-suffix');
|
|
if (controlTypeSuffix) controlTypeSuffix.remove();
|
|
|
|
// Add new suffix.
|
|
const controlType = this.getActiveControlType();
|
|
if (controlType === 'All') return;
|
|
|
|
const span = document.createElement('span');
|
|
span.innerHTML = `[${controlType}]`;
|
|
span.classList.add('control-type-suffix');
|
|
tabNavButton.appendChild(span);
|
|
}
|
|
|
|
/**
|
|
* When 'Inpaint' control type is selected in img2img:
|
|
* - Make image input disabled
|
|
* - Clear existing image input
|
|
*/
|
|
updateImageInputState() {
|
|
if (!this.isImg2Img) return;
|
|
|
|
const tabNavButton = this.getTabNavButton();
|
|
if (!tabNavButton) return;
|
|
|
|
const controlType = this.getActiveControlType();
|
|
if (controlType.toLowerCase() === 'inpaint') {
|
|
this.inputImage.disabled = true;
|
|
this.inputImage.parentNode.addEventListener('click', imageInputDisabledAlert);
|
|
const removeButton = this.tab.querySelector(
|
|
'.cnet-input-image-group .cnet-image button[aria-label="Remove Image"]');
|
|
if (removeButton) removeButton.click();
|
|
} else {
|
|
this.inputImage.disabled = false;
|
|
this.inputImage.parentNode.removeEventListener('click', imageInputDisabledAlert);
|
|
}
|
|
}
|
|
|
|
attachEnabledButtonListener() {
|
|
this.enabledCheckbox.addEventListener('change', () => {
|
|
this.updateActiveState();
|
|
this.updateActiveUnitCount();
|
|
});
|
|
}
|
|
|
|
attachControlTypeRadioListener() {
|
|
for (const radio of this.controlTypeRadios) {
|
|
radio.addEventListener('change', () => {
|
|
this.updateActiveControlType();
|
|
});
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Each time the active tab change, all tab nav buttons are cleared and
|
|
* regenerated by gradio. So we need to reapply the active states on
|
|
* them.
|
|
*/
|
|
attachTabNavChangeObserver() {
|
|
new MutationObserver((mutationsList) => {
|
|
for (const mutation of mutationsList) {
|
|
if (mutation.type === 'childList') {
|
|
this.updateActiveState();
|
|
this.updateActiveControlType();
|
|
}
|
|
}
|
|
}).observe(this.tabNav, { childList: true });
|
|
}
|
|
|
|
attachImageUploadListener() {
|
|
// Automatically check `enable` checkbox when image is uploaded.
|
|
this.inputImage.addEventListener('change', (event) => {
|
|
if (!event.target.files) return;
|
|
if (!this.enabledCheckbox.checked)
|
|
this.enabledCheckbox.click();
|
|
});
|
|
|
|
// Automatically check `enable` checkbox when JSON pose file is uploaded.
|
|
this.tab.querySelector('.cnet-upload-pose input').addEventListener('change', (event) => {
|
|
if (!event.target.files) return;
|
|
if (!this.enabledCheckbox.checked)
|
|
this.enabledCheckbox.click();
|
|
});
|
|
}
|
|
|
|
attachImageStateChangeObserver() {
|
|
new MutationObserver((mutationsList) => {
|
|
const changeObserved = imgChangeObserved(mutationsList);
|
|
|
|
if (changeObserved === ImgChangeType.ADD) {
|
|
// enabling the run preprocessor button
|
|
this.runPreprocessorButton.removeAttribute("disabled");
|
|
this.runPreprocessorButton.title = 'Run preprocessor';
|
|
}
|
|
|
|
if (changeObserved === ImgChangeType.REMOVE) {
|
|
// disabling the run preprocessor button
|
|
this.runPreprocessorButton.setAttribute("disabled", true);
|
|
this.runPreprocessorButton.title = "No ControlNet input image available";
|
|
}
|
|
}).observe(this.inputImageContainer, {
|
|
childList: true,
|
|
subtree: true,
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Observe send PNG info buttons in A1111, as they can also directly
|
|
* set states of ControlNetUnit.
|
|
*/
|
|
attachA1111SendInfoObserver() {
|
|
const pasteButtons = gradioApp().querySelectorAll('#paste');
|
|
const pngButtons = gradioApp().querySelectorAll(
|
|
this.isImg2Img ?
|
|
'#img2img_tab, #inpaint_tab' :
|
|
'#txt2img_tab'
|
|
);
|
|
|
|
for (const button of [...pasteButtons, ...pngButtons]) {
|
|
button.addEventListener('click', () => {
|
|
// The paste/send img generation info feature goes
|
|
// though gradio, which is pretty slow. Ideally we should
|
|
// observe the event when gradio has done the job, but
|
|
// that is not an easy task.
|
|
// Here we just do a 2 second delay until the refresh.
|
|
setTimeout(() => {
|
|
this.updateActiveState();
|
|
this.updateActiveUnitCount();
|
|
}, 2000);
|
|
});
|
|
}
|
|
}
|
|
|
|
attachPresetDropdownObserver() {
|
|
const presetDropDown = this.tab.querySelector('.cnet-preset-dropdown');
|
|
|
|
new MutationObserver((mutationsList) => {
|
|
for (const mutation of mutationsList) {
|
|
if (mutation.removedNodes.length > 0) {
|
|
setTimeout(() => {
|
|
this.updateActiveState();
|
|
this.updateActiveUnitCount();
|
|
this.updateActiveControlType();
|
|
}, 1000);
|
|
return;
|
|
}
|
|
}
|
|
}).observe(presetDropDown, {
|
|
childList: true,
|
|
subtree: true,
|
|
});
|
|
}
|
|
}
|
|
|
|
gradioApp().querySelectorAll('#controlnet').forEach(accordion => {
|
|
if (cnetAllAccordions.has(accordion)) return;
|
|
accordion.querySelectorAll('.cnet-unit-tab')
|
|
.forEach(tab => new ControlNetUnitTab(tab, accordion));
|
|
cnetAllAccordions.add(accordion);
|
|
});
|
|
});
|
|
})(); |