Cozy-Nest/cozy-nest-client/main/nevysha-cozy-nest.js

1426 lines
58 KiB
JavaScript
Raw Blame History

This file contains invisible Unicode characters!

This file contains invisible Unicode characters that may be processed differently from what appears below. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to reveal hidden characters.

import showdown from 'showdown/dist/showdown.js';
import $ from "jquery";
window.$ = window.jQuery = $;
import SimpleTimer from "./SimpleTimer.js";
import {getTheme, isUpToDate, getLuminance} from './cozy-utils.js';
import {
COZY_NEST_DOM_TWEAK_LOAD_DURATION,
COZY_NEST_GRADIO_LOAD_DURATION,
SETTINGS_MIN_WIDTH,
RESULT_MIN_WIDTH,
ANIMATION_SPEED,
WEBUI_UNKNOWN, WEBUI_SDNEXT, WEBUI_A1111
} from "./Constants.js";
import Loading from "./Loading.js";
import {waves, svg_magic_wand, svg_update_info} from "./svg.js";
import {
applyAccentColor, applyBgGradiantColor, applyWavesColor
, wrapDataGenerationInfo, wrapSettings, createVerticalLineComp, applyFontColor, recalcOffsetFromMenuHeight
} from "./tweaks/various-tweaks.js";
import kofiCup from './kofi-cup-border.png'
import {
setupPopupInstanceInfo,
populateInstanceInfoDialog,
showInstanceInfoDialog
} from "./tweaks/troubleshot-dialog.js";
import {CozyLogger} from "./CozyLogger.js";
import clearGeneratedImage from './tweaks/clear-generated-image.js'
import {createAlertDiv, showAlert} from "./tweaks/cozy-alert.js";
const addDraggable = ({prefix}) => {
const settings = document.getElementById(`${prefix}_settings`);
//change min-width to min(420px, 100%)
settings.style.minWidth = `min(${SETTINGS_MIN_WIDTH}px, 100%)`
// Create a new vertical line element
const lineWrapper = createVerticalLineComp();
// Insert the line element after the settings element
settings.insertAdjacentElement('afterend', lineWrapper);
const container = settings.parentElement;
container.classList.add('nevysha', 'resizable-children-container');
const results = document.getElementById(`${prefix}_results`);
//change min-width to 320px
settings.style.minWidth = `min(${RESULT_MIN_WIDTH}px, 100%)`;
//get linePosition from local storage
let linePosition = localStorage.getItem(`${prefix}_linePosition`);
if (!linePosition) {
linePosition = 50;
localStorage.setItem(`${prefix}_linePosition`, `${linePosition}`);
}
settings.style.flexBasis = `${linePosition}%`;
results.style.flexBasis = `${100 - linePosition}%`;
let isDragging = false;
lineWrapper.addEventListener('mousedown', (e) => {
isDragging = true;
e.preventDefault();
});
document.addEventListener('mousemove', (event) => {
if (!isDragging) return;
//calc the offset of the tab
const tab = document.querySelector(`#tab_${prefix}`);
let offsetX = tab.offsetLeft;
let parent = tab.offsetParent;
while (parent) {
offsetX += parent.offsetLeft;
parent = parent.offsetParent;
}
const containerWidth = container.offsetWidth;
const mouseX = event.clientX;
const linePosition = ((mouseX - offsetX) / containerWidth) * 100;
//if settings width is min, return
if (linePosition <= SETTINGS_MIN_WIDTH / containerWidth * 100) {
return;
}
//if results width is min, return
if (linePosition >= (1 - RESULT_MIN_WIDTH / containerWidth) * 100) {
return;
}
//save linePosition to local storage
localStorage.setItem(`${prefix}_linePosition`, `${linePosition}`);
settings.style.flexBasis = `${linePosition}%`;
results.style.flexBasis = `${100 - linePosition}%`;
});
document.addEventListener('mouseup', () => {
isDragging = false;
});
}
const tweakButtonsIcons = () => {
document.querySelectorAll('button').forEach((button) => {
if (button.textContent.includes('📂')) {
// Add SVG element to the SVG container here
button.innerHTML = '<svg class="nevysha svg-icon" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><!--! Font Awesome Pro 6.4.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license (Commercial License) Copyright 2023 Fonticons, Inc. --><path d="M0 96C0 60.7 28.7 32 64 32H196.1c19.1 0 37.4 7.6 50.9 21.1L289.9 96H448c35.3 0 64 28.7 64 64V416c0 35.3-28.7 64-64 64H64c-35.3 0-64-28.7-64-64V96zM64 80c-8.8 0-16 7.2-16 16V416c0 8.8 7.2 16 16 16H448c8.8 0 16-7.2 16-16V160c0-8.8-7.2-16-16-16H286.6c-10.6 0-20.8-4.2-28.3-11.7L213.1 87c-4.5-4.5-10.6-7-17-7H64z"/></svg>';
}
if (button.textContent.includes('🔄')) {
button.innerHTML = '<svg class="nevysha svg-icon" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><!--! Font Awesome Pro 6.4.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license (Commercial License) Copyright 2023 Fonticons, Inc. --><path d="M142.9 142.9c62.2-62.2 162.7-62.5 225.3-1L327 183c-6.9 6.9-8.9 17.2-5.2 26.2s12.5 14.8 22.2 14.8H463.5c0 0 0 0 0 0H472c13.3 0 24-10.7 24-24V72c0-9.7-5.8-18.5-14.8-22.2s-19.3-1.7-26.2 5.2L413.4 96.6c-87.6-86.5-228.7-86.2-315.8 1C73.2 122 55.6 150.7 44.8 181.4c-5.9 16.7 2.9 34.9 19.5 40.8s34.9-2.9 40.8-19.5c7.7-21.8 20.2-42.3 37.8-59.8zM16 312v7.6 .7V440c0 9.7 5.8 18.5 14.8 22.2s19.3 1.7 26.2-5.2l41.6-41.6c87.6 86.5 228.7 86.2 315.8-1c24.4-24.4 42.1-53.1 52.9-83.7c5.9-16.7-2.9-34.9-19.5-40.8s-34.9 2.9-40.8 19.5c-7.7 21.8-20.2 42.3-37.8 59.8c-62.2 62.2-162.7 62.5-225.3 1L185 329c6.9-6.9 8.9-17.2 5.2-26.2s-12.5-14.8-22.2-14.8H48.4h-.7H40c-13.3 0-24 10.7-24 24z"/></svg>';
}
if (button.textContent.includes('↙️')) {
button.innerHTML = '<svg class="nevysha svg-icon rotate" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512"><!--! Font Awesome Pro 6.4.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license (Commercial License) Copyright 2023 Fonticons, Inc. --><path d="M9.4 233.4c-12.5 12.5-12.5 32.8 0 45.3l160 160c12.5 12.5 32.8 12.5 45.3 0s12.5-32.8 0-45.3L109.2 288 416 288c17.7 0 32-14.3 32-32s-14.3-32-32-32l-306.7 0L214.6 118.6c12.5-12.5 12.5-32.8 0-45.3s-32.8-12.5-45.3 0l-160 160z"/></svg>';
}
if (button.textContent.includes('🗑️')) {
button.innerHTML = '<svg class="nevysha svg-icon" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512"><!--! Font Awesome Pro 6.4.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license (Commercial License) Copyright 2023 Fonticons, Inc. --><path d="M135.2 17.7L128 32H32C14.3 32 0 46.3 0 64S14.3 96 32 96H416c17.7 0 32-14.3 32-32s-14.3-32-32-32H320l-7.2-14.3C307.4 6.8 296.3 0 284.2 0H163.8c-12.1 0-23.2 6.8-28.6 17.7zM416 128H32L53.2 467c1.6 25.3 22.6 45 47.9 45H346.9c25.3 0 46.3-19.7 47.9-45L416 128z"/></svg>';
}
if (button.textContent.includes('🎴')) {
button.innerHTML = '<svg class="nevysha svg-icon" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><!--! Font Awesome Pro 6.4.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license (Commercial License) Copyright 2023 Fonticons, Inc. --><path d="M448 80c8.8 0 16 7.2 16 16V415.8l-5-6.5-136-176c-4.5-5.9-11.6-9.3-19-9.3s-14.4 3.4-19 9.3L202 340.7l-30.5-42.7C167 291.7 159.8 288 152 288s-15 3.7-19.5 10.1l-80 112L48 416.3l0-.3V96c0-8.8 7.2-16 16-16H448zM64 32C28.7 32 0 60.7 0 96V416c0 35.3 28.7 64 64 64H448c35.3 0 64-28.7 64-64V96c0-35.3-28.7-64-64-64H64zm80 192a48 48 0 1 0 0-96 48 48 0 1 0 0 96z"/></svg>';
}
if (button.textContent.includes('📋')) {
button.innerHTML = '<svg class="nevysha svg-icon" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 384 512"><!--! Font Awesome Pro 6.4.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license (Commercial License) Copyright 2023 Fonticons, Inc. --><path d="M280 64h40c35.3 0 64 28.7 64 64V448c0 35.3-28.7 64-64 64H64c-35.3 0-64-28.7-64-64V128C0 92.7 28.7 64 64 64h40 9.6C121 27.5 153.3 0 192 0s71 27.5 78.4 64H280zM64 112c-8.8 0-16 7.2-16 16V448c0 8.8 7.2 16 16 16H320c8.8 0 16-7.2 16-16V128c0-8.8-7.2-16-16-16H304v24c0 13.3-10.7 24-24 24H192 104c-13.3 0-24-10.7-24-24V112H64zm128-8a24 24 0 1 0 0-48 24 24 0 1 0 0 48z"/></svg>';
}
if (button.textContent.includes('💾')) {
button.innerHTML = '<svg class="nevysha svg-icon" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512"><!--! Font Awesome Pro 6.4.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license (Commercial License) Copyright 2023 Fonticons, Inc. --><path d="M48 96V416c0 8.8 7.2 16 16 16H384c8.8 0 16-7.2 16-16V170.5c0-4.2-1.7-8.3-4.7-11.3l33.9-33.9c12 12 18.7 28.3 18.7 45.3V416c0 35.3-28.7 64-64 64H64c-35.3 0-64-28.7-64-64V96C0 60.7 28.7 32 64 32H309.5c17 0 33.3 6.7 45.3 18.7l74.5 74.5-33.9 33.9L320.8 84.7c-.3-.3-.5-.5-.8-.8V184c0 13.3-10.7 24-24 24H104c-13.3 0-24-10.7-24-24V80H64c-8.8 0-16 7.2-16 16zm80-16v80H272V80H128zm32 240a64 64 0 1 1 128 0 64 64 0 1 1 -128 0z"/></svg>';
}
if (button.textContent.includes('🎲️')) {
button.innerHTML = '<svg class="nevysha svg-icon" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 640 512"><!-- Font Awesome Pro 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license (Commercial License) --><path d="M592 192H473.26c12.69 29.59 7.12 65.2-17 89.32L320 417.58V464c0 26.51 21.49 48 48 48h224c26.51 0 48-21.49 48-48V240c0-26.51-21.49-48-48-48zM480 376c-13.25 0-24-10.75-24-24 0-13.26 10.75-24 24-24s24 10.74 24 24c0 13.25-10.75 24-24 24zm-46.37-186.7L258.7 14.37c-19.16-19.16-50.23-19.16-69.39 0L14.37 189.3c-19.16 19.16-19.16 50.23 0 69.39L189.3 433.63c19.16 19.16 50.23 19.16 69.39 0L433.63 258.7c19.16-19.17 19.16-50.24 0-69.4zM96 248c-13.25 0-24-10.75-24-24 0-13.26 10.75-24 24-24s24 10.74 24 24c0 13.25-10.75 24-24 24zm128 128c-13.25 0-24-10.75-24-24 0-13.26 10.75-24 24-24s24 10.74 24 24c0 13.25-10.75 24-24 24zm0-128c-13.25 0-24-10.75-24-24 0-13.26 10.75-24 24-24s24 10.74 24 24c0 13.25-10.75 24-24 24zm0-128c-13.25 0-24-10.75-24-24 0-13.26 10.75-24 24-24s24 10.74 24 24c0 13.25-10.75 24-24 24zm128 128c-13.25 0-24-10.75-24-24 0-13.26 10.75-24 24-24s24 10.74 24 24c0 13.25-10.75 24-24 24z"/></svg>';
}
if (button.textContent.includes('♻️')) {
button.innerHTML = '<svg class="nevysha svg-icon" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><!--! Font Awesome Pro 6.4.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license (Commercial License) Copyright 2023 Fonticons, Inc. --><path d="M174.7 45.1C192.2 17 223 0 256 0s63.8 17 81.3 45.1l38.6 61.7 27-15.6c8.4-4.9 18.9-4.2 26.6 1.7s11.1 15.9 8.6 25.3l-23.4 87.4c-3.4 12.8-16.6 20.4-29.4 17l-87.4-23.4c-9.4-2.5-16.3-10.4-17.6-20s3.4-19.1 11.8-23.9l28.4-16.4L283 79c-5.8-9.3-16-15-27-15s-21.2 5.7-27 15l-17.5 28c-9.2 14.8-28.6 19.5-43.6 10.5c-15.3-9.2-20.2-29.2-10.7-44.4l17.5-28zM429.5 251.9c15-9 34.4-4.3 43.6 10.5l24.4 39.1c9.4 15.1 14.4 32.4 14.6 50.2c.3 53.1-42.7 96.4-95.8 96.4L320 448v32c0 9.7-5.8 18.5-14.8 22.2s-19.3 1.7-26.2-5.2l-64-64c-9.4-9.4-9.4-24.6 0-33.9l64-64c6.9-6.9 17.2-8.9 26.2-5.2s14.8 12.5 14.8 22.2v32l96.2 0c17.6 0 31.9-14.4 31.8-32c0-5.9-1.7-11.7-4.8-16.7l-24.4-39.1c-9.5-15.2-4.7-35.2 10.7-44.4zm-364.6-31L36 204.2c-8.4-4.9-13.1-14.3-11.8-23.9s8.2-17.5 17.6-20l87.4-23.4c12.8-3.4 26 4.2 29.4 17L182 241.2c2.5 9.4-.9 19.3-8.6 25.3s-18.2 6.6-26.6 1.7l-26.5-15.3L68.8 335.3c-3.1 5-4.8 10.8-4.8 16.7c-.1 17.6 14.2 32 31.8 32l32.2 0c17.7 0 32 14.3 32 32s-14.3 32-32 32l-32.2 0C42.7 448-.3 404.8 0 351.6c.1-17.8 5.1-35.1 14.6-50.2l50.3-80.5z"/></svg>';
}
});
}
function tweakInpainting() {
//expend button
const img2maskimg = document.getElementById("img2maskimg")
const expendBtn = document.createElement("button")
expendBtn.setAttribute("id", "expendBtn")
expendBtn.setAttribute("class", "nevysha lg primary gradio-button btn")
expendBtn.textContent = "Expand"
img2maskimg.insertAdjacentElement("beforeend", expendBtn)
const inpaintTab = document.getElementById("img2maskimg");
let defaultStyle = inpaintTab.getAttribute("style");
let nodesCanvasData = []
let expended = false;
expendBtn.addEventListener("click", () => {
if (!document.querySelectorAll("canvas")) {
return;
}
if (!expended) {
expendBtn.textContent = "Reduce"
expendBtn.style.position = "fixed";
inpaintTab.style.position = "fixed";
inpaintTab.style.zIndex = 999
inpaintTab.style.top = "5px";
inpaintTab.style.left = "5px";
inpaintTab.style.width = "calc(100vw - 10px)";
inpaintTab.style.height = "calc(100vh - 10px)";
inpaintTab.style.overflow = "";
//hide button to delete image
//this button is inside #img2maskimg > .image-container
//it's the third button
document.querySelector('#img2maskimg > .image-container').querySelector("button:nth-child(3)").style.display = "none"
//apply to canvas
const nodesCanvas = document.querySelectorAll("canvas")
nodesCanvasData = [];
nodesCanvas.forEach((canvas) => {
let canvasId = `nevysha-${canvas.getAttribute("key")}-canvas`;
canvas.setAttribute('id',canvasId)
canvas.classList.add("nevysha")
nodesCanvasData.push({
id: canvasId,
defaultStyle: canvas.getAttribute("style"),
})
canvas.style.maxWidth = "calc(100vw - 20px)";
canvas.style.width = "";
canvas.style.maxHeight = "calc(100vh - 20px)";
canvas.style.height = "";
})
} else {
expendBtn.textContent = "Expand"
expendBtn.style.position = "absolute";
inpaintTab.setAttribute("style", defaultStyle);
document.querySelector('#img2maskimg > .image-container').querySelector("button:nth-child(3)").style.display = "block"
//revert canvas
nodesCanvasData.forEach((canvasData) => {
const canvas = document.getElementById(canvasData.id)
canvas.setAttribute("style", canvasData.defaultStyle);
})
}
expended = !expended;
})
}
function addScrollable(bundle) {
document.getElementById(`${bundle.prefix}_gallery_container`).classList.add("nevysha","nevysha-scrollable")
}
function tweakAWQ() {
const observer = new MutationObserver((mutationsList, observer) => {
for (const mutation of mutationsList) {
if (mutation.type === 'childList') {
const addedNodes = Array.from(mutation.addedNodes);
const targetNode = addedNodes.find(node => node.id === 'AWQ-container');
if (targetNode) {
observer.disconnect();
closure();
break;
}
}
}
});
const observerConfig = {
childList: true,
subtree: true
};
observer.observe(document.documentElement, observerConfig);
const closure = () => {
const awqContainer = document.querySelector("#AWQ-container")
//create a wrapper div
const awqWrapper = document.createElement("div")
awqWrapper.id = "nevysha_awq_wrapper"
awqWrapper.style.zIndex = "9999"
awqWrapper.style.display = "none"
awqWrapper.style.position = "fixed"
awqWrapper.style.bottom = "30px"
document.body.appendChild(awqWrapper)
//bush awqContainer into wrapper
awqWrapper.appendChild(awqContainer)
const btnAWQ = document.createElement("button")
btnAWQ.classList.add("nevysha-btn-menu", "nevysha-btn-menu-awq", "gradio-button", "primary", "nevysha")
btnAWQ.id = "nevyui_awq_btn"
btnAWQ.innerHTML = 'Show/Hide AWQ'
btnAWQ.title = "Show/Hide AWQ"
btnAWQ.setAttribute("style", "position: fixed; bottom: 0; left: calc(50% - 75px); width: 150px;")
document.querySelector('div.app').insertAdjacentElement('beforeend', btnAWQ)
btnAWQ.addEventListener("click", () => {
if (awqWrapper.style.display === "none") {
awqWrapper.style.display = "block"
} else {
awqWrapper.style.display = "none"
}
});
}
}
const addCozyNestCustomBtn = () => {
//create a wrapper div
const nevySettingstabMenuWrapper = document.createElement("div");
nevySettingstabMenuWrapper.classList.add("nevysha-btn-menu-wrapper");
nevySettingstabMenuWrapper.id = "nevysha-btn-menu-wrapper";
//add a new button in the tabnav
const updateInfoBtn = `<button class="nevysha-btn-menu" id="nevyui_update_info" title="Nevysha Cozy Nest Update Info">${svg_update_info}</button>`;
nevySettingstabMenuWrapper.insertAdjacentHTML('beforeend', updateInfoBtn);
//add the wrapper div in the tabnav
document.querySelector("#tabs > div.tab-nav").insertAdjacentElement('beforeend', nevySettingstabMenuWrapper);
//create a div that will contain a dialog
//create a wrapper div
const updateTab = document.createElement("div");
updateTab.classList.add("nevysha-update-tab", "nevysha", "nevysha-tab", "nevysha-tab-settings");
updateTab.id = "nevyui_update_info_panel";
updateTab.style = "display: none;";
document.querySelector("#tabs").insertAdjacentElement("beforeend", updateTab)
// add click event to the new update info button
function listenerClosure() {
let shown = false;
document.querySelector("#nevyui_update_info").addEventListener("click", (e) => {
//cancel event
e.preventDefault();
e.stopPropagation();
//show tab_nevyui by default to bypass gradio hidding tabs
document.querySelector("#tab_nevyui").style.display = "block";
//toggle the panel with a slide animation using jquery
if (shown) {
$("#nevyui_update_info_panel").slideUp(ANIMATION_SPEED);
} else {
$("#nevyui_update_info_panel").slideDown(ANIMATION_SPEED);
}
shown = !shown;
});
}
listenerClosure();
//fetch version_data.json
loadVersionData().then(ignored => ignored)
}
async function loadVersionData() {
const current_version_data = await (await fetch(`file=extensions/Cozy-Nest/version_data.json?${new Date()}`)).json()
const remote_version_data = await (await fetch(`https://raw.githubusercontent.com/Nevysha/Cozy-Nest/main/version_data.json?${new Date()}`)).json()
let remote_patchnote = await (await fetch(`https://raw.githubusercontent.com/Nevysha/Cozy-Nest/main/PATCHNOTE.md?t=${new Date()}`)).text();
//insert "Patchnote" title div
let patchnoteTitle = `<div class="nevysha-tabnav nevysha-tabnav-settings"><h2 class="nevysha-tabnav-title">Patchnote [${remote_version_data.version}]</h2></div>`;
//if local version si higher than remote version, fetch from local file (for dev purpose)
if (import.meta.env.VITE_CONTEXT === 'DEV' && isUpToDate(current_version_data.version, remote_version_data.version)) {
remote_patchnote = await (await fetch(`file=extensions/Cozy-Nest/PATCHNOTE.md?t=${new Date()}`)).text();
patchnoteTitle = `<div class="nevysha-tabnav nevysha-tabnav-settings"><h2 class="nevysha-tabnav-title">Patchnote [${current_version_data.version}_DEV]</h2></div>`;
}
document.querySelector("#nevyui_update_info_panel").insertAdjacentHTML('beforeend', patchnoteTitle);
//regex to replace [x] with a checkmark
const regex = /\[x\]/g;
remote_patchnote = remote_patchnote.replace(regex, ""); //TODO add icon ?
//regex to replace [ ] with a cross
const regex2 = /\[ \]/g;
remote_patchnote = remote_patchnote.replace(regex2, ""); //TODO add icon ?
const converter = new showdown.Converter();
const article = `<article class="markdown-body nevysha nevysha-scrollable">${converter.makeHtml(remote_patchnote)}</article>`
document.querySelector('#nevyui_update_info_panel').insertAdjacentHTML('beforeend', article)
//create a div that will contain info related to version compliance
const versionInfo = document.createElement("div");
versionInfo.classList.add("nevysha-version-info", "nevysha-emphasis");
versionInfo.id = "nevysha-version-info";
//add div to the beginning of the updateTab
document.querySelector('#nevyui_update_info_panel').insertAdjacentElement('afterbegin', versionInfo)
//add a button to update the extension
const updateBtn =
`<button class="nevysha-btn-menu lg primary gradio-button nevysha generate-button" id="nevyui_update_btn" title="Update Cozy Nest">Update</button>`;
document.querySelector('#nevysha-version-info').insertAdjacentHTML('beforeend', updateBtn)
document.querySelector('#nevyui_update_btn').addEventListener('click', (e) => {
//prevent default behavior
e.preventDefault();
e.stopPropagation();
//trigger click on nevyui_sh_options_update button
document.querySelector('#nevyui_sh_options_update').click()
//wait for 5s and trigger reloadUI by clicking settings_restart_gradio button
//change nevyui_update_btn to Update in progress
document.querySelector('#nevyui_update_btn').innerHTML = "Update in progress..."
setTimeout(() => {
document.querySelector('#settings_restart_gradio').click()
}, 5000);
});
//compare versions and display info
if (isUpToDate(current_version_data.version, remote_version_data.version)) {
//versions are the same
const p = `<p class="nevysha-version-info-text">You are up to date! (installed: v${current_version_data.version})</p>`
//add p to the beginning of nevysha-version-info
document.querySelector('#nevysha-version-info').insertAdjacentHTML('afterbegin', p)
//hide update button
document.querySelector('#nevyui_update_btn').style.display = "none";
//add version info to the bottom-right corner (no update available)
document.getElementsByClassName("versions")[0].innerHTML += 'Cozy Nest: <a href="https://github.com/Nevysha/Cozy-Nest" target="_blank">v' + current_version_data.version + '</a>';
}
else {
//local version is older than remote version
const p = `<p class="nevysha-version-info-text">An update is available! (installed: v${current_version_data.version}, new : v${remote_version_data.version})</p>`
//add p to the end of nevysha-version-info
document.querySelector('#nevysha-version-info').insertAdjacentHTML('afterbegin', p)
//set fill color of .nevysha-btn-menu-wrapper > button > svg to red
document.querySelector('#nevyui_update_info > svg').style.fill = "red";
//add version info to the bottom-right corner with a notice about an update
document.getElementsByClassName("versions")[0].innerHTML += 'Cozy Nest:<span style="color: #f9e02d; text-decoration: underline;" title="Nevysha\'s Cozy Nest Update Available! Latest version: v' + remote_version_data.version + '.\nView update info in the top-right corner for more details.">v' + current_version_data.version + '</span>';
}
//add close button
const closeBtn = `<button class="nevysha-btn-menu lg primary gradio-button nevysha generate-button" id="nevyui_update_info_close_btn" title="Close">Close</button>`;
document.querySelector('#nevysha-version-info').insertAdjacentHTML('afterbegin', closeBtn)
document.querySelector('#nevyui_update_info_close_btn').addEventListener('click', (e) => {
//prevent default behavior
e.preventDefault();
e.stopPropagation();
document.querySelector('#nevyui_update_info').click();
});
}
function createFolderListComponent() {
// create component to add and remove folders to scrap for images browser
const componentContainer = document.querySelector('#cnib_output_folder').parentElement;
const textarea = document.querySelector('#cnib_output_folder textarea');
componentContainer.classList.remove('hidden')
$(componentContainer).css('padding', '0 10px')
//add a label
const label = document.createElement('label');
label.classList.add('nevysha-label');
label.innerHTML = 'Folders to scrap for images';
componentContainer.appendChild(label);
function updateList(foldersList) {
document.querySelectorAll('.nevysha-image-browser-folder-container').forEach(el => el.remove());
textarea.value = JSON.stringify(foldersList);
//throw change event to update textarea
textarea.dispatchEvent(new Event('change'));
textarea.dispatchEvent(new Event('blur'));
parseAndDisplayFolderSettings();
}
function parseAndDisplayFolderSettings() {
const forldersListJson = textarea.value;
const foldersList = JSON.parse(forldersListJson);
for (const folderIndex in foldersList) {
const folder = foldersList[folderIndex];
const imageBrowserFolderContainer = document.createElement('div');
imageBrowserFolderContainer.classList.add('nevysha-image-browser-folder-container');
componentContainer.appendChild(imageBrowserFolderContainer);
//input for path
const imageBrowserFolder = document.createElement('textarea');
imageBrowserFolder.classList.add('nevysha-image-browser-folder');
imageBrowserFolder.value = folder;
imageBrowserFolder.setAttribute('enabled', 'false');
imageBrowserFolderContainer.appendChild(imageBrowserFolder);
//button to remove folder
const imageBrowserFolderRemoveBtn = document.createElement('button');
imageBrowserFolderRemoveBtn.classList.add('nevysha-image-browser-folder-btn');
imageBrowserFolderRemoveBtn.innerHTML = 'Remove';
imageBrowserFolderRemoveBtn.addEventListener('click', (e) => {
//prevent default behavior
e.preventDefault();
e.stopPropagation();
//remove from list using index
foldersList.splice(folderIndex, 1);
updateList(foldersList);
});
imageBrowserFolderContainer.appendChild(imageBrowserFolderRemoveBtn);
}
//add a last empty one to add a new folder
const imageBrowserFolderContainer = document.createElement('div');
imageBrowserFolderContainer.classList.add('nevysha-image-browser-folder-container');
componentContainer.appendChild(imageBrowserFolderContainer);
//input for path
const imageBrowserFolder = document.createElement('textarea');
imageBrowserFolder.classList.add('nevysha-image-browser-folder');
imageBrowserFolder.setAttribute("placeholder", 'Paste a folder path here...');
imageBrowserFolderContainer.appendChild(imageBrowserFolder);
//button to remove folder
const imageBrowserFolderAddBtn = document.createElement('button');
imageBrowserFolderAddBtn.classList.add('nevysha-image-browser-folder-btn');
imageBrowserFolderAddBtn.innerHTML = 'Add';
imageBrowserFolderAddBtn.addEventListener('click', (e) => {
//prevent default behavior
e.preventDefault();
e.stopPropagation();
const folder = imageBrowserFolder.value;
if (folder.length <= 0) {
showAlert('Warning',"Please enter a folder path to add.");
return;
}
const foldersListJson = textarea.value;
const foldersList = JSON.parse(foldersListJson);
//check if folder already exists
if (foldersList.includes(folder)) {
showAlert('Warning',"This folder is already in the list.");
return;
}
foldersList.push(folder);
updateList(foldersList);
});
imageBrowserFolderContainer.appendChild(imageBrowserFolderAddBtn);
}
parseAndDisplayFolderSettings();
}
function observeElementAdded(targetSelector, callback) {
// Create a new MutationObserver instance
const observer = new MutationObserver(function(mutationsList) {
for (const mutation of mutationsList) {
// Check if the target element is added
if (mutation.type === 'childList' && mutation.addedNodes.length > 0) {
for(const node of mutation.addedNodes) {
if(node.matches && node.matches(targetSelector)) {
observer.disconnect();
callback(node);
}
}
}
}
});
// Start observing the document body for changes
observer.observe(document.body, { childList: true, subtree: true });
}
function tweakExtraNetworks({prefix}) {
let extraNetworks = document.querySelector(`div#${prefix}_extra_networks`);
//hide base button txt2img_extra_networks
document.querySelector(`button#${prefix}_extra_networks`).style.display = 'none';
extraNetworks.style.display = 'flex'
// txt2img and img2img extra network are not built the same way in the DOM (:
// so we handle the DOM tweak differently
let extraNetworkGradioWrapper
let extraNetworkNevyshaWrapper
function tweakTabBehavior() {
//add an event listener to show it when the user clicks on the button #img2img_extra_networks
window.extraNetworkHandler = window.extraNetworkHandler || {};
window.extraNetworkHandler[prefix] = (e) => {
function closure() {
//tweak height
const $cards_html = $('div[id$="_cards_html"]');
$cards_html.css('height', `${document.querySelector(`#tab_${prefix}`).offsetHeight - 100}px`);
$cards_html.css('display', 'flex');
//add classes : 'nevysha', 'nevysha-scrollable'
const $cards = $('div[id$="_cards"]');
// $cards.css('height', '100%')
$cards.addClass('nevysha');
$cards.addClass('nevysha-scrollable');
const $subdirs = $('div[id$="_subdirs"]');
$subdirs.addClass('nevysha');
$subdirs.addClass('nevysha-scrollable');
let shown = extraNetworkGradioWrapper.style.display === 'flex';
if (!shown) {
//show the extra network
extraNetworkGradioWrapper.style.display = 'flex';
extraNetworkGradioWrapper.style.marginRight = `-${extraNetworkGradioWrapper.offsetWidth}px`;
$(extraNetworkGradioWrapper).animate({"margin-right": `+=${extraNetworkGradioWrapper.offsetWidth}`}, ANIMATION_SPEED);
} else {
//hide the extra network
$(extraNetworkGradioWrapper).animate({
"margin-right": `-=${extraNetworkGradioWrapper.offsetWidth}`},
ANIMATION_SPEED,
() => {
// hide it after the animation is done
extraNetworkGradioWrapper.style.display = 'none';
});
}
}
if (document.querySelector(`#${prefix}_textual_inversion_cards`)) {
closure();
}
else {
// Start observing the target node
observeElementAdded(`#${prefix}_textual_inversion_cards`, closure);
}
};
//add a listener to close the extra network when the user press the escape key
document.addEventListener('keydown', (e) => {
let shown = extraNetworkGradioWrapper.style.display === 'flex';
if (e.key === 'Escape' && shown) {
$(extraNetworkGradioWrapper).animate({"margin-right": `-=${extraNetworkGradioWrapper.offsetWidth}`}, ANIMATION_SPEED, () => {
extraNetworkGradioWrapper.style.display = 'none';
});
}
});
}
if (prefix === 'img2img') {
extraNetworkGradioWrapper = document.createElement('div');
extraNetworkGradioWrapper.setAttribute('id', `${prefix}_extra_networks_parent`);
//hide it
extraNetworkGradioWrapper.style.display = 'none';
tweakTabBehavior();
//add extraNetworkGradioWrapper at the beginning of the extraNetworks parent
extraNetworks.parentElement.insertAdjacentElement('afterbegin', extraNetworkGradioWrapper);
extraNetworkNevyshaWrapper = document.createElement('div');
extraNetworkNevyshaWrapper.setAttribute('id', `${prefix}_extra_networks_nevysha_wrapper`);
extraNetworkNevyshaWrapper.appendChild(extraNetworks);
extraNetworkGradioWrapper.appendChild(extraNetworkNevyshaWrapper);
}
else if (prefix === 'txt2img') {
// move everything that's inside extraNetworkGradioWrapper to a new div
extraNetworkGradioWrapper = extraNetworks.parentElement;
extraNetworkGradioWrapper.setAttribute('id', `${prefix}_extra_networks_parent`);
//hide it
extraNetworkGradioWrapper.style.display = 'none';
extraNetworkNevyshaWrapper = document.createElement('div');
extraNetworkNevyshaWrapper.setAttribute('id', `${prefix}_extra_networks_nevysha_wrapper`);
extraNetworkNevyshaWrapper.appendChild(extraNetworks);
extraNetworkGradioWrapper.appendChild(extraNetworkNevyshaWrapper);
tweakTabBehavior();
}
//apply the width saved in local storage
const extraNetworksWidth = localStorage.getItem('nevysha_extra_networks_width');
if (extraNetworksWidth) {
extraNetworkGradioWrapper.style.width = extraNetworksWidth;
}
// Create a vertical line component
const lineWrapper = createVerticalLineComp();
//add the line to the beginning of the extraNetworkNevyshaWrapper
extraNetworkNevyshaWrapper.insertBefore(lineWrapper, extraNetworkNevyshaWrapper.firstChild);
//add a close button inside the line
const closeENButton = document.createElement('button');
closeENButton.setAttribute('id', `${prefix}_floating_close_extra_networks`);
//add button class
closeENButton.classList.add('nevysha', 'lg', 'primary', 'gradio-button', 'nevysha-extra-network-floating-btn');
closeENButton.innerHTML = '<div>Close</div>';
//click the original button to close the extra network
closeENButton.addEventListener('click', (e) => {
window.extraNetworkHandler[prefix]();
});
//add the button at the begining of the div
lineWrapper.insertBefore(closeENButton, lineWrapper.firstChild);
// Add an event listener to the resizer element to track mouse movement
lineWrapper.addEventListener('mousedown', function(e) {
e.preventDefault();
// Set the initial values for the width and height of the container
let width = extraNetworkGradioWrapper.offsetWidth;
// Set the initial mouse position
let x = e.clientX;
// Track mouse movement while dragging
document.addEventListener('mousemove', drag);
document.addEventListener('mouseup', stopDrag);
function drag(e) {
// Calculate the difference in mouse position
const diffX = e.clientX - x;
// Update the container's width
extraNetworkGradioWrapper.style.width = (width - diffX) + 'px';
}
function stopDrag() {
//save the new width in local storage
localStorage.setItem(`nevysha_extra_networks_width`, extraNetworkGradioWrapper.style.width);
document.removeEventListener('mousemove', drag);
document.removeEventListener('mouseup', stopDrag);
}
});
}
function addExtraNetworksBtn({prefix}) {
//create button
const extraNetworksBtn = document.createElement('button');
extraNetworksBtn.setAttribute('id', `${prefix}_extra_networks_right_button`);
extraNetworksBtn.classList.add('nevysha', 'lg', 'primary', 'gradio-button');
extraNetworksBtn.innerHTML = '<div>Extra Networks</div>';
//click the original button to close the extra network
extraNetworksBtn.addEventListener('click', (e) => {
if (!e.isTrusted) return
window.extraNetworkHandler[prefix]();
});
//add button to the begining of the wrapper div
const rightPanBtnWrapper = document.querySelector(`div#right_button_wrapper`);
rightPanBtnWrapper.insertBefore(extraNetworksBtn, rightPanBtnWrapper.firstChild);
if (prefix === 'img2img') {
document.querySelector(`#${prefix}_extra_networks_right_button`).style.display = 'none';
}
}
const addTabWrapper = () => {
const tabWrapper = document.createElement('button');
//add tabWrapper after the gradio tab
const gradioTab = document.querySelector(`div#tabs > .tab-nav`);
tabWrapper.setAttribute('id', `nevysha_tab_wrapper`);
tabWrapper.classList.add('nevysha', 'tab-wrapper',);
//retrive the svelte random class from the gradioTab.firstChild css class. it start with "svelte-"
gradioTab.querySelector('button').classList.forEach((className) => {
if (className.startsWith('svelte-')) {
tabWrapper.classList.add(className);
}
})
tabWrapper.innerHTML = `Others`;
//insert before nevysha-btn-menu-wrapper
gradioTab.insertBefore(tabWrapper, gradioTab.lastChild);
//create a div that will hold the other tabs
const otherTabs = document.createElement('div');
otherTabs.setAttribute('id', `nevysha_other_tabs`);
otherTabs.classList.add('nevysha', 'other-tabs',);
otherTabs.style.display = 'none';
//add at the end of div#tabs
tabWrapper.appendChild(otherTabs);
//show floating div when click on tabWrapper
let shown = false;
tabWrapper.addEventListener('click', (e) => {
//cancel event
e.preventDefault();
e.stopPropagation();
//toggle the display of the floating div
//toggle the panel with a slide animation using jquery
if (shown) {
$("#nevysha_other_tabs").slideUp(100);
} else {
$("#nevysha_other_tabs").slideDown(200);
}
shown = !shown;
});
//go through all the tabs button in gradioTab and add a drag event listener
function dragStart(event) {
event.dataTransfer.setData("text/plain", event.target.id);
}
function dragEnd(event) {
//add tab in moved tab array in local storage
const movedTabs = JSON.parse(localStorage.getItem('nevysha_moved_tabs')) || [];
//check if the tab is already in the array
if (!movedTabs.includes(event.target.id)) {
movedTabs.push(event.target.id);
localStorage.setItem('nevysha_moved_tabs', JSON.stringify(movedTabs));
}
}
function dragOver(event) {
event.preventDefault();
}
function cloneAndPush(tab) {
//check if the tab is not already in the otherTabs
if (otherTabs.querySelector(`button #${tab.id}`)) {
return;
}
const newTab = document.createElement('button');
newTab.setAttribute('id', tab.id);
newTab.classList.add('nevysha', 'tab-nav', 'nevysha-other-tab');
// newTab.innerHTML = `<span id="remove-${tab.id}" class="remove-nevysha-other-tab">X</span><span class="nevysha-other-tab-text">${tab.innerHTML}</span>`;
newTab.setAttribute('draggable', true);
newTab.addEventListener("dragstart", dragStart);
newTab.addEventListener("dragend", dragEnd);
newTab.addEventListener("click", (e) => {
//cancel event
e.preventDefault();
e.stopPropagation();
document.querySelector(`div#tabs > .tab-nav > #${tab.id}`).click();
//hide the floating div
$("#nevysha_other_tabs").slideUp();
shown = !shown;
});
tab.style.display = 'none';
//add the new tab to the otherTabs
otherTabs.appendChild(newTab);
const removeBtn = document.createElement('span');
removeBtn.setAttribute('id', `remove-${tab.id}`);
removeBtn.classList.add('remove-nevysha-other-tab');
removeBtn.innerHTML = 'X';
removeBtn.addEventListener('click', (e) => {
//cancel event
e.preventDefault();
e.stopPropagation();
//remove the tab from the otherTabs
otherTabs.removeChild(newTab);
//show the original tab
document.querySelector(`div#tabs > .tab-nav > #${tab.id}`).style.display = 'block';
//remove the tab from the moved tab array in local storage
const movedTabs = JSON.parse(localStorage.getItem('nevysha_moved_tabs')) || [];
const index = movedTabs.indexOf(tab.id);
if (index > -1) {
movedTabs.splice(index, 1);
}
localStorage.setItem('nevysha_moved_tabs', JSON.stringify(movedTabs));
});
newTab.appendChild(removeBtn);
const tabText = document.createElement('span');
tabText.classList.add('nevysha-other-tab-text');
tabText.innerHTML = tab.innerHTML;
newTab.appendChild(tabText);
}
function drop(event) {
event.preventDefault();
//create a new tab with the same content as the original tab and hide the original tab
const tab = document.querySelector(`button#${event.dataTransfer.getData("text/plain")}`);
cloneAndPush(tab);
shown = !shown;
}
otherTabs.addEventListener("dragover", dragOver);
otherTabs.addEventListener("drop", drop);
const tabs = gradioTab.querySelectorAll('button');
function addHandlerToTab(tab) {
//skip the tabWrapper
if (tab.id
// below : some tabs have no inner text, skip them
|| !tab.innerText) {
return;
}
// set an id for the tab from its text
tab.id =
tab.innerText.toLowerCase()
//remove all non-alphanumeric characters
.replace(/\s/g, '_')
//remove numbers
.replace(/[0-9]/g, '')
//set draggable to true
tab.setAttribute('draggable', true);
tab.addEventListener("dragstart", dragStart);
tab.addEventListener("dragend", dragEnd);
}
tabs.forEach((tab) => {
addHandlerToTab(tab);
//check if tab is marked as moved in local storage
const movedTabs = JSON.parse(localStorage.getItem('nevysha_moved_tabs')) || [];
if (movedTabs.includes(tab.id)) {
cloneAndPush(tab)
}
});
//add a MutableObserver on gradioTab to handle gradio deleting and recreating the tab when it is clicked
const observer = new MutationObserver((mutations) => {
mutations.forEach((mutation) => {
if (mutation.type !== 'childList') return
if (mutation.addedNodes.length === 0) return
const tab = mutation.addedNodes[0];
addHandlerToTab(tab);
//check if tab is marked as moved in local storage
const movedTabs = JSON.parse(localStorage.getItem('nevysha_moved_tabs')) || [];
if (movedTabs.includes(tab.id)) {
tab.style.display = 'none';
cloneAndPush(tab)
}
});
});
//observe on added nodes
const config = { attributes: false, childList: true, subtree: false };
observer.observe(gradioTab, config);
}
function buildRightSlidePanelFor(label, buttonLabel, rightPanBtnWrapper, tab) {
const cozyImgBrowserBtn = document.createElement('button');
cozyImgBrowserBtn.setAttribute('id', `${label}_right_button`);
cozyImgBrowserBtn.classList.add('nevysha', 'lg', 'primary', 'gradio-button');
cozyImgBrowserBtn.innerHTML = `<div>${buttonLabel}</div>`;
rightPanBtnWrapper.appendChild(cozyImgBrowserBtn);
//create a panel to display Cozy Image Browser
const cozyImgBrowserPanel =
`<div id="${label}_panel" class="nevysha slide-right-browser-panel" style="display: none">
<div class="nevysha slide-right-browser-panel-container nevysha-scrollable">
<div class="nevysha" id="${label}-react"/>
</div>
</div>`;
//add the panel to the end of the tab
tab.insertAdjacentHTML('beforeend', cozyImgBrowserPanel);
// Create a vertical line component
const lineWrapper = createVerticalLineComp();
const cozyImgBrowserPanelWrapper = document.querySelector(`#${label}_panel`);
//set cozyImgBrowserPanelWrapper.style.width from local storage value if it exists
const cozyImgBrowserPanelWidth = localStorage.getItem(`${label}_panelWidth`);
if (cozyImgBrowserPanelWidth) {
cozyImgBrowserPanelWrapper.style.width = cozyImgBrowserPanelWidth;
}
cozyImgBrowserPanelWrapper.appendChild(lineWrapper)
//TODO refactor to factorise code bellow with extraNetwork
//add a close button inside the line
const closeCozyImgBrowser = document.createElement('button');
closeCozyImgBrowser.setAttribute('id', `floating_close_${label}__panel_button`);
//add button class
closeCozyImgBrowser.classList.add('nevysha', 'lg', 'primary', 'gradio-button', 'nevysha-extra-network-floating-btn');
closeCozyImgBrowser.innerHTML = '<div>Close</div>';
//click the original button to close the extra network
closeCozyImgBrowser.addEventListener('click', (e) => {
cozyImgBrowserBtn.click();
});
//add the button at the begining of the div
lineWrapper.insertBefore(closeCozyImgBrowser, lineWrapper.firstChild);
//Add an event listener to the resizer element to track mouse movement
lineWrapper.addEventListener('mousedown', function (e) {
e.preventDefault();
// Set the initial values for the width and height of the container
let width = cozyImgBrowserPanelWrapper.offsetWidth;
// Set the initial mouse position
let x = e.clientX;
// Track mouse movement while dragging
document.addEventListener('mousemove', drag);
document.addEventListener('mouseup', stopDrag);
function drag(e) {
// Calculate the difference in mouse position
const diffX = e.clientX - x;
// Update the container's width
cozyImgBrowserPanelWrapper.style.width = (width - diffX) + 'px';
}
function stopDrag() {
//save the new width in local storage
localStorage.setItem(`${label}_panelWidth`, cozyImgBrowserPanelWrapper.style.width);
document.removeEventListener('mousemove', drag);
document.removeEventListener('mouseup', stopDrag);
}
});
//add listener to open or close the panel using jquery animate
cozyImgBrowserBtn.addEventListener('click', function(e) {
e.preventDefault();
e.stopPropagation();
const panel = document.querySelector(`#${label}_panel`);
if (panel.style.display === 'none') {
panel.style.display = 'flex'
panel.style.marginRight = `-${panel.offsetWidth}px`;
$(panel).animate({"margin-right": `+=${panel.offsetWidth}`}, ANIMATION_SPEED);
} else {
$(panel).animate({"margin-right": `-=${panel.offsetWidth}`}, ANIMATION_SPEED, () => {
panel.style.display = 'none'
});
}
});
}
function createRightWrapperDiv() {
const tab = document.querySelector(`div#tabs`);
//create wrapper div for the button
const rightPanBtnWrapper = document.createElement('div');
rightPanBtnWrapper.setAttribute('id', `right_button_wrapper`);
rightPanBtnWrapper.classList.add('nevysha', 'nevysha-right-button-wrapper');
//add button to the begining of the tab
tab.insertAdjacentElement('beforeend', rightPanBtnWrapper);
if (COZY_NEST_CONFIG.enable_extra_network_tweaks === true) {
buildRightSlidePanelFor('cozy-txt2img-extra-network', 'Extra Network', rightPanBtnWrapper, tab);
document.getElementById('cozy-txt2img-extra-network-react').classList.add('cozy-extra-network')
buildRightSlidePanelFor('cozy-img2img-extra-network', 'Extra Network', rightPanBtnWrapper, tab);
document.getElementById('cozy-img2img-extra-network-react').classList.add('cozy-extra-network')
document.querySelector(`#cozy-img2img-extra-network_right_button`).style.display = 'none';
}
if (COZY_NEST_CONFIG.disable_image_browser !== true) {
buildRightSlidePanelFor('cozy-img-browser', 'Cozy Image Browser', rightPanBtnWrapper, tab);
}
}
function setButtonVisibilityFromCurrentTab(id) {
CozyLogger.debug(`setButtonVisibilityFromCurrentTab(${id})`);
document.querySelector(`#cozy-txt2img-extra-network_right_button`).style.display = 'none';
document.querySelector(`#cozy-img2img-extra-network_right_button`).style.display = 'none';
if (id === 'tab_txt2img') {
document.querySelector(`#cozy-txt2img-extra-network_right_button`).style.display = 'flex';
}
else if (id === 'tab_img2img') {
document.querySelector(`#cozy-img2img-extra-network_right_button`).style.display = 'flex';
}
}
function errorPipe(data) {
populateInstanceInfoDialog();
document.querySelector('#cozy_nest_error_handling_display').innerHTML = `An error in socket handler`;
document.querySelector('#cozy_nest_error_handling_display_stack').innerHTML = data.error;
document.querySelector('#cozy_nest_error_handling_display_stack').setAttribute('style', 'display: block;');
showInstanceInfoDialog();
}
window.errorPipe = errorPipe;
async function sendToPipe(where, elemImgFrom) {
class FakeDataTransfer {
constructor(imgFromBlob) {
this.files = [imgFromBlob];
}
}
// let elemImgFrom = document.querySelector("#cozy-img-browser-react > div.browser.nevysha.nevysha-scrollable > div:nth-child(1) > div.image-wrapper > img");
let inputTo = document.querySelector("#nevysha-send-to > #nevysha_pnginfo_image > div.image-container > div")
//get image file from elemImgFrom.src
const blob = await fetch(elemImgFrom.src).then(r => r.blob());
// Create a new drop event
let dropEvent = new DragEvent("drop", {
bubbles: true,
cancelable: true,
});
Object.defineProperty(dropEvent, 'dataTransfer', {
value: new FakeDataTransfer(blob)
});
// Dispatch the drop event on the target element
inputTo.dispatchEvent(dropEvent);
//ensure navigation to the dedicated tab
if (where === 'txt2img') {
document.querySelector('#tabs > div.tab-nav').querySelector('#txtimg').click();
}
else if (where === 'img2img') {
document.querySelector('#tabs > div.tab-nav').querySelector('#imgimg').click();
document.querySelectorAll('#mode_img2img > div > button')[0].click();
}
else if (where === 'inpainting') {
document.querySelector('#tabs > div.tab-nav').querySelector('#imgimg').click();
document.querySelectorAll('#mode_img2img > div > button')[2].click();
}
//wait a bit for the gradio loading to end and click #nevysha-send-to-button > button#txt2img_tab
setTimeout(() => {
let clicker;
if (where === 'txt2img') {
clicker = document.querySelector("#nevysha-send-to-button > button#txt2img_tab")
}
else if (where === 'img2img') {
clicker = document.querySelector("#nevysha-send-to-button > button#img2img_tab")
}
else if (where === 'inpainting') {
clicker = document.querySelector("#nevysha-send-to-button > button#inpaint_tab")
}
clicker.click()
}, 1000)
}
window.sendToPipe = sendToPipe;
window.troubleshootSize = {}
function addOptionsObserver() {
// Select the target node
const targetNode = document.body;
function recalcOptionsMaxHeight(addedNode) {
const options = addedNode;
options.style.position = 'fixed';
//look for a parent with class "gradio-dropdown"
const parent = options.parentElement;
options.style.width = parent.offsetWidth + 'px';
//check if the parent is near the top or bottom of the window
const parentRect = parent.getBoundingClientRect();
const windowHeight = window.innerHeight;
// Calculate the distance of the element from the top and bottom of the window
const distanceFromTop = parentRect.top;
const distanceFromBottom = windowHeight - parentRect.bottom;
// Compare the distances and determine the position
if (distanceFromTop < distanceFromBottom) {
//closer to the top
options.style.top = (parentRect.top + parentRect.height) + 'px';
} else {
//closer to the bottom
options.style.bottom = (window.innerHeight - parentRect.top) + 'px';
}
}
// Create a function to be called when mutations are observed
const mutationCallback = function (mutationsList, observer) {
for (const mutation of mutationsList) {
if (mutation.type === 'childList') {
// Check if a new element with class "options" is added
for (const addedNode of mutation.addedNodes) {
if (addedNode.nodeType === 1 && addedNode.classList.contains('options')) {
// Call your desired function when a new element is added
recalcOptionsMaxHeight(addedNode);
}
}
}
}
};
// Create a new observer instance
const observer = new MutationObserver(mutationCallback);
// Configure the observer to only look for childList changes
const config = { childList: true, subtree: true };
// Start observing the target node for mutations
observer.observe(targetNode, config);
}
const onloadSafe = (done) => {
// try {
onLoad(done);
// } catch (e) {
// console.error("Failed to init Cozy Nest", e);
// done();
// }
}
const onLoad = (done) => {
let gradioApp = window.gradioApp;
if (typeof gradioApp !== "function") {
setTimeout(() => onloadSafe(done), 200);
return
}
const quicksettings = gradioApp().getElementById("quicksettings")
if (!quicksettings) {
setTimeout(() => onloadSafe(done), 200);
return
}
// log time for onLoad execution after gradio has loaded
SimpleTimer.time(COZY_NEST_DOM_TWEAK_LOAD_DURATION);
// check for gradio theme (vlad's fork)
if (document.querySelector('#setting_gradio_theme input')) {
const gradioTheme = document.querySelector('#setting_gradio_theme input').value
if (gradioTheme !== 'gradio/default' && gradioTheme !== '' && gradioTheme !== 'Default') {
showAlert('Warning','Cozy Nest may not be compatible with this theme. Please switch to the default theme. You can do this by going to the settings tab and selecting "gradio/default" or "Default" from the dropdown menu under "User interface > UI theme".')
}
}
//add quicksettings_gap after checkpoint reload button
// Select the target element
const refresh_sd_model_checkpoint = document.querySelector('#refresh_sd_model_checkpoint');
// Create a new div element
const quicksettings_gap = "<div id='quicksettings_gap' class='nevysha' />"
// Insert the new div after the target element
refresh_sd_model_checkpoint.insertAdjacentHTML('afterend', quicksettings_gap);
//get body from DOM
const body = document.querySelector("body")
//remove default body style
body.style = ""
//add triple wave div to the end of body for uwuness
body.insertAdjacentHTML('beforeend', waves);
const tabs = document.querySelectorAll('#tabs > .tabitem')
//add nevysha css class to each main tab for easier css
tabs.forEach(tab => tab.setAttribute('class', `${tab.getAttribute('class')} nevysha`))
//add nevysha css class to tabnav
document.querySelectorAll('#tabs > div.tab-nav').forEach(tabnav => tabnav.setAttribute('class', `${tabnav.getAttribute('class')} nevysha nevysha-tabnav`))
document.querySelectorAll('input[type="number"]').forEach(input => input.setAttribute('class', `${input.getAttribute('class')} nevysha`))
//add .nevysha-scrollable to some elem
document.querySelectorAll('.extra-network-cards').forEach(elem => elem.setAttribute('class', `${elem.getAttribute('class')} nevysha nevysha-scrollable`))
document.querySelectorAll('#cozy_nest_settings_tabs > .tabitem').forEach(elem => elem.classList.add('nevysha', 'nevysha-scrollable'))
//hide "send to" panel in settings
//this panel is used to transfert image data into tab
document.querySelector('#nevysha-send-to').setAttribute('style', 'display: none;')
//create a wrapper div on the right for slidable panels
createRightWrapperDiv();
onUiTabChange(() => {
CozyLogger.debug("onUiTabChange", get_uiCurrentTabContent().id);
if (COZY_NEST_CONFIG.enable_extra_network_tweaks) {
setButtonVisibilityFromCurrentTab(get_uiCurrentTabContent().id);
}
});
//manage text2img tab
const nevysha_magic = (bundle) => {
wrapSettings(bundle);
wrapDataGenerationInfo(bundle);
addDraggable(bundle);
addScrollable(bundle);
//add a clear button to generated image
clearGeneratedImage(bundle);
}
nevysha_magic({prefix: "txt2img"});
nevysha_magic({prefix: "img2img"});
//general
tweakButtonsIcons();
//style tweak to be MORE IMPORTANT than important
gradioApp().querySelector('.tabs').querySelectorAll(".block.padded:not(.gradio-accordion, .gradio-dropdown, #nevyui_sh_options)").forEach(elem => elem.setAttribute("style", `${elem.getAttribute("style")} padding: 10px !important;`))
gradioApp().querySelectorAll('#quicksettings > div.block').forEach(elem => elem.style.padding = "0 !important")
//add expend to inpainting
tweakInpainting();
addCozyNestCustomBtn();
//load settings
recalcOffsetFromMenuHeight();
//add tab wrapper
addTabWrapper();
//apply theme
if (getTheme() === "light") {
document.querySelector("body").classList.add("nevysha-light")
document.querySelectorAll('.gradio-accordion').forEach(elem => elem.setAttribute("style", `${elem.getAttribute("style")} box-shadow: 1px 1px 3px rgba(0, 0, 0, 0.3) !important;`))
}
else {
document.querySelector("body").classList.remove("nevysha-light")
}
//add observer for .options resize
addOptionsObserver();
/* --------------- TWEAK SOME EXTENSION --------------- */
//if AWQ-container is present in COZY_NEST_CONFIG.extensions array from localStorage, tweak AWQ
if (COZY_NEST_CONFIG.extensions
&& COZY_NEST_CONFIG.extensions.length > 0
&& COZY_NEST_CONFIG.extensions.includes("SDAtom-WebUi-client-queue-ext")) {
tweakAWQ();
}
done();
};
export async function saveCozyNestConfig(config) {
config = config || COZY_NEST_CONFIG;
await fetch('/cozy-nest/config', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify(config)
})
}
async function detectWebuiContext() {
// we try to determine the webui. This should have been made during startup, but we need a fallback
// because this function is not available before a recent commit of sd.next
if (COZY_NEST_CONFIG.webui === WEBUI_UNKNOWN) {
CozyLogger.debug("webui is UNKNOWN. Trying to detect it.")
//check for meta tag to verify if we are in SD.Next
const metaTitle = document.querySelector('meta[property="og:title"]')?.getAttribute('content');
if (metaTitle && metaTitle === WEBUI_SDNEXT) {
COZY_NEST_CONFIG.webui = WEBUI_SDNEXT;
shouldDisplaySDNextWarning = true;
}
else {
COZY_NEST_CONFIG.webui = WEBUI_A1111;
}
await saveCozyNestConfig();
}
CozyLogger.debug(`webui is ${COZY_NEST_CONFIG.webui}`)
}
/**
* Need to be called after the page and the script is loaded
* either from main.js for Dev or from the loader in the extension folder
* @returns {Promise<void>}
*/
export default async function cozyNestModuleLoader(extraCozyNestModulesLoader) {
//check if the param CozyNest=No is present in the url
const urlParams = new URLSearchParams(window.location.search);
const cozyNestParam = urlParams.get('CozyNest');
if (cozyNestParam === "No") {
CozyLogger.log("disabled by url param")
//remove the css with Cozy-Nest in the url
document.querySelectorAll('link').forEach(link => {
if (link.href.includes("Cozy-Nest")) link.remove()
})
return
}
// fetch file=extensions/Cozy-Nest/nevyui_settings.json. add Date to avoid cache
await fetchCozyNestConfig();
await detectWebuiContext();
Loading.start();
createAlertDiv();
setupPopupInstanceInfo();
setupErrorHandling();
// wrap onloadSafe in Promise
return new Promise(resolve => {
onloadSafe(async () => {
await extraCozyNestModulesLoader();
//remove #nevysha-loading from DOM
Loading.stop();
CozyLogger.log(`running.`);
SimpleTimer.end(COZY_NEST_DOM_TWEAK_LOAD_DURATION);
SimpleTimer.end(COZY_NEST_GRADIO_LOAD_DURATION);
if (shouldDisplaySDNextWarning)
showAlert(
"Warning",
"Cozy Nest detected that you are using SD.Next and running Cozy Nest for the first time. To ensure compatibility, please restart the server."
)
resolve();
});
})
};
function setupErrorHandling() {
//set a global error handler
window.addEventListener('error', function ({message, filename , lineno, colno, error }) {
//TODO uncomment
// // get setting_nevyui_errorPopup checkbox value
// const errorPopup = document.querySelector('#setting_nevyui_errorPopup').querySelector("input").checked;
// if (!errorPopup) return;
//
// //if filename does not contains Cozy-Nest, ignore
// if (!filename.toLowerCase().includes('cozy-nest')) return;
//
// // Handle the error here
// populateInstanceInfoDialog();
// document.querySelector('#cozy_nest_error_handling_display').innerHTML = `An error occurred: ${message} at ${filename } line ${lineno} column ${colno}`;
// document.querySelector('#cozy_nest_error_handling_display_stack').innerHTML = error.stack;
// document.querySelector('#cozy_nest_error_handling_display_stack').setAttribute('style', 'display: block;');
// showInstanceInfoDialog();
});
}
let COZY_NEST_CONFIG;
let shouldDisplaySDNextWarning = false;
export async function fetchCozyNestConfig() {
const response = await fetch(`file=extensions/Cozy-Nest/nevyui_settings.json?t=${Date.now()}`);
if (response.ok) {
COZY_NEST_CONFIG = await response.json();
//save in local storage
localStorage.setItem('COZY_NEST_CONFIG', JSON.stringify(COZY_NEST_CONFIG));
window.COZY_NEST_CONFIG = COZY_NEST_CONFIG;
}
}