mirror of
https://github.com/willmiao/ComfyUI-Lora-Manager.git
synced 2026-03-25 15:15:44 -03:00
feat: enhance model root fetching and moving functionality across various components
This commit is contained in:
@@ -111,8 +111,30 @@ class CheckpointRoutes(BaseModelRoutes):
|
|||||||
|
|
||||||
async def get_checkpoints_roots(self, request: web.Request) -> web.Response:
|
async def get_checkpoints_roots(self, request: web.Request) -> web.Response:
|
||||||
"""Return the list of checkpoint roots from config"""
|
"""Return the list of checkpoint roots from config"""
|
||||||
return web.json_response({"checkpoints_roots": config.checkpoints_roots})
|
try:
|
||||||
|
roots = config.checkpoints_roots
|
||||||
|
return web.json_response({
|
||||||
|
"success": True,
|
||||||
|
"roots": roots
|
||||||
|
})
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"Error getting checkpoint roots: {e}", exc_info=True)
|
||||||
|
return web.json_response({
|
||||||
|
"success": False,
|
||||||
|
"error": str(e)
|
||||||
|
}, status=500)
|
||||||
|
|
||||||
async def get_unet_roots(self, request: web.Request) -> web.Response:
|
async def get_unet_roots(self, request: web.Request) -> web.Response:
|
||||||
"""Return the list of unet roots from config"""
|
"""Return the list of unet roots from config"""
|
||||||
return web.json_response({"unet_roots": config.unet_roots})
|
try:
|
||||||
|
roots = config.unet_roots
|
||||||
|
return web.json_response({
|
||||||
|
"success": True,
|
||||||
|
"roots": roots
|
||||||
|
})
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"Error getting unet roots: {e}", exc_info=True)
|
||||||
|
return web.json_response({
|
||||||
|
"success": False,
|
||||||
|
"error": str(e)
|
||||||
|
}, status=500)
|
||||||
@@ -555,6 +555,12 @@ export class BaseModelApiClient {
|
|||||||
|
|
||||||
async fetchModelRoots() {
|
async fetchModelRoots() {
|
||||||
try {
|
try {
|
||||||
|
// For checkpoints, use the specific method that considers modelType
|
||||||
|
// if (this.modelType === 'checkpoints') {
|
||||||
|
// const pageState = this.getPageState();
|
||||||
|
// return await this.fetchModelRoots(pageState.modelType || 'checkpoint');
|
||||||
|
// }
|
||||||
|
|
||||||
const response = await fetch(this.apiConfig.endpoints.roots);
|
const response = await fetch(this.apiConfig.endpoints.roots);
|
||||||
if (!response.ok) {
|
if (!response.ok) {
|
||||||
throw new Error(`Failed to fetch ${this.apiConfig.config.displayName} roots`);
|
throw new Error(`Failed to fetch ${this.apiConfig.config.displayName} roots`);
|
||||||
|
|||||||
@@ -64,4 +64,30 @@ export class CheckpointApiClient extends BaseModelApiClient {
|
|||||||
throw error;
|
throw error;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get appropriate roots based on model type
|
||||||
|
*/
|
||||||
|
async fetchModelRoots(modelType = 'checkpoint') {
|
||||||
|
try {
|
||||||
|
let response;
|
||||||
|
if (modelType === 'diffusion_model') {
|
||||||
|
response = await fetch(this.apiConfig.endpoints.specific.unet_roots, {
|
||||||
|
method: 'GET'
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
response = await fetch(this.apiConfig.endpoints.specific.checkpoints_roots, {
|
||||||
|
method: 'GET'
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!response.ok) {
|
||||||
|
throw new Error(`Failed to fetch ${modelType} roots`);
|
||||||
|
}
|
||||||
|
return await response.json();
|
||||||
|
} catch (error) {
|
||||||
|
console.error(`Error fetching ${modelType} roots:`, error);
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
import { BaseContextMenu } from './BaseContextMenu.js';
|
import { BaseContextMenu } from './BaseContextMenu.js';
|
||||||
import { ModelContextMenuMixin } from './ModelContextMenuMixin.js';
|
import { ModelContextMenuMixin } from './ModelContextMenuMixin.js';
|
||||||
import { getModelApiClient, resetAndReload } from '../../api/modelApiFactory.js';
|
import { getModelApiClient, resetAndReload } from '../../api/modelApiFactory.js';
|
||||||
import { showToast } from '../../utils/uiHelpers.js';
|
|
||||||
import { showDeleteModal, showExcludeModal } from '../../utils/modalUtils.js';
|
import { showDeleteModal, showExcludeModal } from '../../utils/modalUtils.js';
|
||||||
|
import { moveManager } from '../../managers/MoveManager.js';
|
||||||
|
|
||||||
export class CheckpointContextMenu extends BaseContextMenu {
|
export class CheckpointContextMenu extends BaseContextMenu {
|
||||||
constructor() {
|
constructor() {
|
||||||
@@ -54,8 +54,7 @@ export class CheckpointContextMenu extends BaseContextMenu {
|
|||||||
apiClient.refreshSingleModelMetadata(this.currentCard.dataset.filepath);
|
apiClient.refreshSingleModelMetadata(this.currentCard.dataset.filepath);
|
||||||
break;
|
break;
|
||||||
case 'move':
|
case 'move':
|
||||||
// Move to folder (placeholder)
|
moveManager.showMoveModal(this.currentCard.dataset.filepath, this.currentCard.dataset.model_type);
|
||||||
showToast('Move to folder feature coming soon', 'info');
|
|
||||||
break;
|
break;
|
||||||
case 'exclude':
|
case 'exclude':
|
||||||
showExcludeModal(this.currentCard.dataset.filepath);
|
showExcludeModal(this.currentCard.dataset.filepath);
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import { BaseContextMenu } from './BaseContextMenu.js';
|
import { BaseContextMenu } from './BaseContextMenu.js';
|
||||||
import { ModelContextMenuMixin } from './ModelContextMenuMixin.js';
|
import { ModelContextMenuMixin } from './ModelContextMenuMixin.js';
|
||||||
import { getModelApiClient, resetAndReload } from '../../api/modelApiFactory.js';
|
import { getModelApiClient, resetAndReload } from '../../api/modelApiFactory.js';
|
||||||
import { showToast } from '../../utils/uiHelpers.js';
|
import { moveManager } from '../../managers/MoveManager.js';
|
||||||
import { showDeleteModal, showExcludeModal } from '../../utils/modalUtils.js';
|
import { showDeleteModal, showExcludeModal } from '../../utils/modalUtils.js';
|
||||||
|
|
||||||
export class EmbeddingContextMenu extends BaseContextMenu {
|
export class EmbeddingContextMenu extends BaseContextMenu {
|
||||||
@@ -54,8 +54,7 @@ export class EmbeddingContextMenu extends BaseContextMenu {
|
|||||||
apiClient.refreshSingleModelMetadata(this.currentCard.dataset.filepath);
|
apiClient.refreshSingleModelMetadata(this.currentCard.dataset.filepath);
|
||||||
break;
|
break;
|
||||||
case 'move':
|
case 'move':
|
||||||
// Move to folder (placeholder)
|
moveManager.showMoveModal(this.currentCard.dataset.filepath);
|
||||||
showToast('Move to folder feature coming soon', 'info');
|
|
||||||
break;
|
break;
|
||||||
case 'exclude':
|
case 'exclude':
|
||||||
showExcludeModal(this.currentCard.dataset.filepath);
|
showExcludeModal(this.currentCard.dataset.filepath);
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ import { ModelContextMenuMixin } from './ModelContextMenuMixin.js';
|
|||||||
import { getModelApiClient, resetAndReload } from '../../api/modelApiFactory.js';
|
import { getModelApiClient, resetAndReload } from '../../api/modelApiFactory.js';
|
||||||
import { copyToClipboard, sendLoraToWorkflow } from '../../utils/uiHelpers.js';
|
import { copyToClipboard, sendLoraToWorkflow } from '../../utils/uiHelpers.js';
|
||||||
import { showExcludeModal, showDeleteModal } from '../../utils/modalUtils.js';
|
import { showExcludeModal, showDeleteModal } from '../../utils/modalUtils.js';
|
||||||
|
import { moveManager } from '../../managers/MoveManager.js';
|
||||||
|
|
||||||
export class LoraContextMenu extends BaseContextMenu {
|
export class LoraContextMenu extends BaseContextMenu {
|
||||||
constructor() {
|
constructor() {
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import { toggleShowcase } from './showcase/ShowcaseView.js';
|
|||||||
import { bulkManager } from '../../managers/BulkManager.js';
|
import { bulkManager } from '../../managers/BulkManager.js';
|
||||||
import { modalManager } from '../../managers/ModalManager.js';
|
import { modalManager } from '../../managers/ModalManager.js';
|
||||||
import { NSFW_LEVELS } from '../../utils/constants.js';
|
import { NSFW_LEVELS } from '../../utils/constants.js';
|
||||||
|
import { MODEL_TYPES } from '../../api/apiConfig.js';
|
||||||
import { getModelApiClient } from '../../api/modelApiFactory.js';
|
import { getModelApiClient } from '../../api/modelApiFactory.js';
|
||||||
import { showDeleteModal } from '../../utils/modalUtils.js';
|
import { showDeleteModal } from '../../utils/modalUtils.js';
|
||||||
|
|
||||||
@@ -152,7 +153,7 @@ async function toggleFavorite(card) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function handleSendToWorkflow(card, replaceMode, modelType) {
|
function handleSendToWorkflow(card, replaceMode, modelType) {
|
||||||
if (modelType === 'loras') {
|
if (modelType === MODEL_TYPES.LORA) {
|
||||||
const usageTips = JSON.parse(card.dataset.usage_tips || '{}');
|
const usageTips = JSON.parse(card.dataset.usage_tips || '{}');
|
||||||
const strength = usageTips.strength || 1;
|
const strength = usageTips.strength || 1;
|
||||||
const loraSyntax = `<lora:${card.dataset.file_name}:${strength}>`;
|
const loraSyntax = `<lora:${card.dataset.file_name}:${strength}>`;
|
||||||
@@ -164,16 +165,16 @@ function handleSendToWorkflow(card, replaceMode, modelType) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function handleCopyAction(card, modelType) {
|
function handleCopyAction(card, modelType) {
|
||||||
if (modelType === 'loras') {
|
if (modelType === MODEL_TYPES.LORA) {
|
||||||
const usageTips = JSON.parse(card.dataset.usage_tips || '{}');
|
const usageTips = JSON.parse(card.dataset.usage_tips || '{}');
|
||||||
const strength = usageTips.strength || 1;
|
const strength = usageTips.strength || 1;
|
||||||
const loraSyntax = `<lora:${card.dataset.file_name}:${strength}>`;
|
const loraSyntax = `<lora:${card.dataset.file_name}:${strength}>`;
|
||||||
copyToClipboard(loraSyntax, 'LoRA syntax copied to clipboard');
|
copyToClipboard(loraSyntax, 'LoRA syntax copied to clipboard');
|
||||||
} else if (modelType === 'checkpoints') {
|
} else if (modelType === MODEL_TYPES.CHECKPOINT) {
|
||||||
// Checkpoint copy functionality - copy checkpoint name
|
// Checkpoint copy functionality - copy checkpoint name
|
||||||
const checkpointName = card.dataset.file_name;
|
const checkpointName = card.dataset.file_name;
|
||||||
copyToClipboard(checkpointName, 'Checkpoint name copied');
|
copyToClipboard(checkpointName, 'Checkpoint name copied');
|
||||||
} else if (modelType === 'embeddings') {
|
} else if (modelType === MODEL_TYPES.EMBEDDING) {
|
||||||
const embeddingName = card.dataset.file_name;
|
const embeddingName = card.dataset.file_name;
|
||||||
copyToClipboard(embeddingName, 'Embedding name copied');
|
copyToClipboard(embeddingName, 'Embedding name copied');
|
||||||
}
|
}
|
||||||
@@ -377,10 +378,15 @@ export function createModelCard(model, modelType) {
|
|||||||
card.dataset.favorite = model.favorite ? 'true' : 'false';
|
card.dataset.favorite = model.favorite ? 'true' : 'false';
|
||||||
|
|
||||||
// LoRA specific data
|
// LoRA specific data
|
||||||
if (modelType === 'loras') {
|
if (modelType === MODEL_TYPES.LORA) {
|
||||||
card.dataset.usage_tips = model.usage_tips;
|
card.dataset.usage_tips = model.usage_tips;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// checkpoint specific data
|
||||||
|
if (modelType === MODEL_TYPES.CHECKPOINT) {
|
||||||
|
card.dataset.model_type = model.model_type; // checkpoint or diffusion_model
|
||||||
|
}
|
||||||
|
|
||||||
// Store metadata if available
|
// Store metadata if available
|
||||||
if (model.civitai) {
|
if (model.civitai) {
|
||||||
card.dataset.meta = JSON.stringify(model.civitai || {});
|
card.dataset.meta = JSON.stringify(model.civitai || {});
|
||||||
@@ -406,7 +412,7 @@ export function createModelCard(model, modelType) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Apply selection state if in bulk mode and this card is in the selected set (LoRA only)
|
// Apply selection state if in bulk mode and this card is in the selected set (LoRA only)
|
||||||
if (modelType === 'loras' && state.bulkMode && state.selectedLoras.has(model.file_path)) {
|
if (modelType === MODEL_TYPES.LORA && state.bulkMode && state.selectedLoras.has(model.file_path)) {
|
||||||
card.classList.add('selected');
|
card.classList.add('selected');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -2,7 +2,6 @@ import { appCore } from './core.js';
|
|||||||
import { state } from './state/index.js';
|
import { state } from './state/index.js';
|
||||||
import { updateCardsForBulkMode } from './components/shared/ModelCard.js';
|
import { updateCardsForBulkMode } from './components/shared/ModelCard.js';
|
||||||
import { bulkManager } from './managers/BulkManager.js';
|
import { bulkManager } from './managers/BulkManager.js';
|
||||||
import { moveManager } from './managers/MoveManager.js';
|
|
||||||
import { LoraContextMenu } from './components/ContextMenu/index.js';
|
import { LoraContextMenu } from './components/ContextMenu/index.js';
|
||||||
import { createPageControls } from './components/controls/index.js';
|
import { createPageControls } from './components/controls/index.js';
|
||||||
import { confirmDelete, closeDeleteModal, confirmExclude, closeExcludeModal } from './utils/modalUtils.js';
|
import { confirmDelete, closeDeleteModal, confirmExclude, closeExcludeModal } from './utils/modalUtils.js';
|
||||||
@@ -33,7 +32,6 @@ class LoraPageManager {
|
|||||||
window.closeDeleteModal = closeDeleteModal;
|
window.closeDeleteModal = closeDeleteModal;
|
||||||
window.confirmExclude = confirmExclude;
|
window.confirmExclude = confirmExclude;
|
||||||
window.closeExcludeModal = closeExcludeModal;
|
window.closeExcludeModal = closeExcludeModal;
|
||||||
window.moveManager = moveManager;
|
|
||||||
|
|
||||||
// Bulk operations
|
// Bulk operations
|
||||||
window.toggleBulkMode = () => bulkManager.toggleBulkMode();
|
window.toggleBulkMode = () => bulkManager.toggleBulkMode();
|
||||||
|
|||||||
@@ -9,99 +9,107 @@ class MoveManager {
|
|||||||
this.currentFilePath = null;
|
this.currentFilePath = null;
|
||||||
this.bulkFilePaths = null;
|
this.bulkFilePaths = null;
|
||||||
this.modal = document.getElementById('moveModal');
|
this.modal = document.getElementById('moveModal');
|
||||||
this.loraRootSelect = document.getElementById('moveLoraRoot');
|
this.modelRootSelect = document.getElementById('moveModelRoot');
|
||||||
this.folderBrowser = document.getElementById('moveFolderBrowser');
|
this.folderBrowser = document.getElementById('moveFolderBrowser');
|
||||||
this.newFolderInput = document.getElementById('moveNewFolder');
|
this.newFolderInput = document.getElementById('moveNewFolder');
|
||||||
this.pathDisplay = document.getElementById('moveTargetPathDisplay');
|
this.pathDisplay = document.getElementById('moveTargetPathDisplay');
|
||||||
this.modalTitle = document.getElementById('moveModalTitle');
|
this.modalTitle = document.getElementById('moveModalTitle');
|
||||||
|
this.rootLabel = document.getElementById('moveRootLabel');
|
||||||
|
|
||||||
this.initializeEventListeners();
|
this.initializeEventListeners();
|
||||||
}
|
}
|
||||||
|
|
||||||
initializeEventListeners() {
|
initializeEventListeners() {
|
||||||
// 初始化LoRA根目录选择器
|
// Initialize model root directory selector
|
||||||
this.loraRootSelect.addEventListener('change', () => this.updatePathPreview());
|
this.modelRootSelect.addEventListener('change', () => this.updatePathPreview());
|
||||||
|
|
||||||
// 文件夹选择事件
|
// Folder selection event
|
||||||
this.folderBrowser.addEventListener('click', (e) => {
|
this.folderBrowser.addEventListener('click', (e) => {
|
||||||
const folderItem = e.target.closest('.folder-item');
|
const folderItem = e.target.closest('.folder-item');
|
||||||
if (!folderItem) return;
|
if (!folderItem) return;
|
||||||
|
|
||||||
// 如果点击已选中的文件夹,则取消选择
|
// If clicking already selected folder, deselect it
|
||||||
if (folderItem.classList.contains('selected')) {
|
if (folderItem.classList.contains('selected')) {
|
||||||
folderItem.classList.remove('selected');
|
folderItem.classList.remove('selected');
|
||||||
} else {
|
} else {
|
||||||
// 取消其他选中状态
|
// Deselect other folders
|
||||||
this.folderBrowser.querySelectorAll('.folder-item').forEach(item => {
|
this.folderBrowser.querySelectorAll('.folder-item').forEach(item => {
|
||||||
item.classList.remove('selected');
|
item.classList.remove('selected');
|
||||||
});
|
});
|
||||||
// 设置当前选中状态
|
// Select current folder
|
||||||
folderItem.classList.add('selected');
|
folderItem.classList.add('selected');
|
||||||
}
|
}
|
||||||
|
|
||||||
this.updatePathPreview();
|
this.updatePathPreview();
|
||||||
});
|
});
|
||||||
|
|
||||||
// 新文件夹输入事件
|
// New folder input event
|
||||||
this.newFolderInput.addEventListener('input', () => this.updatePathPreview());
|
this.newFolderInput.addEventListener('input', () => this.updatePathPreview());
|
||||||
}
|
}
|
||||||
|
|
||||||
async showMoveModal(filePath) {
|
async showMoveModal(filePath, modelType = null) {
|
||||||
// Reset state
|
// Reset state
|
||||||
this.currentFilePath = null;
|
this.currentFilePath = null;
|
||||||
this.bulkFilePaths = null;
|
this.bulkFilePaths = null;
|
||||||
|
|
||||||
|
const apiClient = getModelApiClient();
|
||||||
|
const currentPageType = state.currentPageType;
|
||||||
|
const modelConfig = apiClient.apiConfig.config;
|
||||||
|
|
||||||
// Handle bulk mode
|
// Handle bulk mode
|
||||||
if (filePath === 'bulk') {
|
if (filePath === 'bulk') {
|
||||||
const selectedPaths = Array.from(state.selectedLoras);
|
const selectedPaths = Array.from(state.selectedModels);
|
||||||
if (selectedPaths.length === 0) {
|
if (selectedPaths.length === 0) {
|
||||||
showToast('No LoRAs selected', 'warning');
|
showToast('No models selected', 'warning');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
this.bulkFilePaths = selectedPaths;
|
this.bulkFilePaths = selectedPaths;
|
||||||
this.modalTitle.textContent = `Move ${selectedPaths.length} LoRAs`;
|
this.modalTitle.textContent = `Move ${selectedPaths.length} ${modelConfig.displayName}s`;
|
||||||
} else {
|
} else {
|
||||||
// Single file mode
|
// Single file mode
|
||||||
this.currentFilePath = filePath;
|
this.currentFilePath = filePath;
|
||||||
this.modalTitle.textContent = "Move Model";
|
this.modalTitle.textContent = `Move ${modelConfig.displayName}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 清除之前的选择
|
// Update UI labels based on model type
|
||||||
|
this.rootLabel.textContent = `Select ${modelConfig.displayName} Root:`;
|
||||||
|
this.pathDisplay.querySelector('.path-text').textContent = `Select a ${modelConfig.displayName.toLowerCase()} root directory`;
|
||||||
|
|
||||||
|
// Clear previous selections
|
||||||
this.folderBrowser.querySelectorAll('.folder-item').forEach(item => {
|
this.folderBrowser.querySelectorAll('.folder-item').forEach(item => {
|
||||||
item.classList.remove('selected');
|
item.classList.remove('selected');
|
||||||
});
|
});
|
||||||
this.newFolderInput.value = '';
|
this.newFolderInput.value = '';
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// Fetch LoRA roots
|
// Fetch model roots
|
||||||
const rootsResponse = await fetch('/api/loras/roots');
|
let rootsData;
|
||||||
if (!rootsResponse.ok) {
|
if (modelType) {
|
||||||
throw new Error('Failed to fetch LoRA roots');
|
// For checkpoints, use the specific API method that considers modelType
|
||||||
|
rootsData = await apiClient.fetchModelRoots(modelType);
|
||||||
|
} else {
|
||||||
|
// For other model types, use the generic method
|
||||||
|
rootsData = await apiClient.fetchModelRoots();
|
||||||
}
|
}
|
||||||
|
|
||||||
const rootsData = await rootsResponse.json();
|
|
||||||
if (!rootsData.roots || rootsData.roots.length === 0) {
|
if (!rootsData.roots || rootsData.roots.length === 0) {
|
||||||
throw new Error('No LoRA roots found');
|
throw new Error(`No ${modelConfig.displayName.toLowerCase()} roots found`);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 填充LoRA根目录选择器
|
// Populate model root selector
|
||||||
this.loraRootSelect.innerHTML = rootsData.roots.map(root =>
|
this.modelRootSelect.innerHTML = rootsData.roots.map(root =>
|
||||||
`<option value="${root}">${root}</option>`
|
`<option value="${root}">${root}</option>`
|
||||||
).join('');
|
).join('');
|
||||||
|
|
||||||
// Set default lora root if available
|
// Set default root if available
|
||||||
const defaultRoot = getStorageItem('settings', {}).default_lora_root;
|
const settingsKey = `default_${currentPageType.slice(0, -1)}_root`; // Remove 's' from plural
|
||||||
|
const defaultRoot = getStorageItem('settings', {})[settingsKey];
|
||||||
if (defaultRoot && rootsData.roots.includes(defaultRoot)) {
|
if (defaultRoot && rootsData.roots.includes(defaultRoot)) {
|
||||||
this.loraRootSelect.value = defaultRoot;
|
this.modelRootSelect.value = defaultRoot;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Fetch folders dynamically
|
// Fetch folders dynamically
|
||||||
const foldersResponse = await fetch('/api/loras/folders');
|
const foldersData = await apiClient.fetchModelFolders();
|
||||||
if (!foldersResponse.ok) {
|
|
||||||
throw new Error('Failed to fetch folders');
|
|
||||||
}
|
|
||||||
|
|
||||||
const foldersData = await foldersResponse.json();
|
|
||||||
|
|
||||||
// Update folder browser with dynamic content
|
// Update folder browser with dynamic content
|
||||||
this.folderBrowser.innerHTML = foldersData.folders.map(folder =>
|
this.folderBrowser.innerHTML = foldersData.folders.map(folder =>
|
||||||
@@ -112,13 +120,13 @@ class MoveManager {
|
|||||||
modalManager.showModal('moveModal');
|
modalManager.showModal('moveModal');
|
||||||
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Error fetching LoRA roots or folders:', error);
|
console.error(`Error fetching ${modelConfig.displayName.toLowerCase()} roots or folders:`, error);
|
||||||
showToast(error.message, 'error');
|
showToast(error.message, 'error');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
updatePathPreview() {
|
updatePathPreview() {
|
||||||
const selectedRoot = this.loraRootSelect.value;
|
const selectedRoot = this.modelRootSelect.value;
|
||||||
const selectedFolder = this.folderBrowser.querySelector('.folder-item.selected')?.dataset.folder || '';
|
const selectedFolder = this.folderBrowser.querySelector('.folder-item.selected')?.dataset.folder || '';
|
||||||
const newFolder = this.newFolderInput.value.trim();
|
const newFolder = this.newFolderInput.value.trim();
|
||||||
|
|
||||||
@@ -134,7 +142,7 @@ class MoveManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async moveModel() {
|
async moveModel() {
|
||||||
const selectedRoot = this.loraRootSelect.value;
|
const selectedRoot = this.modelRootSelect.value;
|
||||||
const selectedFolder = this.folderBrowser.querySelector('.folder-item.selected')?.dataset.folder || '';
|
const selectedFolder = this.folderBrowser.querySelector('.folder-item.selected')?.dataset.folder || '';
|
||||||
const newFolder = this.newFolderInput.value.trim();
|
const newFolder = this.newFolderInput.value.trim();
|
||||||
|
|
||||||
@@ -191,11 +199,8 @@ class MoveManager {
|
|||||||
|
|
||||||
// Refresh folder tags after successful move
|
// Refresh folder tags after successful move
|
||||||
try {
|
try {
|
||||||
const foldersResponse = await fetch('/api/loras/folders');
|
const foldersData = await apiClient.fetchModelFolders();
|
||||||
if (foldersResponse.ok) {
|
updateFolderTags(foldersData.folders);
|
||||||
const foldersData = await foldersResponse.json();
|
|
||||||
updateFolderTags(foldersData.folders);
|
|
||||||
}
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Error refreshing folder tags:', error);
|
console.error('Error refreshing folder tags:', error);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -89,6 +89,9 @@ export const state = {
|
|||||||
baseModel: [],
|
baseModel: [],
|
||||||
tags: []
|
tags: []
|
||||||
},
|
},
|
||||||
|
modelType: 'checkpoint', // 'checkpoint' or 'diffusion_model'
|
||||||
|
bulkMode: false,
|
||||||
|
selectedModels: new Set(),
|
||||||
showFavoritesOnly: false,
|
showFavoritesOnly: false,
|
||||||
duplicatesMode: false,
|
duplicatesMode: false,
|
||||||
},
|
},
|
||||||
@@ -112,6 +115,8 @@ export const state = {
|
|||||||
baseModel: [],
|
baseModel: [],
|
||||||
tags: []
|
tags: []
|
||||||
},
|
},
|
||||||
|
bulkMode: false,
|
||||||
|
selectedModels: new Set(),
|
||||||
showFavoritesOnly: false,
|
showFavoritesOnly: false,
|
||||||
duplicatesMode: false,
|
duplicatesMode: false,
|
||||||
}
|
}
|
||||||
@@ -154,12 +159,43 @@ export const state = {
|
|||||||
get filters() { return this.pages[this.currentPageType].filters; },
|
get filters() { return this.pages[this.currentPageType].filters; },
|
||||||
set filters(value) { this.pages[this.currentPageType].filters = value; },
|
set filters(value) { this.pages[this.currentPageType].filters = value; },
|
||||||
|
|
||||||
get bulkMode() { return this.pages.loras.bulkMode; },
|
get bulkMode() {
|
||||||
set bulkMode(value) { this.pages.loras.bulkMode = value; },
|
const currentType = this.currentPageType;
|
||||||
|
if (currentType === MODEL_TYPES.LORA) {
|
||||||
|
return this.pages.loras.bulkMode;
|
||||||
|
} else {
|
||||||
|
return this.pages[currentType].bulkMode;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
set bulkMode(value) {
|
||||||
|
const currentType = this.currentPageType;
|
||||||
|
if (currentType === MODEL_TYPES.LORA) {
|
||||||
|
this.pages.loras.bulkMode = value;
|
||||||
|
} else {
|
||||||
|
this.pages[currentType].bulkMode = value;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
get selectedLoras() { return this.pages.loras.selectedLoras; },
|
get selectedLoras() { return this.pages.loras.selectedLoras; },
|
||||||
set selectedLoras(value) { this.pages.loras.selectedLoras = value; },
|
set selectedLoras(value) { this.pages.loras.selectedLoras = value; },
|
||||||
|
|
||||||
|
get selectedModels() {
|
||||||
|
const currentType = this.currentPageType;
|
||||||
|
if (currentType === MODEL_TYPES.LORA) {
|
||||||
|
return this.pages.loras.selectedLoras;
|
||||||
|
} else {
|
||||||
|
return this.pages[currentType].selectedModels;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
set selectedModels(value) {
|
||||||
|
const currentType = this.currentPageType;
|
||||||
|
if (currentType === MODEL_TYPES.LORA) {
|
||||||
|
this.pages.loras.selectedLoras = value;
|
||||||
|
} else {
|
||||||
|
this.pages[currentType].selectedModels = value;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
get loraMetadataCache() { return this.pages.loras.loraMetadataCache; },
|
get loraMetadataCache() { return this.pages.loras.loraMetadataCache; },
|
||||||
set loraMetadataCache(value) { this.pages.loras.loraMetadataCache = value; },
|
set loraMetadataCache(value) { this.pages.loras.loraMetadataCache = value; },
|
||||||
|
|
||||||
|
|||||||
@@ -9,13 +9,13 @@
|
|||||||
<div class="path-preview">
|
<div class="path-preview">
|
||||||
<label>Target Location Preview:</label>
|
<label>Target Location Preview:</label>
|
||||||
<div class="path-display" id="moveTargetPathDisplay">
|
<div class="path-display" id="moveTargetPathDisplay">
|
||||||
<span class="path-text">Select a LoRA root directory</span>
|
<span class="path-text">Select a model root directory</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="input-group">
|
<div class="input-group">
|
||||||
<label>Select LoRA Root:</label>
|
<label id="moveRootLabel">Select Model Root:</label>
|
||||||
<select id="moveLoraRoot"></select>
|
<select id="moveModelRoot"></select>
|
||||||
</div>
|
</div>
|
||||||
<div class="input-group">
|
<div class="input-group">
|
||||||
<label>Target Folder:</label>
|
<label>Target Folder:</label>
|
||||||
|
|||||||
Reference in New Issue
Block a user