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