mirror of
https://github.com/willmiao/ComfyUI-Lora-Manager.git
synced 2026-03-21 21:22:11 -03:00
feat: refactor model API structure to support specific model types with dedicated API clients for Checkpoints, LoRAs, and Embeddings
refactor: consolidate model API client creation into a factory function for better maintainability feat: implement move operations for LoRAs and handle unsupported operations for Checkpoints and Embeddings
This commit is contained in:
@@ -8,12 +8,16 @@ import {
|
|||||||
DOWNLOAD_ENDPOINTS,
|
DOWNLOAD_ENDPOINTS,
|
||||||
WS_ENDPOINTS
|
WS_ENDPOINTS
|
||||||
} from './apiConfig.js';
|
} from './apiConfig.js';
|
||||||
|
import { createModelApiClient } from './modelApiFactory.js';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Universal API client for all model types
|
* Abstract base class for all model API clients
|
||||||
*/
|
*/
|
||||||
class ModelApiClient {
|
export class BaseModelApiClient {
|
||||||
constructor(modelType = null) {
|
constructor(modelType = null) {
|
||||||
|
if (this.constructor === BaseModelApiClient) {
|
||||||
|
throw new Error("BaseModelApiClient is abstract and cannot be instantiated directly");
|
||||||
|
}
|
||||||
this.modelType = modelType || getCurrentModelType();
|
this.modelType = modelType || getCurrentModelType();
|
||||||
this.apiConfig = getCompleteApiConfig(this.modelType);
|
this.apiConfig = getCompleteApiConfig(this.modelType);
|
||||||
}
|
}
|
||||||
@@ -42,9 +46,6 @@ class ModelApiClient {
|
|||||||
return pageState;
|
return pageState;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Fetch models with pagination
|
|
||||||
*/
|
|
||||||
async fetchModelsPage(page = 1, pageSize = null) {
|
async fetchModelsPage(page = 1, pageSize = null) {
|
||||||
const pageState = this.getPageState();
|
const pageState = this.getPageState();
|
||||||
const actualPageSize = pageSize || pageState.pageSize || this.apiConfig.config.defaultPageSize;
|
const actualPageSize = pageSize || pageState.pageSize || this.apiConfig.config.defaultPageSize;
|
||||||
@@ -79,9 +80,6 @@ class ModelApiClient {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Reset and reload models with virtual scrolling
|
|
||||||
*/
|
|
||||||
async loadMoreWithVirtualScroll(resetPage = false, updateFolders = false) {
|
async loadMoreWithVirtualScroll(resetPage = false, updateFolders = false) {
|
||||||
const pageState = this.getPageState();
|
const pageState = this.getPageState();
|
||||||
|
|
||||||
@@ -93,24 +91,20 @@ class ModelApiClient {
|
|||||||
pageState.currentPage = 1; // Reset to first page
|
pageState.currentPage = 1; // Reset to first page
|
||||||
}
|
}
|
||||||
|
|
||||||
// Fetch the current page
|
|
||||||
const startTime = performance.now();
|
const startTime = performance.now();
|
||||||
const result = await this.fetchModelsPage(pageState.currentPage, pageState.pageSize);
|
const result = await this.fetchModelsPage(pageState.currentPage, pageState.pageSize);
|
||||||
const endTime = performance.now();
|
const endTime = performance.now();
|
||||||
console.log(`fetchModelsPage耗时: ${(endTime - startTime).toFixed(2)} ms`);
|
console.log(`fetchModelsPage耗时: ${(endTime - startTime).toFixed(2)} ms`);
|
||||||
|
|
||||||
// Update the virtual scroller
|
|
||||||
state.virtualScroller.refreshWithData(
|
state.virtualScroller.refreshWithData(
|
||||||
result.items,
|
result.items,
|
||||||
result.totalItems,
|
result.totalItems,
|
||||||
result.hasMore
|
result.hasMore
|
||||||
);
|
);
|
||||||
|
|
||||||
// Update state
|
|
||||||
pageState.hasMore = result.hasMore;
|
pageState.hasMore = result.hasMore;
|
||||||
pageState.currentPage = pageState.currentPage + 1;
|
pageState.currentPage = pageState.currentPage + 1;
|
||||||
|
|
||||||
// Update folders if needed
|
|
||||||
if (updateFolders && result.folders) {
|
if (updateFolders && result.folders) {
|
||||||
updateFolderTags(result.folders);
|
updateFolderTags(result.folders);
|
||||||
}
|
}
|
||||||
@@ -126,9 +120,6 @@ class ModelApiClient {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Delete a model
|
|
||||||
*/
|
|
||||||
async deleteModel(filePath) {
|
async deleteModel(filePath) {
|
||||||
try {
|
try {
|
||||||
state.loadingManager.showSimpleLoading(`Deleting ${this.apiConfig.config.singularName}...`);
|
state.loadingManager.showSimpleLoading(`Deleting ${this.apiConfig.config.singularName}...`);
|
||||||
@@ -163,9 +154,6 @@ class ModelApiClient {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Exclude a model
|
|
||||||
*/
|
|
||||||
async excludeModel(filePath) {
|
async excludeModel(filePath) {
|
||||||
try {
|
try {
|
||||||
state.loadingManager.showSimpleLoading(`Excluding ${this.apiConfig.config.singularName}...`);
|
state.loadingManager.showSimpleLoading(`Excluding ${this.apiConfig.config.singularName}...`);
|
||||||
@@ -200,9 +188,6 @@ class ModelApiClient {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Rename a model file
|
|
||||||
*/
|
|
||||||
async renameModelFile(filePath, newFileName) {
|
async renameModelFile(filePath, newFileName) {
|
||||||
try {
|
try {
|
||||||
state.loadingManager.showSimpleLoading(`Renaming ${this.apiConfig.config.singularName} file...`);
|
state.loadingManager.showSimpleLoading(`Renaming ${this.apiConfig.config.singularName} file...`);
|
||||||
@@ -239,9 +224,6 @@ class ModelApiClient {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Replace model preview
|
|
||||||
*/
|
|
||||||
replaceModelPreview(filePath) {
|
replaceModelPreview(filePath) {
|
||||||
const input = document.createElement('input');
|
const input = document.createElement('input');
|
||||||
input.type = 'file';
|
input.type = 'file';
|
||||||
@@ -257,9 +239,6 @@ class ModelApiClient {
|
|||||||
input.click();
|
input.click();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Upload preview image
|
|
||||||
*/
|
|
||||||
async uploadPreview(filePath, file, nsfwLevel = 0) {
|
async uploadPreview(filePath, file, nsfwLevel = 0) {
|
||||||
try {
|
try {
|
||||||
state.loadingManager.showSimpleLoading('Uploading preview...');
|
state.loadingManager.showSimpleLoading('Uploading preview...');
|
||||||
@@ -281,7 +260,6 @@ class ModelApiClient {
|
|||||||
const data = await response.json();
|
const data = await response.json();
|
||||||
const pageState = this.getPageState();
|
const pageState = this.getPageState();
|
||||||
|
|
||||||
// Update the version timestamp
|
|
||||||
const timestamp = Date.now();
|
const timestamp = Date.now();
|
||||||
if (pageState.previewVersions) {
|
if (pageState.previewVersions) {
|
||||||
pageState.previewVersions.set(filePath, timestamp);
|
pageState.previewVersions.set(filePath, timestamp);
|
||||||
@@ -305,9 +283,6 @@ class ModelApiClient {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Save model metadata
|
|
||||||
*/
|
|
||||||
async saveModelMetadata(filePath, data) {
|
async saveModelMetadata(filePath, data) {
|
||||||
try {
|
try {
|
||||||
state.loadingManager.showSimpleLoading('Saving metadata...');
|
state.loadingManager.showSimpleLoading('Saving metadata...');
|
||||||
@@ -332,9 +307,6 @@ class ModelApiClient {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Refresh models (scan)
|
|
||||||
*/
|
|
||||||
async refreshModels(fullRebuild = false) {
|
async refreshModels(fullRebuild = false) {
|
||||||
try {
|
try {
|
||||||
state.loadingManager.showSimpleLoading(
|
state.loadingManager.showSimpleLoading(
|
||||||
@@ -360,9 +332,6 @@ class ModelApiClient {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Fetch CivitAI metadata for single model
|
|
||||||
*/
|
|
||||||
async refreshSingleModelMetadata(filePath) {
|
async refreshSingleModelMetadata(filePath) {
|
||||||
try {
|
try {
|
||||||
state.loadingManager.showSimpleLoading('Refreshing metadata...');
|
state.loadingManager.showSimpleLoading('Refreshing metadata...');
|
||||||
@@ -399,9 +368,6 @@ class ModelApiClient {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Fetch CivitAI metadata for all models
|
|
||||||
*/
|
|
||||||
async fetchCivitaiMetadata() {
|
async fetchCivitaiMetadata() {
|
||||||
let ws = null;
|
let ws = null;
|
||||||
|
|
||||||
@@ -477,9 +443,6 @@ class ModelApiClient {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Fetch CivitAI metadata for multiple models with progress tracking
|
|
||||||
*/
|
|
||||||
async refreshBulkModelMetadata(filePaths) {
|
async refreshBulkModelMetadata(filePaths) {
|
||||||
if (!filePaths || filePaths.length === 0) {
|
if (!filePaths || filePaths.length === 0) {
|
||||||
throw new Error('No file paths provided');
|
throw new Error('No file paths provided');
|
||||||
@@ -493,7 +456,6 @@ class ModelApiClient {
|
|||||||
const progressController = state.loadingManager.showEnhancedProgress('Starting metadata refresh...');
|
const progressController = state.loadingManager.showEnhancedProgress('Starting metadata refresh...');
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// Process files sequentially to avoid overwhelming the API
|
|
||||||
for (let i = 0; i < filePaths.length; i++) {
|
for (let i = 0; i < filePaths.length; i++) {
|
||||||
const filePath = filePaths[i];
|
const filePath = filePaths[i];
|
||||||
const fileName = filePath.split('/').pop();
|
const fileName = filePath.split('/').pop();
|
||||||
@@ -535,7 +497,6 @@ class ModelApiClient {
|
|||||||
processedCount++;
|
processedCount++;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Show completion message
|
|
||||||
let completionMessage;
|
let completionMessage;
|
||||||
if (successCount === totalItems) {
|
if (successCount === totalItems) {
|
||||||
completionMessage = `Successfully refreshed all ${successCount} ${this.apiConfig.config.displayName}s`;
|
completionMessage = `Successfully refreshed all ${successCount} ${this.apiConfig.config.displayName}s`;
|
||||||
@@ -575,113 +536,6 @@ class ModelApiClient {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Move a single model to target path
|
|
||||||
* @returns {string|null} - The new file path if moved, null if not moved
|
|
||||||
*/
|
|
||||||
async moveSingleModel(filePath, targetPath) {
|
|
||||||
if (filePath.substring(0, filePath.lastIndexOf('/')) === targetPath) {
|
|
||||||
showToast('Model is already in the selected folder', 'info');
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
const response = await fetch(this.apiConfig.endpoints.specific.moveModel, {
|
|
||||||
method: 'POST',
|
|
||||||
headers: {
|
|
||||||
'Content-Type': 'application/json',
|
|
||||||
},
|
|
||||||
body: JSON.stringify({
|
|
||||||
file_path: filePath,
|
|
||||||
target_path: targetPath
|
|
||||||
})
|
|
||||||
});
|
|
||||||
|
|
||||||
const result = await response.json();
|
|
||||||
|
|
||||||
if (!response.ok) {
|
|
||||||
if (result && result.error) {
|
|
||||||
throw new Error(result.error);
|
|
||||||
}
|
|
||||||
throw new Error('Failed to move model');
|
|
||||||
}
|
|
||||||
|
|
||||||
if (result && result.message) {
|
|
||||||
showToast(result.message, 'info');
|
|
||||||
} else {
|
|
||||||
showToast('Model moved successfully', 'success');
|
|
||||||
}
|
|
||||||
|
|
||||||
// Return new file path if move succeeded
|
|
||||||
if (result.success) {
|
|
||||||
return result.new_file_path;
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Move multiple models to target path
|
|
||||||
* @returns {Array<string>} - Array of new file paths that were moved successfully
|
|
||||||
*/
|
|
||||||
async moveBulkModels(filePaths, targetPath) {
|
|
||||||
const movedPaths = filePaths.filter(path => {
|
|
||||||
return path.substring(0, path.lastIndexOf('/')) !== targetPath;
|
|
||||||
});
|
|
||||||
|
|
||||||
if (movedPaths.length === 0) {
|
|
||||||
showToast('All selected models are already in the target folder', 'info');
|
|
||||||
return [];
|
|
||||||
}
|
|
||||||
|
|
||||||
const response = await fetch(this.apiConfig.endpoints.specific.moveBulk, {
|
|
||||||
method: 'POST',
|
|
||||||
headers: {
|
|
||||||
'Content-Type': 'application/json',
|
|
||||||
},
|
|
||||||
body: JSON.stringify({
|
|
||||||
file_paths: movedPaths,
|
|
||||||
target_path: targetPath
|
|
||||||
})
|
|
||||||
});
|
|
||||||
|
|
||||||
const result = await response.json();
|
|
||||||
|
|
||||||
if (!response.ok) {
|
|
||||||
throw new Error('Failed to move models');
|
|
||||||
}
|
|
||||||
|
|
||||||
let successFilePaths = [];
|
|
||||||
if (result.success) {
|
|
||||||
if (result.failure_count > 0) {
|
|
||||||
showToast(`Moved ${result.success_count} models, ${result.failure_count} failed`, 'warning');
|
|
||||||
console.log('Move operation results:', result.results);
|
|
||||||
const failedFiles = result.results
|
|
||||||
.filter(r => !r.success)
|
|
||||||
.map(r => {
|
|
||||||
const fileName = r.path.substring(r.path.lastIndexOf('/') + 1);
|
|
||||||
return `${fileName}: ${r.message}`;
|
|
||||||
});
|
|
||||||
if (failedFiles.length > 0) {
|
|
||||||
const failureMessage = failedFiles.length <= 3
|
|
||||||
? failedFiles.join('\n')
|
|
||||||
: failedFiles.slice(0, 3).join('\n') + `\n(and ${failedFiles.length - 3} more)`;
|
|
||||||
showToast(`Failed moves:\n${failureMessage}`, 'warning', 6000);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
showToast(`Successfully moved ${result.success_count} models`, 'success');
|
|
||||||
}
|
|
||||||
// Collect new file paths for successful moves
|
|
||||||
successFilePaths = result.results
|
|
||||||
.filter(r => r.success)
|
|
||||||
.map(r => r.path);
|
|
||||||
} else {
|
|
||||||
throw new Error(result.message || 'Failed to move models');
|
|
||||||
}
|
|
||||||
return successFilePaths;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Fetch Civitai model versions
|
|
||||||
*/
|
|
||||||
async fetchCivitaiVersions(modelId) {
|
async fetchCivitaiVersions(modelId) {
|
||||||
try {
|
try {
|
||||||
const response = await fetch(`${this.apiConfig.endpoints.civitaiVersions}/${modelId}`);
|
const response = await fetch(`${this.apiConfig.endpoints.civitaiVersions}/${modelId}`);
|
||||||
@@ -699,9 +553,6 @@ class ModelApiClient {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Fetch model roots
|
|
||||||
*/
|
|
||||||
async fetchModelRoots() {
|
async fetchModelRoots() {
|
||||||
try {
|
try {
|
||||||
const response = await fetch(this.apiConfig.endpoints.roots);
|
const response = await fetch(this.apiConfig.endpoints.roots);
|
||||||
@@ -715,9 +566,6 @@ class ModelApiClient {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Fetch model folders
|
|
||||||
*/
|
|
||||||
async fetchModelFolders() {
|
async fetchModelFolders() {
|
||||||
try {
|
try {
|
||||||
const response = await fetch(this.apiConfig.endpoints.folders);
|
const response = await fetch(this.apiConfig.endpoints.folders);
|
||||||
@@ -731,9 +579,6 @@ class ModelApiClient {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Download a model
|
|
||||||
*/
|
|
||||||
async downloadModel(modelId, versionId, modelRoot, relativePath, downloadId) {
|
async downloadModel(modelId, versionId, modelRoot, relativePath, downloadId) {
|
||||||
try {
|
try {
|
||||||
const response = await fetch(DOWNLOAD_ENDPOINTS.download, {
|
const response = await fetch(DOWNLOAD_ENDPOINTS.download, {
|
||||||
@@ -759,13 +604,9 @@ class ModelApiClient {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Build query parameters for API requests
|
|
||||||
*/
|
|
||||||
_buildQueryParams(baseParams, pageState) {
|
_buildQueryParams(baseParams, pageState) {
|
||||||
const params = new URLSearchParams(baseParams);
|
const params = new URLSearchParams(baseParams);
|
||||||
|
|
||||||
// Add common parameters
|
|
||||||
if (pageState.activeFolder !== null) {
|
if (pageState.activeFolder !== null) {
|
||||||
params.append('folder', pageState.activeFolder);
|
params.append('folder', pageState.activeFolder);
|
||||||
}
|
}
|
||||||
@@ -774,12 +615,10 @@ class ModelApiClient {
|
|||||||
params.append('favorites_only', 'true');
|
params.append('favorites_only', 'true');
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add letter filter for supported model types
|
|
||||||
if (this.apiConfig.config.supportsLetterFilter && pageState.activeLetterFilter) {
|
if (this.apiConfig.config.supportsLetterFilter && pageState.activeLetterFilter) {
|
||||||
params.append('first_letter', pageState.activeLetterFilter);
|
params.append('first_letter', pageState.activeLetterFilter);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add search parameters
|
|
||||||
if (pageState.filters?.search) {
|
if (pageState.filters?.search) {
|
||||||
params.append('search', pageState.filters.search);
|
params.append('search', pageState.filters.search);
|
||||||
params.append('fuzzy', 'true');
|
params.append('fuzzy', 'true');
|
||||||
@@ -794,7 +633,6 @@ class ModelApiClient {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add filter parameters
|
|
||||||
if (pageState.filters) {
|
if (pageState.filters) {
|
||||||
if (pageState.filters.tags && pageState.filters.tags.length > 0) {
|
if (pageState.filters.tags && pageState.filters.tags.length > 0) {
|
||||||
pageState.filters.tags.forEach(tag => {
|
pageState.filters.tags.forEach(tag => {
|
||||||
@@ -809,17 +647,12 @@ class ModelApiClient {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add model-specific parameters
|
|
||||||
this._addModelSpecificParams(params, pageState);
|
this._addModelSpecificParams(params, pageState);
|
||||||
|
|
||||||
return params;
|
return params;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Add model-specific parameters to query
|
|
||||||
*/
|
|
||||||
_addModelSpecificParams(params, pageState) {
|
_addModelSpecificParams(params, pageState) {
|
||||||
// Override in specific implementations or handle via configuration
|
|
||||||
if (this.modelType === 'loras') {
|
if (this.modelType === 'loras') {
|
||||||
const filterLoraHash = getSessionItem('recipe_to_lora_filterLoraHash');
|
const filterLoraHash = getSessionItem('recipe_to_lora_filterLoraHash');
|
||||||
const filterLoraHashes = getSessionItem('recipe_to_lora_filterLoraHashes');
|
const filterLoraHashes = getSessionItem('recipe_to_lora_filterLoraHashes');
|
||||||
@@ -837,23 +670,12 @@ class ModelApiClient {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
// Export factory functions and utilities
|
async moveSingleModel(filePath, targetPath) {
|
||||||
export function createModelApiClient(modelType = null) {
|
throw new Error("moveSingleModel must be implemented by subclass");
|
||||||
return new ModelApiClient(modelType);
|
|
||||||
}
|
|
||||||
|
|
||||||
let _singletonClient = null;
|
|
||||||
|
|
||||||
export function getModelApiClient() {
|
|
||||||
if (!_singletonClient) {
|
|
||||||
_singletonClient = new ModelApiClient();
|
|
||||||
}
|
}
|
||||||
_singletonClient.setModelType(state.currentPageType);
|
|
||||||
return _singletonClient;
|
|
||||||
}
|
|
||||||
|
|
||||||
export async function resetAndReload(updateFolders = false) {
|
async moveBulkModels(filePaths, targetPath) {
|
||||||
return getModelApiClient().loadMoreWithVirtualScroll(true, updateFolders);
|
throw new Error("moveBulkModels must be implemented by subclass");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
47
static/js/api/checkpointApi.js
Normal file
47
static/js/api/checkpointApi.js
Normal file
@@ -0,0 +1,47 @@
|
|||||||
|
import { BaseModelApiClient } from './baseModelApi.js';
|
||||||
|
import { showToast } from '../utils/uiHelpers.js';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checkpoint-specific API client
|
||||||
|
*/
|
||||||
|
export class CheckpointApiClient extends BaseModelApiClient {
|
||||||
|
/**
|
||||||
|
* Checkpoints don't support move operations
|
||||||
|
*/
|
||||||
|
async moveSingleModel(filePath, targetPath) {
|
||||||
|
showToast('Moving checkpoints is not supported', 'warning');
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checkpoints don't support bulk move operations
|
||||||
|
*/
|
||||||
|
async moveBulkModels(filePaths, targetPath) {
|
||||||
|
showToast('Moving checkpoints is not supported', 'warning');
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get checkpoint information
|
||||||
|
*/
|
||||||
|
async getCheckpointInfo(filePath) {
|
||||||
|
try {
|
||||||
|
const response = await fetch(this.apiConfig.endpoints.specific.info, {
|
||||||
|
method: 'POST',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json'
|
||||||
|
},
|
||||||
|
body: JSON.stringify({ file_path: filePath })
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!response.ok) {
|
||||||
|
throw new Error('Failed to fetch checkpoint info');
|
||||||
|
}
|
||||||
|
|
||||||
|
return await response.json();
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error fetching checkpoint info:', error);
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
30
static/js/api/embeddingApi.js
Normal file
30
static/js/api/embeddingApi.js
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
import { BaseModelApiClient } from './baseModelApi.js';
|
||||||
|
import { showToast } from '../utils/uiHelpers.js';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Embedding-specific API client
|
||||||
|
*/
|
||||||
|
export class EmbeddingApiClient extends BaseModelApiClient {
|
||||||
|
/**
|
||||||
|
* Move a single embedding to target path
|
||||||
|
*/
|
||||||
|
async moveSingleModel(filePath, targetPath) {
|
||||||
|
if (filePath.substring(0, filePath.lastIndexOf('/')) === targetPath) {
|
||||||
|
showToast('Embedding is already in the selected folder', 'info');
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: Implement embedding move endpoint when available
|
||||||
|
showToast('Moving embeddings is not yet implemented', 'info');
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Move multiple embeddings to target path
|
||||||
|
*/
|
||||||
|
async moveBulkModels(filePaths, targetPath) {
|
||||||
|
// TODO: Implement embedding bulk move endpoint when available
|
||||||
|
showToast('Moving embeddings is not yet implemented', 'info');
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
}
|
||||||
194
static/js/api/loraApi.js
Normal file
194
static/js/api/loraApi.js
Normal file
@@ -0,0 +1,194 @@
|
|||||||
|
import { BaseModelApiClient } from './baseModelApi.js';
|
||||||
|
import { showToast } from '../utils/uiHelpers.js';
|
||||||
|
import { getSessionItem } from '../utils/storageHelpers.js';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* LoRA-specific API client
|
||||||
|
*/
|
||||||
|
export class LoraApiClient extends BaseModelApiClient {
|
||||||
|
/**
|
||||||
|
* Add LoRA-specific parameters to query
|
||||||
|
*/
|
||||||
|
_addModelSpecificParams(params, pageState) {
|
||||||
|
const filterLoraHash = getSessionItem('recipe_to_lora_filterLoraHash');
|
||||||
|
const filterLoraHashes = getSessionItem('recipe_to_lora_filterLoraHashes');
|
||||||
|
|
||||||
|
if (filterLoraHash) {
|
||||||
|
params.append('lora_hash', filterLoraHash);
|
||||||
|
} else if (filterLoraHashes) {
|
||||||
|
try {
|
||||||
|
if (Array.isArray(filterLoraHashes) && filterLoraHashes.length > 0) {
|
||||||
|
params.append('lora_hashes', filterLoraHashes.join(','));
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error parsing lora hashes from session storage:', error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Move a single LoRA to target path
|
||||||
|
*/
|
||||||
|
async moveSingleModel(filePath, targetPath) {
|
||||||
|
if (filePath.substring(0, filePath.lastIndexOf('/')) === targetPath) {
|
||||||
|
showToast('LoRA is already in the selected folder', 'info');
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
const response = await fetch(this.apiConfig.endpoints.specific.moveModel, {
|
||||||
|
method: 'POST',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
},
|
||||||
|
body: JSON.stringify({
|
||||||
|
file_path: filePath,
|
||||||
|
target_path: targetPath
|
||||||
|
})
|
||||||
|
});
|
||||||
|
|
||||||
|
const result = await response.json();
|
||||||
|
|
||||||
|
if (!response.ok) {
|
||||||
|
if (result && result.error) {
|
||||||
|
throw new Error(result.error);
|
||||||
|
}
|
||||||
|
throw new Error('Failed to move LoRA');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (result && result.message) {
|
||||||
|
showToast(result.message, 'info');
|
||||||
|
} else {
|
||||||
|
showToast('LoRA moved successfully', 'success');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (result.success) {
|
||||||
|
return result.new_file_path;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Move multiple LoRAs to target path
|
||||||
|
*/
|
||||||
|
async moveBulkModels(filePaths, targetPath) {
|
||||||
|
const movedPaths = filePaths.filter(path => {
|
||||||
|
return path.substring(0, path.lastIndexOf('/')) !== targetPath;
|
||||||
|
});
|
||||||
|
|
||||||
|
if (movedPaths.length === 0) {
|
||||||
|
showToast('All selected LoRAs are already in the target folder', 'info');
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
const response = await fetch(this.apiConfig.endpoints.specific.moveBulk, {
|
||||||
|
method: 'POST',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
},
|
||||||
|
body: JSON.stringify({
|
||||||
|
file_paths: movedPaths,
|
||||||
|
target_path: targetPath
|
||||||
|
})
|
||||||
|
});
|
||||||
|
|
||||||
|
const result = await response.json();
|
||||||
|
|
||||||
|
if (!response.ok) {
|
||||||
|
throw new Error('Failed to move LoRAs');
|
||||||
|
}
|
||||||
|
|
||||||
|
let successFilePaths = [];
|
||||||
|
if (result.success) {
|
||||||
|
if (result.failure_count > 0) {
|
||||||
|
showToast(`Moved ${result.success_count} LoRAs, ${result.failure_count} failed`, 'warning');
|
||||||
|
console.log('Move operation results:', result.results);
|
||||||
|
const failedFiles = result.results
|
||||||
|
.filter(r => !r.success)
|
||||||
|
.map(r => {
|
||||||
|
const fileName = r.path.substring(r.path.lastIndexOf('/') + 1);
|
||||||
|
return `${fileName}: ${r.message}`;
|
||||||
|
});
|
||||||
|
if (failedFiles.length > 0) {
|
||||||
|
const failureMessage = failedFiles.length <= 3
|
||||||
|
? failedFiles.join('\n')
|
||||||
|
: failedFiles.slice(0, 3).join('\n') + `\n(and ${failedFiles.length - 3} more)`;
|
||||||
|
showToast(`Failed moves:\n${failureMessage}`, 'warning', 6000);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
showToast(`Successfully moved ${result.success_count} LoRAs`, 'success');
|
||||||
|
}
|
||||||
|
successFilePaths = result.results
|
||||||
|
.filter(r => r.success)
|
||||||
|
.map(r => r.path);
|
||||||
|
} else {
|
||||||
|
throw new Error(result.message || 'Failed to move LoRAs');
|
||||||
|
}
|
||||||
|
return successFilePaths;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get LoRA notes
|
||||||
|
*/
|
||||||
|
async getLoraNote(filePath) {
|
||||||
|
try {
|
||||||
|
const response = await fetch(this.apiConfig.endpoints.specific.notes,
|
||||||
|
{
|
||||||
|
method: 'POST',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json'
|
||||||
|
},
|
||||||
|
body: JSON.stringify({ file_path: filePath })
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!response.ok) {
|
||||||
|
throw new Error('Failed to fetch LoRA notes');
|
||||||
|
}
|
||||||
|
|
||||||
|
return await response.json();
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error fetching LoRA notes:', error);
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get LoRA trigger words
|
||||||
|
*/
|
||||||
|
async getLoraTriggerWords(filePath) {
|
||||||
|
try {
|
||||||
|
const response = await fetch(this.apiConfig.endpoints.specific.triggerWords, {
|
||||||
|
method: 'POST',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json'
|
||||||
|
},
|
||||||
|
body: JSON.stringify({ file_path: filePath })
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!response.ok) {
|
||||||
|
throw new Error('Failed to fetch trigger words');
|
||||||
|
}
|
||||||
|
|
||||||
|
return await response.json();
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error fetching trigger words:', error);
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get letter counts for LoRAs
|
||||||
|
*/
|
||||||
|
async getLetterCounts() {
|
||||||
|
try {
|
||||||
|
const response = await fetch(this.apiConfig.endpoints.specific.letterCounts);
|
||||||
|
if (!response.ok) {
|
||||||
|
throw new Error('Failed to fetch letter counts');
|
||||||
|
}
|
||||||
|
return await response.json();
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error fetching letter counts:', error);
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
35
static/js/api/modelApiFactory.js
Normal file
35
static/js/api/modelApiFactory.js
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
import { LoraApiClient } from './loraApi.js';
|
||||||
|
import { CheckpointApiClient } from './checkpointApi.js';
|
||||||
|
import { EmbeddingApiClient } from './embeddingApi.js';
|
||||||
|
import { MODEL_TYPES } from './apiConfig.js';
|
||||||
|
import { state } from '../state/index.js';
|
||||||
|
|
||||||
|
export function createModelApiClient(modelType) {
|
||||||
|
switch (modelType) {
|
||||||
|
case MODEL_TYPES.LORA:
|
||||||
|
return new LoraApiClient();
|
||||||
|
case MODEL_TYPES.CHECKPOINT:
|
||||||
|
return new CheckpointApiClient();
|
||||||
|
case MODEL_TYPES.EMBEDDING:
|
||||||
|
return new EmbeddingApiClient();
|
||||||
|
default:
|
||||||
|
throw new Error(`Unsupported model type: ${modelType}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let _singletonClient = null;
|
||||||
|
|
||||||
|
export function getModelApiClient() {
|
||||||
|
const currentType = state.currentPageType;
|
||||||
|
|
||||||
|
if (!_singletonClient || _singletonClient.modelType !== currentType) {
|
||||||
|
_singletonClient = createModelApiClient(currentType);
|
||||||
|
}
|
||||||
|
|
||||||
|
return _singletonClient;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function resetAndReload(updateFolders = false) {
|
||||||
|
const client = getModelApiClient();
|
||||||
|
return client.loadMoreWithVirtualScroll(true, updateFolders);
|
||||||
|
}
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
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/baseModelApi.js';
|
import { getModelApiClient, resetAndReload } from '../../api/modelApiFactory.js';
|
||||||
import { showToast } from '../../utils/uiHelpers.js';
|
import { showToast } from '../../utils/uiHelpers.js';
|
||||||
import { showDeleteModal, showExcludeModal } from '../../utils/modalUtils.js';
|
import { showDeleteModal, showExcludeModal } from '../../utils/modalUtils.js';
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
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/baseModelApi.js';
|
import { getModelApiClient, resetAndReload } from '../../api/modelApiFactory.js';
|
||||||
import { showToast } from '../../utils/uiHelpers.js';
|
import { showToast } from '../../utils/uiHelpers.js';
|
||||||
import { showDeleteModal, showExcludeModal } from '../../utils/modalUtils.js';
|
import { showDeleteModal, showExcludeModal } from '../../utils/modalUtils.js';
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
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/baseModelApi.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';
|
||||||
|
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
import { showToast } from '../utils/uiHelpers.js';
|
import { showToast } from '../utils/uiHelpers.js';
|
||||||
import { state, getCurrentPageState } from '../state/index.js';
|
import { state, getCurrentPageState } from '../state/index.js';
|
||||||
import { formatDate } from '../utils/formatters.js';
|
import { formatDate } from '../utils/formatters.js';
|
||||||
import { resetAndReload} from '../api/baseModelApi.js';
|
import { resetAndReload} from '../api/modelApiFactory.js';
|
||||||
import { LoadingManager } from '../managers/LoadingManager.js';
|
import { LoadingManager } from '../managers/LoadingManager.js';
|
||||||
|
|
||||||
export class ModelDuplicatesManager {
|
export class ModelDuplicatesManager {
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
// AlphabetBar.js - Component for alphabet filtering
|
// AlphabetBar.js - Component for alphabet filtering
|
||||||
import { getCurrentPageState } from '../../state/index.js';
|
import { getCurrentPageState } from '../../state/index.js';
|
||||||
import { getStorageItem, setStorageItem } from '../../utils/storageHelpers.js';
|
import { getStorageItem, setStorageItem } from '../../utils/storageHelpers.js';
|
||||||
import { resetAndReload } from '../../api/baseModelApi.js';
|
import { resetAndReload } from '../../api/modelApiFactory.js';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* AlphabetBar class - Handles the alphabet filtering UI and interactions
|
* AlphabetBar class - Handles the alphabet filtering UI and interactions
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
// CheckpointsControls.js - Specific implementation for the Checkpoints page
|
// CheckpointsControls.js - Specific implementation for the Checkpoints page
|
||||||
import { PageControls } from './PageControls.js';
|
import { PageControls } from './PageControls.js';
|
||||||
import { getModelApiClient, resetAndReload } from '../../api/baseModelApi.js';
|
import { getModelApiClient, resetAndReload } from '../../api/modelApiFactory.js';
|
||||||
import { showToast } from '../../utils/uiHelpers.js';
|
import { showToast } from '../../utils/uiHelpers.js';
|
||||||
import { downloadManager } from '../../managers/DownloadManager.js';
|
import { downloadManager } from '../../managers/DownloadManager.js';
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
// EmbeddingsControls.js - Specific implementation for the Embeddings page
|
// EmbeddingsControls.js - Specific implementation for the Embeddings page
|
||||||
import { PageControls } from './PageControls.js';
|
import { PageControls } from './PageControls.js';
|
||||||
import { getModelApiClient, resetAndReload } from '../../api/baseModelApi.js';
|
import { getModelApiClient, resetAndReload } from '../../api/modelApiFactory.js';
|
||||||
import { showToast } from '../../utils/uiHelpers.js';
|
import { showToast } from '../../utils/uiHelpers.js';
|
||||||
import { downloadManager } from '../../managers/DownloadManager.js';
|
import { downloadManager } from '../../managers/DownloadManager.js';
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
// LorasControls.js - Specific implementation for the LoRAs page
|
// LorasControls.js - Specific implementation for the LoRAs page
|
||||||
import { PageControls } from './PageControls.js';
|
import { PageControls } from './PageControls.js';
|
||||||
import { getModelApiClient, resetAndReload } from '../../api/baseModelApi.js';
|
import { getModelApiClient, resetAndReload } from '../../api/modelApiFactory.js';
|
||||||
import { getSessionItem, removeSessionItem } from '../../utils/storageHelpers.js';
|
import { getSessionItem, removeSessionItem } from '../../utils/storageHelpers.js';
|
||||||
import { createAlphabetBar } from '../alphabet/index.js';
|
import { createAlphabetBar } from '../alphabet/index.js';
|
||||||
import { downloadManager } from '../../managers/DownloadManager.js';
|
import { downloadManager } from '../../managers/DownloadManager.js';
|
||||||
|
|||||||
@@ -5,7 +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 { getModelApiClient } from '../../api/baseModelApi.js';
|
import { getModelApiClient } from '../../api/modelApiFactory.js';
|
||||||
import { showDeleteModal } from '../../utils/modalUtils.js';
|
import { showDeleteModal } from '../../utils/modalUtils.js';
|
||||||
|
|
||||||
// Add global event delegation handlers
|
// Add global event delegation handlers
|
||||||
|
|||||||
@@ -121,7 +121,7 @@ export function setupModelDescriptionEditing(filePath) {
|
|||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
// Save to backend
|
// Save to backend
|
||||||
const { getModelApiClient } = await import('../../api/baseModelApi.js');
|
const { getModelApiClient } = await import('../../api/modelApiFactory.js');
|
||||||
await getModelApiClient().saveModelMetadata(filePath, { modelDescription: newValue });
|
await getModelApiClient().saveModelMetadata(filePath, { modelDescription: newValue });
|
||||||
showToast('Model description updated', 'success');
|
showToast('Model description updated', 'success');
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
*/
|
*/
|
||||||
import { showToast } from '../../utils/uiHelpers.js';
|
import { showToast } from '../../utils/uiHelpers.js';
|
||||||
import { BASE_MODELS } from '../../utils/constants.js';
|
import { BASE_MODELS } from '../../utils/constants.js';
|
||||||
import { getModelApiClient } from '../../api/baseModelApi.js';
|
import { getModelApiClient } from '../../api/modelApiFactory.js';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set up model name editing functionality
|
* Set up model name editing functionality
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ import {
|
|||||||
setupFileNameEditing
|
setupFileNameEditing
|
||||||
} from './ModelMetadata.js';
|
} from './ModelMetadata.js';
|
||||||
import { setupTagEditMode } from './ModelTags.js';
|
import { setupTagEditMode } from './ModelTags.js';
|
||||||
import { getModelApiClient } from '../../api/baseModelApi.js';
|
import { getModelApiClient } from '../../api/modelApiFactory.js';
|
||||||
import { renderCompactTags, setupTagTooltip, formatFileSize } from './utils.js';
|
import { renderCompactTags, setupTagTooltip, formatFileSize } from './utils.js';
|
||||||
import { renderTriggerWords, setupTriggerWordsEditMode } from './TriggerWords.js';
|
import { renderTriggerWords, setupTriggerWordsEditMode } from './TriggerWords.js';
|
||||||
import { parsePresets, renderPresetTags } from './PresetTags.js';
|
import { parsePresets, renderPresetTags } from './PresetTags.js';
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
* Module for handling model tag editing functionality - 共享版本
|
* Module for handling model tag editing functionality - 共享版本
|
||||||
*/
|
*/
|
||||||
import { showToast } from '../../utils/uiHelpers.js';
|
import { showToast } from '../../utils/uiHelpers.js';
|
||||||
import { getModelApiClient } from '../../api/baseModelApi.js';
|
import { getModelApiClient } from '../../api/modelApiFactory.js';
|
||||||
|
|
||||||
// Preset tag suggestions
|
// Preset tag suggestions
|
||||||
const PRESET_TAGS = [
|
const PRESET_TAGS = [
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
* PresetTags.js
|
* PresetTags.js
|
||||||
* Handles LoRA model preset parameter tags - Shared version
|
* Handles LoRA model preset parameter tags - Shared version
|
||||||
*/
|
*/
|
||||||
import { getModelApiClient } from '../../api/baseModelApi.js';
|
import { getModelApiClient } from '../../api/modelApiFactory.js';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Parse preset parameters
|
* Parse preset parameters
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
* Moved to shared directory for consistency
|
* Moved to shared directory for consistency
|
||||||
*/
|
*/
|
||||||
import { showToast, copyToClipboard } from '../../utils/uiHelpers.js';
|
import { showToast, copyToClipboard } from '../../utils/uiHelpers.js';
|
||||||
import { getModelApiClient } from '../../api/baseModelApi.js';
|
import { getModelApiClient } from '../../api/modelApiFactory.js';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Fetch trained words for a model
|
* Fetch trained words for a model
|
||||||
|
|||||||
@@ -5,7 +5,7 @@
|
|||||||
*/
|
*/
|
||||||
import { showToast, copyToClipboard } from '../../../utils/uiHelpers.js';
|
import { showToast, copyToClipboard } from '../../../utils/uiHelpers.js';
|
||||||
import { state } from '../../../state/index.js';
|
import { state } from '../../../state/index.js';
|
||||||
import { getModelApiClient } from '../../../api/baseModelApi.js';
|
import { getModelApiClient } from '../../../api/modelApiFactory.js';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Try to load local image first, fall back to remote if local fails
|
* Try to load local image first, fall back to remote if local fails
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import { state } from '../state/index.js';
|
|||||||
import { showToast, copyToClipboard, sendLoraToWorkflow } from '../utils/uiHelpers.js';
|
import { showToast, copyToClipboard, sendLoraToWorkflow } from '../utils/uiHelpers.js';
|
||||||
import { updateCardsForBulkMode } from '../components/shared/ModelCard.js';
|
import { updateCardsForBulkMode } from '../components/shared/ModelCard.js';
|
||||||
import { modalManager } from './ModalManager.js';
|
import { modalManager } from './ModalManager.js';
|
||||||
import { getModelApiClient } from '../api/baseModelApi.js';
|
import { getModelApiClient } from '../api/modelApiFactory.js';
|
||||||
|
|
||||||
export class BulkManager {
|
export class BulkManager {
|
||||||
constructor() {
|
constructor() {
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import { modalManager } from './ModalManager.js';
|
import { modalManager } from './ModalManager.js';
|
||||||
import { showToast } from '../utils/uiHelpers.js';
|
import { showToast } from '../utils/uiHelpers.js';
|
||||||
import { LoadingManager } from './LoadingManager.js';
|
import { LoadingManager } from './LoadingManager.js';
|
||||||
import { getModelApiClient, resetAndReload } from '../api/baseModelApi.js';
|
import { getModelApiClient, resetAndReload } from '../api/modelApiFactory.js';
|
||||||
import { getStorageItem, setStorageItem } from '../utils/storageHelpers.js';
|
import { getStorageItem, setStorageItem } from '../utils/storageHelpers.js';
|
||||||
|
|
||||||
export class DownloadManager {
|
export class DownloadManager {
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { getCurrentPageState } from '../state/index.js';
|
import { getCurrentPageState } from '../state/index.js';
|
||||||
import { showToast, updatePanelPositions } from '../utils/uiHelpers.js';
|
import { showToast, updatePanelPositions } from '../utils/uiHelpers.js';
|
||||||
import { getModelApiClient } from '../api/baseModelApi.js';
|
import { getModelApiClient } from '../api/modelApiFactory.js';
|
||||||
import { removeStorageItem, setStorageItem, getStorageItem } from '../utils/storageHelpers.js';
|
import { removeStorageItem, setStorageItem, getStorageItem } from '../utils/storageHelpers.js';
|
||||||
|
|
||||||
export class FilterManager {
|
export class FilterManager {
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import { showToast, updateFolderTags } from '../utils/uiHelpers.js';
|
|||||||
import { state, getCurrentPageState } from '../state/index.js';
|
import { state, getCurrentPageState } from '../state/index.js';
|
||||||
import { modalManager } from './ModalManager.js';
|
import { modalManager } from './ModalManager.js';
|
||||||
import { getStorageItem } from '../utils/storageHelpers.js';
|
import { getStorageItem } from '../utils/storageHelpers.js';
|
||||||
import { getModelApiClient } from '../api/baseModelApi.js';
|
import { getModelApiClient } from '../api/modelApiFactory.js';
|
||||||
|
|
||||||
class MoveManager {
|
class MoveManager {
|
||||||
constructor() {
|
constructor() {
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { updatePanelPositions } from "../utils/uiHelpers.js";
|
import { updatePanelPositions } from "../utils/uiHelpers.js";
|
||||||
import { getCurrentPageState } from "../state/index.js";
|
import { getCurrentPageState } from "../state/index.js";
|
||||||
import { getModelApiClient } from "../api/baseModelApi.js";
|
import { getModelApiClient } from "../api/modelApiFactory.js";
|
||||||
import { setStorageItem, getStorageItem } from "../utils/storageHelpers.js";
|
import { setStorageItem, getStorageItem } from "../utils/storageHelpers.js";
|
||||||
/**
|
/**
|
||||||
* SearchManager - Handles search functionality across different pages
|
* SearchManager - Handles search functionality across different pages
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import { modalManager } from './ModalManager.js';
|
import { modalManager } from './ModalManager.js';
|
||||||
import { showToast } from '../utils/uiHelpers.js';
|
import { showToast } from '../utils/uiHelpers.js';
|
||||||
import { state } from '../state/index.js';
|
import { state } from '../state/index.js';
|
||||||
import { resetAndReload } from '../api/baseModelApi.js';
|
import { resetAndReload } from '../api/modelApiFactory.js';
|
||||||
import { setStorageItem, getStorageItem } from '../utils/storageHelpers.js';
|
import { setStorageItem, getStorageItem } from '../utils/storageHelpers.js';
|
||||||
import { DOWNLOAD_PATH_TEMPLATES, MAPPABLE_BASE_MODELS } from '../utils/constants.js';
|
import { DOWNLOAD_PATH_TEMPLATES, MAPPABLE_BASE_MODELS } from '../utils/constants.js';
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import { state, getCurrentPageState } from '../state/index.js';
|
import { state, getCurrentPageState } from '../state/index.js';
|
||||||
import { VirtualScroller } from './VirtualScroller.js';
|
import { VirtualScroller } from './VirtualScroller.js';
|
||||||
import { createModelCard, setupModelCardEventDelegation } from '../components/shared/ModelCard.js';
|
import { createModelCard, setupModelCardEventDelegation } from '../components/shared/ModelCard.js';
|
||||||
import { getModelApiClient } from '../api/baseModelApi.js';
|
import { getModelApiClient } from '../api/modelApiFactory.js';
|
||||||
import { showToast } from './uiHelpers.js';
|
import { showToast } from './uiHelpers.js';
|
||||||
|
|
||||||
// Function to dynamically import the appropriate card creator based on page type
|
// Function to dynamically import the appropriate card creator based on page type
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { modalManager } from '../managers/ModalManager.js';
|
import { modalManager } from '../managers/ModalManager.js';
|
||||||
import { getModelApiClient } from '../api/baseModelApi.js';
|
import { getModelApiClient } from '../api/modelApiFactory.js';
|
||||||
|
|
||||||
const apiClient = getModelApiClient();
|
const apiClient = getModelApiClient();
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user