add en details view - phase one

This commit is contained in:
Vladimir Mandic
2023-09-23 17:49:31 -04:00
parent 257601bbb8
commit 940b4122f9
7 changed files with 501 additions and 463 deletions
+2
View File
@@ -9,6 +9,8 @@
any ui state including state of open/closed menus can be saved as default!
see *System -> User interface -> Set menu states*
- **Extra networks**:
- new details interface to view and save data about extra networks
main ui now has a single button on each en to trigger details view
- faster search, ability to show/hide/sort networks
- refactored subfolder handling
*note*: this will trigger model hash recaclulation on first model use
+1
View File
@@ -94,6 +94,7 @@
{"id":"","label":"save","localized":"","hint":"Save image"},
{"id":"","label":"zip","localized":"","hint":"Create zip archive from images"},
{"id":"","label":"delete","localized":"","hint":"Delete image"},
{"id":"","label":"replace","localized":"","hint":"Replace image"},
{"id":"","label":"➠ text","localized":"","hint":"Transfer image to text interface"},
{"id":"","label":"➠ image","localized":"","hint":"Transfer image to image interface"},
{"id":"","label":"➠ inpaint","localized":"","hint":"Transfer image to inpaint interface"},
+143 -137
View File
@@ -1,34 +1,42 @@
let globalPopup = null;
let globalPopupInner = null;
let previousCard = null;
const activePromptTextarea = {};
const re_extranet = /<([^:]+:[^:]+):[\d\.]+>/;
const re_extranet_g = /\s+<([^:]+:[^:]+):[\d\.]+>/g;
const getENActiveTab = () => gradioApp().getElementById('tab_txt2img').style.display === 'block' ? 'txt2img' : 'img2img';
// helpers
function requestGet(url, data, handler, errorHandler) {
const requestGet = (url, data, handler) => {
const xhr = new XMLHttpRequest();
const args = Object.keys(data).map((k) => `${encodeURIComponent(k)}=${encodeURIComponent(data[k])}`).join('&');
xhr.open('GET', `${url}?${args}`, true);
xhr.onreadystatechange = () => {
if (xhr.readyState === 4) {
if (xhr.status === 200) {
try {
const js = JSON.parse(xhr.responseText);
handler(js);
} catch (error) {
console.error(error);
errorHandler();
}
} else {
errorHandler();
}
if (xhr.status === 200) handler(JSON.parse(xhr.responseText));
else console.error(`Request: url=${url} status=${xhr.status} err`);
}
};
const js = JSON.stringify(data);
xhr.send(js);
}
xhr.send(JSON.stringify(data));
};
const getENActiveTab = () => gradioApp().getElementById('tab_txt2img').style.display === 'block' ? 'txt2img' : 'img2img';
const getENActivePage = () => {
const tabname = getENActiveTab();
const page = gradioApp().querySelector(`#${tabname}_extra_networks > .tabs > .tab-nav > .selected`);
return page ? page.innerText : '';
};
const setENState = (state) => {
if (!state) return;
state.tab = getENActiveTab();
state.page = getENActivePage();
log('setENState', state);
const el = gradioApp().querySelector(`#${state.tab}_extra_state > label > textarea`);
el.value = JSON.stringify(state);
updateInput(el);
};
// popup
let globalPopup = null;
let globalPopupInner = null;
function popup(contents) {
if (!globalPopup) {
@@ -51,6 +59,45 @@ function popup(contents) {
globalPopup.style.display = 'flex';
}
// methods
function showCardDetails(event) {
const tabname = getENActiveTab();
setENState({ op: 'showCardDetails' });
const btn = gradioApp().getElementById(`${tabname}_extra_details_btn`);
btn.click();
event.stopPropagation();
event.preventDefault();
}
function getCardDetails(...args) {
const el = event?.target?.parentElement?.parentElement?.parentElement;
if (!el?.classList?.contains('card')) return [...args];
const tabname = getENActiveTab();
setENState({ op: 'getCardDetails', item: el.dataset.name });
return [...args];
}
function saveCardDescription(event) {
const el = event?.target?.parentElement?.parentElement?.parentElement;
if (!el?.classList?.contains('card')) return;
const tabname = getENActiveTab();
const description = gradioApp().getElementById(`${tabname}_description`)?.children[0].children[1].value;
el.dataset.description = description;
setENState({ op: 'saveCardDescription', item: el.dataset.name, description });
event.stopPropagation();
event.preventDefault();
}
function saveCardPreview(event) {
const el = event?.target?.parentElement?.parentElement?.parentElement;
if (!el?.classList?.contains('card')) return;
const index = selected_gallery_index();
setENState({ op: 'saveCardPreview', item: el.dataset.name, index });
event.stopPropagation();
event.preventDefault();
}
function readCardMetadata(event, extraPage, cardName) {
requestGet('./sd_extra_networks/metadata', { page: extraPage, item: cardName }, (data) => {
if (data?.metadata) {
@@ -60,7 +107,7 @@ function readCardMetadata(event, extraPage, cardName) {
elem.textContent = data.metadata;
popup(elem);
}
}, () => { });
});
event.stopPropagation();
event.preventDefault();
}
@@ -88,54 +135,14 @@ function readCardTags(el, tags) {
}
}
function readCardInformation(event, extraPage, cardName) {
requestGet('./sd_extra_networks/info', { page: extraPage, item: cardName }, (data) => {
if (data?.info && (typeof (data?.info) === 'string')) {
const elem = document.createElement('pre');
elem.classList.add('popup-metadata');
elem.textContent = data.info;
popup(elem);
}
}, () => { });
event.stopPropagation();
event.preventDefault();
}
function readCardDescription(filename, descript) {
const tabname = getENActiveTab();
const fn = gradioApp().querySelector(`#${tabname}_description_filename > label > textarea`);
const description = gradioApp().querySelector(`#${tabname}_description > label > textarea`);
fn.value = filename;
description.value = descript?.trim() || '';
description.focus();
updateInput(fn);
updateInput(description);
}
function saveCardPreview(event) {
const tabname = getENActiveTab();
const textarea = gradioApp().querySelector(`#${tabname}_preview_filename > label > textarea`);
const button = gradioApp().getElementById(`${tabname}_save_preview`);
const el = event?.target?.parentElement?.parentElement?.parentElement;
if (el?.classList?.contains('card')) {
textarea.value = el.dataset.filename;
updateInput(textarea);
button.click();
}
event.stopPropagation();
event.preventDefault();
}
function saveCardDescription(event) {
const tabname = getENActiveTab();
const el = event?.target?.parentElement?.parentElement?.parentElement;
if (el?.classList?.contains('card')) el.dataset.description = gradioApp().getElementById(`${tabname}_description`)?.children[0].children[1].value;
const button = gradioApp().getElementById(`${tabname}_save_description`);
button.click();
event.stopPropagation();
event.preventDefault();
}
async function filterExtraNetworksForTab(tabname, searchTerm) {
let found = 0;
let items = 0;
@@ -161,6 +168,79 @@ async function filterExtraNetworksForTab(tabname, searchTerm) {
else log(`filterExtraNetworks: text=all items=${items} time=${Math.round(1000 * (t1 - t0)) / 1000000}`);
}
function tryToRemoveExtraNetworkFromPrompt(textarea, text) {
const re_extranet = /<([^:]+:[^:]+):[\d\.]+>/;
const re_extranet_g = /\s+<([^:]+:[^:]+):[\d\.]+>/g;
let m = text.match(re_extranet);
let replaced = false;
let newTextareaText;
if (m) {
const partToSearch = m[1];
newTextareaText = textarea.value.replaceAll(re_extranet_g, (found) => {
m = found.match(re_extranet);
if (m[1] === partToSearch) {
replaced = true;
return '';
}
return found;
});
} else {
newTextareaText = textarea.value.replaceAll(new RegExp(text, 'g'), (found) => {
if (found === text) {
replaced = true;
return '';
}
return found;
});
}
if (replaced) {
textarea.value = newTextareaText;
return true;
}
return false;
}
function refreshExtraNetworks(tabname) {
log('refreshExtraNetworks', tabname, gradioApp().querySelector(`#${tabname}_extra_networks textarea`)?.value);
gradioApp().querySelector(`#${tabname}_extra_networks textarea`)?.dispatchEvent(new Event('input'));
}
function cardClicked(textToAdd, allowNegativePrompt) {
const tabname = getENActiveTab();
const textarea = allowNegativePrompt ? activePromptTextarea[tabname] : gradioApp().querySelector(`#${tabname}_prompt > label > textarea`);
if (textarea.value.indexOf(textToAdd) !== -1) textarea.value = textarea.value.replace(textToAdd, '');
else textarea.value += textToAdd;
updateInput(textarea);
}
function extraNetworksSearchButton(event) {
const tabname = getENActiveTab();
const searchTextarea = gradioApp().querySelector(`#${tabname}_extra_tabs > div > div > textarea`);
const button = event.target;
const text = button.classList.contains('search-all') ? '' : `${button.textContent.trim()}/`;
searchTextarea.value = text;
updateInput(searchTextarea);
}
let desiredStyle = '';
function selectStyle(name) {
desiredStyle = name;
const tabname = getENActiveTab();
const button = gradioApp().querySelector(`#${tabname}_styles_select`);
button.click();
}
function applyStyles(styles) {
let newStyles = [];
if (styles) newStyles = Array.isArray(styles) ? styles : [styles];
const index = newStyles.indexOf(desiredStyle);
if (index > -1) newStyles.splice(index, 1);
else newStyles.push(desiredStyle);
return newStyles.join('|');
}
// init
function setupExtraNetworksForTab(tabname) {
gradioApp().querySelector(`#${tabname}_extra_tabs`).classList.add('extra-networks');
const tabs = gradioApp().querySelector(`#${tabname}_extra_tabs > div`);
@@ -188,6 +268,7 @@ function setupExtraNetworksForTab(tabname) {
});
let hoverTimer = null;
let previousCard = null;
gradioApp().getElementById(`${tabname}_extra_tabs`).onmouseover = (e) => {
const el = e.target.closest('.card'); // bubble-up to card
if (!el || (el.title === previousCard)) return;
@@ -261,79 +342,4 @@ function setupExtraNetworks() {
log('initExtraNetworks');
}
function tryToRemoveExtraNetworkFromPrompt(textarea, text) {
let m = text.match(re_extranet);
let replaced = false;
let newTextareaText;
if (m) {
const partToSearch = m[1];
newTextareaText = textarea.value.replaceAll(re_extranet_g, (found) => {
m = found.match(re_extranet);
if (m[1] === partToSearch) {
replaced = true;
return '';
}
return found;
});
} else {
newTextareaText = textarea.value.replaceAll(new RegExp(text, 'g'), (found) => {
if (found === text) {
replaced = true;
return '';
}
return found;
});
}
if (replaced) {
textarea.value = newTextareaText;
return true;
}
return false;
}
function refreshExtraNetworks(tabname) {
console.log('refreshExtraNetworks', tabname, gradioApp().querySelector(`#${tabname}_extra_networks textarea`)?.value);
gradioApp().querySelector(`#${tabname}_extra_networks textarea`)?.dispatchEvent(new Event('input'));
}
function cardClicked(textToAdd, allowNegativePrompt) {
const tabname = getENActiveTab();
const textarea = allowNegativePrompt ? activePromptTextarea[tabname] : gradioApp().querySelector(`#${tabname}_prompt > label > textarea`);
if (textarea.value.indexOf(textToAdd) !== -1) textarea.value = textarea.value.replace(textToAdd, '');
else textarea.value += textToAdd;
updateInput(textarea);
}
function extraNetworksSearchButton(event) {
const tabname = getENActiveTab();
const searchTextarea = gradioApp().querySelector(`#${tabname}_extra_tabs > div > div > textarea`);
const button = event.target;
const text = button.classList.contains('search-all') ? '' : `${button.textContent.trim()}/`;
searchTextarea.value = text;
updateInput(searchTextarea);
}
function getENActivePage() {
const tabname = getENActiveTab();
const page = gradioApp().querySelector(`#${tabname}_extra_networks > .tabs > .tab-nav > .selected`);
return page ? page.innerText : '';
}
let desiredStyle = '';
function selectStyle(name) {
desiredStyle = name;
const tabname = getENActiveTab();
const button = gradioApp().querySelector(`#${tabname}_styles_select`);
button.click();
}
function applyStyles(styles) {
let newStyles = [];
if (styles) newStyles = Array.isArray(styles) ? styles : [styles];
const index = newStyles.indexOf(desiredStyle);
if (index > -1) newStyles.splice(index, 1);
else newStyles.push(desiredStyle);
return newStyles.join('|');
}
onUiLoaded(setupExtraNetworks);
+145 -145
View File
@@ -38,7 +38,7 @@
html { font-size: var(--font-size); }
body, button, input, select, textarea { font-family: var(--font);}
button { font-size: 1.2rem; max-width: 400px; }
button { font-size: 1.2rem; max-width: 400px; --button-large-radius: var(--radius-lg) }
img { background-color: var(--background-color); }
input[type=range] { height: var(--line-sm); appearance: none; margin-top: 0; min-width: 160px; background-color: var(--background-color); width: 100%; background: transparent; }
input[type=range]::-webkit-slider-runnable-track { width: 100%; height: var(--line-sm); cursor: pointer; box-shadow: 2px 2px 3px #111111; background: var(--input-background-fill); border-radius: var(--radius-lg); border: 0px solid #222222; }
@@ -128,137 +128,27 @@ svg.feather.feather-image, .feather .feather-image { display: none }
/* based on gradio built-in dark theme */
:root, .light, .dark {
--body-background-fill: var(--background-color);
--color-accent-soft: var(--neutral-700);
--background-fill-secondary: none;
--border-color-accent: var(--background-color);
--border-color-primary: var(--background-color);
--link-text-color-active: var(--secondary-500);
--link-text-color: var(--secondary-500);
--link-text-color-hover: var(--secondary-400);
--link-text-color-visited: var(--secondary-600);
--shadow-spread: 1px;
--block-background-fill: None;
--block-border-color: var(--border-color-primary);
--block_border_width: None;
--block-info-text-color: var(--body-text-color-subdued);
--block-label-background-fill: var(--background-fill-secondary);
--block-label-border-color: var(--border-color-primary);
--block_label_border_width: None;
--block-label-text-color: var(--neutral-200);
--block_shadow: None;
--block_title_background_fill: None;
--block_title_border_color: None;
--block_title_border_width: None;
--panel-background-fill: var(--background-fill-secondary);
--panel-border-color: var(--border-color-primary);
--panel_border_width: None;
--checkbox-background-color: var(--neutral-500);
--checkbox-background-color-focus: var(--checkbox-background-color);
--checkbox-background-color-hover: var(--checkbox-background-color);
--checkbox-background-color-selected: var(--primary-500);
--checkbox-border-color: transparent;
--checkbox-border-color-focus: var(--secondary-500);
--checkbox-border-color-hover: var(--neutral-600);
--checkbox-border-color-selected: var(--primary-600);
--checkbox-border-width: var(--input-border-width);
--checkbox-label-background-fill: None;
--checkbox-label-background-fill-hover: None;
--checkbox-label-background-fill-selected: var(--checkbox-label-background-fill);
--checkbox-label-border-color: var(--border-color-primary);
--checkbox-label-border-color-hover: var(--checkbox-label-border-color);
--checkbox-label-border-width: var(--input-border-width);
--checkbox-label-text-color: var(--body-text-color);
--checkbox-label-text-color-selected: var(--checkbox-label-text-color);
--error-background-fill: var(--background-fill-primary);
--error-border-color: var(--border-color-primary);
--error_border_width: None;
--error-text-color: #ef4444;
--input-background-fill-focus: var(--secondary-600);
--input-background-fill-hover: var(--input-background-fill);
--input-border-color: var(--border-color-primary);
--input-border-color-focus: var(--neutral-700);
--input-border-color-hover: var(--input-border-color);
--input_border_width: None;
--input-placeholder-color: var(--neutral-500);
--input-shadow-focus: 2px 2px 2px 2px #111111;
--loader_color: None;
--slider_color: None;
--stat-background-fill: linear-gradient(to right, var(--primary-400), var(--primary-600));
--table-border-color: var(--neutral-700);
--table-even-background-fill: #222222;
--table-odd-background-fill: #333333;
--table-row-focus: var(--color-accent-soft);
--button-border-width: var(--input-border-width);
--button-cancel-background-fill: linear-gradient(to bottom right, #dc2626, #b91c1c);
--button-cancel-background-fill-hover: linear-gradient(to bottom right, #dc2626, #dc2626);
--button-cancel-border-color: #dc2626;
--button-cancel-border-color-hover: var(--button-cancel-border-color);
--button-cancel-text-color: white;
--button-cancel-text-color-hover: var(--button-cancel-text-color);
--button-primary-background-fill: linear-gradient(to bottom right, var(--primary-500), var(--primary-800));
--button-primary-background-fill-hover: linear-gradient(to bottom right, var(--primary-500), var(--primary-300));
--button-primary-border-color: var(--primary-500);
--button-primary-border-color-hover: var(--button-primary-border-color);
--button-primary-text-color: white;
--button-primary-text-color-hover: var(--button-primary-text-color);
--button-secondary-border-color: var(--neutral-600);
--button-secondary-border-color-hover: var(--button-secondary-border-color);
--button-secondary-text-color-hover: var(--button-secondary-text-color);
--secondary-50: #eff6ff;
--secondary-100: #dbeafe;
--secondary-200: #bfdbfe;
--secondary-300: #93c5fd;
--secondary-400: #60a5fa;
--secondary-500: #3b82f6;
--secondary-600: #2563eb;
--secondary-700: #1d4ed8;
--secondary-800: #1e40af;
--secondary-900: #1e3a8a;
--secondary-950: #1d3660;
--neutral-50: #f0f0f0;
--neutral-100: #e0e0e0;
--neutral-200: #d0d0d0;
--neutral-300: #b0b0b0;
--neutral-400: #909090;
--neutral-500: #707070;
--neutral-600: #606060;
--neutral-700: #404040;
--neutral-800: #333333;
--neutral-900: #111827;
--neutral-950: #0b0f19;
--spacing-xxs: 1px;
--spacing-xs: 2px;
--spacing-sm: 4px;
--spacing-lg: 8px;
--spacing-xl: 10px;
--radius-xxs: 0;
--radius-xs: 0;
--radius-md: 0;
--radius-xl: 0;
--radius-xxl: 0;
--text-xxs: 9px;
--text-xs: 10px;
--text-sm: 12px;
--text-md: 14px;
--text-lg: 16px;
--text-xl: 22px;
--text-xxl: 26px;
--body-text-size: var(--text-md);
--body-text-weight: 400;
--embed-radius: var(--radius-lg);
--color-accent: var(--primary-500);
--shadow-drop: 0;
--shadow-drop-lg: 0 1px 3px 0 rgb(0 0 0 / 0.1), 0 1px 2px -1px rgb(0 0 0 / 0.1);
--shadow-inset: rgba(0,0,0,0.05) 0px 2px 4px 0px inset;
--block-background-fill: None;
--block-border-color: var(--border-color-primary);
--block-border-width: 1px;
--block-info-text-color: var(--body-text-color-subdued);
--block-info-text-size: var(--text-sm);
--block-info-text-weight: 400;
--block-label-background-fill: var(--background-fill-secondary);
--block-label-border-color: var(--border-color-primary);
--block-label-border-width: 1px;
--block-label-margin: 0;
--block-label-padding: var(--spacing-sm) var(--spacing-lg);
--block-label-radius: calc(var(--radius-lg) - 1px) 0 calc(var(--radius-lg) - 1px) 0;
--block-label-right-radius: 0 calc(var(--radius-lg) - 1px) 0 calc(var(--radius-lg) - 1px);
--block-label-text-color: var(--neutral-200);
--block-label-text-size: var(--text-sm);
--block-label-text-weight: 400;
--block-padding: var(--spacing-xl) calc(var(--spacing-xl) + 2px);
@@ -271,44 +161,154 @@ svg.feather.feather-image, .feather .feather-image { display: none }
--block-title-radius: none;
--block-title-text-size: var(--text-md);
--block-title-text-weight: 400;
--container-radius: var(--radius-lg);
--form-gap-width: 1px;
--layout-gap: var(--spacing-xxl);
--panel-border-width: 0;
--section-header-text-size: var(--text-md);
--section-header-text-weight: 400;
--checkbox-border-radius: var(--radius-sm);
--checkbox-label-gap: 2px;
--checkbox-label-padding: var(--spacing-md);
--checkbox-label-shadow: var(--shadow-drop);
--checkbox-label-text-size: var(--text-md);
--checkbox-label-text-weight: 400;
--checkbox-check: url("data:image/svg+xml,%3csvg viewBox='0 0 16 16' fill='white' xmlns='http://www.w3.org/2000/svg'%3e%3cpath d='M12.207 4.793a1 1 0 010 1.414l-5 5a1 1 0 01-1.414 0l-2-2a1 1 0 011.414-1.414L6.5 9.086l4.293-4.293a1 1 0 011.414 0z'/%3e%3c/svg%3e");
--radio-circle: url("data:image/svg+xml,%3csvg viewBox='0 0 16 16' fill='white' xmlns='http://www.w3.org/2000/svg'%3e%3ccircle cx='8' cy='8' r='3'/%3e%3c/svg%3e");
--checkbox-shadow: var(--input-shadow);
--error-border-width: 1px;
--input-border-width: 0;
--input-radius: var(--radius-lg);
--input-text-size: var(--text-md);
--input-text-weight: 400;
--loader-color: var(--color-accent);
--prose-text-size: var(--text-md);
--prose-text-weight: 400;
--prose-header-text-weight: 600;
--slider-color: ;
--table-radius: var(--radius-lg);
--body-background-fill: var(--background-color);
--body-text-size: var(--text-md);
--body-text-weight: 400;
--border-color-accent: var(--background-color);
--border-color-primary: var(--background-color);
--button-border-width: var(--input-border-width);
--button-cancel-background-fill-hover: linear-gradient(to bottom right, #dc2626, #dc2626);
--button-cancel-background-fill: linear-gradient(to bottom right, #dc2626, #b91c1c);
--button-cancel-border-color-hover: var(--button-cancel-border-color);
--button-cancel-border-color: #dc2626;
--button-cancel-text-color-hover: var(--button-cancel-text-color);
--button-cancel-text-color: white;
--button-large-padding: 2px 10px;
--button-large-radius: var(--radius-lg);
--button-large-text-size: var(--text-lg);
--button-large-text-weight: 400;
--button-shadow: 4px 4px 4px 0px #333333;
--button-primary-background-fill-hover: linear-gradient(to bottom right, var(--primary-500), var(--primary-300));
--button-primary-background-fill: linear-gradient(to bottom right, var(--primary-500), var(--primary-800));
--button-primary-border-color-hover: var(--button-primary-border-color);
--button-primary-border-color: var(--primary-500);
--button-primary-text-color-hover: var(--button-primary-text-color);
--button-primary-text-color: white;
--button-secondary-border-color-hover: var(--button-secondary-border-color);
--button-secondary-border-color: var(--neutral-600);
--button-secondary-text-color-hover: var(--button-secondary-text-color);
--button-shadow-active: 1px 1px 4px 0px #555555;
--button-shadow-hover: 1px 1px 4px 0px #555555;
--button-shadow: 4px 4px 4px 0px #333333;
--button-small-padding: var(--spacing-sm) calc(2 * var(--spacing-sm));
--button-small-radius: var(--radius-lg);
--button-small-text-size: var(--text-md);
--button-small-text-weight: 400;
--button-transition: none;
--size-9: 64px;
--checkbox-background-color-focus: var(--checkbox-background-color);
--checkbox-background-color-hover: var(--checkbox-background-color);
--checkbox-background-color-selected: var(--primary-500);
--checkbox-background-color: var(--neutral-500);
--checkbox-border-color-focus: var(--secondary-500);
--checkbox-border-color-hover: var(--neutral-600);
--checkbox-border-color-selected: var(--primary-600);
--checkbox-border-color: transparent;
--checkbox-border-radius: var(--radius-sm);
--checkbox-border-width: var(--input-border-width);
--checkbox-check: url("data:image/svg+xml,%3csvg viewBox='0 0 16 16' fill='white' xmlns='http://www.w3.org/2000/svg'%3e%3cpath d='M12.207 4.793a1 1 0 010 1.414l-5 5a1 1 0 01-1.414 0l-2-2a1 1 0 011.414-1.414L6.5 9.086l4.293-4.293a1 1 0 011.414 0z'/%3e%3c/svg%3e");
--checkbox-label-background-fill-hover: None;
--checkbox-label-background-fill-selected: var(--checkbox-label-background-fill);
--checkbox-label-background-fill: None;
--checkbox-label-border-color-hover: var(--checkbox-label-border-color);
--checkbox-label-border-color: var(--border-color-primary);
--checkbox-label-border-width: var(--input-border-width);
--checkbox-label-gap: 2px;
--checkbox-label-padding: var(--spacing-md);
--checkbox-label-shadow: var(--shadow-drop);
--checkbox-label-text-color-selected: var(--checkbox-label-text-color);
--checkbox-label-text-color: var(--body-text-color);
--checkbox-label-text-size: var(--text-md);
--checkbox-label-text-weight: 400;
--checkbox-shadow: var(--input-shadow);
--color-accent-soft: var(--neutral-700);
--color-accent: var(--primary-500);
--container-radius: var(--radius-lg);
--embed-radius: var(--radius-lg);
--error_border_width: None;
--error-background-fill: var(--background-fill-primary);
--error-border-color: var(--border-color-primary);
--error-border-width: 1px;
--error-text-color: #ef4444;
--form-gap-width: 1px;
--input_border_width: None;
--input-background-fill-focus: var(--secondary-600);
--input-background-fill-hover: var(--input-background-fill);
--input-border-color-focus: var(--neutral-700);
--input-border-color-hover: var(--input-border-color);
--input-border-color: var(--border-color-primary);
--input-border-width: 0;
--input-placeholder-color: var(--neutral-500);
--input-radius: var(--radius-lg);
--input-shadow-focus: 2px 2px 2px 2px #111111;
--input-text-size: var(--text-md);
--input-text-weight: 400;
--layout-gap: var(--spacing-xxl);
--link-text-color-active: var(--secondary-500);
--link-text-color-hover: var(--secondary-400);
--link-text-color-visited: var(--secondary-600);
--link-text-color: var(--secondary-500);
--loader_color: None;
--loader-color: var(--color-accent);
--neutral-100: #e0e0e0;
--neutral-200: #d0d0d0;
--neutral-300: #b0b0b0;
--neutral-400: #909090;
--neutral-50: #f0f0f0;
--neutral-500: #707070;
--neutral-600: #606060;
--neutral-700: #404040;
--neutral-800: #333333;
--neutral-900: #111827;
--neutral-950: #0b0f19;
--panel_border_width: None;
--panel-background-fill: var(--background-fill-secondary);
--panel-border-color: var(--border-color-primary);
--panel-border-width: 0;
--prose-header-text-weight: 600;
--prose-text-size: var(--text-md);
--prose-text-weight: 400;
--radio-circle: url("data:image/svg+xml,%3csvg viewBox='0 0 16 16' fill='white' xmlns='http://www.w3.org/2000/svg'%3e%3ccircle cx='8' cy='8' r='3'/%3e%3c/svg%3e");
--radius-md: 0;
--radius-xl: 0;
--radius-xs: 0;
--radius-xxl: 0;
--radius-xxs: 0;
--secondary-100: #dbeafe;
--secondary-200: #bfdbfe;
--secondary-300: #93c5fd;
--secondary-400: #60a5fa;
--secondary-50: #eff6ff;
--secondary-500: #3b82f6;
--secondary-600: #2563eb;
--secondary-700: #1d4ed8;
--secondary-800: #1e40af;
--secondary-900: #1e3a8a;
--secondary-950: #1d3660;
--section-header-text-size: var(--text-md);
--section-header-text-weight: 400;
--shadow-drop-lg: 0 1px 3px 0 rgb(0 0 0 / 0.1), 0 1px 2px -1px rgb(0 0 0 / 0.1);
--shadow-drop: 0;
--shadow-inset: rgba(0,0,0,0.05) 0px 2px 4px 0px inset;
--shadow-spread: 1px;
--size-14: 64px;
--size-9: 64px;
--slider_color: None;
--slider-color: ;
--spacing-lg: 8px;
--spacing-sm: 4px;
--spacing-xl: 10px;
--spacing-xs: 2px;
--spacing-xxs: 1px;
--stat-background-fill: linear-gradient(to right, var(--primary-400), var(--primary-600));
--table-border-color: var(--neutral-700);
--table-even-background-fill: #222222;
--table-odd-background-fill: #333333;
--table-radius: var(--radius-lg);
--table-row-focus: var(--color-accent-soft);
--text-lg: 16px;
--text-md: 14px;
--text-sm: 12px;
--text-xl: 22px;
--text-xs: 10px;
--text-xxl: 26px;
--text-xxs: 9px;
}
+17 -24
View File
@@ -1,7 +1,6 @@
:root, .dark{ --checkbox-label-gap: 0.25em 0.1em; --section-header-text-size: 12pt; --block-background-fill: transparent;}
a { font-weight: bold; cursor: pointer; }
h2 { margin-top: 1em !important; font-size: 1.4em !important; }
table { overflow-x: auto !important; overflow-y: auto !important; }
div.gradio-container{ max-width: unset !important; padding: 8px !important; }
div.tabitem { padding: 0 !important; }
div.form { border-width: 0; box-shadow: none; background: transparent; overflow: visible; gap: 0.5em 1em; flex-grow: 1 !important; }
@@ -35,7 +34,9 @@ div.gradio-html.min{ min-height: 0; }
.small-accordion .label-wrap .icon { margin-right: 1.6em; margin-left: 0.6em; color: var(--button-primary-border-color); }
.hidden { display: none; }
footer { display: none; }
td { border-bottom: none !important; }
table { overflow-x: auto !important; overflow-y: auto !important; }
td { border-bottom: none !important; padding: 0.1em 0.5em !important; }
tr { border-bottom: none !important; padding: 0.1em 0.5em !important; }
/* general styled components */
.gradio-button.tool { max-width: min-content; min-width: min-content !important; align-self: end; font-size: 1.4em; color: var(--body-text-color) !important; }
@@ -248,39 +249,31 @@ table.settings-value-table td { padding: 0.4em; border: 1px solid #ccc; max-widt
.extra-network-cards .card .preview { box-shadow: var(--button-shadow); min-height: 30px; }
.extra-network-cards .card:hover .overlay { background: rgba(0, 0, 0, 0.40); }
.extra-network-cards .card:hover .preview { box-shadow: none; filter: grayscale(100%); }
.extra-network-cards .card:hover .overlay { background: rgba(0, 0, 0, 0.40); }
.extra-network-cards .card .overlay .tags { margin: 4px; display: none; overflow-wrap: break-word; }
.extra-network-cards .card .overlay .tag { padding: 2px; margin: 2px; background: var(--neutral-700); cursor: pointer; display: inline-block; }
.extra-network-cards .card .overlay .actions { font-size: 2.2em; display: none; text-align-last: center; cursor: pointer; font-variant: unicase; height: 0.8em }
.extra-network-cards .card .overlay .actions > span { padding: 4px; }
.extra-network-cards .card .overlay .description { line-break: anywhere; display: none; color: white; }
.extra-network-cards .card:hover .overlay .actions { display: block; }
.extra-network-cards .card:hover .overlay .tags { display: block; }
.extra-network-cards .card:hover .overlay .description { display: block; }
#txt2img_description, #img2img_description { max-height: 63px; overflow-y: auto !important; }
#txt2img_description > label > textarea, #img2img_description > label > textarea { font-size: 0.9em }
.extra-network-cards .card .info {
position: absolute;
font-size: 1.5em;
top: 0.1em;
right: 0.1em;
background: var(--button-primary-border-color);
width: 1.3em;
text-align: center;
border-radius: var(--block-radius);
height: 1.3em;
line-height: 1.2em;
cursor: pointer;
visibility: hidden;
z-index: 100;
}
.extra-network-cards .card:hover .info { visibility: visible; }
.extra-networks_details {
#txt2img_extra_details, #img2img_extra_details {
position: fixed;
bottom: 50%;
left: 50%;
transform: translate(-50%, 50%);
padding: 1em;
padding: 0.8em;
border: var(--block-border-width) solid var(--highlight-color) !important;
z-index: 100;
box-shadow: var(--button-shadow);
width: 80vw;
display: none !important;
}
#txt2img_extra_details > div, #img2img_extra_details > div { overflow-y: auto; min-height: 40vh; max-height: 80vh; align-self: flex-start; }
#txt2img_extra_details td:first-child, #img2img_extra_details td:first-child { font-weight: bold; }
/* controlnet
.controlnet_control_type .controlnet_control_type_filter_group .wrap:last-of-type { display: grid; grid-auto-flow: row; grid-template-columns: repeat(4, minmax(0, 1fr)); }
+192 -156
View File
@@ -5,13 +5,14 @@ import html
import os.path
import urllib.parse
import threading
from datetime import datetime
from types import SimpleNamespace
from pathlib import Path
from collections import OrderedDict
import gradio as gr
from PIL import Image
from starlette.responses import FileResponse, JSONResponse
from modules import shared, scripts, modelloader
from modules.generation_parameters_copypaste import image_from_url_text
from modules.ui_components import ToolButton
import modules.ui_symbols as symbols
@@ -28,10 +29,7 @@ def listdir(path):
if path in dir_cache and os.path.getmtime(path) == dir_cache[path][0]:
return dir_cache[path][1]
else:
dir_cache[path] = (
os.path.getmtime(path),
[os.path.join(path, f) for f in os.listdir(path)]
)
dir_cache[path] = (os.path.getmtime(path), [os.path.join(path, f) for f in os.listdir(path)])
return dir_cache[path][1]
@@ -45,41 +43,39 @@ def register_page(page):
allowed_dirs.append(os.path.abspath(folder))
def fetch_file(filename: str = ""):
if not os.path.exists(filename):
return JSONResponse({ "error": f"file {filename}: not found" }, status_code=404)
if filename.startswith('html/') or filename.startswith('models/'):
def init_api(app):
def fetch_file(filename: str = ""):
if not os.path.exists(filename):
return JSONResponse({ "error": f"file {filename}: not found" }, status_code=404)
if filename.startswith('html/') or filename.startswith('models/'):
return FileResponse(filename, headers={"Accept-Ranges": "bytes"})
if not any(Path(folder).absolute() in Path(filename).absolute().parents for folder in allowed_dirs):
return JSONResponse({ "error": f"file {filename}: must be in one of allowed directories" }, status_code=403)
if os.path.splitext(filename)[1].lower() not in (".png", ".jpg", ".jpeg", ".webp"):
return JSONResponse({"error": f"file {filename}: not an image file"}, status_code=403)
return FileResponse(filename, headers={"Accept-Ranges": "bytes"})
if not any(Path(folder).absolute() in Path(filename).absolute().parents for folder in allowed_dirs):
return JSONResponse({ "error": f"file {filename}: must be in one of allowed directories" }, status_code=403)
if os.path.splitext(filename)[1].lower() not in (".png", ".jpg", ".jpeg", ".webp"):
return JSONResponse({"error": f"file {filename}: not an image file"}, status_code=403)
return FileResponse(filename, headers={"Accept-Ranges": "bytes"})
def get_metadata(page: str = "", item: str = ""):
page = next(iter([x for x in shared.extra_networks if x.name == page]), None)
if page is None:
return JSONResponse({ 'metadata': 'none' })
metadata = page.metadata.get(item, 'none')
if metadata is None:
metadata = ''
shared.log.debug(f"Extra networks metadata: page='{page}' item={item} len={len(metadata)}")
return JSONResponse({"metadata": metadata})
def get_metadata(page: str = "", item: str = ""):
page = next(iter([x for x in shared.extra_networks if x.name == page]), None)
if page is None:
return JSONResponse({ 'metadata': 'none' })
metadata = page.metadata.get(item, 'none')
if metadata is None:
metadata = ''
shared.log.debug(f"Extra networks metadata: page='{page}' item={item} len={len(metadata)}")
return JSONResponse({"metadata": metadata})
def get_info(page: str = "", item: str = ""):
page = next(iter([x for x in shared.extra_networks if x.name == page]), None)
if page is None:
return JSONResponse({ 'info': 'none' })
info = page.info.get(item, 'none')
if info is None:
info = ''
shared.log.debug(f"Extra networks info: page='{page}' item={item} len={len(info)}")
return JSONResponse({"info": info})
def get_info(page: str = "", item: str = ""):
page = next(iter([x for x in shared.extra_networks if x.name == page]), None)
if page is None:
return JSONResponse({ 'info': 'none' })
info = page.info.get(item, 'none')
if info is None:
info = ''
shared.log.debug(f"Extra networks info: page='{page}' item={item} len={len(info)}")
return JSONResponse({"info": info})
def add_pages_to_demo(app):
app.add_api_route("/sd_extra_networks/thumb", fetch_file, methods=["GET"])
app.add_api_route("/sd_extra_networks/metadata", get_metadata, methods=["GET"])
app.add_api_route("/sd_extra_networks/info", get_info, methods=["GET"])
@@ -98,16 +94,16 @@ class ExtraNetworksPage:
self.refresh_time = None
# class additional is to keep old extensions happy
self.card = '''
<div class='card' onclick={card_click} title='{title}' data-filename='{local_preview}' data-description='{description}' data-tags='{tags}'>
<div class='card' onclick={card_click} title='{name}' data-name='{name}' data-filename='{local_preview}' data-description='{description}' data-tags='{tags}'>
<div class='overlay'>
<span style="display:none" class='search_term'>{search_term}</span>
<div class='name'>{name}</div>
<div class='name'>{title}</div>
<div class='tags'></div>
<div class='actions'>
<div class='additional'><ul></ul></div>
<span title="Save current image as preview image" onclick={card_save_preview}></span>
<span title="Save current description" onclick={card_save_desc}>🛅</span>
{card_extra}
<span title="Save current image as preview image" onclick="saveCardPreview(event)"></span>
<span title="Save current description" onclick="saveCardDescription(event)">🛅</span>
<span title="Get details" onclick="showCardDetails(event)">&#x1f6c8;</span>
</div>
</div>
<img class='preview' src='{preview}' style='width: {width}px; height: {height}px; object-fit: {fit}' loading='lazy'></img>
@@ -223,11 +219,11 @@ class ExtraNetworksPage:
return []
def create_html(self, item, tabname):
# try:
try:
args = {
"tabname": json.dumps(tabname),
"name": item["name"].replace('_', ' '),
"title": item["name"],
"name": item["name"],
"title": item["name"].replace('_', ' '),
"tags": '|'.join([item.get("tags")] if isinstance(item.get("tags", {}), str) else list(item.get("tags", {}).keys())),
"preview": html.escape(item.get("preview", self.link_preview('html/card-no-preview.png'))),
"width": shared.opts.extra_networks_card_size,
@@ -238,25 +234,23 @@ class ExtraNetworksPage:
"description": item.get("description") or "",
"local_preview": item.get("local_preview"),
"card_click": item.get("onclick", '"' + html.escape(f'return cardClicked({item.get("prompt", None)}, {"true" if self.allow_negative_prompt else "false"})') + '"'),
"card_save_preview": '"' + html.escape('return saveCardPreview(event)') + '"',
"card_save_desc": '"' + html.escape('return saveCardDescription(event)') + '"',
"card_extra": "",
}
metadata = item.get("metadata", None)
if metadata is not None and len(metadata) > 0:
card_read_meta = '"' + html.escape(f'return readCardMetadata(event, {json.dumps(self.name)}, {json.dumps(item["name"])})') + '"'
args['card_extra'] += f'<span title="Read metadata" onclick={card_read_meta}>📘</span>'
info = item.get("info", None)
if info is not None and len(info) > 0:
card_read_info = '"' + html.escape(f'return readCardInformation(event, {json.dumps(self.name)}, {json.dumps(item["name"])})') + '"'
args['card_extra'] += f'<span title="Read info" onclick={card_read_info}>️</span>' # noqa
alias = item.get("alias", None)
if alias is not None:
args['title'] += f'\nAlias: {alias}'
return self.card.format(**args)
# except Exception as e:
# shared.log.error(f'Extra networks item error: page={tabname} item={item["name"]} {e}')
# return ""
except Exception as e:
shared.log.error(f'Extra networks item error: page={tabname} item={item["name"]} {e}')
return ""
def find_preview_file(self, path):
preview_extensions = ["jpg", "jpeg", "png", "webp", "tiff", "jp2"]
fn = os.path.splitext(path)[0]
files = listdir(os.path.dirname(path))
for file in [f'{fn}{mid}{ext}' for ext in preview_extensions for mid in ['.thumb.', '.preview.', '.']]:
if file in files:
return file
return 'html/card-no-preview.png'
def find_preview(self, path):
preview_extensions = ["jpg", "jpeg", "png", "webp", "tiff", "jp2"]
@@ -295,42 +289,6 @@ class ExtraNetworksPage:
pass
return ''
def save_preview(self, index, images, filename):
try:
image = image_from_url_text(images[int(index)])
except Exception as e:
shared.log.error(f'Extra network save preview: {filename} {e}')
return
is_allowed = False
for page in shared.extra_networks:
if any(path_is_parent(x, filename) for x in page.allowed_directories_for_previews()):
is_allowed = True
break
if not is_allowed:
shared.log.error(f'Extra network save preview: {filename} not allowed')
return
if image.width > 512 or image.height > 512:
image = image.convert('RGB')
image.thumbnail((512, 512), Image.HAMMING)
image.save(filename, quality=50)
fn, _ext = os.path.splitext(filename)
thumb = fn + '.thumb.jpg'
if os.path.exists(thumb):
shared.log.debug(f'Extra network delete thumbnail: {thumb}')
os.remove(thumb)
shared.log.info(f'Extra network save preview: {filename}')
def save_description(self, filename, desc):
lastDotIndex = filename.rindex('.')
filename = filename[0:lastDotIndex]+".txt"
if desc != "":
try:
with open(filename, 'w', encoding='utf-8') as f:
f.write(desc)
shared.log.info(f'Extra network save description: {filename} {desc}')
except Exception as e:
shared.log.error(f'Extra network save description: {filename} {e}')
def initialize():
shared.extra_networks.clear()
@@ -364,31 +322,128 @@ def get_pages():
class ExtraNetworksUi:
def __init__(self):
self.pages = None
self.button_save_preview = None
self.preview_target_filename = None
self.button_save_description = None
self.button_read_description = None
self.description_target_filename = None
self.description = None
self.tags = None
self.tabname = None
self.search = None
self.tabname: str = None
self.pages: list(str) = None
self.visible: gr.State = None
self.state: gr.Textbox = None
self.details: gr.Group = None
self.tabs: gr.Tabs = None
self.gallery: gr.Gallery = None
self.description: gr.Textbox = None
self.search: gr.Textbox = None
self.button_details: gr.Button = None
self.details_components: list = []
def create_ui(container, button, tabname, skip_indexing = False):
def create_ui(container, button_parent, tabname, skip_indexing = False):
ui = ExtraNetworksUi()
ui.pages = []
ui.tabname = tabname
with gr.Tabs(elem_id=tabname+"_extra_tabs"):
ui.pages = []
ui.state = gr.Textbox('{}', elem_id=tabname+"_extra_state", visible=False)
ui.visible = gr.State(value=False) # pylint: disable=abstract-class-instantiated
ui.details = gr.Group(elem_id=tabname+"_extra_details", visible=False)
ui.tabs = gr.Tabs(elem_id=tabname+"_extra_tabs")
ui.button_details = gr.Button('Details', elem_id=tabname+"_extra_details_btn", visible=False)
state = {}
def get_item(state):
if state is None or state.page is None or state.item is None:
return None, None
page = next(iter([x for x in get_pages() if x.title == state.page]), None)
if page is None:
return None, None
item = next(iter([x for x in page.items if x["name"] == state.item]), None)
if item is None:
return page, None
item = SimpleNamespace(**item)
return page, item
# main event that is triggered when js updates state text field with json values, used to communicate js -> python
def state_change(state_text):
try:
nonlocal state
state = SimpleNamespace(**json.loads(state_text))
except Exception as e:
shared.log.error(f'Extra networks state error: {e}')
return
page, item = get_item(state)
shared.log.debug(f'Extra network: op={state.op} page={page.title if page is not None else None} item={item.filename if item is not None else None}')
if state.op == 'getCardDetails':
pass
if state.op == 'saveCardDescription':
if item is None or state.description is None or len(state.description) == 0:
return
try:
filename = os.path.splitext(item.filename)[1] + '.txt'
with open(filename, 'w', encoding='utf-8') as f:
f.write(state.description)
shared.log.info(f'Extra network save: file="{filename}" description={state.description}')
except Exception as e:
shared.log.error(f'Extra network save: file="{filename}" {e}')
if state.op == 'saveCardPreview':
if item is None or item.local_preview is None or len(item.local_preview) == 0:
return
try:
images = list(ui.gallery.temp_files)
if len(images) < 1:
shared.log.error(f'Extra network save: preview="{item.local_preview}" no images')
return
image = Image.open(images[state.index])
except Exception as e:
shared.log.error(f'Extra network save: preview="{item.local_preview}" {e}')
return
if image.width > 512 or image.height > 512:
image = image.convert('RGB')
image.thumbnail((512, 512), Image.HAMMING)
image.save(item.local_preview, quality=50)
thumb = os.path.splitext(item.local_preview)[0] + '.thumb.jpg'
if os.path.exists(thumb):
shared.log.debug(f'Extra network delete: thumbnail="{thumb}"')
os.remove(thumb)
shared.log.info(f'Extra network save: preview="{item.local_preview}"')
def toggle_visibility(is_visible):
is_visible = not is_visible
return is_visible, gr.update(visible=is_visible), gr.update(variant=("secondary-down" if is_visible else "secondary"))
with ui.details:
details_close = ToolButton(symbols.close, elem_id=tabname+"_extra_details_close")
details_close.click(fn=lambda: gr.update(visible=False), inputs=[], outputs=[ui.details])
with gr.Row():
with gr.Column(scale=1):
text = gr.HTML('<div>title</div>')
ui.details_components.append(text)
with gr.Column(scale=1):
img = gr.Image(value=None, show_label=False, interactive=False, container=True)
ui.details_components.append(img)
with gr.Row():
gr.Button('Replace')
gr.Button('Delete')
with gr.Tabs():
with gr.Tab('Description'):
desc = gr.Textbox('', show_label=False, lines=8)
ui.details_components.append(desc)
with gr.Row():
gr.Button('Save')
gr.Button('Delete')
with gr.Tab('Info'):
info = gr.Textbox('', show_label=False, lines=8)
ui.details_components.append(info)
with gr.Row():
gr.Button('Save')
gr.Button('Delete')
with gr.Tab('Metadata'):
meta = gr.JSON({}, show_label=False, lines=8)
ui.details_components.append(meta)
with ui.tabs:
button_refresh = ToolButton(symbols.refresh, elem_id=tabname+"_extra_refresh")
button_close = ToolButton(symbols.close, elem_id=tabname+"_extra_close")
ui.search = gr.Textbox('', show_label=False, elem_id=tabname+"_extra_search", placeholder="Search...", elem_classes="textbox", lines=2)
ui.description = gr.Textbox('', show_label=False, elem_id=tabname+"_description", placeholder="Save/Replace Extra Network Description...", elem_classes="textbox", lines=2)
ui.preview_target_filename = gr.Textbox('Preview save filename', elem_id=tabname+"_preview_filename", visible=False)
ui.button_save_preview = gr.Button('Save preview', elem_id=tabname+"_save_preview", visible=False)
ui.description_target_filename = gr.Textbox('Description save filename', elem_id=tabname+"_description_filename", visible=False)
ui.button_save_description = gr.Button('Save description', elem_id=tabname+"_save_description", visible=False)
if ui.tabname == 'txt2img': # refresh only once
global refresh_time # pylint: disable=global-statement
@@ -397,12 +452,29 @@ def create_ui(container, button, tabname, skip_indexing = False):
page.create_page(ui.tabname, skip_indexing)
with gr.Tab(page.title, id=page.title.lower().replace(" ", "_"), elem_classes="extra-networks-tab"):
hmtl = gr.HTML(page.html, elem_id=f'{tabname}{page.name}_extra_page', elem_classes="extra-networks-page")
# hmtl.change(fn=lambda: None, _js=f'() => refreshExtraNetworks("{tabname}")', inputs=[], outputs=[])
ui.pages.append(hmtl)
def toggle_visibility(is_visible):
is_visible = not is_visible
return is_visible, gr.update(visible=is_visible), gr.update(variant=("secondary-down" if is_visible else "secondary"))
def show_details(text, img, desc, info, meta):
page, item = get_item(state)
if item is not None and os.path.exists(item.filename):
stat = os.stat(item.filename)
text = f'''
<h2 style="border-bottom: 1px solid var(--button-primary-border-color); margin-bottom: 1em; margin-top: -1.3em !important;">{item.name}</h2>
<table style="width: 100%; line-height: 1.3em;"><tbody>
<tr><td>Type</td><td>{page.title}</td></tr>
<tr><td>Alias</td><td>{getattr(item, 'alias', 'N/A')}</td></tr>
<tr><td>Tags</td><td>{getattr(item, 'tags', 'N/A')}</td></tr>
<tr><td>Filename</td><td>{item.filename}</td></tr>
<tr><td>Hash</td><td>{getattr(item, 'hash', 'N/A')}</td></tr>
<tr><td>Size</td><td>{round(stat.st_size/1024/1024, 2)} MB</td></tr>
<tr><td>Last modified</td><td>{datetime.fromtimestamp(stat.st_mtime)}</td></tr>
</tbody></table>
'''
desc = item.description
info = page.info.get(item.name, 'N/A')
meta = page.metadata.get(item.name, 'N/A')
img = page.find_preview_file(item.filename)
return [text, img, desc, info, meta, gr.update(visible=True)]
def en_refresh(title):
pages = []
@@ -416,49 +488,13 @@ def create_ui(container, button, tabname, skip_indexing = False):
ui.search.update(value = ui.search.value)
return pages
state_visible = gr.State(value=False) # pylint: disable=abstract-class-instantiated
button.click(fn=toggle_visibility, inputs=[state_visible], outputs=[state_visible, container, button])
button_close.click(fn=toggle_visibility, inputs=[state_visible], outputs=[state_visible, container])
button_parent.click(fn=toggle_visibility, inputs=[ui.visible], outputs=[ui.visible, container, button_parent])
button_close.click(fn=toggle_visibility, inputs=[ui.visible], outputs=[ui.visible, container])
button_refresh.click(_js='getENActivePage', fn=en_refresh, inputs=[ui.search], outputs=ui.pages)
ui.state.change(state_change, inputs=[ui.state], outputs=[])
ui.button_details.click(show_details, _js="getCardDetails", inputs=ui.details_components, outputs=ui.details_components + [ui.details])
return ui
def path_is_parent(parent_path, child_path):
parent_path = os.path.abspath(parent_path)
child_path = os.path.abspath(child_path)
return child_path.startswith(parent_path)
def setup_ui(ui, gallery):
def save_preview(pagename, index, images, filename):
pages = []
for page in get_pages():
if pagename is None or pagename == '' or pagename == page.title or len(page.html) == 0:
page.save_preview(index, images, filename)
page.create_page(ui.tabname)
pages.append(page.html)
return pages
ui.button_save_preview.click(
fn=save_preview,
_js="function(t, i, y, z) {return [getENActivePage(), selected_gallery_index(), y, z]}",
inputs=[ui.search, ui.preview_target_filename, gallery, ui.preview_target_filename],
outputs=ui.pages
)
def save_description(pagename, filename, desc):
pages = []
for page in get_pages():
if pagename is None or pagename == '' or pagename == page.title or len(page.html) == 0:
page.save_description(filename, desc)
page.create_page(ui.tabname)
pages.append(page.html)
return pages
ui.button_save_description.click(
fn=save_description,
_js="function(t, x, y) { return [getENActivePage(), x, y] }",
inputs=[ui.search, ui.description_target_filename, ui.description],
outputs=ui.pages
)
ui.gallery = gallery
+1 -1
View File
@@ -281,7 +281,7 @@ def start_ui():
create_api(app)
timer.startup.record("api")
ui_extra_networks.add_pages_to_demo(app)
ui_extra_networks.init_api(app)
modules.script_callbacks.app_started_callback(shared.demo, app)
timer.startup.record("app-started")