feat(versions): add View all local versions button to model versions tab

Clicking the button closes the modal, writes filter params to sessionStorage,
and reloads the page to show all local versions of the model as individual
cards (bypassing group-by-model dedup). The filter respects the update flag
strategy and the versions-filter-toggle state (same-base vs all versions).

Supporting changes:
- sessionStorage keys vlm_model_id / vlm_model_name / vlm_base_model
- BaseModelApiClient._addModelSpecificParams adds civitai_model_id param
- LoraApiClient calls super._addModelSpecificParams for VLM detection
- LorasControls / CheckpointsControls clearCustomFilter checks VLM first
- PageControls.checkVlmFilter shows customFilterIndicator with label
- Backend parses civitai_model_id, filters before group_by_model dedup
This commit is contained in:
Will Miao
2026-06-21 11:13:53 +08:00
parent 559ca946dc
commit fc29cde82a
10 changed files with 148 additions and 7 deletions

View File

@@ -95,6 +95,16 @@ export class CheckpointsControls extends PageControls {
* Clear checkpoint custom filter and reload
*/
async clearCustomFilter() {
// Check for View Local Versions filter first
const vlmModelId = getSessionItem('vlm_model_id');
if (vlmModelId) {
removeSessionItem('vlm_model_id');
removeSessionItem('vlm_model_name');
removeSessionItem('vlm_base_model');
window.location.reload();
return;
}
removeSessionItem('recipe_to_checkpoint_filterHash');
removeSessionItem('recipe_to_checkpoint_filterHashes');
removeSessionItem('filterCheckpointRecipeName');

View File

@@ -112,6 +112,16 @@ export class LorasControls extends PageControls {
* Clear the custom filter and reload the page
*/
async clearCustomFilter() {
// Check for View Local Versions filter first (handles VLM and reloads)
const vlmModelId = getSessionItem('vlm_model_id');
if (vlmModelId) {
removeSessionItem('vlm_model_id');
removeSessionItem('vlm_model_name');
removeSessionItem('vlm_base_model');
window.location.reload();
return;
}
console.log("Clearing custom filter...");
// Remove filter parameters from session storage
removeSessionItem('recipe_to_lora_filterLoraHash');

View File

@@ -1,6 +1,6 @@
// PageControls.js - Manages controls for both LoRAs and Checkpoints pages
import { state, getCurrentPageState, setCurrentPageType } from '../../state/index.js';
import { getStorageItem, setStorageItem, getSessionItem, setSessionItem } from '../../utils/storageHelpers.js';
import { getStorageItem, setStorageItem, getSessionItem, setSessionItem, removeSessionItem } from '../../utils/storageHelpers.js';
import { showToast, openCivitaiByMetadata } from '../../utils/uiHelpers.js';
import { performModelUpdateCheck } from '../../utils/updateCheckHelpers.js';
import { sidebarManager } from '../SidebarManager.js';
@@ -129,6 +129,9 @@ export class PageControls {
clearFilterBtn.addEventListener('click', () => this.clearCustomFilter());
}
// Check for View Local Versions filter
this.checkVlmFilter();
// Page-specific event listeners
this.initPageSpecificListeners();
}
@@ -459,15 +462,57 @@ export class PageControls {
this.api.toggleBulkMode();
}
/**
* Clear custom filter
*/
/**
* Check for View Local Versions filter in sessionStorage
*/
checkVlmFilter() {
const vlmModelId = getSessionItem('vlm_model_id');
const vlmModelName = getSessionItem('vlm_model_name');
const vlmBaseModel = getSessionItem('vlm_base_model');
if (vlmModelId && vlmModelName) {
const indicator = document.getElementById('customFilterIndicator');
const filterText = indicator?.querySelector('.customFilterText');
if (indicator && filterText) {
indicator.classList.remove('hidden');
const prefix = vlmBaseModel
? 'Showing same-base versions from'
: 'Showing all versions from';
const displayText = `${prefix}: ${vlmModelName}`;
filterText.textContent = this._truncateText(displayText, 40);
filterText.setAttribute('title', displayText);
}
}
}
/**
* Clear custom filter
*/
async clearCustomFilter() {
// Check for View Local Versions filter first
const vlmModelId = getSessionItem('vlm_model_id');
if (vlmModelId) {
removeSessionItem('vlm_model_id');
removeSessionItem('vlm_model_name');
removeSessionItem('vlm_base_model');
// Full page reload to restore initial state (mirrors the "set" action)
window.location.reload();
return;
}
// Otherwise delegate to subclass for recipe filters
if (!this.api) {
console.error('API methods not registered');
return;
}
try {
await this.api.clearCustomFilter();
} catch (error) {
@@ -475,6 +520,14 @@ export class PageControls {
showToast('toast.controls.clearFilterFailed', { message: error.message }, 'error');
}
}
/**
* Truncate text with ellipsis
*/
_truncateText(text, maxLength) {
if (!text) return '';
return text.length > maxLength ? `${text.substring(0, maxLength - 3)}...` : text;
}
/**
* Initialize the favorites filter button state

View File

@@ -752,6 +752,7 @@ export async function showModelModal(model, modelType) {
modelId: civitaiModelId,
currentVersionId: civitaiVersionId,
currentBaseModel: modelWithFullData.base_model,
modelName: model.model_name,
onUpdateStatusChange: handleUpdateStatusChange,
});
setupEditableFields(modelWithFullData.file_path, modelType);

View File

@@ -6,6 +6,7 @@ import { translate } from '../../utils/i18nHelpers.js';
import { state } from '../../state/index.js';
import { buildCivitaiModelUrl } from '../../utils/civitaiUtils.js';
import { formatFileSize } from './utils.js';
import { setSessionItem, removeSessionItem } from '../../utils/storageHelpers.js';
const VIDEO_EXTENSIONS = ['.mp4', '.webm', '.mov', '.mkv'];
const PREVIEW_PLACEHOLDER_URL = '/loras_static/images/no-preview.png';
@@ -744,7 +745,7 @@ function renderToolbar(record, toolbarState = {}) {
<button class="versions-toolbar-btn versions-toolbar-btn-primary" data-versions-action="toggle-model-ignore">
${escapeHtml(ignoreText)}
</button>
<button class="versions-toolbar-btn versions-toolbar-btn-secondary" data-versions-action="view-local" title="${escapeHtml(translate('modals.model.versions.actions.viewLocalTooltip', {}, 'Coming soon'))}" disabled>
<button class="versions-toolbar-btn versions-toolbar-btn-secondary" data-versions-action="view-local" title="${escapeHtml(translate('modals.model.versions.actions.viewLocalTooltip', {}, 'Show all local versions of this model on the main page'))}">
${escapeHtml(viewLocalText)}
</button>
</div>
@@ -792,6 +793,7 @@ export function initVersionsTab({
modelId,
currentVersionId,
currentBaseModel,
modelName,
onUpdateStatusChange,
}) {
const pane = document.querySelector(`#${modalId} #versions-tab`);
@@ -1019,6 +1021,31 @@ export function initVersionsTab({
render(controller.record);
}
function handleViewLocalVersions() {
if (!controller.record || !modelId) {
return;
}
// Determine base model filter based on current display mode
const baseModelInfo = getCurrentVersionBaseModel(controller.record, normalizedCurrentVersionId);
const isFilteringActive =
displayMode === DISPLAY_FILTER_MODES.SAME_BASE &&
Boolean(baseModelInfo.normalized);
// Write filter params to sessionStorage
setSessionItem('vlm_model_id', String(modelId));
setSessionItem('vlm_model_name', modelName || String(modelId));
if (isFilteringActive) {
// Use raw (non-normalized) base model for exact backend matching
setSessionItem('vlm_base_model', baseModelInfo.raw);
} else {
removeSessionItem('vlm_base_model');
}
// Close the modal and reload the page to show filtered cards
modalManager.closeModal(modalId);
window.location.reload();
}
async function handleToggleVersionIgnore(button, versionId) {
if (!controller.record) {
return;
@@ -1348,6 +1375,10 @@ export function initVersionsTab({
event.preventDefault();
handleToggleVersionDisplayMode();
break;
case 'view-local':
event.preventDefault();
handleViewLocalVersions();
break;
default:
break;
}