Merge branch 'dev' into master

This commit is contained in:
Vladimir Mandic
2025-09-01 11:41:07 -04:00
committed by GitHub
226 changed files with 5840 additions and 763 deletions
+4 -4
View File
@@ -98,7 +98,7 @@ table.settings-value-table td { padding: 0.4em; border: 1px solid #ccc; max-widt
.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 .tags { margin: 4px; display: none; overflow-wrap: break-word; }
.extra-network-cards .card .tags { margin: 4px; display: none; overflow-wrap: anywhere; }
.extra-network-cards .card .tag { padding: 2px; margin: 2px; background: var(--neutral-700); cursor: pointer; display: inline-block; }
.extra-network-cards .card .actions > span { padding: 4px; }
.extra-network-cards .card:hover .actions { display: block; }
@@ -128,8 +128,8 @@ div:has(>#tab-browser-folders) { flex-grow: 0 !important; background-color: var(
/* loader */
.splash { position: fixed; top: 0; left: 0; width: 100vw; height: 100vh; z-index: 1000; display: block; text-align: center; }
.motd { margin-top: 2em; color: var(--body-text-color-subdued); font-family: monospace; font-variant: all-petite-caps; }
.splash-img { margin: 10% auto 0 auto; width: 512px; background-repeat: no-repeat; height: 512px; animation: color 10s infinite alternate; max-width: 80vw; background-size: contain; }
.motd { margin-top: 2em; color: var(--body-text-color-subdued); font-family: monospace; font-variant: all-petite-caps; font-size: 1.2em; }
.splash-img { margin: 10% auto 0 auto; width: 512px; background-repeat: no-repeat; height: 512px; animation: hue 5s infinite alternate; max-width: 80vw; background-size: contain; }
.loading { color: white; position: absolute; top: 20%; left: 50%; transform: translateX(-50%); }
.loader { width: 300px; height: 300px; border: var(--spacing-md) solid transparent; border-radius: 50%; border-top: var(--spacing-md) solid var(--primary-600); animation: spin 4s linear infinite; position: relative; }
.loader::before, .loader::after { content: ""; position: absolute; top: 6px; bottom: 6px; left: 6px; right: 6px; border-radius: 50%; border: var(--spacing-md) solid transparent; }
@@ -137,4 +137,4 @@ div:has(>#tab-browser-folders) { flex-grow: 0 !important; background-color: var(
.loader::after { border-top-color: var(--primary-300); animation: spin 1.5s linear infinite; }
@keyframes move { from { background-position-x: 0, -40px; } to { background-position-x: 0, 40px; } }
@keyframes spin { from { transform: rotate(0deg); } to { transform: rotate(360deg); } }
@keyframes color { from { filter: hue-rotate(0deg) } to { filter: hue-rotate(360deg) } }
@keyframes hue { from { filter: hue-rotate(0deg) } to { filter: hue-rotate(360deg) } }
+3 -3
View File
@@ -953,7 +953,7 @@ svg.feather.feather-image,
}
/* No Preview Card Styles */
.extra-network-cards .card:has(>img[src*="card-no-preview.png"])::before {
.extra-network-cards .card:has(>img[src*="missing.png"])::before {
content: '';
position: absolute;
width: 100%;
@@ -1007,11 +1007,11 @@ svg.feather.feather-image,
}
.splash-img {
margin: 0;
margin: 10% auto 0 auto;
width: 512px;
height: 512px;
background-repeat: no-repeat;
animation: color 8s infinite alternate, move 3s infinite alternate;
animation: hue 5s infinite alternate;
}
.loading {
+1 -1
View File
@@ -107,7 +107,7 @@ async function modelCardClick(id) {
downloads: data.downloads?.toString() || '',
creator,
desc: data.desc || 'no description available',
image: images.length > 0 ? images[0] : '/sdapi/v1/network/thumb?filename=html/card-no-preview.png',
image: images.length > 0 ? images[0] : '/sdapi/v1/network/thumb?filename=html/missing.png',
versions: versionsHTML || '',
});
el.innerHTML = modelHTML;
+32 -4
View File
@@ -14,8 +14,13 @@ const getENActiveTab = () => {
else if (gradioApp().getElementById('extras_image')?.checkVisibility()) tabName = 'process';
else if (gradioApp().getElementById('interrogate_image')?.checkVisibility()) tabName = 'caption';
else if (gradioApp().getElementById('tab-gallery-search')?.checkVisibility()) tabName = 'gallery';
if (tabName in ['process', 'caption', 'gallery']) tabName = lastTab;
else lastTab = tabName;
if (['process', 'caption', 'gallery'].includes(tabName)) {
tabName = lastTab;
} else if (tabName !== '') {
lastTab = tabName;
}
if (tabName !== '') return tabName;
// legacy method
if (gradioApp().getElementById('tab_txt2img')?.style.display === 'block') tabName = 'txt2img';
@@ -277,8 +282,31 @@ function extraNetworksSearchButton(event) {
const tabName = getENActiveTab();
const searchTextarea = gradioApp().querySelector(`#${tabName}_extra_search textarea`);
const button = event.target;
searchTextarea.value = `${button.textContent.trim()}/`;
updateInput(searchTextarea);
if (searchTextarea) {
searchTextarea.value = `${button.textContent.trim()}/`;
updateInput(searchTextarea);
} else {
console.error(`Could not find the search textarea for the tab: ${tabName}`);
}
}
function extraNetworksFilterVersion(event) {
// log('extraNetworksFilterVersion', event);
const version = event.target.textContent.trim();
const activeTab = gradioApp().querySelector('.extra-networks-tab:not([style*="display: none"])');
if (!activeTab) return;
const cardContainer = activeTab.querySelector('.extra-network-cards');
if (!cardContainer) return;
if (cardContainer.dataset.activeVersion === version) {
cardContainer.dataset.activeVersion = '';
cardContainer.querySelectorAll('.card').forEach((card) => card.style.display = '');
} else {
cardContainer.dataset.activeVersion = version;
cardContainer.querySelectorAll('.card').forEach((card) => {
if (card.dataset.version === version) card.style.display = '';
else card.style.display = 'none';
});
}
}
let desiredStyle = '';
+96 -50
View File
@@ -1197,7 +1197,7 @@ table.settings-value-table td {
}
.extra-networks .search textarea {
width: calc(120px / 1.1);
width: calc(140px / 1.1);
resize: none;
margin-right: 2px;
}
@@ -1233,7 +1233,7 @@ table.settings-value-table td {
padding: 3px 3px 3px 12px;
text-align: left;
text-indent: -6px;
width: 120px;
width: 140px;
width: 100%;
}
@@ -1249,7 +1249,7 @@ table.settings-value-table td {
.extra-network-subdirs {
background: var(--input-background-fill);
border-radius: 4px;
min-width: max(15%, 120px);
min-width: max(15%, 140px);
overflow-x: hidden;
overflow-y: auto;
padding-top: 0.5em;
@@ -1376,7 +1376,17 @@ table.settings-value-table td {
display: block;
}
.extra-network-cards .card:has(>img[src*="card-no-preview.png"])::before {
.extra-network-cards .card:hover {
z-index: 100;
position: relative;
}
.extra-network-cards .card:hover .tags {
display: block;
z-index: 101; /* Optional: ensure tags are above everything */
}
.extra-network-cards .card:has(>img[src*="missing.png"])::before {
background-color: var(--data-color);
content: '';
height: 100%;
@@ -1461,7 +1471,6 @@ table.settings-value-table td {
overflow-y: auto;
}
.extra-details td:first-child {
font-weight: bold;
vertical-align: top;
@@ -1471,6 +1480,29 @@ table.settings-value-table td {
max-height: 50vh;
}
.network-folder::before {
content: "󰉖 ";
margin-right: 0.8em;
}
.network-reference {
filter: contrast(0.9);
}
.network-reference::before {
content: "󰴊 ";
margin-right: 0.8em;
}
.network-model {
opacity: 0.6;
}
.network-model::before {
content: "󰴉 ";
margin-right: 0.8em;
}
.input-accordion-checkbox {
display: none !important;
}
@@ -1716,6 +1748,13 @@ background: var(--background-color)
width: max-content;
}
#tab-gallery-files gallery-file {
/* Add a vertical gutter between items (left/right), matching existing small row spacing */
display: inline-block;
margin-right: 0.2em;
vertical-align: top; /* keep rows aligned on the top edge */
}
#tab-gallery-files {
display: block;
height: 75vh;
@@ -1813,6 +1852,24 @@ div:has(>#tab-gallery-folders) {
object-fit: contain;
}
/* Gallery video preview matches image preview sizing and layout */
#tab-gallery-video {
height: 63vh;
}
/* Ensure the <video> element fills the preview column and preserves aspect */
#tab-gallery-video video {
width: 100%;
height: 100% !important;
object-fit: contain;
background: none;
}
/* Gradio container around the video should not add extra spacing */
#tab-gallery-video .wrap {
height: 100%;
}
.gallery-sort {
background: var(--input-background-fill) !important;
margin: 0 !important;
@@ -1867,70 +1924,73 @@ div:has(>#tab-gallery-folders) {
}
.splash {
display: block;
height: 100vh;
left: 0;
position: fixed;
text-align: center;
top: 0;
left: 0;
width: 100vw;
height: 100vh;
z-index: 1000;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
background-color: rgba(0, 0, 0, 0.8);
}
.motd {
margin-top: 1em;
color: var(--body-text-color-subdued);
font-family: monospace;
font-variant: all-petite-caps;
margin-top: 2em;
font-size: 1.2em;
}
.splash-img {
animation: color 10s infinite alternate;
background-repeat: no-repeat;
background-size: contain;
height: 512px;
margin: 10% auto 0 auto;
max-width: 80vw;
width: 512px;
height: 512px;
background-repeat: no-repeat;
animation: hue 5s infinite alternate;
}
.loading {
color: white;
left: 50%;
position: absolute;
top: 20%;
transform: translateX(-50%);
position: border-box;
top: 85%;
font-size: 1.5em;
}
.loader {
animation: spin 4s linear infinite;
width: 100px;
height: 100px;
border: var(--spacing-md) solid transparent;
border-radius: 50%;
border-top: var(--spacing-md) solid var(--primary-600);
height: 300px;
position: relative;
width: 300px;
animation: spin 2s linear infinite, hue 5s infinite alternate;
position: border-box;
}
.loader::before, .loader::after {
border: var(--spacing-md) solid transparent;
border-radius: 50%;
bottom: 6px;
.loader::before,
.loader::after {
content: "";
left: 6px;
position: absolute;
right: 6px;
top: 6px;
bottom: 6px;
left: 6px;
right: 6px;
border-radius: 50%;
border: var(--spacing-md) solid transparent;
animation: hue 5s infinite alternate;
}
.loader::before {
animation: 3s spin linear infinite;
border-top-color: var(--primary-900);
animation: spin 3s linear infinite;
}
.loader::after {
animation: spin 1.5s linear infinite;
border-top-color: var(--primary-300);
animation: spin 1.5s linear infinite;
}
.docs-search textarea {
@@ -2087,35 +2147,21 @@ div:has(>#tab-gallery-folders) {
filter: blur(0);
}
@keyframes move {
from {
background-position-x: 0, -40px;
}
to {
background-position-x: 0, 40px;
}
}
@keyframes spin {
from {
transform: rotate(0deg);
}
to {
transform: rotate(360deg);
}
}
@keyframes color {
from {
filter: hue-rotate(0deg)
@keyframes hue {
0% {
filter: hue-rotate(0deg);
}
to {
filter: hue-rotate(360deg)
100% {
filter: hue-rotate(360deg);
}
}
+114 -52
View File
@@ -9,10 +9,12 @@ const localeData = {
type: 2,
hint: null,
btn: null,
expandTimeout: null, // New property for expansion timeout
expandTimeout: null, // Property for expansion timeout
currentElement: null, // Track current element for expansion
observer: null, // MutationObserver for DOM changes
};
let localeTimeout = null;
const isTouchDevice = 'ontouchstart' in window;
async function cycleLocale() {
clearTimeout(localeTimeout);
@@ -62,43 +64,49 @@ async function tooltipCreate() {
if (window.opts.tooltips === 'None') localeData.type = 0;
if (window.opts.tooltips === 'Browser default') localeData.type = 1;
if (window.opts.tooltips === 'UI tooltips') localeData.type = 2;
if (localeData.type === 2) { // setup event delegation for tooltips instead of individual listeners
if (isTouchDevice) {
gradioApp().addEventListener('touchstart', tooltipShowDelegated); // eslint-disable-line no-use-before-define
gradioApp().addEventListener('touchend', tooltipHideDelegated); // eslint-disable-line no-use-before-define
}
gradioApp().addEventListener('pointerover', tooltipShowDelegated); // eslint-disable-line no-use-before-define
gradioApp().addEventListener('pointerout', tooltipHideDelegated); // eslint-disable-line no-use-before-define
}
if (!localeData.observer) initializeDOMObserver(); // eslint-disable-line no-use-before-define
}
async function expandTooltip(element, longHint) {
if (localeData.currentElement === element && localeData.hint.classList.contains('tooltip-show')) {
// Hide the progress ring
const ring = localeData.hint.querySelector('.tooltip-progress-ring');
if (ring) {
ring.style.opacity = '0';
}
// Expand the container
if (ring) ring.style.opacity = '0';
localeData.hint.classList.add('tooltip-expanded');
// After container starts expanding, reveal the long content
setTimeout(() => {
const longContent = localeData.hint.querySelector('.long-content');
if (longContent) {
longContent.classList.add('show');
}
if (longContent) longContent.classList.add('show');
}, 100);
}
}
async function tooltipShowDelegated(e) { // use event delegation to handle dynamically created elements
if (e.target.dataset && e.target.dataset.hint) tooltipShow(e); // eslint-disable-line no-use-before-define
}
async function tooltipHideDelegated(e) {
if (e.target.dataset && e.target.dataset.hint) tooltipHide(e); // eslint-disable-line no-use-before-define
}
async function tooltipShow(e) {
// Clear any existing expansion timeout
if (localeData.expandTimeout) {
if (localeData.expandTimeout) { // clear any existing expansion timeout
clearTimeout(localeData.expandTimeout);
localeData.expandTimeout = null;
}
// Remove expanded class and reset current element
localeData.hint.classList.remove('tooltip-expanded');
localeData.hint.classList.remove('tooltip-expanded'); // remove expanded class and reset current element
localeData.currentElement = e.target;
if (e.target.dataset.hint) {
// Create progress ring SVG
const progressRing = `
const progressRing = ` // create progress ring SVG
<div class="tooltip-progress-ring">
<svg viewBox="0 0 12 12">
<circle class="ring-background" cx="6" cy="6" r="5"></circle>
@@ -106,8 +114,7 @@ async function tooltipShow(e) {
</svg>
</div>
`;
// Set up the complete content structure from the start
// set up the complete content structure from the start
let content = `
<div class="tooltip-header">
<b>${e.target.textContent}</b>
@@ -116,21 +123,12 @@ async function tooltipShow(e) {
<div class="separator"></div>
${e.target.dataset.hint}
`;
// Add long content if available, but keep it hidden
if (e.target.dataset.longHint) {
content += `<div class="long-content"><div class="separator"></div>${e.target.dataset.longHint}</div>`;
}
// Add reload notice if needed
if (e.target.dataset.reload) {
if (e.target.dataset.longHint) content += `<div class="long-content"><div class="separator"></div>${e.target.dataset.longHint}</div>`; // add long content if available, but keep it hidden
if (e.target.dataset.reload) { // add reload notice if needed
const reloadType = e.target.dataset.reload;
let reloadText = '';
if (reloadType === 'model') {
reloadText = 'Requires model reload';
} else if (reloadType === 'server') {
reloadText = 'Requires server restart';
}
if (reloadType === 'model') reloadText = 'Requires model reload';
else if (reloadType === 'server') reloadText = 'Requires server restart';
if (reloadText) {
content += `
<div class="tooltip-reload-notice">
@@ -144,40 +142,28 @@ async function tooltipShow(e) {
localeData.hint.innerHTML = content;
localeData.hint.classList.add('tooltip-show');
if (e.clientX > window.innerWidth / 2) {
localeData.hint.classList.add('tooltip-left');
} else {
localeData.hint.classList.remove('tooltip-left');
}
if (e.clientX > window.innerWidth / 2) localeData.hint.classList.add('tooltip-left');
else localeData.hint.classList.remove('tooltip-left');
// Set up expansion timer if long hint is available
if (e.target.dataset.longHint) {
// Start progress ring animation
const ring = localeData.hint.querySelector('.tooltip-progress-ring');
if (e.target.dataset.longHint) { // set up expansion timer if long hint is available
const ring = localeData.hint.querySelector('.tooltip-progress-ring'); // start progress ring animation
const ringProgress = localeData.hint.querySelector('.ring-progress');
if (ring && ringProgress) {
// Show the ring and start animation
setTimeout(() => {
ring.classList.add('active');
ringProgress.classList.add('animate');
}, 100);
}
localeData.expandTimeout = setTimeout(() => {
expandTooltip(e.target, e.target.dataset.longHint);
}, 3000);
localeData.expandTimeout = setTimeout(() => expandTooltip(e.target, e.target.dataset.longHint), 3000);
}
}
}
async function tooltipHide(e) {
// Clear expansion timeout when hiding
if (localeData.expandTimeout) {
clearTimeout(localeData.expandTimeout);
localeData.expandTimeout = null;
}
localeData.hint.classList.remove('tooltip-show', 'tooltip-expanded');
localeData.currentElement = null;
}
@@ -294,8 +280,6 @@ async function setHint(el, entry) {
el.dataset.hint = entry.hint;
if (entry.longHint && entry.longHint.length > 0) el.dataset.longHint = entry.longHint;
if (entry.reload && entry.reload.length > 0) el.dataset.reload = entry.reload;
el.addEventListener('mouseover', tooltipShow);
el.addEventListener('mouseout', tooltipHide);
} else {
// tooltips disabled
}
@@ -345,6 +329,7 @@ async function setHints(analyze = false) {
localeData.initial = false;
const t1 = performance.now();
// localeData.btn.style.backgroundColor = localeData.locale !== 'en' ? 'var(--primary-500)' : '';
log('touchDevice', isTouchDevice);
log('setHints', { type: localeData.type, locale: localeData.locale, elements: elements.length, localized, hints, data: localeData.data.length, override: overrideData.length, time: Math.round(t1 - t0) });
// sortUIElements();
if (analyze) {
@@ -359,3 +344,80 @@ const analyzeHints = async () => {
localeData.data = [];
await setHints(true);
};
// Apply hints to a single element immediately
async function applyHintToElement(el) {
if (!localeData.data || localeData.data.length === 0) return;
if (!el.textContent) return;
// check if element matches our selector criteria
const isValidElement = el.tagName === 'BUTTON'
|| el.tagName === 'H2'
|| (el.tagName === 'SPAN' && (el.parentElement?.tagName === 'LABEL' || el.parentElement?.classList.contains('label-wrap')));
if (!isValidElement) return;
let found; // find matching hint data
if (el.dataset.original) found = localeData.data.find((l) => l.label.toLowerCase().trim() === el.dataset.original.toLowerCase().trim());
else found = localeData.data.find((l) => l.label.toLowerCase().trim() === el.textContent.toLowerCase().trim());
if (found?.localized?.length > 0) { // apply localization if found
if (!el.dataset.original) el.dataset.original = el.textContent;
replaceTextContent(el, found.localized);
}
if (found?.hint?.length > 0) setHint(el, found); // apply hint if found
}
// Initialize MutationObserver for immediate hint application
function initializeDOMObserver() {
if (localeData.observer) {
localeData.observer.disconnect();
}
localeData.observer = new MutationObserver((mutations) => {
// Process added nodes immediately
for (const mutation of mutations) {
if (mutation.type === 'childList') {
for (const node of mutation.addedNodes) {
if (node.nodeType === Node.ELEMENT_NODE) {
// Apply hints to the node itself
applyHintToElement(node);
// Apply hints to all relevant children
const elements = [
...Array.from(node.querySelectorAll('button')),
...Array.from(node.querySelectorAll('h2')),
...Array.from(node.querySelectorAll('label > span')),
...Array.from(node.querySelectorAll('.label-wrap > span')),
];
// Include the node itself if it matches
if (node.matches && (
node.matches('button')
|| node.matches('h2')
|| node.matches('label > span')
|| node.matches('.label-wrap > span')
)) {
elements.push(node);
}
// Apply hints immediately to all found elements
elements.forEach((el) => applyHintToElement(el));
}
}
}
}
});
// Start observing the entire gradio app for changes
const targetNode = gradioApp();
if (targetNode) {
localeData.observer.observe(targetNode, {
childList: true,
subtree: true,
});
}
}
// Export for external use if needed
const forceReapplyHints = () => setHints();
+12 -12
View File
@@ -8,18 +8,18 @@ async function initStartup() {
if (window.setupLogger) await setupLogger();
// all items here are non-blocking async calls
initModels();
getUIDefaults();
initPromptChecker();
initContextMenu();
initDragDrop();
initAccordions();
initSettings();
initImageViewer();
initGallery();
initiGenerationParams();
initChangelog();
setupControlUI();
await initModels();
await getUIDefaults();
await initPromptChecker();
await initContextMenu();
await initDragDrop();
await initAccordions();
await initSettings();
await initImageViewer();
await initGallery();
await initiGenerationParams();
await initChangelog();
await setupControlUI();
// reconnect server session
await reconnectUI();