mirror of
https://github.com/willmiao/ComfyUI-Lora-Manager.git
synced 2026-03-22 13:42:12 -03:00
- Updated all relevant routes in `stats_routes.py` and `update_routes.py` to include the new '/api/lm/' prefix for consistency. - Modified API endpoint configurations in `apiConfig.js` to reflect the new structure, ensuring all CRUD and bulk operations are correctly routed. - Adjusted fetch calls in various components and managers to utilize the updated API paths, including recipe, model, and example image operations. - Ensured all instances of the old API paths were replaced with the new '/api/lm/' prefix across the codebase for uniformity and to prevent broken links.
319 lines
12 KiB
JavaScript
319 lines
12 KiB
JavaScript
// AlphabetBar.js - Component for alphabet filtering
|
|
import { getCurrentPageState } from '../../state/index.js';
|
|
import { getStorageItem, setStorageItem } from '../../utils/storageHelpers.js';
|
|
import { resetAndReload } from '../../api/modelApiFactory.js';
|
|
|
|
/**
|
|
* AlphabetBar class - Handles the alphabet filtering UI and interactions
|
|
*/
|
|
export class AlphabetBar {
|
|
constructor(pageType = 'loras') {
|
|
// Store the page type
|
|
this.pageType = pageType;
|
|
|
|
// Get the current page state
|
|
this.pageState = getCurrentPageState();
|
|
|
|
// Initialize letter counts
|
|
this.letterCounts = {};
|
|
|
|
// Initialize the component
|
|
this.initializeComponent();
|
|
}
|
|
|
|
/**
|
|
* Initialize the alphabet bar component
|
|
*/
|
|
async initializeComponent() {
|
|
// Get letter counts from API
|
|
await this.fetchLetterCounts();
|
|
|
|
// Initialize event listeners
|
|
this.initEventListeners();
|
|
|
|
// Restore the active letter filter from storage if available
|
|
this.restoreActiveLetterFilter();
|
|
|
|
// Restore collapse state from storage
|
|
this.restoreCollapseState();
|
|
|
|
// Update the toggle button indicator if there's an active letter filter
|
|
this.updateToggleIndicator();
|
|
}
|
|
|
|
/**
|
|
* Fetch letter counts from the API
|
|
*/
|
|
async fetchLetterCounts() {
|
|
try {
|
|
const response = await fetch('/api/lm/loras/letter-counts');
|
|
|
|
if (!response.ok) {
|
|
throw new Error(`Failed to fetch letter counts: ${response.statusText}`);
|
|
}
|
|
|
|
const data = await response.json();
|
|
|
|
if (data.success && data.letter_counts) {
|
|
this.letterCounts = data.letter_counts;
|
|
|
|
// Update the count display in the UI
|
|
this.updateLetterCountsDisplay();
|
|
}
|
|
} catch (error) {
|
|
console.error('Error fetching letter counts:', error);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Update the letter counts display in the UI
|
|
*/
|
|
updateLetterCountsDisplay() {
|
|
const letterChips = document.querySelectorAll('.letter-chip');
|
|
|
|
letterChips.forEach(chip => {
|
|
const letter = chip.dataset.letter;
|
|
const count = this.letterCounts[letter] || 0;
|
|
|
|
// Update the title attribute for tooltip display
|
|
if (count > 0) {
|
|
chip.title = `${letter}: ${count} LoRAs`;
|
|
chip.classList.remove('disabled');
|
|
} else {
|
|
chip.title = `${letter}: No LoRAs`;
|
|
chip.classList.add('disabled');
|
|
}
|
|
|
|
// Keep the count span for backward compatibility
|
|
const countSpan = chip.querySelector('.count');
|
|
if (countSpan) {
|
|
countSpan.textContent = ` (${count})`;
|
|
}
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Initialize event listeners for the alphabet bar
|
|
*/
|
|
initEventListeners() {
|
|
const alphabetBar = document.querySelector('.alphabet-bar');
|
|
const toggleButton = document.querySelector('.toggle-alphabet-bar');
|
|
const alphabetBarContainer = document.querySelector('.alphabet-bar-container');
|
|
|
|
if (alphabetBar) {
|
|
// Use event delegation for letter chips
|
|
alphabetBar.addEventListener('click', (e) => {
|
|
const letterChip = e.target.closest('.letter-chip');
|
|
|
|
if (letterChip && !letterChip.classList.contains('disabled')) {
|
|
this.handleLetterClick(letterChip);
|
|
}
|
|
});
|
|
|
|
// Add toggle button listener
|
|
if (toggleButton && alphabetBarContainer) {
|
|
toggleButton.addEventListener('click', () => {
|
|
alphabetBarContainer.classList.toggle('collapsed');
|
|
|
|
// If expanding and there's an active letter, scroll it into view
|
|
if (!alphabetBarContainer.classList.contains('collapsed')) {
|
|
this.scrollActiveLetterIntoView();
|
|
}
|
|
|
|
// Save collapse state to storage
|
|
setStorageItem(`${this.pageType}_alphabetBarCollapsed`,
|
|
alphabetBarContainer.classList.contains('collapsed'));
|
|
|
|
// Update toggle indicator
|
|
this.updateToggleIndicator();
|
|
});
|
|
}
|
|
|
|
// Add keyboard shortcut listeners
|
|
document.addEventListener('keydown', (e) => {
|
|
// Alt + letter shortcuts
|
|
if (e.altKey && !e.ctrlKey && !e.metaKey) {
|
|
const key = e.key.toUpperCase();
|
|
|
|
// Check if it's a letter A-Z
|
|
if (/^[A-Z]$/.test(key)) {
|
|
const letterChip = document.querySelector(`.letter-chip[data-letter="${key}"]`);
|
|
|
|
if (letterChip && !letterChip.classList.contains('disabled')) {
|
|
this.handleLetterClick(letterChip);
|
|
e.preventDefault();
|
|
}
|
|
}
|
|
// Special cases for non-letter filters
|
|
else if (e.key === '0' || e.key === ')') {
|
|
// Alt+0 for numbers (#)
|
|
const letterChip = document.querySelector('.letter-chip[data-letter="#"]');
|
|
|
|
if (letterChip && !letterChip.classList.contains('disabled')) {
|
|
this.handleLetterClick(letterChip);
|
|
e.preventDefault();
|
|
}
|
|
} else if (e.key === '2' || e.key === '@') {
|
|
// Alt+@ for special characters
|
|
const letterChip = document.querySelector('.letter-chip[data-letter="@"]');
|
|
|
|
if (letterChip && !letterChip.classList.contains('disabled')) {
|
|
this.handleLetterClick(letterChip);
|
|
e.preventDefault();
|
|
}
|
|
} else if (e.key === 'c' || e.key === 'C') {
|
|
// Alt+C for CJK characters
|
|
const letterChip = document.querySelector('.letter-chip[data-letter="漢"]');
|
|
|
|
if (letterChip && !letterChip.classList.contains('disabled')) {
|
|
this.handleLetterClick(letterChip);
|
|
e.preventDefault();
|
|
}
|
|
}
|
|
}
|
|
});
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Restore the collapse state from storage
|
|
*/
|
|
restoreCollapseState() {
|
|
const alphabetBarContainer = document.querySelector('.alphabet-bar-container');
|
|
|
|
if (alphabetBarContainer) {
|
|
const isCollapsed = getStorageItem(`${this.pageType}_alphabetBarCollapsed`);
|
|
|
|
// If there's a stored preference, apply it
|
|
if (isCollapsed !== null) {
|
|
if (isCollapsed) {
|
|
alphabetBarContainer.classList.add('collapsed');
|
|
} else {
|
|
alphabetBarContainer.classList.remove('collapsed');
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Handle letter chip click
|
|
* @param {HTMLElement} letterChip - The letter chip that was clicked
|
|
*/
|
|
handleLetterClick(letterChip) {
|
|
const letter = letterChip.dataset.letter;
|
|
const wasActive = letterChip.classList.contains('active');
|
|
|
|
// Remove active class from all letter chips
|
|
document.querySelectorAll('.letter-chip').forEach(chip => {
|
|
chip.classList.remove('active');
|
|
});
|
|
|
|
if (!wasActive) {
|
|
// Set the new active letter
|
|
letterChip.classList.add('active');
|
|
this.pageState.activeLetterFilter = letter;
|
|
|
|
// Save to storage
|
|
setStorageItem(`${this.pageType}_activeLetterFilter`, letter);
|
|
} else {
|
|
// Clear the active letter filter
|
|
this.pageState.activeLetterFilter = null;
|
|
|
|
// Remove from storage
|
|
setStorageItem(`${this.pageType}_activeLetterFilter`, null);
|
|
}
|
|
|
|
// Update visual indicator on toggle button
|
|
this.updateToggleIndicator();
|
|
|
|
// Trigger a reload with the new filter
|
|
resetAndReload(false);
|
|
}
|
|
|
|
/**
|
|
* Restore the active letter filter from storage
|
|
*/
|
|
restoreActiveLetterFilter() {
|
|
const activeLetterFilter = getStorageItem(`${this.pageType}_activeLetterFilter`);
|
|
|
|
if (activeLetterFilter) {
|
|
const letterChip = document.querySelector(`.letter-chip[data-letter="${activeLetterFilter}"]`);
|
|
|
|
if (letterChip && !letterChip.classList.contains('disabled')) {
|
|
letterChip.classList.add('active');
|
|
this.pageState.activeLetterFilter = activeLetterFilter;
|
|
|
|
// Scroll the active letter into view if the alphabet bar is expanded
|
|
this.scrollActiveLetterIntoView();
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Clear the active letter filter
|
|
*/
|
|
clearActiveLetterFilter() {
|
|
// Remove active class from all letter chips
|
|
document.querySelectorAll('.letter-chip').forEach(chip => {
|
|
chip.classList.remove('active');
|
|
});
|
|
|
|
// Clear the active letter filter
|
|
this.pageState.activeLetterFilter = null;
|
|
|
|
// Remove from storage
|
|
setStorageItem(`${this.pageType}_activeLetterFilter`, null);
|
|
|
|
// Update the toggle button indicator
|
|
this.updateToggleIndicator();
|
|
}
|
|
|
|
/**
|
|
* Update letter counts with new data
|
|
* @param {Object} newCounts - New letter count data
|
|
*/
|
|
updateCounts(newCounts) {
|
|
this.letterCounts = { ...newCounts };
|
|
this.updateLetterCountsDisplay();
|
|
}
|
|
|
|
/**
|
|
* Update the toggle button visual indicator based on active filter
|
|
*/
|
|
updateToggleIndicator() {
|
|
const toggleButton = document.querySelector('.toggle-alphabet-bar');
|
|
const hasActiveFilter = this.pageState.activeLetterFilter !== null;
|
|
|
|
if (toggleButton) {
|
|
if (hasActiveFilter) {
|
|
toggleButton.classList.add('has-active-letter');
|
|
} else {
|
|
toggleButton.classList.remove('has-active-letter');
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Scroll the active letter into view if the alphabet bar is expanded
|
|
*/
|
|
scrollActiveLetterIntoView() {
|
|
if (!this.pageState.activeLetterFilter) return;
|
|
|
|
|
|
const alphabetBarContainer = document.querySelector('.alphabet-bar-container');
|
|
if (alphabetBarContainer) {
|
|
const activeLetterChip = document.querySelector(`.letter-chip.active`);
|
|
|
|
if (activeLetterChip) {
|
|
// Use a small timeout to ensure the alphabet bar is fully expanded
|
|
setTimeout(() => {
|
|
activeLetterChip.scrollIntoView({
|
|
behavior: 'smooth',
|
|
block: 'center',
|
|
inline: 'center'
|
|
});
|
|
}, 300);
|
|
}
|
|
}
|
|
}
|
|
} |