mirror of
https://github.com/willmiao/ComfyUI-Lora-Manager.git
synced 2026-03-25 07:05:43 -03:00
checkpoint
This commit is contained in:
@@ -29,6 +29,8 @@ body.modal-open {
|
|||||||
border: 1px solid var(--lora-border);
|
border: 1px solid var(--lora-border);
|
||||||
box-shadow: 0 8px 32px rgba(0, 0, 0, 0.1);
|
box-shadow: 0 8px 32px rgba(0, 0, 0, 0.1);
|
||||||
transform: translateZ(0); /* 创建新的堆叠上下文 */
|
transform: translateZ(0); /* 创建新的堆叠上下文 */
|
||||||
|
max-height: 90vh; /* 限制最大高度为视窗高度的90% */
|
||||||
|
overflow-y: auto; /* 添加垂直滚动条 */
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Delete Modal specific styles */
|
/* Delete Modal specific styles */
|
||||||
@@ -617,44 +619,66 @@ body.modal-open {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.carousel {
|
.carousel {
|
||||||
display: flex;
|
transition: max-height 0.3s ease-in-out;
|
||||||
flex-direction: column; /* 改为垂直布局 */
|
overflow: hidden;
|
||||||
gap: var(--space-3);
|
|
||||||
overflow-y: auto; /* 垂直滚动 */
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.carousel img,
|
.carousel.collapsed {
|
||||||
.carousel video {
|
max-height: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.carousel-container {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: var(--space-2);
|
||||||
|
}
|
||||||
|
|
||||||
|
.media-wrapper {
|
||||||
|
position: relative;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: auto;
|
background: var(--lora-surface);
|
||||||
max-height: 70vh; /* 限制单张图片最大高度 */
|
margin-bottom: var(--space-2);
|
||||||
|
}
|
||||||
|
|
||||||
|
.media-wrapper:last-child {
|
||||||
|
margin-bottom: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.media-wrapper img,
|
||||||
|
.media-wrapper video {
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
object-fit: contain;
|
object-fit: contain;
|
||||||
border-radius: var(--border-radius-base);
|
|
||||||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Scroll Indicator */
|
/* Scroll Indicator */
|
||||||
.scroll-indicator {
|
.scroll-indicator {
|
||||||
position: sticky;
|
cursor: pointer;
|
||||||
top: 0;
|
|
||||||
left: 0;
|
|
||||||
right: 0;
|
|
||||||
text-align: center;
|
|
||||||
padding: var(--space-2);
|
padding: var(--space-2);
|
||||||
background: linear-gradient(to bottom,
|
background: var(--lora-surface);
|
||||||
var(--lora-surface) 0%,
|
border: 1px solid var(--lora-border);
|
||||||
transparent 100%);
|
border-radius: var(--border-radius-sm);
|
||||||
opacity: 0.8;
|
display: flex;
|
||||||
transition: opacity 0.3s;
|
align-items: center;
|
||||||
pointer-events: none;
|
justify-content: center;
|
||||||
z-index: 1; /* 确保滚动提示在图片上层 */
|
gap: 8px;
|
||||||
|
margin-bottom: var(--space-2);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Remove horizontal scrolling styles */
|
.scroll-indicator:hover {
|
||||||
.carousel {
|
background: oklch(var(--lora-accent) / 0.1);
|
||||||
scroll-snap-type: none;
|
}
|
||||||
grid-auto-flow: initial;
|
|
||||||
overflow-x: initial;
|
.lazy {
|
||||||
|
opacity: 0;
|
||||||
|
transition: opacity 0.3s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.lazy[src] {
|
||||||
|
opacity: 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Update Trigger Words styles */
|
/* Update Trigger Words styles */
|
||||||
|
|||||||
@@ -171,15 +171,7 @@ export function showLoraModal(lora) {
|
|||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="showcase-section">
|
${renderShowcaseImages(lora.civitai.images)}
|
||||||
<div class="scroll-indicator">
|
|
||||||
<i class="fas fa-chevron-down"></i>
|
|
||||||
Scroll for more examples
|
|
||||||
</div>
|
|
||||||
<div class="carousel">
|
|
||||||
${renderShowcaseImages(lora.civitai.images)}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
`;
|
`;
|
||||||
@@ -275,21 +267,101 @@ function renderTriggerWords(words) {
|
|||||||
function renderShowcaseImages(images) {
|
function renderShowcaseImages(images) {
|
||||||
if (!images?.length) return '';
|
if (!images?.length) return '';
|
||||||
|
|
||||||
return images.map(img => {
|
return `
|
||||||
if (img.type === 'video') {
|
<div class="showcase-section">
|
||||||
return `
|
<div class="scroll-indicator" onclick="toggleShowcase(this)">
|
||||||
<video controls autoplay muted loop crossorigin="anonymous" referrerpolicy="no-referrer">
|
<i class="fas fa-chevron-down"></i>
|
||||||
<source src="${img.url}" type="video/mp4">
|
<span>Show ${images.length} examples</span>
|
||||||
Your browser does not support video playback
|
</div>
|
||||||
</video>
|
<div class="carousel collapsed">
|
||||||
`;
|
<div class="carousel-container">
|
||||||
}
|
${images.map(img => {
|
||||||
return `
|
// 计算适当的展示高度:
|
||||||
<img src="${img.url}"
|
// 1. 保持原始宽高比
|
||||||
alt="Preview"
|
// 2. 限制最大高度为视窗高度的60%
|
||||||
crossorigin="anonymous"
|
// 3. 确保最小高度为容器宽度的40%
|
||||||
referrerpolicy="no-referrer"
|
const aspectRatio = (img.height / img.width) * 100;
|
||||||
loading="lazy">
|
const containerWidth = 800; // modal content的最大宽度
|
||||||
`;
|
const minHeightPercent = 40; // 最小高度为容器宽度的40%
|
||||||
}).join('');
|
const maxHeightPercent = (window.innerHeight * 0.6 / containerWidth) * 100;
|
||||||
|
const heightPercent = Math.max(
|
||||||
|
minHeightPercent,
|
||||||
|
Math.min(maxHeightPercent, aspectRatio)
|
||||||
|
);
|
||||||
|
|
||||||
|
if (img.type === 'video') {
|
||||||
|
return `
|
||||||
|
<div class="media-wrapper" style="padding-bottom: ${heightPercent}%">
|
||||||
|
<video controls autoplay muted loop crossorigin="anonymous"
|
||||||
|
referrerpolicy="no-referrer" data-src="${img.url}"
|
||||||
|
class="lazy">
|
||||||
|
<source data-src="${img.url}" type="video/mp4">
|
||||||
|
Your browser does not support video playback
|
||||||
|
</video>
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
return `
|
||||||
|
<div class="media-wrapper" style="padding-bottom: ${heightPercent}%">
|
||||||
|
<img data-src="${img.url}"
|
||||||
|
alt="Preview"
|
||||||
|
crossorigin="anonymous"
|
||||||
|
referrerpolicy="no-referrer"
|
||||||
|
width="${img.width}"
|
||||||
|
height="${img.height}"
|
||||||
|
class="lazy">
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
}).join('')}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add this to the window object for global access
|
||||||
|
export function toggleShowcase(element) {
|
||||||
|
const carousel = element.nextElementSibling;
|
||||||
|
const isCollapsed = carousel.classList.contains('collapsed');
|
||||||
|
const indicator = element.querySelector('span');
|
||||||
|
const icon = element.querySelector('i');
|
||||||
|
|
||||||
|
carousel.classList.toggle('collapsed');
|
||||||
|
|
||||||
|
if (isCollapsed) {
|
||||||
|
indicator.textContent = 'Hide examples';
|
||||||
|
icon.classList.replace('fa-chevron-down', 'fa-chevron-up');
|
||||||
|
initLazyLoading(carousel);
|
||||||
|
} else {
|
||||||
|
const count = carousel.querySelectorAll('.media-wrapper').length;
|
||||||
|
indicator.textContent = `Show ${count} examples`;
|
||||||
|
icon.classList.replace('fa-chevron-up', 'fa-chevron-down');
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Add lazy loading initialization
|
||||||
|
export function initLazyLoading(container) {
|
||||||
|
const lazyElements = container.querySelectorAll('.lazy');
|
||||||
|
|
||||||
|
const lazyLoad = (element) => {
|
||||||
|
if (element.tagName.toLowerCase() === 'video') {
|
||||||
|
element.src = element.dataset.src;
|
||||||
|
element.querySelector('source').src = element.dataset.src;
|
||||||
|
element.load();
|
||||||
|
} else {
|
||||||
|
element.src = element.dataset.src;
|
||||||
|
}
|
||||||
|
element.classList.remove('lazy');
|
||||||
|
};
|
||||||
|
|
||||||
|
const observer = new IntersectionObserver((entries) => {
|
||||||
|
entries.forEach(entry => {
|
||||||
|
if (entry.isIntersecting) {
|
||||||
|
lazyLoad(entry.target);
|
||||||
|
observer.unobserve(entry.target);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
lazyElements.forEach(element => observer.observe(element));
|
||||||
}
|
}
|
||||||
@@ -2,7 +2,7 @@ import { debounce } from './utils/debounce.js';
|
|||||||
import { LoadingManager } from './managers/LoadingManager.js';
|
import { LoadingManager } from './managers/LoadingManager.js';
|
||||||
import { modalManager } from './managers/ModalManager.js';
|
import { modalManager } from './managers/ModalManager.js';
|
||||||
import { state } from './state/index.js';
|
import { state } from './state/index.js';
|
||||||
import { showLoraModal } from './components/LoraCard.js';
|
import { showLoraModal, toggleShowcase, initLazyLoading } from './components/LoraCard.js';
|
||||||
import { loadMoreLoras, fetchCivitai, deleteModel, replacePreview, resetAndReload, refreshLoras } from './api/loraApi.js';
|
import { loadMoreLoras, fetchCivitai, deleteModel, replacePreview, resetAndReload, refreshLoras } from './api/loraApi.js';
|
||||||
import {
|
import {
|
||||||
showToast,
|
showToast,
|
||||||
@@ -45,6 +45,7 @@ window.toggleFolderTags = toggleFolderTags;
|
|||||||
window.settingsManager = new SettingsManager();
|
window.settingsManager = new SettingsManager();
|
||||||
window.toggleApiKeyVisibility = toggleApiKeyVisibility;
|
window.toggleApiKeyVisibility = toggleApiKeyVisibility;
|
||||||
window.moveManager = moveManager;
|
window.moveManager = moveManager;
|
||||||
|
window.toggleShowcase = toggleShowcase;
|
||||||
|
|
||||||
// Initialize everything when DOM is ready
|
// Initialize everything when DOM is ready
|
||||||
document.addEventListener('DOMContentLoaded', () => {
|
document.addEventListener('DOMContentLoaded', () => {
|
||||||
@@ -58,6 +59,7 @@ document.addEventListener('DOMContentLoaded', () => {
|
|||||||
initTheme();
|
initTheme();
|
||||||
initFolderTagsVisibility();
|
initFolderTagsVisibility();
|
||||||
initBackToTop();
|
initBackToTop();
|
||||||
|
initLazyLoading();
|
||||||
window.searchManager = new SearchManager();
|
window.searchManager = new SearchManager();
|
||||||
new LoraContextMenu();
|
new LoraContextMenu();
|
||||||
});
|
});
|
||||||
|
|||||||
Reference in New Issue
Block a user