mirror of
https://github.com/willmiao/ComfyUI-Lora-Manager.git
synced 2026-03-21 21:22:11 -03:00
feat: add checkpoint hash filtering and navigation
- Add checkpoint hash parameter parsing to backend routes - Implement checkpoint hash filtering in frontend API client - Add click navigation from recipe modal to checkpoints page - Update checkpoint items to use pointer cursor for better UX Checkpoint items in recipe modal are now clickable and will navigate to the checkpoints page with appropriate hash filtering applied. This improves user workflow when wanting to view checkpoint details from recipes.
This commit is contained in:
@@ -877,6 +877,21 @@ export class BaseModelApiClient {
|
||||
console.error('Error parsing lora hashes from session storage:', error);
|
||||
}
|
||||
}
|
||||
} else if (this.modelType === 'checkpoints') {
|
||||
const filterCheckpointHash = getSessionItem('recipe_to_checkpoint_filterHash');
|
||||
const filterCheckpointHashes = getSessionItem('recipe_to_checkpoint_filterHashes');
|
||||
|
||||
if (filterCheckpointHash) {
|
||||
params.append('checkpoint_hash', filterCheckpointHash);
|
||||
} else if (filterCheckpointHashes) {
|
||||
try {
|
||||
if (Array.isArray(filterCheckpointHashes) && filterCheckpointHashes.length > 0) {
|
||||
params.append('checkpoint_hashes', filterCheckpointHashes.join(','));
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Error parsing checkpoint hashes from session storage:', error);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -351,6 +351,7 @@ class RecipeModal {
|
||||
if (recipe.checkpoint && typeof recipe.checkpoint === 'object') {
|
||||
checkpointContainer.innerHTML = this.renderCheckpoint(recipe.checkpoint);
|
||||
this.setupCheckpointActions(checkpointContainer, recipe.checkpoint);
|
||||
this.setupCheckpointNavigation(checkpointContainer, recipe.checkpoint);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1150,6 +1151,15 @@ class RecipeModal {
|
||||
}
|
||||
}
|
||||
|
||||
setupCheckpointNavigation(container, checkpoint) {
|
||||
const checkpointItem = container.querySelector('.checkpoint-item');
|
||||
if (!checkpointItem) return;
|
||||
|
||||
checkpointItem.addEventListener('click', () => {
|
||||
this.navigateToCheckpointPage(checkpoint);
|
||||
});
|
||||
}
|
||||
|
||||
canDownloadCheckpoint(checkpoint) {
|
||||
if (!checkpoint) return false;
|
||||
const modelId = checkpoint.modelId || checkpoint.modelID || checkpoint.model_id;
|
||||
@@ -1238,8 +1248,42 @@ class RecipeModal {
|
||||
}
|
||||
}
|
||||
|
||||
navigateToCheckpointPage(checkpoint) {
|
||||
const checkpointHash = this._getCheckpointHash(checkpoint);
|
||||
|
||||
if (!checkpointHash) {
|
||||
showToast('toast.recipes.missingCheckpointInfo', {}, 'error');
|
||||
return;
|
||||
}
|
||||
|
||||
modalManager.closeModal('recipeModal');
|
||||
|
||||
removeSessionItem('recipe_to_checkpoint_filterHash');
|
||||
removeSessionItem('recipe_to_checkpoint_filterHashes');
|
||||
removeSessionItem('filterCheckpointRecipeName');
|
||||
|
||||
setSessionItem('recipe_to_checkpoint_filterHash', checkpointHash.toLowerCase());
|
||||
if (this.currentRecipe?.title) {
|
||||
setSessionItem('filterCheckpointRecipeName', this.currentRecipe.title);
|
||||
}
|
||||
|
||||
window.location.href = '/checkpoints';
|
||||
}
|
||||
|
||||
_getCheckpointHash(checkpoint) {
|
||||
if (!checkpoint) return '';
|
||||
const hash =
|
||||
checkpoint.hash ||
|
||||
checkpoint.sha256 ||
|
||||
checkpoint.sha256_hash ||
|
||||
checkpoint.sha256Hash ||
|
||||
checkpoint.SHA256;
|
||||
return hash ? hash.toString() : '';
|
||||
}
|
||||
|
||||
// New method to navigate to the LoRAs page
|
||||
navigateToLorasPage(specificLoraIndex = null) {
|
||||
debugger;
|
||||
// Close the current modal
|
||||
modalManager.closeModal('recipeModal');
|
||||
|
||||
@@ -1278,7 +1322,7 @@ class RecipeModal {
|
||||
|
||||
// New method to make LoRA items clickable
|
||||
setupLoraItemsClickable() {
|
||||
const loraItems = document.querySelectorAll('.recipe-lora-item');
|
||||
const loraItems = document.querySelectorAll('.recipe-lora-item:not(.checkpoint-item)');
|
||||
loraItems.forEach(item => {
|
||||
// Get the lora index from the data attribute
|
||||
const loraIndex = parseInt(item.dataset.loraIndex);
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
// CheckpointsControls.js - Specific implementation for the Checkpoints page
|
||||
import { PageControls } from './PageControls.js';
|
||||
import { getModelApiClient, resetAndReload } from '../../api/modelApiFactory.js';
|
||||
import { showToast } from '../../utils/uiHelpers.js';
|
||||
import { getSessionItem, removeSessionItem } from '../../utils/storageHelpers.js';
|
||||
import { downloadManager } from '../../managers/DownloadManager.js';
|
||||
|
||||
/**
|
||||
@@ -14,6 +14,9 @@ export class CheckpointsControls extends PageControls {
|
||||
|
||||
// Register API methods specific to the Checkpoints page
|
||||
this.registerCheckpointsAPI();
|
||||
|
||||
// Check for custom filters (e.g., from recipe navigation)
|
||||
this.checkCustomFilters();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -52,14 +55,65 @@ export class CheckpointsControls extends PageControls {
|
||||
}
|
||||
},
|
||||
|
||||
// No clearCustomFilter implementation is needed for checkpoints
|
||||
// as custom filters are currently only used for LoRAs
|
||||
clearCustomFilter: async () => {
|
||||
showToast('toast.filters.noCustomFilterToClear', {}, 'info');
|
||||
await this.clearCustomFilter();
|
||||
}
|
||||
};
|
||||
|
||||
// Register the API
|
||||
this.registerAPI(checkpointsAPI);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check for custom filters sent from other pages (e.g., recipe modal)
|
||||
*/
|
||||
checkCustomFilters() {
|
||||
const filterCheckpointHash = getSessionItem('recipe_to_checkpoint_filterHash');
|
||||
const filterRecipeName = getSessionItem('filterCheckpointRecipeName');
|
||||
|
||||
if (filterCheckpointHash && filterRecipeName) {
|
||||
const indicator = document.getElementById('customFilterIndicator');
|
||||
const filterText = indicator?.querySelector('.customFilterText');
|
||||
|
||||
if (indicator && filterText) {
|
||||
indicator.classList.remove('hidden');
|
||||
|
||||
const displayText = `Viewing checkpoint from: ${filterRecipeName}`;
|
||||
filterText.textContent = this._truncateText(displayText, 30);
|
||||
filterText.setAttribute('title', displayText);
|
||||
|
||||
const filterElement = indicator.querySelector('.filter-active');
|
||||
if (filterElement) {
|
||||
filterElement.classList.add('animate');
|
||||
setTimeout(() => filterElement.classList.remove('animate'), 600);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Clear checkpoint custom filter and reload
|
||||
*/
|
||||
async clearCustomFilter() {
|
||||
removeSessionItem('recipe_to_checkpoint_filterHash');
|
||||
removeSessionItem('recipe_to_checkpoint_filterHashes');
|
||||
removeSessionItem('filterCheckpointRecipeName');
|
||||
|
||||
const indicator = document.getElementById('customFilterIndicator');
|
||||
if (indicator) {
|
||||
indicator.classList.add('hidden');
|
||||
}
|
||||
|
||||
await resetAndReload();
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper to truncate text with ellipsis
|
||||
* @param {string} text
|
||||
* @param {number} maxLength
|
||||
* @returns {string}
|
||||
*/
|
||||
_truncateText(text, maxLength) {
|
||||
return text.length > maxLength ? `${text.substring(0, maxLength - 3)}...` : text;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user