| |
| const script = document.createElement('script'); |
| script.src = 'file=extensions/sd-model-organizer/javascript/tinymce/tinymce.min.js'; |
| document.head.appendChild(script); |
|
|
| let isHomeInitialStateInvoked = false |
|
|
| |
| |
| |
| |
| |
| function findElem(elementId) { |
| return document.getElementById(elementId) |
| |
| } |
|
|
| |
| |
| |
| |
| function logMo(text) { |
| |
| } |
|
|
| |
| |
| |
| |
| |
| function setupDescriptionPreview(content, theme) { |
| if (tinymce.get('mo-description-preview') == null) { |
| tinymce.init({ |
| selector: '#mo-description-preview', |
| toolbar: false, |
| menubar: false, |
| statusbar: false, |
| promotion: false, |
| plugins: 'autoresize', |
| skin: theme === 'dark' ? 'oxide-dark' : 'oxide', |
| content_css: theme === 'dark' ? 'dark' : 'default', |
| init_instance_callback: function (inst) { |
| inst.mode.set("readonly") |
| inst.setContent(content) |
| } |
| }); |
| } |
|
|
| const inst = tinymce.get('mo-description-preview') |
| if (inst.initialized) { |
| inst.setContent(content) |
| } |
| } |
|
|
| |
| |
| |
| |
| |
| function handleDescriptionPreviewContentChange(content) { |
| logMo('handleDescriptionPreviewContentChange') |
|
|
| getTheme() |
| .then(theme => { |
| setupDescriptionPreview(content, theme) |
| }) |
|
|
| return [] |
| } |
|
|
| |
| |
| |
| |
| |
| function setupDescriptionEdit(content, theme) { |
| let contentData = content.replace(/<\[\[token=".*?"]]>/, ''); |
|
|
| if (tinymce.get('mo-description-editor') == null) { |
| tinymce.init({ |
| selector: '#mo-description-editor', |
| promotion: false, |
| plugins: 'anchor autolink charmap codesample emoticons image link lists media searchreplace table visualblocks', |
| toolbar: 'undo redo | blocks fontfamily fontsize | bold italic underline strikethrough | link image media table mergetags | addcomment showcomments | spellcheckdialog a11ycheck typography | align lineheight | checklist numlist bullist indent outdent | emoticons charmap | removeformat', |
| skin: theme === 'dark' ? 'oxide-dark' : 'oxide', |
| content_css: theme === 'dark' ? 'dark' : 'default', |
| init_instance_callback: function (inst) { |
| inst.setContent(contentData) |
| } |
| }); |
| } |
|
|
| const inst = tinymce.get('mo-description-editor') |
| if (inst.initialized) { |
| inst.setContent(contentData) |
| } |
| } |
|
|
| |
| |
| |
| |
| |
| function handleDescriptionEditorContentChange(content) { |
| logMo('handleDescriptionEditorContentChange') |
|
|
| getTheme() |
| .then(theme => { |
| setupDescriptionEdit(content, theme) |
| }) |
|
|
| return [] |
| } |
|
|
| function handleRecordSave() { |
| logMo('Handling record save') |
|
|
| |
| const token = '<[[token="' + generateUUID() + '"]]>' |
|
|
| let output; |
| if (tinymce.get('mo-description-editor') == null) { |
| output = token |
| } else { |
| output = token + tinymce.get('mo-description-editor').getContent() |
| } |
|
|
| const textArea = findElem('mo-description-output-widget').querySelector('textarea') |
| const event = new Event('input', { 'bubbles': true, "composed": true }); |
| textArea.value = output |
| findElem('mo-description-output-widget').querySelector('textarea').dispatchEvent(event); |
| logMo('Description content dispatched: ' + output) |
| return [] |
| } |
|
|
| function updateDownloadBlockVisibility(id, tag, isVisible, visibleUnit) { |
| const block = findElem(tag + '-' + id) |
| const previewBlock = findElem(tag + '-preview-' + id) |
|
|
| if (block) { |
| block.style.display = isVisible ? visibleUnit : 'none' |
| logMo(block.id + " display =" + block.style.display) |
| } |
|
|
| if (previewBlock) { |
| previewBlock.style.display = isVisible ? visibleUnit : 'none' |
| logMo(previewBlock.id + " display =" + previewBlock.style.display) |
| } |
| } |
|
|
| function updateDownloadCardState(id, state) { |
| let cardClass = '' |
| let isUrlVisible = false |
| let isDownloadProgressVisible = false |
| let isResultBoxVisible = false |
|
|
| if (state === 'Pending') { |
| cardClass = 'mo-alert-secondary' |
| isUrlVisible = true |
| } else if (state === 'In Progress') { |
| cardClass = 'mo-alert-primary' |
| isUrlVisible = true |
| isDownloadProgressVisible = true |
| } else if (state === 'Completed') { |
| cardClass = 'mo-alert-success' |
| isResultBoxVisible = true |
| } else if (state === 'Exists') { |
| cardClass = 'mo-alert-info' |
| isResultBoxVisible = true |
| } else if (state === 'Error') { |
| cardClass = 'mo-alert-danger' |
| isResultBoxVisible = true |
| } else if (state === 'Cancelled') { |
| cardClass = 'mo-alert-warning' |
| } else { |
| return |
| } |
| logMo(cardClass) |
| const className = 'mo-downloads-card ' + cardClass |
| logMo(className) |
| const cardElement = findElem('download-card-' + id) |
| cardElement.className = className |
| findElem('status-' + id).textContent = state |
|
|
| updateDownloadBlockVisibility(id, 'url', isUrlVisible, 'block') |
| updateDownloadBlockVisibility(id, 'info-bar', isDownloadProgressVisible, 'flex') |
| updateDownloadBlockVisibility(id, 'progress', isDownloadProgressVisible, 'flex') |
| updateDownloadBlockVisibility(id, 'result-box', isResultBoxVisible, 'block') |
| } |
|
|
| function updateResultText(id, title, text) { |
| const elem = findElem('result-box-' + id) |
| if (elem) { |
| let resultContent = '<p>' + title + ':</p>' |
| if (Array.isArray(text)) { |
| text.forEach(function (txt) { |
| resultContent += '<p style="margin-left: 1rem; padding: 0 !important; line-height: 1.4 !important;">' |
| resultContent += txt |
| resultContent += '</p>' |
| }); |
| } else { |
| resultContent += '<p style="margin-left: 1rem; padding: 0 !important; line-height: 1.4 !important;">' |
| resultContent += text |
| resultContent += '</p>' |
| } |
| elem.innerHTML = resultContent |
| } |
| } |
|
|
| function updateText(id, tag, isPreview, value) { |
| const p = isPreview ? '-preview-' : '-' |
| const elem = findElem(tag + p + id) |
| if (elem) { |
| elem.textContent = value |
| } |
| } |
|
|
| function updateProgressBar(id, tag, isPreview, value) { |
| const p = isPreview ? '-preview-' : '-' |
| const elem = findElem(tag + p + id) |
| if (elem) { |
| const val = value + '%' |
| elem.style.width = val |
| elem.textContent = val |
| } |
| } |
|
|
| function handleProgressUpdates(value) { |
| const data = JSON.parse(value); |
|
|
| if (data.hasOwnProperty('records')) { |
| data.records.forEach(function (item, index) { |
| handleRecordUpdates(item) |
| }); |
| } |
|
|
| return [] |
| } |
|
|
| function handleRecordUpdates(data) { |
| const id = data.id; |
|
|
| if (data.hasOwnProperty('status')) { |
| updateDownloadCardState(id, data.status) |
| } |
|
|
| if (data.hasOwnProperty('result_text')) { |
| let resultTitle = data.hasOwnProperty('result_title') ? data.result_title : 'Result' |
| updateResultText(id, resultTitle, data.result_text) |
| } |
|
|
| if (data.hasOwnProperty('progress_info_left')) { |
| updateText(id, 'progress-info-left', false, data.progress_info_left) |
| } |
|
|
| if (data.hasOwnProperty('progress_info_center')) { |
| updateText(id, 'progress-info-center', false, data.progress_info_center) |
| } |
|
|
| if (data.hasOwnProperty('progress_info_right')) { |
| updateText(id, 'progress-info-right', false, data.progress_info_right) |
| } |
|
|
| if (data.hasOwnProperty('progress_preview_info_left')) { |
| updateText(id, 'progress-info-left', true, data.progress_preview_info_left) |
| } |
|
|
| if (data.hasOwnProperty('progress_preview_info_center')) { |
| updateText(id, 'progress-info-center', true, data.progress_preview_info_center) |
| } |
|
|
| if (data.hasOwnProperty('progress_preview_info_right')) { |
| updateText(id, 'progress-info-right', true, data.progress_preview_info_right) |
| } |
|
|
| if (data.hasOwnProperty('progress')) { |
| updateProgressBar(id, 'progress-bar', false, data.progress) |
| } |
|
|
| if (data.hasOwnProperty('progress_preview')) { |
| updateProgressBar(id, 'progress-bar', true, data.progress_preview) |
| } |
| } |
|
|
| function generateUUID() { |
| let d = new Date().getTime(); |
| return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function (c) { |
| const r = (d + Math.random() * 16) % 16 | 0; |
| d = Math.floor(d / 16); |
| return (c === 'x' ? r : (r & 0x3 | 0x8)).toString(16); |
| }); |
| } |
|
|
| function populateBackstack() { |
| const textArea = findElem('mo_json_nav_box').querySelector('textarea') |
| const currentNavigationJson = textArea.value |
| const backstack = [] |
| if (Boolean(currentNavigationJson)) { |
|
|
| const currentNavigation = JSON.parse(currentNavigationJson); |
| logMo('Current Navigation: ' + currentNavigation) |
|
|
| if (currentNavigation.hasOwnProperty('backstack')) { |
| currentNavigation.backstack.forEach(function (item, index) { |
| backstack.push(item); |
| }); |
| delete currentNavigation.backstack; |
| } |
|
|
| if (currentNavigation.hasOwnProperty('token')) { |
| delete currentNavigation.token; |
| } |
| logMo('previous backstack: ' + backstack) |
|
|
| backstack.unshift(currentNavigation); |
| logMo('new backstack: ' + backstack) |
| } |
| return backstack |
| } |
|
|
| function navigateHome() { |
| logMo('Navigate home screen') |
| const navObj = {}; |
| deliverNavObject(navObj) |
| return [] |
| } |
|
|
| function navigateBack() { |
| const textArea = findElem('mo_json_nav_box').querySelector('textarea') |
| const currentNavigationJson = textArea.value |
| let backNav = {} |
| if (Boolean(currentNavigationJson)) { |
| const currentNavigation = JSON.parse(currentNavigationJson); |
| logMo('Current Navigation: ' + currentNavigation) |
|
|
| if (currentNavigation.hasOwnProperty('backstack') && currentNavigation.backstack.length !== 0) { |
| backNav = currentNavigation.backstack.shift() |
|
|
| if (currentNavigation.backstack.length !== 0) { |
| backNav.backstack = currentNavigation.backstack |
| } |
| } |
| } |
| deliverNavObject(backNav) |
| return [] |
| } |
|
|
| function navigateDetails(id, event) { |
| if (event !== undefined) { |
| event.stopPropagation(); |
| event.preventDefault(); |
| } |
| logMo('Navigate details screen for id: ' + id) |
| const navObj = { |
| screen: "details", |
| record_id: id, |
| token: generateUUID(), |
| backstack: populateBackstack() |
| }; |
| deliverNavObject(navObj) |
| return [] |
| } |
|
|
| function navigateAdd() { |
| logMo('Navigate add screen') |
| const navObj = { |
| screen: "edit", |
| token: generateUUID(), |
| backstack: populateBackstack() |
| }; |
| deliverNavObject(navObj) |
| return [] |
| } |
|
|
| function navigateImportExport(filter_state) { |
| logMo('Navigate import_export screen') |
| const navObj = { |
| screen: "import_export", |
| token: generateUUID(), |
| backstack: populateBackstack(), |
| filter_state: filter_state |
| }; |
| deliverNavObject(navObj) |
| return [] |
| } |
|
|
| function navigateDebug(event) { |
| logMo('Navigate debug screen') |
| const navObj = { |
| screen: "debug", |
| token: generateUUID(), |
| backstack: populateBackstack() |
| }; |
| deliverNavObject(navObj) |
| return [] |
| } |
|
|
| function navigateEdit(id, event) { |
| if (event !== undefined) { |
| event.stopPropagation(); |
| event.preventDefault(); |
| } |
| logMo('Navigate edit screen for id: ' + id) |
| const navObj = { |
| screen: "edit", |
| record_id: id, |
| token: generateUUID(), |
| backstack: populateBackstack() |
| }; |
| deliverNavObject(navObj) |
| return [] |
| } |
|
|
| function navigateEditPrefilled(json_data, event) { |
| if (event !== undefined) { |
| event.stopPropagation(); |
| event.preventDefault(); |
| } |
| logMo('Navigate edit screen for prefilled json: ' + json_data) |
| const navObj = { |
| screen: "edit", |
| prefilled_json: json_data, |
| token: generateUUID(), |
| backstack: populateBackstack() |
| }; |
|
|
|
|
| setTimeout((event) => { |
| |
| var json_elem = gradioApp().getElementById('settings_json'); |
| if (json_elem == null) return; |
|
|
| var textarea = json_elem.querySelector('textarea'); |
| var jsdata = textarea.value; |
| opts = JSON.parse(jsdata); |
|
|
|
|
|
|
|
|
| |
|
|
| |
| |
| |
| |
| |
|
|
| |
| |
| |
|
|
| if (opts['mo_autobind_file']) { |
| var bind = gradioApp().querySelector('#model_organizer_add_bind input'); |
| var modelName = gradioApp().querySelector('#model_organizer_edit_name input'); |
| bind.value = modelName.value; |
| bind.dispatchEvent(changeEvent); |
| terminate = true; |
| } |
| }, 300); |
| deliverNavObject(navObj) |
|
|
|
|
| return [] |
| } |
|
|
| function navigateDownloadRecord(id, event) { |
| if (event !== undefined) { |
| event.stopPropagation(); |
| event.preventDefault(); |
| } |
| logMo('Navigate download screen for id: ' + id) |
| const navObj = { |
| screen: "download", |
| record_id: id, |
| token: generateUUID(), |
| backstack: populateBackstack() |
| }; |
| deliverNavObject(navObj) |
| return [] |
| } |
|
|
| function navigateDownloadRecordList(filter_state) { |
| logMo('Navigate download screen for records with filter state: ' + filter_state) |
| const navObj = { |
| screen: "download", |
| filter_state: filter_state, |
| token: generateUUID(), |
| backstack: populateBackstack() |
| }; |
| deliverNavObject(navObj) |
| return [] |
| } |
|
|
| function navigateDownloadGroup(groupName) { |
| logMo('Navigate download screen for group: ' + groupName) |
| const navObj = { |
| screen: "download", |
| group: groupName, |
| token: generateUUID(), |
| backstack: populateBackstack() |
| }; |
| deliverNavObject(navObj) |
| return [] |
| } |
|
|
| function navigateRemove(id, event) { |
| if (event !== undefined) { |
| event.stopPropagation(); |
| event.preventDefault(); |
| } |
| logMo('Navigate removal screen for id: ' + id) |
| const navObj = { |
| screen: "remove", |
| record_id: id, |
| token: generateUUID(), |
| backstack: populateBackstack() |
| }; |
| deliverNavObject(navObj) |
| return [] |
| } |
|
|
| function deliverNavObject(navObj) { |
| const navJson = JSON.stringify(navObj); |
| const textArea = findElem('mo_json_nav_box').querySelector('textarea') |
| const event = new Event('input', { 'bubbles': true, "composed": true }); |
| textArea.value = navJson |
| findElem('mo_json_nav_box').querySelector('textarea').dispatchEvent(event); |
| logMo('JSON Nav dispatched: ' + navJson) |
| } |
|
|
| function invokeHomeInitialStateLoad() { |
| logMo('invokeHomeInitialStateLoad') |
| if (!isHomeInitialStateInvoked) { |
| const initialStateTextArea = findElem('mo-initial-state-box').querySelector('textarea') |
| const stateTextArea = findElem('mo-home-state-box').querySelector('textarea') |
| stateTextArea.value = initialStateTextArea.value |
| const event = new Event('input', { 'bubbles': true, "composed": true }); |
| findElem('mo-home-state-box').querySelector('textarea').dispatchEvent(event); |
| isHomeInitialStateInvoked = true |
| logMo('initial home state invoked') |
| } |
| return [] |
| } |
|
|
| function getTheme() { |
| return new Promise((resolve, _) => { |
| const parsedUrl = new URL(window.location.href) |
| const theme = parsedUrl.searchParams.get('__theme') |
| if (theme != null) { |
| logMo('theme resolved: ' + theme) |
| resolve(theme) |
| } else { |
| fetch(origin + '/mo/display-options') |
| .then(response => response.json()) |
| .then(data => { |
| logMo('display options received:') |
| logMo(data) |
| resolve(data.theme) |
| }) |
| .catch(_ => { |
| resolve('light') |
| }); |
| } |
| }); |
| } |
|
|
| function getCardsSize() { |
| return new Promise((resolve) => { |
| fetch(origin + '/mo/display-options') |
| .then(response => response.json()) |
| .then(data => { |
| resolve([data.card_width, data.card_height]) |
| }) |
| .catch(_ => { |
| resolve([250, 350]) |
| }); |
| } |
| ) |
| } |
|
|
| function installCardsSize(width, height) { |
| const styleElement = document.createElement('style'); |
| styleElement.textContent = ':root {\n' + |
| ' --mo-card-width: ' + width + 'px;\n' + |
| ' --mo-card-height: ' + height + 'px;\n' + |
| '}'; |
|
|
| document.documentElement.appendChild(styleElement); |
| } |
|
|
| function installStyles(theme) { |
| const linkElementColors = document.createElement('link'); |
| linkElementColors.rel = 'stylesheet'; |
|
|
| logMo("theme:" + theme) |
| const timestamp = '?v=' + new Date().getTime(); |
|
|
| if (theme === 'dark') { |
| logMo('installing dark theme') |
| linkElementColors.href = 'file=extensions/sd-model-organizer/styles/colors-dark.css' + timestamp; |
| } else { |
| logMo('installing light theme') |
| linkElementColors.href = 'file=extensions/sd-model-organizer/styles/colors-light.css' + timestamp; |
| } |
|
|
| document.head.appendChild(linkElementColors); |
|
|
| const linkElementStyles = document.createElement('link'); |
| linkElementStyles.rel = 'stylesheet'; |
| linkElementStyles.href = 'file=extensions/sd-model-organizer/styles/styles.css' + timestamp; |
| document.head.appendChild(linkElementStyles); |
| } |
|
|
| onUiLoaded(function () { |
| logMo("UI loaded") |
| const homeTab = findElem('mo_home_tab') |
| const intersectionObserver = new IntersectionObserver((entries) => { |
| if (entries[0].intersectionRatio > 0) invokeHomeInitialStateLoad(); |
| }); |
| intersectionObserver.observe(homeTab); |
|
|
| getTheme() |
| .then(data => { |
| installStyles(data) |
| }) |
|
|
| getCardsSize() |
| .then(size => { |
| installCardsSize(size[0], size[1]) |
| }) |
| }) |
|
|
| let organizerTab = null; |
| let lastTabName = 'txt2img'; |
| let finishedSearching = false; |
| const inputEvent = new Event('input', { 'bubbles': true, "composed": true }); |
| const changeEvent = new Event('change', { 'bubbles': true, "composed": true }); |
| |
| |
| onUiUpdate(function () { |
|
|
| |
| let tabs = gradioApp().querySelectorAll("#tabs > div:first-of-type button"); |
| if (typeof tabs != "undefined" && tabs != null && tabs.length > 0) { |
| tabs.forEach(tab => { |
| if (tab.innerText == "Model Organizer") { |
| organizerTab = tab; |
| } |
| }); |
| } |
|
|
| |
| let thumbCards = gradioApp().querySelectorAll("#txt2img_extra_tabs .card:not([organizer-hijack]), #img2img_extra_tabs .card:not([organizer-hijack])"); |
| if (typeof thumbCards != "undefined" && thumbCards != null && thumbCards.length > 0) { |
| thumbCards.forEach(card => { |
| let buttonRow = card.querySelector('.button-row'); |
| |
| |
| let modelName = card.getAttribute('data-sort-name'); |
|
|
| |
| let organizerBtnOpen = document.createElement("div"); |
| organizerBtnOpen.className = "organizer-buttonOpen card-button info"; |
| organizerBtnOpen.title = "Go To Record"; |
| organizerBtnOpen.onclick = function (event) { |
| addRecordClick(event, modelName); |
| }; |
| buttonRow.prepend(organizerBtnOpen); |
|
|
| |
| card.setAttribute("organizer-hijack", true); |
| }); |
| } |
| }) |
|
|
| |
| function switchToOrganizerTab(event, name) { |
| event.stopPropagation(); |
| event.preventDefault(); |
|
|
| var tabs = gradioApp().querySelectorAll('#tab_txt2img, #tab_img2img'); |
| if (typeof tabs != "undefined" && tabs != null && tabs.length > 0) { |
| tabs.forEach(tab => { |
| styleattr = tab.getAttribute('style'); |
| if (styleattr.includes('block')) { |
| lastTabName = tab.id.substring(4); |
| } |
| }); |
| } |
|
|
|
|
| organizerTab.click(); |
| organizerTab.dispatchEvent(inputEvent); |
|
|
| var statebox = gradioApp().querySelector("#mo-home-state-box"); |
| statebox.dispatchEvent(changeEvent); |
|
|
| var accordion = gradioApp().querySelector("#model_organizer_accordion"); |
| var labelWrap = accordion.querySelector('.label-wrap'); |
|
|
| if (!labelWrap.classList.contains('open')) { |
| labelWrap.click(); |
| labelWrap.dispatchEvent(inputEvent); |
| } |
|
|
| var searchArea = gradioApp().querySelector("#model_organizer_searchbox textarea"); |
| setTimeout((event) => { |
| searchArea.value = name; |
| searchArea.dispatchEvent(inputEvent); |
| }, 150); |
| } |
|
|
| function addRecordClick(event, name) { |
| switchToOrganizerTab(event, name); |
|
|
| |
| setTimeout((event) => { |
| var recordButtons = gradioApp().querySelectorAll('#organizer_record_table button.mo-btn.mo-btn-success, #organizer_record_card_grid button.mo-btn.mo-btn-success'); |
| if (recordButtons.length == 1) { |
| recordButtons[0].click(); |
| } |
| }, 400); |
|
|
| } |
|
|
| function fillPrompt(recordid) { |
| logMo('Loading record info for id: ' + recordid) |
| const navObj = { |
| screen: "record_info", |
| record_info_id: recordid, |
| token: generateUUID(), |
| backstack: populateBackstack() |
| }; |
| deliverNavObject(navObj) |
| var counter = 0; |
| var timer = setInterval(() => { |
| counter++; |
| record_data = gradioApp().querySelector('#mo_record_info_nav_box textarea'); |
| if (counter > 10) clearInterval(timer); |
| if (record_data == null) return; |
| var terminate = false; |
| var jsdata = record_data.value; |
| var jsdata = jsdata.replace(/'/g, '"').replace(/"checkpoint": True/mg, '"checkpoint": true').replace(/"checkpoint": False/mg, '"checkpoint": false'); |
| recordInfo = JSON.parse(jsdata); |
| if (recordInfo.hasOwnProperty("id") && recordInfo["id"] === recordid) { |
| var pos = ""; |
| var neg = ""; |
| if (recordInfo.hasOwnProperty('positive_prompts')) { |
| pos = recordInfo['positive_prompts']; |
| terminate = true; |
| } |
| if (recordInfo.hasOwnProperty('negative_prompts')) { |
| neg = recordInfo['negative_prompts']; |
| terminate = true; |
| } |
| if (recordInfo.hasOwnProperty('checkpoint') && recordInfo['checkpoint']) { |
| selectCheckpoint(recordInfo['positive_prompts']); |
| terminate = true; |
| } else { |
| if (pos !== "") { |
| cardClicked(lastTabName, pos, "", false); |
| } |
| if (neg !== "") { |
| cardClicked(lastTabName, "", neg, true); |
| } |
| } |
| } |
| if (terminate) { |
| clearInterval(timer); |
| } |
| }, 100) |
|
|
| return [] |
| } |
|
|