|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
(function () { |
|
const cnetAllAccordions = new Set(); |
|
onUiUpdate(() => { |
|
const ImgChangeType = { |
|
NO_CHANGE: 0, |
|
REMOVE: 1, |
|
ADD: 2, |
|
SRC_CHANGE: 3, |
|
}; |
|
|
|
function imgChangeObserved(mutationsList) { |
|
|
|
for (let mutation of mutationsList) { |
|
|
|
if (mutation.type === 'childList') { |
|
|
|
if (mutation.addedNodes.length > 0) { |
|
for (const node of mutation.addedNodes) { |
|
if (node.tagName === 'IMG') { |
|
return ImgChangeType.ADD; |
|
} |
|
} |
|
} |
|
|
|
|
|
if (mutation.removedNodes.length > 0) { |
|
for (const node of mutation.removedNodes) { |
|
if (node.tagName === 'IMG') { |
|
return ImgChangeType.REMOVE; |
|
} |
|
} |
|
} |
|
} |
|
|
|
else if (mutation.type === 'attributes') { |
|
if (mutation.target.tagName === 'IMG' && mutation.attributeName === 'src') { |
|
return ImgChangeType.SRC_CHANGE; |
|
} |
|
} |
|
} |
|
return ImgChangeType.NO_CHANGE; |
|
} |
|
|
|
function childIndex(element) { |
|
|
|
let children = Array.from(element.parentNode.childNodes); |
|
|
|
|
|
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; |
|
|
|
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'); |
|
|
|
|
|
if (span.childNodes.length !== 1) { |
|
span.removeChild(span.lastChild); |
|
} |
|
|
|
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); |
|
} |
|
} |
|
|
|
|
|
|
|
|
|
updateActiveControlType() { |
|
const tabNavButton = this.getTabNavButton(); |
|
if (!tabNavButton) return; |
|
|
|
|
|
const controlTypeSuffix = tabNavButton.querySelector('.control-type-suffix'); |
|
if (controlTypeSuffix) controlTypeSuffix.remove(); |
|
|
|
|
|
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); |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
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(); |
|
}); |
|
} |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
attachTabNavChangeObserver() { |
|
new MutationObserver((mutationsList) => { |
|
for (const mutation of mutationsList) { |
|
if (mutation.type === 'childList') { |
|
this.updateActiveState(); |
|
this.updateActiveControlType(); |
|
} |
|
} |
|
}).observe(this.tabNav, { childList: true }); |
|
} |
|
|
|
attachImageUploadListener() { |
|
|
|
this.inputImage.addEventListener('change', (event) => { |
|
if (!event.target.files) return; |
|
if (!this.enabledCheckbox.checked) |
|
this.enabledCheckbox.click(); |
|
}); |
|
|
|
|
|
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) { |
|
|
|
this.runPreprocessorButton.removeAttribute("disabled"); |
|
this.runPreprocessorButton.title = 'Run preprocessor'; |
|
} |
|
|
|
if (changeObserved === ImgChangeType.REMOVE) { |
|
|
|
this.runPreprocessorButton.setAttribute("disabled", true); |
|
this.runPreprocessorButton.title = "No ControlNet input image available"; |
|
} |
|
}).observe(this.inputImageContainer, { |
|
childList: true, |
|
subtree: true, |
|
}); |
|
} |
|
|
|
|
|
|
|
|
|
|
|
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', () => { |
|
|
|
|
|
|
|
|
|
|
|
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); |
|
}); |
|
}); |
|
})(); |