diff --git a/static/js/api/baseModelApi.js b/static/js/api/baseModelApi.js index 37d7c726..0227f692 100644 --- a/static/js/api/baseModelApi.js +++ b/static/js/api/baseModelApi.js @@ -455,10 +455,15 @@ export async function deleteModel(filePath, modelType = 'lora') { const data = await response.json(); if (data.success) { - // Remove the card from UI - const card = document.querySelector(`.lora-card[data-filepath="${filePath}"]`); - if (card) { - card.remove(); + // If virtual scroller exists, update its data + if (state.virtualScroller) { + state.virtualScroller.removeItemByFilePath(filePath); + } else { + // Legacy approach: remove the card from UI directly + const card = document.querySelector(`.lora-card[data-filepath="${filePath}"]`); + if (card) { + card.remove(); + } } showToast(`${modelType} deleted successfully`, 'success'); @@ -673,10 +678,15 @@ export async function excludeModel(filePath, modelType = 'lora') { const data = await response.json(); if (data.success) { - // Remove the card from UI - const card = document.querySelector(`.lora-card[data-filepath="${filePath}"]`); - if (card) { - card.remove(); + // If virtual scroller exists, update its data + if (state.virtualScroller) { + state.virtualScroller.removeItemByFilePath(filePath); + } else { + // Legacy approach: remove the card from UI directly + const card = document.querySelector(`.lora-card[data-filepath="${filePath}"]`); + if (card) { + card.remove(); + } } showToast(`${modelType} excluded successfully`, 'success'); diff --git a/static/js/api/loraApi.js b/static/js/api/loraApi.js index b4bcb767..7cd496cc 100644 --- a/static/js/api/loraApi.js +++ b/static/js/api/loraApi.js @@ -13,7 +13,6 @@ import { excludeModel as baseExcludeModel } from './baseModelApi.js'; import { state, getCurrentPageState } from '../state/index.js'; -import { showToast } from '../utils/uiHelpers.js'; /** * Save model metadata to the server diff --git a/static/js/utils/VirtualScroller.js b/static/js/utils/VirtualScroller.js index fdcf88b4..9efc88a3 100644 --- a/static/js/utils/VirtualScroller.js +++ b/static/js/utils/VirtualScroller.js @@ -721,4 +721,61 @@ export class VirtualScroller { console.log('Virtual scroller enabled'); } + + // New method to remove an item by file path + removeItemByFilePath(filePath) { + if (!filePath || this.disabled || this.items.length === 0) return false; + + // Find the index of the item with the matching file path + const index = this.items.findIndex(item => + item.file_path === filePath || + item.filepath === filePath || + item.path === filePath + ); + + if (index === -1) { + console.warn(`Item with file path ${filePath} not found in virtual scroller data`); + return false; + } + + // Remove the item from the data array + this.items.splice(index, 1); + + // Decrement total count + this.totalItems = Math.max(0, this.totalItems - 1); + + // Remove the item from rendered items if it exists + if (this.renderedItems.has(index)) { + this.renderedItems.get(index).remove(); + this.renderedItems.delete(index); + } + + // Shift all rendered items with higher indices down by 1 + const indicesToUpdate = []; + + // Collect all indices that need to be updated + for (const [idx, element] of this.renderedItems.entries()) { + if (idx > index) { + indicesToUpdate.push(idx); + } + } + + // Update the elements and map entries + for (const idx of indicesToUpdate) { + const element = this.renderedItems.get(idx); + this.renderedItems.delete(idx); + // The item is now at the previous index + this.renderedItems.set(idx - 1, element); + } + + // Update the spacer height to reflect the new total + this.updateSpacerHeight(); + + // Re-render to ensure proper layout + this.clearRenderedItems(); + this.scheduleRender(); + + console.log(`Removed item with file path ${filePath} from virtual scroller data`); + return true; + } } diff --git a/static/js/utils/modalUtils.js b/static/js/utils/modalUtils.js index 0bb098f6..3041be4d 100644 --- a/static/js/utils/modalUtils.js +++ b/static/js/utils/modalUtils.js @@ -28,8 +28,6 @@ export function showDeleteModal(filePath, modelType = 'lora') { export async function confirmDelete() { if (!pendingDeletePath) return; - const card = document.querySelector(`.lora-card[data-filepath="${pendingDeletePath}"]`); - try { // Use appropriate delete function based on model type if (pendingModelType === 'checkpoint') { @@ -37,10 +35,7 @@ export async function confirmDelete() { } else { await deleteLora(pendingDeletePath); } - - if (card) { - card.remove(); - } + closeDeleteModal(); } catch (error) { console.error('Error deleting model:', error);