mirror of
https://github.com/willmiao/ComfyUI-Lora-Manager.git
synced 2026-03-21 21:22:11 -03:00
Add help modal functionality and update related UI components
This commit is contained in:
@@ -136,6 +136,30 @@
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
/* Badge styling */
|
||||
.update-badge {
|
||||
position: absolute;
|
||||
top: -3px;
|
||||
right: -3px;
|
||||
width: 8px;
|
||||
height: 8px;
|
||||
background-color: var(--lora-error);
|
||||
border-radius: 50%;
|
||||
border: 2px solid var(--card-bg);
|
||||
transition: all 0.2s ease;
|
||||
pointer-events: none;
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
.update-badge.visible {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.update-badge.hidden,
|
||||
.update-badge:not(.visible) {
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
/* Mobile adjustments */
|
||||
@media (max-width: 768px) {
|
||||
.app-title {
|
||||
|
||||
@@ -544,7 +544,7 @@ input:checked + .toggle-slider:before {
|
||||
gap: 8px;
|
||||
padding: 8px 16px;
|
||||
background-color: var(--card-bg);
|
||||
color: var(--text-color);
|
||||
color: var (--text-color);
|
||||
border: 1px solid var(--border-color);
|
||||
border-radius: var(--border-radius-sm);
|
||||
cursor: pointer;
|
||||
@@ -718,4 +718,204 @@ input:checked + .toggle-slider:before {
|
||||
|
||||
.density-description li {
|
||||
margin-bottom: 4px;
|
||||
}
|
||||
|
||||
/* Help Modal styles */
|
||||
.help-modal {
|
||||
max-width: 850px;
|
||||
}
|
||||
|
||||
.help-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin-bottom: var(--space-2);
|
||||
}
|
||||
|
||||
.modal-help-icon {
|
||||
font-size: 24px;
|
||||
color: var(--lora-accent);
|
||||
margin-right: var(--space-2);
|
||||
vertical-align: text-bottom;
|
||||
}
|
||||
|
||||
/* Tab navigation styles */
|
||||
.help-tabs {
|
||||
display: flex;
|
||||
border-bottom: 1px solid var(--lora-border);
|
||||
margin-bottom: var(--space-2);
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.tab-btn {
|
||||
padding: 8px 16px;
|
||||
background: transparent;
|
||||
border: none;
|
||||
border-bottom: 2px solid transparent;
|
||||
color: var(--text-color);
|
||||
cursor: pointer;
|
||||
font-weight: 500;
|
||||
transition: all 0.2s;
|
||||
opacity: 0.7;
|
||||
}
|
||||
|
||||
.tab-btn:hover {
|
||||
background-color: rgba(0, 0, 0, 0.05);
|
||||
opacity: 0.9;
|
||||
}
|
||||
|
||||
.tab-btn.active {
|
||||
color: var(--lora-accent);
|
||||
border-bottom: 2px solid var(--lora-accent);
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
/* Tab content styles */
|
||||
.help-content {
|
||||
padding: var(--space-1) 0;
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
.tab-pane {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.tab-pane.active {
|
||||
display: block;
|
||||
}
|
||||
|
||||
/* Video embed styles */
|
||||
.video-embed {
|
||||
position: relative;
|
||||
padding-bottom: 56.25%; /* 16:9 aspect ratio */
|
||||
height: 0;
|
||||
overflow: hidden;
|
||||
max-width: 100%;
|
||||
margin-bottom: var(--space-2);
|
||||
border-radius: var(--border-radius-sm);
|
||||
}
|
||||
|
||||
.video-embed iframe {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.video-embed.small {
|
||||
max-width: 100%;
|
||||
margin-bottom: var(--space-1);
|
||||
}
|
||||
|
||||
.help-text {
|
||||
margin: var(--space-2) 0;
|
||||
}
|
||||
|
||||
.help-text ul {
|
||||
padding-left: 20px;
|
||||
margin-top: 8px;
|
||||
}
|
||||
|
||||
.help-text li {
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
/* Documentation link styles */
|
||||
.docs-section {
|
||||
margin-bottom: var(--space-3);
|
||||
}
|
||||
|
||||
.docs-section h4 {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
margin-bottom: var(--space-1);
|
||||
}
|
||||
|
||||
.docs-links {
|
||||
list-style-type: none;
|
||||
padding-left: var(--space-3);
|
||||
}
|
||||
|
||||
.docs-links li {
|
||||
margin-bottom: var(--space-1);
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.docs-links li:before {
|
||||
content: "•";
|
||||
position: absolute;
|
||||
left: -15px;
|
||||
color: var(--lora-accent);
|
||||
}
|
||||
|
||||
.docs-links a {
|
||||
color: var(--lora-accent);
|
||||
text-decoration: none;
|
||||
transition: color 0.2s;
|
||||
}
|
||||
|
||||
.docs-links a:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
/* Update video list styles */
|
||||
.video-list {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: var(--space-3);
|
||||
}
|
||||
|
||||
.video-item {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.video-info {
|
||||
padding: var(--space-1);
|
||||
}
|
||||
|
||||
.video-info h4 {
|
||||
margin-bottom: var(--space-1);
|
||||
}
|
||||
|
||||
.video-info p {
|
||||
font-size: 0.9em;
|
||||
opacity: 0.8;
|
||||
}
|
||||
|
||||
/* Dark theme adjustments */
|
||||
[data-theme="dark"] .tab-btn:hover {
|
||||
background-color: rgba(255, 255, 255, 0.05);
|
||||
}
|
||||
|
||||
/* Update date badge styles */
|
||||
.update-date-badge {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
font-size: 0.75em;
|
||||
font-weight: 500;
|
||||
background-color: var(--lora-accent);
|
||||
color: var(--lora-text);
|
||||
padding: 4px 8px;
|
||||
border-radius: 12px;
|
||||
margin-left: 10px;
|
||||
vertical-align: middle;
|
||||
animation: fadeIn 0.5s ease-in-out;
|
||||
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
.update-date-badge i {
|
||||
margin-right: 5px;
|
||||
font-size: 0.9em;
|
||||
}
|
||||
|
||||
@keyframes fadeIn {
|
||||
from { opacity: 0; transform: translateY(-5px); }
|
||||
to { opacity: 1; transform: translateY(0); }
|
||||
}
|
||||
|
||||
/* Dark theme adjustments */
|
||||
[data-theme="dark"] .update-date-badge {
|
||||
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.3);
|
||||
}
|
||||
@@ -75,7 +75,9 @@ export class HeaderManager {
|
||||
const supportToggle = document.getElementById('supportToggleBtn');
|
||||
if (supportToggle) {
|
||||
supportToggle.addEventListener('click', () => {
|
||||
// Handle support panel logic
|
||||
if (window.modalManager) {
|
||||
window.modalManager.toggleModal('supportModal');
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@@ -106,5 +108,15 @@ export class HeaderManager {
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Handle help toggle
|
||||
// const helpToggle = document.querySelector('.help-toggle');
|
||||
// if (helpToggle) {
|
||||
// helpToggle.addEventListener('click', () => {
|
||||
// if (window.modalManager) {
|
||||
// window.modalManager.toggleModal('helpModal');
|
||||
// }
|
||||
// });
|
||||
// }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@ import { updateService } from './managers/UpdateService.js';
|
||||
import { HeaderManager } from './components/Header.js';
|
||||
import { settingsManager } from './managers/SettingsManager.js';
|
||||
import { exampleImagesManager } from './managers/ExampleImagesManager.js';
|
||||
import { helpManager } from './managers/HelpManager.js';
|
||||
import { showToast, initTheme, initBackToTop } from './utils/uiHelpers.js';
|
||||
import { initializeInfiniteScroll } from './utils/infiniteScroll.js';
|
||||
import { migrateStorageItems } from './utils/storageHelpers.js';
|
||||
@@ -30,6 +31,7 @@ export class AppCore {
|
||||
window.modalManager = modalManager;
|
||||
window.settingsManager = settingsManager;
|
||||
window.exampleImagesManager = exampleImagesManager;
|
||||
window.helpManager = helpManager;
|
||||
|
||||
// Initialize UI components
|
||||
window.headerManager = new HeaderManager();
|
||||
@@ -38,6 +40,8 @@ export class AppCore {
|
||||
|
||||
// Initialize the example images manager
|
||||
exampleImagesManager.initialize();
|
||||
// Initialize the help manager
|
||||
helpManager.initialize();
|
||||
|
||||
// Mark as initialized
|
||||
this.initialized = true;
|
||||
|
||||
155
static/js/managers/HelpManager.js
Normal file
155
static/js/managers/HelpManager.js
Normal file
@@ -0,0 +1,155 @@
|
||||
import { getStorageItem, setStorageItem } from '../utils/storageHelpers.js';
|
||||
|
||||
/**
|
||||
* Manages help modal functionality and tutorial update notifications
|
||||
*/
|
||||
export class HelpManager {
|
||||
constructor() {
|
||||
this.lastViewedTimestamp = getStorageItem('help_last_viewed', 0);
|
||||
this.latestContentTimestamp = 0; // Will be updated from server or config
|
||||
this.isInitialized = false;
|
||||
|
||||
// Default latest content data - could be fetched from server
|
||||
this.latestVideoData = {
|
||||
timestamp: new Date('2024-06-09').getTime(), // Default timestamp
|
||||
walkthrough: {
|
||||
id: 'hvKw31YpE-U',
|
||||
title: 'Getting Started with LoRA Manager'
|
||||
},
|
||||
playlistUpdated: true
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize the help manager
|
||||
*/
|
||||
initialize() {
|
||||
if (this.isInitialized) return;
|
||||
|
||||
console.log('HelpManager: Initializing...');
|
||||
|
||||
// Set up event handlers
|
||||
this.setupEventListeners();
|
||||
|
||||
// Check if we need to show the badge
|
||||
this.updateHelpBadge();
|
||||
|
||||
// Fetch latest video data (could be implemented to fetch from remote source)
|
||||
this.fetchLatestVideoData();
|
||||
|
||||
this.isInitialized = true;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set up event listeners for help modal
|
||||
*/
|
||||
setupEventListeners() {
|
||||
// Help toggle button
|
||||
const helpToggleBtn = document.getElementById('helpToggleBtn');
|
||||
if (helpToggleBtn) {
|
||||
helpToggleBtn.addEventListener('click', () => this.openHelpModal());
|
||||
}
|
||||
|
||||
// Help modal tab functionality
|
||||
const tabButtons = document.querySelectorAll('.help-tabs .tab-btn');
|
||||
tabButtons.forEach(button => {
|
||||
button.addEventListener('click', (event) => {
|
||||
// Remove active class from all buttons and panes
|
||||
document.querySelectorAll('.help-tabs .tab-btn').forEach(btn => {
|
||||
btn.classList.remove('active');
|
||||
});
|
||||
document.querySelectorAll('.help-content .tab-pane').forEach(pane => {
|
||||
pane.classList.remove('active');
|
||||
});
|
||||
|
||||
// Add active class to clicked button
|
||||
event.currentTarget.classList.add('active');
|
||||
|
||||
// Show corresponding tab content
|
||||
const tabId = event.currentTarget.getAttribute('data-tab');
|
||||
document.getElementById(tabId).classList.add('active');
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Open the help modal
|
||||
*/
|
||||
openHelpModal() {
|
||||
// Use modalManager to open the help modal
|
||||
if (window.modalManager) {
|
||||
window.modalManager.toggleModal('helpModal');
|
||||
|
||||
// Update the last viewed timestamp
|
||||
this.markContentAsViewed();
|
||||
|
||||
// Hide the badge
|
||||
this.hideHelpBadge();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Mark content as viewed by saving current timestamp
|
||||
*/
|
||||
markContentAsViewed() {
|
||||
this.lastViewedTimestamp = Date.now();
|
||||
setStorageItem('help_last_viewed', this.lastViewedTimestamp);
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetch latest video data (could be implemented to actually fetch from a remote source)
|
||||
*/
|
||||
fetchLatestVideoData() {
|
||||
// In a real implementation, you'd fetch this from your server
|
||||
// For now, we'll just use the hardcoded data from constructor
|
||||
|
||||
// Update the timestamp with the latest data
|
||||
this.latestContentTimestamp = this.latestVideoData.timestamp;
|
||||
|
||||
// Check again if we need to show the badge with this new data
|
||||
this.updateHelpBadge();
|
||||
}
|
||||
|
||||
/**
|
||||
* Update help badge visibility based on timestamps
|
||||
*/
|
||||
updateHelpBadge() {
|
||||
if (this.hasNewContent()) {
|
||||
this.showHelpBadge();
|
||||
} else {
|
||||
this.hideHelpBadge();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if there's new content the user hasn't seen
|
||||
*/
|
||||
hasNewContent() {
|
||||
// If user has never viewed the help, or the content is newer than last viewed
|
||||
return this.lastViewedTimestamp === 0 || this.latestContentTimestamp > this.lastViewedTimestamp;
|
||||
}
|
||||
|
||||
/**
|
||||
* Show the help badge
|
||||
*/
|
||||
showHelpBadge() {
|
||||
const helpBadge = document.querySelector('#helpToggleBtn .update-badge');
|
||||
if (helpBadge) {
|
||||
helpBadge.classList.add('visible');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Hide the help badge
|
||||
*/
|
||||
hideHelpBadge() {
|
||||
const helpBadge = document.querySelector('#helpToggleBtn .update-badge');
|
||||
if (helpBadge) {
|
||||
helpBadge.classList.remove('visible');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Create singleton instance
|
||||
export const helpManager = new HelpManager();
|
||||
@@ -207,11 +207,18 @@ export class ModalManager {
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Set up event listeners for modal toggles
|
||||
const supportToggle = document.getElementById('supportToggleBtn');
|
||||
if (supportToggle) {
|
||||
supportToggle.addEventListener('click', () => this.toggleModal('supportModal'));
|
||||
|
||||
// Add helpModal registration
|
||||
const helpModal = document.getElementById('helpModal');
|
||||
if (helpModal) {
|
||||
this.registerModal('helpModal', {
|
||||
element: helpModal,
|
||||
onClose: () => {
|
||||
this.getModal('helpModal').element.style.display = 'none';
|
||||
document.body.classList.remove('modal-open');
|
||||
},
|
||||
closeOnOutsideClick: true
|
||||
});
|
||||
}
|
||||
|
||||
document.addEventListener('keydown', this.boundHandleEscape);
|
||||
|
||||
@@ -119,7 +119,6 @@ export class UpdateService {
|
||||
updateBadgeVisibility() {
|
||||
const updateToggle = document.querySelector('.update-toggle');
|
||||
const updateBadge = document.querySelector('.update-toggle .update-badge');
|
||||
const cornerBadge = document.querySelector('.corner-badge');
|
||||
|
||||
if (updateToggle) {
|
||||
updateToggle.title = this.updateNotificationsEnabled && this.updateAvailable
|
||||
@@ -134,11 +133,6 @@ export class UpdateService {
|
||||
updateBadge.classList.toggle('hidden', !shouldShow);
|
||||
console.log("Update badge visibility:", !shouldShow ? "hidden" : "visible");
|
||||
}
|
||||
|
||||
if (cornerBadge) {
|
||||
cornerBadge.classList.toggle('hidden', !shouldShow);
|
||||
console.log("Corner badge visibility:", !shouldShow ? "hidden" : "visible");
|
||||
}
|
||||
}
|
||||
|
||||
updateModalContent() {
|
||||
|
||||
@@ -43,6 +43,10 @@
|
||||
<div class="settings-toggle" title="Settings">
|
||||
<i class="fas fa-cog"></i>
|
||||
</div>
|
||||
<div class="help-toggle" id="helpToggleBtn" title="Help & Tutorials">
|
||||
<i class="fas fa-question-circle"></i>
|
||||
<span class="update-badge"></span>
|
||||
</div>
|
||||
<div class="update-toggle" id="updateToggleBtn" title="Check Updates">
|
||||
<i class="fas fa-bell"></i>
|
||||
<span class="update-badge hidden"></span>
|
||||
|
||||
@@ -462,4 +462,113 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Help Modal -->
|
||||
<div id="helpModal" class="modal">
|
||||
<div class="modal-content help-modal">
|
||||
<button class="close" onclick="modalManager.closeModal('helpModal')">×</button>
|
||||
<div class="help-header">
|
||||
<h2>Help & Tutorials</h2>
|
||||
</div>
|
||||
|
||||
<div class="help-tabs">
|
||||
<button class="tab-btn active" data-tab="getting-started">Getting Started</button>
|
||||
<button class="tab-btn" data-tab="update-vlogs">Update Vlogs</button>
|
||||
<button class="tab-btn" data-tab="documentation">Documentation</button>
|
||||
</div>
|
||||
|
||||
<div class="help-content">
|
||||
<!-- Getting Started Tab -->
|
||||
<div class="tab-pane active" id="getting-started">
|
||||
<h3>Getting Started with LoRA Manager</h3>
|
||||
<div class="video-embed">
|
||||
<iframe src="https://www.youtube.com/embed/hvKw31YpE-U"
|
||||
title="Getting Started with LoRA Manager"
|
||||
frameborder="0"
|
||||
allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture"
|
||||
allowfullscreen></iframe>
|
||||
</div>
|
||||
<div class="help-text">
|
||||
<h4>Key Features:</h4>
|
||||
<ul>
|
||||
<li><strong>Seamless Integration:</strong> One-click workflow integration with ComfyUI</li>
|
||||
<li><strong>Visual Management:</strong> Organize and manage all your LoRA models with an intuitive interface</li>
|
||||
<li><strong>Automatic Metadata:</strong> Fetch previews, trigger words, and details automatically</li>
|
||||
<li><strong>Civitai Integration:</strong> Direct downloads with full API support</li>
|
||||
<li><strong>Offline Preview Storage:</strong> Store and manage model examples locally</li>
|
||||
<li><strong>Advanced Controls:</strong> Trigger word toggles and customizable loader node</li>
|
||||
<li><strong>Recipe System:</strong> Create, save and share your perfect combinations</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Update Vlogs Tab -->
|
||||
<div class="tab-pane" id="update-vlogs">
|
||||
<h3>
|
||||
Latest Updates
|
||||
<span class="update-date-badge">
|
||||
<i class="fas fa-calendar-alt"></i>
|
||||
Apr 28, 2025
|
||||
</span>
|
||||
</h3>
|
||||
<div class="video-list">
|
||||
<div class="video-item">
|
||||
<div class="video-embed small">
|
||||
<iframe src="https://www.youtube.com/embed/videoseries?list=PLU2fMdHNl8ohz1u7Ke3ooOuMbU5Y4sgoj"
|
||||
title="LoRA Manager Updates"
|
||||
frameborder="0"
|
||||
allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture"
|
||||
allowfullscreen></iframe>
|
||||
</div>
|
||||
<div class="video-info">
|
||||
<h4>LoRA Manager Updates Playlist</h4>
|
||||
<p>Watch all update videos showcasing the latest features and improvements.</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Documentation Tab -->
|
||||
<div class="tab-pane" id="documentation">
|
||||
<h3>Documentation</h3>
|
||||
|
||||
<div class="docs-section">
|
||||
<h4><i class="fas fa-book"></i> General</h4>
|
||||
<ul class="docs-links">
|
||||
<li><a href="https://github.com/willmiao/ComfyUI-Lora-Manager/wiki" target="_blank">Wiki Home</a></li>
|
||||
<li><a href="https://github.com/willmiao/ComfyUI-Lora-Manager/blob/main/README.md" target="_blank">README</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div class="docs-section">
|
||||
<h4><i class="fas fa-tools"></i> Troubleshooting</h4>
|
||||
<ul class="docs-links">
|
||||
<li><a href="https://github.com/willmiao/ComfyUI-Lora-Manager/wiki/FAQ-(Frequently-Asked-Questions)" target="_blank">FAQ (Frequently Asked Questions)</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div class="docs-section">
|
||||
<h4><i class="fas fa-layer-group"></i> Model Management</h4>
|
||||
<ul class="docs-links">
|
||||
<li><a href="https://github.com/willmiao/ComfyUI-Lora-Manager/wiki/Example-Images" target="_blank">Example Images (WIP)</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div class="docs-section">
|
||||
<h4><i class="fas fa-book-open"></i> Recipes</h4>
|
||||
<ul class="docs-links">
|
||||
<li><a href="https://github.com/willmiao/ComfyUI-Lora-Manager/wiki/%F0%9F%93%96-Recipes-Feature-Tutorial-%E2%80%93-ComfyUI-LoRA-Manager" target="_blank">Recipes Tutorial</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div class="docs-section">
|
||||
<h4><i class="fas fa-cog"></i> Settings & Configuration</h4>
|
||||
<ul class="docs-links">
|
||||
<li><a href="https://github.com/willmiao/ComfyUI-Lora-Manager/wiki/Configuration" target="_blank">Configuration Options (WIP)</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
Reference in New Issue
Block a user