mirror of
https://github.com/willmiao/ComfyUI-Lora-Manager.git
synced 2026-03-22 13:42:12 -03:00
Compare commits
6 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
32f42bafaa | ||
|
|
4081b7f022 | ||
|
|
a5808193a6 | ||
|
|
854ca322c1 | ||
|
|
c1d9b5137a | ||
|
|
f33d5745b3 |
@@ -119,10 +119,10 @@ class RecipeMetadataParser(ABC):
|
||||
# Check if exists locally
|
||||
if recipe_scanner and lora_entry['hash']:
|
||||
lora_scanner = recipe_scanner._lora_scanner
|
||||
exists_locally = lora_scanner.has_lora_hash(lora_entry['hash'])
|
||||
exists_locally = lora_scanner.has_hash(lora_entry['hash'])
|
||||
if exists_locally:
|
||||
try:
|
||||
local_path = lora_scanner.get_lora_path_by_hash(lora_entry['hash'])
|
||||
local_path = lora_scanner.get_path_by_hash(lora_entry['hash'])
|
||||
lora_entry['existsLocally'] = True
|
||||
lora_entry['localPath'] = local_path
|
||||
lora_entry['file_name'] = os.path.splitext(os.path.basename(local_path))[0]
|
||||
|
||||
@@ -55,7 +55,7 @@ class RecipeFormatParser(RecipeMetadataParser):
|
||||
# Check if this LoRA exists locally by SHA256 hash
|
||||
if lora.get('hash') and recipe_scanner:
|
||||
lora_scanner = recipe_scanner._lora_scanner
|
||||
exists_locally = lora_scanner.has_lora_hash(lora['hash'])
|
||||
exists_locally = lora_scanner.has_hash(lora['hash'])
|
||||
if exists_locally:
|
||||
lora_cache = await lora_scanner.get_cached_data()
|
||||
lora_item = next((item for item in lora_cache.raw_data if item['sha256'].lower() == lora['hash'].lower()), None)
|
||||
|
||||
@@ -408,7 +408,7 @@ class BaseModelRoutes(ABC):
|
||||
group["models"].append(await self.service.format_response(model))
|
||||
|
||||
# Find the model from the main index too
|
||||
hash_val = self.service.scanner._hash_index.get_hash_by_filename(filename)
|
||||
hash_val = self.service.scanner.get_hash_by_filename(filename)
|
||||
if hash_val:
|
||||
main_path = self.service.get_path_by_hash(hash_val)
|
||||
if main_path and main_path not in paths:
|
||||
|
||||
@@ -167,6 +167,9 @@ class MiscRoutes:
|
||||
|
||||
# Validate and update settings
|
||||
for key, value in data.items():
|
||||
if value == settings.get(key):
|
||||
# No change, skip
|
||||
continue
|
||||
# Special handling for example_images_path - verify path exists
|
||||
if key == 'example_images_path' and value:
|
||||
if not os.path.exists(value):
|
||||
|
||||
@@ -367,7 +367,7 @@ class UpdateRoutes:
|
||||
|
||||
git_info = {
|
||||
'commit_hash': 'unknown',
|
||||
'short_hash': 'unknown',
|
||||
'short_hash': 'stable',
|
||||
'branch': 'unknown',
|
||||
'commit_date': 'unknown'
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
[project]
|
||||
name = "comfyui-lora-manager"
|
||||
description = "Revolutionize your workflow with the ultimate LoRA companion for ComfyUI!"
|
||||
version = "0.8.22"
|
||||
version = "0.8.23"
|
||||
license = {file = "LICENSE"}
|
||||
dependencies = [
|
||||
"aiohttp",
|
||||
|
||||
@@ -424,6 +424,33 @@
|
||||
font-size: 0.85em;
|
||||
}
|
||||
|
||||
/* Style for version name */
|
||||
.version-name {
|
||||
display: inline-block;
|
||||
color: rgba(255,255,255,0.8); /* Muted white */
|
||||
text-shadow: 1px 1px 2px rgba(0, 0, 0, 0.5);
|
||||
font-size: 0.85em;
|
||||
word-break: break-word;
|
||||
overflow: hidden;
|
||||
line-height: 1.4;
|
||||
margin-top: 2px;
|
||||
opacity: 0.8; /* Slightly transparent for better readability */
|
||||
border: 1px solid rgba(255,255,255,0.25); /* Subtle border */
|
||||
border-radius: var(--border-radius-xs);
|
||||
padding: 1px 6px;
|
||||
background: rgba(0,0,0,0.18); /* Optional: subtle background for contrast */
|
||||
}
|
||||
|
||||
/* Medium density adjustments for version name */
|
||||
.medium-density .version-name {
|
||||
font-size: 0.8em;
|
||||
}
|
||||
|
||||
/* Compact density adjustments for version name */
|
||||
.compact-density .version-name {
|
||||
font-size: 0.75em;
|
||||
}
|
||||
|
||||
/* Prevent text selection on cards and interactive elements */
|
||||
.model-card,
|
||||
.model-card *,
|
||||
|
||||
@@ -183,7 +183,11 @@
|
||||
outline: none;
|
||||
}
|
||||
|
||||
.edit-file-name-btn {
|
||||
/* 合并编辑按钮样式 */
|
||||
.edit-model-name-btn,
|
||||
.edit-file-name-btn,
|
||||
.edit-base-model-btn,
|
||||
.edit-model-description-btn {
|
||||
background: transparent;
|
||||
border: none;
|
||||
color: var(--text-color);
|
||||
@@ -195,17 +199,28 @@
|
||||
margin-left: var(--space-1);
|
||||
}
|
||||
|
||||
.edit-model-name-btn.visible,
|
||||
.edit-file-name-btn.visible,
|
||||
.file-name-wrapper:hover .edit-file-name-btn {
|
||||
.edit-base-model-btn.visible,
|
||||
.edit-model-description-btn.visible,
|
||||
.model-name-header:hover .edit-model-name-btn,
|
||||
.file-name-wrapper:hover .edit-file-name-btn,
|
||||
.base-model-display:hover .edit-base-model-btn,
|
||||
.model-name-header:hover .edit-model-description-btn {
|
||||
opacity: 0.5;
|
||||
}
|
||||
|
||||
.edit-file-name-btn:hover {
|
||||
.edit-model-name-btn:hover,
|
||||
.edit-file-name-btn:hover,
|
||||
.edit-base-model-btn:hover,
|
||||
.edit-model-description-btn:hover {
|
||||
opacity: 0.8 !important;
|
||||
background: rgba(0, 0, 0, 0.05);
|
||||
}
|
||||
|
||||
[data-theme="dark"] .edit-file-name-btn:hover {
|
||||
[data-theme="dark"] .edit-model-name-btn:hover,
|
||||
[data-theme="dark"] .edit-file-name-btn:hover,
|
||||
[data-theme="dark"] .edit-base-model-btn:hover {
|
||||
background: rgba(255, 255, 255, 0.05);
|
||||
}
|
||||
|
||||
@@ -234,32 +249,6 @@
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.edit-base-model-btn {
|
||||
background: transparent;
|
||||
border: none;
|
||||
color: var(--text-color);
|
||||
opacity: 0;
|
||||
cursor: pointer;
|
||||
padding: 2px 5px;
|
||||
border-radius: var(--border-radius-xs);
|
||||
transition: all 0.2s ease;
|
||||
margin-left: var(--space-1);
|
||||
}
|
||||
|
||||
.edit-base-model-btn.visible,
|
||||
.base-model-display:hover .edit-base-model-btn {
|
||||
opacity: 0.5;
|
||||
}
|
||||
|
||||
.edit-base-model-btn:hover {
|
||||
opacity: 0.8 !important;
|
||||
background: rgba(0, 0, 0, 0.05);
|
||||
}
|
||||
|
||||
[data-theme="dark"] .edit-base-model-btn:hover {
|
||||
background: rgba(255, 255, 255, 0.05);
|
||||
}
|
||||
|
||||
.base-model-selector {
|
||||
width: 100%;
|
||||
padding: 3px 5px;
|
||||
@@ -316,32 +305,6 @@
|
||||
background: var(--bg-color);
|
||||
}
|
||||
|
||||
.edit-model-name-btn {
|
||||
background: transparent;
|
||||
border: none;
|
||||
color: var(--text-color);
|
||||
opacity: 0;
|
||||
cursor: pointer;
|
||||
padding: 2px 5px;
|
||||
border-radius: var(--border-radius-xs);
|
||||
transition: all 0.2s ease;
|
||||
margin-left: var(--space-1);
|
||||
}
|
||||
|
||||
.edit-model-name-btn.visible,
|
||||
.model-name-header:hover .edit-model-name-btn {
|
||||
opacity: 0.5;
|
||||
}
|
||||
|
||||
.edit-model-name-btn:hover {
|
||||
opacity: 0.8 !important;
|
||||
background: rgba(0, 0, 0, 0.05);
|
||||
}
|
||||
|
||||
[data-theme="dark"] .edit-model-name-btn:hover {
|
||||
background: rgba(255, 255, 255, 0.05);
|
||||
}
|
||||
|
||||
/* Tab System Styling */
|
||||
.showcase-tabs {
|
||||
display: flex;
|
||||
|
||||
@@ -482,6 +482,7 @@ export function createModelCard(model, modelType) {
|
||||
<div class="card-footer">
|
||||
<div class="model-info">
|
||||
<span class="model-name">${model.model_name}</span>
|
||||
${model.civitai?.name ? `<span class="version-name">${model.civitai.name}</span>` : ''}
|
||||
</div>
|
||||
<div class="card-actions">
|
||||
<i class="fas fa-folder-open"
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
import { showToast } from '../../utils/uiHelpers.js';
|
||||
|
||||
/**
|
||||
* ModelDescription.js
|
||||
* Handles model description related functionality - General version
|
||||
@@ -40,4 +42,99 @@ export function setupTabSwitching() {
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Set up model description editing functionality
|
||||
* @param {string} filePath - File path
|
||||
*/
|
||||
export function setupModelDescriptionEditing(filePath) {
|
||||
const descContent = document.querySelector('.model-description-content');
|
||||
const descContainer = document.querySelector('.model-description-container');
|
||||
if (!descContent || !descContainer) return;
|
||||
|
||||
// Add edit button if not present
|
||||
let editBtn = descContainer.querySelector('.edit-model-description-btn');
|
||||
if (!editBtn) {
|
||||
editBtn = document.createElement('button');
|
||||
editBtn.className = 'edit-model-description-btn';
|
||||
editBtn.title = 'Edit model description';
|
||||
editBtn.innerHTML = '<i class="fas fa-pencil-alt"></i>';
|
||||
descContainer.insertBefore(editBtn, descContent);
|
||||
}
|
||||
|
||||
// Show edit button on hover
|
||||
descContainer.addEventListener('mouseenter', () => {
|
||||
editBtn.classList.add('visible');
|
||||
});
|
||||
descContainer.addEventListener('mouseleave', () => {
|
||||
if (!descContainer.classList.contains('editing')) {
|
||||
editBtn.classList.remove('visible');
|
||||
}
|
||||
});
|
||||
|
||||
// Handle edit button click
|
||||
editBtn.addEventListener('click', () => {
|
||||
descContainer.classList.add('editing');
|
||||
descContent.setAttribute('contenteditable', 'true');
|
||||
descContent.dataset.originalValue = descContent.innerHTML.trim();
|
||||
descContent.focus();
|
||||
|
||||
// Place cursor at the end
|
||||
const range = document.createRange();
|
||||
const sel = window.getSelection();
|
||||
range.selectNodeContents(descContent);
|
||||
range.collapse(false);
|
||||
sel.removeAllRanges();
|
||||
sel.addRange(range);
|
||||
|
||||
editBtn.classList.add('visible');
|
||||
});
|
||||
|
||||
// Keyboard events
|
||||
descContent.addEventListener('keydown', function(e) {
|
||||
if (!this.getAttribute('contenteditable')) return;
|
||||
if (e.key === 'Enter' && !e.shiftKey) {
|
||||
e.preventDefault();
|
||||
this.blur();
|
||||
} else if (e.key === 'Escape') {
|
||||
e.preventDefault();
|
||||
this.innerHTML = this.dataset.originalValue;
|
||||
exitEditMode();
|
||||
}
|
||||
});
|
||||
|
||||
// Save on blur
|
||||
descContent.addEventListener('blur', async function() {
|
||||
if (!this.getAttribute('contenteditable')) return;
|
||||
const newValue = this.innerHTML.trim();
|
||||
const originalValue = this.dataset.originalValue;
|
||||
if (newValue === originalValue) {
|
||||
exitEditMode();
|
||||
return;
|
||||
}
|
||||
if (!newValue) {
|
||||
this.innerHTML = originalValue;
|
||||
showToast('Description cannot be empty', 'error');
|
||||
exitEditMode();
|
||||
return;
|
||||
}
|
||||
try {
|
||||
// Save to backend
|
||||
const { getModelApiClient } = await import('../../api/baseModelApi.js');
|
||||
await getModelApiClient().saveModelMetadata(filePath, { modelDescription: newValue });
|
||||
showToast('Model description updated', 'success');
|
||||
} catch (err) {
|
||||
this.innerHTML = originalValue;
|
||||
showToast('Failed to update model description', 'error');
|
||||
} finally {
|
||||
exitEditMode();
|
||||
}
|
||||
});
|
||||
|
||||
function exitEditMode() {
|
||||
descContent.removeAttribute('contenteditable');
|
||||
descContainer.classList.remove('editing');
|
||||
editBtn.classList.remove('visible');
|
||||
}
|
||||
}
|
||||
@@ -6,7 +6,7 @@ import {
|
||||
scrollToTop,
|
||||
loadExampleImages
|
||||
} from './showcase/ShowcaseView.js';
|
||||
import { setupTabSwitching } from './ModelDescription.js';
|
||||
import { setupTabSwitching, setupModelDescriptionEditing } from './ModelDescription.js';
|
||||
import {
|
||||
setupModelNameEditing,
|
||||
setupBaseModelEditing,
|
||||
@@ -33,7 +33,6 @@ export function showModelModal(model, modelType) {
|
||||
model.civitai.trainedWords.map(word => word.replace(/'/g, '\\\'')) : [];
|
||||
|
||||
// Generate model type specific content
|
||||
// const typeSpecificContent = modelType === 'loras' ? renderLoraSpecificContent(model, escapedWords) : '';
|
||||
let typeSpecificContent;
|
||||
if (modelType === 'loras') {
|
||||
typeSpecificContent = renderLoraSpecificContent(model, escapedWords);
|
||||
@@ -211,6 +210,7 @@ export function showModelModal(model, modelType) {
|
||||
setupModelNameEditing(model.file_path);
|
||||
setupBaseModelEditing(model.file_path);
|
||||
setupFileNameEditing(model.file_path);
|
||||
setupModelDescriptionEditing(model.file_path, model.modelDescription || '');
|
||||
setupEventHandlers(model.file_path);
|
||||
|
||||
// LoRA specific setup
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
import { modalManager } from './ModalManager.js';
|
||||
import { showToast } from '../utils/uiHelpers.js';
|
||||
import { LoadingManager } from './LoadingManager.js';
|
||||
import { getStorageItem } from '../utils/storageHelpers.js';
|
||||
import { ImportStepManager } from './import/ImportStepManager.js';
|
||||
import { ImageProcessor } from './import/ImageProcessor.js';
|
||||
import { RecipeDataManager } from './import/RecipeDataManager.js';
|
||||
@@ -86,8 +84,8 @@ export class ImportManager {
|
||||
const uploadError = document.getElementById('uploadError');
|
||||
if (uploadError) uploadError.textContent = '';
|
||||
|
||||
const urlError = document.getElementById('urlError');
|
||||
if (urlError) urlError.textContent = '';
|
||||
const importUrlError = document.getElementById('importUrlError');
|
||||
if (importUrlError) importUrlError.textContent = '';
|
||||
|
||||
const recipeName = document.getElementById('recipeName');
|
||||
if (recipeName) recipeName.value = '';
|
||||
@@ -167,10 +165,10 @@ export class ImportManager {
|
||||
|
||||
// Clear error messages
|
||||
const uploadError = document.getElementById('uploadError');
|
||||
const urlError = document.getElementById('urlError');
|
||||
const importUrlError = document.getElementById('importUrlError');
|
||||
|
||||
if (uploadError) uploadError.textContent = '';
|
||||
if (urlError) urlError.textContent = '';
|
||||
if (importUrlError) importUrlError.textContent = '';
|
||||
}
|
||||
|
||||
handleImageUpload(event) {
|
||||
@@ -224,8 +222,8 @@ export class ImportManager {
|
||||
const uploadError = document.getElementById('uploadError');
|
||||
if (uploadError) uploadError.textContent = '';
|
||||
|
||||
const urlError = document.getElementById('urlError');
|
||||
if (urlError) urlError.textContent = '';
|
||||
const importUrlError = document.getElementById('importUrlError');
|
||||
if (importUrlError) importUrlError.textContent = '';
|
||||
}
|
||||
|
||||
backToDetails() {
|
||||
|
||||
@@ -90,7 +90,7 @@ class MoveManager {
|
||||
).join('');
|
||||
|
||||
// Set default lora root if available
|
||||
const defaultRoot = getStorageItem('settings', {}).default_loras_root;
|
||||
const defaultRoot = getStorageItem('settings', {}).default_lora_root;
|
||||
if (defaultRoot && rootsData.roots.includes(defaultRoot)) {
|
||||
this.loraRootSelect.value = defaultRoot;
|
||||
}
|
||||
|
||||
@@ -15,29 +15,39 @@ export class SettingsManager {
|
||||
|
||||
// Ensure settings are loaded from localStorage
|
||||
this.loadSettingsFromStorage();
|
||||
|
||||
|
||||
// Sync settings to backend if needed
|
||||
this.syncSettingsToBackendIfNeeded();
|
||||
|
||||
this.initialize();
|
||||
}
|
||||
|
||||
loadSettingsFromStorage() {
|
||||
// Get saved settings from localStorage
|
||||
const savedSettings = getStorageItem('settings');
|
||||
|
||||
|
||||
// Migrate legacy default_loras_root to default_lora_root if present
|
||||
if (savedSettings && savedSettings.default_loras_root && !savedSettings.default_lora_root) {
|
||||
savedSettings.default_lora_root = savedSettings.default_loras_root;
|
||||
delete savedSettings.default_loras_root;
|
||||
setStorageItem('settings', savedSettings);
|
||||
}
|
||||
|
||||
// Apply saved settings to state if available
|
||||
if (savedSettings) {
|
||||
state.global.settings = { ...state.global.settings, ...savedSettings };
|
||||
}
|
||||
|
||||
|
||||
// Initialize default values for new settings if they don't exist
|
||||
if (state.global.settings.compactMode === undefined) {
|
||||
state.global.settings.compactMode = false;
|
||||
}
|
||||
|
||||
|
||||
// Set default for optimizeExampleImages if undefined
|
||||
if (state.global.settings.optimizeExampleImages === undefined) {
|
||||
state.global.settings.optimizeExampleImages = true;
|
||||
}
|
||||
|
||||
|
||||
// Set default for cardInfoDisplay if undefined
|
||||
if (state.global.settings.cardInfoDisplay === undefined) {
|
||||
state.global.settings.cardInfoDisplay = 'always';
|
||||
@@ -74,6 +84,50 @@ export class SettingsManager {
|
||||
}
|
||||
}
|
||||
|
||||
async syncSettingsToBackendIfNeeded() {
|
||||
// Get local settings from storage
|
||||
const localSettings = getStorageItem('settings') || {};
|
||||
|
||||
// Fields that need to be synced to backend
|
||||
const fieldsToSync = [
|
||||
'civitai_api_key',
|
||||
'default_lora_root',
|
||||
'default_checkpoint_root',
|
||||
'default_embedding_root',
|
||||
'base_model_path_mappings',
|
||||
'download_path_template'
|
||||
];
|
||||
|
||||
// Build payload for syncing
|
||||
const payload = {};
|
||||
|
||||
fieldsToSync.forEach(key => {
|
||||
if (localSettings[key] !== undefined) {
|
||||
if (key === 'base_model_path_mappings') {
|
||||
payload[key] = JSON.stringify(localSettings[key]);
|
||||
} else {
|
||||
payload[key] = localSettings[key];
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// Only send request if there is something to sync
|
||||
if (Object.keys(payload).length > 0) {
|
||||
try {
|
||||
await fetch('/api/settings', {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify(payload)
|
||||
});
|
||||
// Log success to console
|
||||
console.log('Settings synced to backend');
|
||||
} catch (e) {
|
||||
// Log error to console
|
||||
console.error('Failed to sync settings to backend:', e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
initialize() {
|
||||
if (this.initialized) return;
|
||||
|
||||
@@ -159,8 +213,6 @@ export class SettingsManager {
|
||||
|
||||
// Load default embedding root
|
||||
await this.loadEmbeddingRoots();
|
||||
|
||||
// Backend settings are loaded from the template directly
|
||||
}
|
||||
|
||||
async loadLoraRoots() {
|
||||
@@ -193,7 +245,7 @@ export class SettingsManager {
|
||||
});
|
||||
|
||||
// Set selected value from settings
|
||||
const defaultRoot = state.global.settings.default_loras_root || '';
|
||||
const defaultRoot = state.global.settings.default_lora_root || '';
|
||||
defaultLoraRootSelect.value = defaultRoot;
|
||||
|
||||
} catch (error) {
|
||||
@@ -507,7 +559,7 @@ export class SettingsManager {
|
||||
|
||||
try {
|
||||
// For backend settings, make API call
|
||||
if (['show_only_sfw', 'blur_mature_content', 'autoplay_on_hover', 'optimize_example_images', 'use_centralized_examples'].includes(settingKey)) {
|
||||
if (['show_only_sfw'].includes(settingKey)) {
|
||||
const payload = {};
|
||||
payload[settingKey] = value;
|
||||
|
||||
@@ -552,7 +604,7 @@ export class SettingsManager {
|
||||
|
||||
// Update frontend state
|
||||
if (settingKey === 'default_lora_root') {
|
||||
state.global.settings.default_loras_root = value;
|
||||
state.global.settings.default_lora_root = value;
|
||||
} else if (settingKey === 'default_checkpoint_root') {
|
||||
state.global.settings.default_checkpoint_root = value;
|
||||
} else if (settingKey === 'default_embedding_root') {
|
||||
@@ -632,10 +684,7 @@ export class SettingsManager {
|
||||
// Update state
|
||||
state.global.settings[settingKey] = value;
|
||||
|
||||
// Save to localStorage if appropriate
|
||||
if (!settingKey.includes('api_key')) { // Don't store API keys in localStorage for security
|
||||
setStorageItem('settings', state.global.settings);
|
||||
}
|
||||
setStorageItem('settings', state.global.settings);
|
||||
|
||||
// For backend settings, make API call
|
||||
const payload = {};
|
||||
@@ -717,69 +766,6 @@ export class SettingsManager {
|
||||
}
|
||||
}
|
||||
|
||||
async saveSettings() {
|
||||
// Get frontend settings from UI
|
||||
const blurMatureContent = document.getElementById('blurMatureContent').checked;
|
||||
const showOnlySFW = document.getElementById('showOnlySFW').checked;
|
||||
const defaultLoraRoot = document.getElementById('defaultLoraRoot').value;
|
||||
const defaultCheckpointRoot = document.getElementById('defaultCheckpointRoot').value;
|
||||
const autoplayOnHover = document.getElementById('autoplayOnHover').checked;
|
||||
const optimizeExampleImages = document.getElementById('optimizeExampleImages').checked;
|
||||
|
||||
// Get backend settings
|
||||
const apiKey = document.getElementById('civitaiApiKey').value;
|
||||
|
||||
// Update frontend state and save to localStorage
|
||||
state.global.settings.blurMatureContent = blurMatureContent;
|
||||
state.global.settings.show_only_sfw = showOnlySFW;
|
||||
state.global.settings.default_loras_root = defaultLoraRoot;
|
||||
state.global.settings.default_checkpoint_root = defaultCheckpointRoot;
|
||||
state.global.settings.autoplayOnHover = autoplayOnHover;
|
||||
state.global.settings.optimizeExampleImages = optimizeExampleImages;
|
||||
|
||||
// Save settings to localStorage
|
||||
setStorageItem('settings', state.global.settings);
|
||||
|
||||
try {
|
||||
// Save backend settings via API
|
||||
const response = await fetch('/api/settings', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
body: JSON.stringify({
|
||||
civitai_api_key: apiKey,
|
||||
show_only_sfw: showOnlySFW,
|
||||
optimize_example_images: optimizeExampleImages,
|
||||
default_checkpoint_root: defaultCheckpointRoot
|
||||
})
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error('Failed to save settings');
|
||||
}
|
||||
|
||||
showToast('Settings saved successfully', 'success');
|
||||
modalManager.closeModal('settingsModal');
|
||||
|
||||
// Apply frontend settings immediately
|
||||
this.applyFrontendSettings();
|
||||
|
||||
if (this.currentPage === 'loras') {
|
||||
// Reload the loras without updating folders
|
||||
await resetAndReload(false);
|
||||
} else if (this.currentPage === 'recipes') {
|
||||
// Reload the recipes without updating folders
|
||||
await window.recipeManager.loadRecipes();
|
||||
} else if (this.currentPage === 'checkpoints') {
|
||||
// Reload the checkpoints without updating folders
|
||||
await window.checkpointsManager.loadCheckpoints();
|
||||
}
|
||||
} catch (error) {
|
||||
showToast('Failed to save settings: ' + error.message, 'error');
|
||||
}
|
||||
}
|
||||
|
||||
applyFrontendSettings() {
|
||||
// Apply blur setting to existing content
|
||||
const blurSetting = state.global.settings.blurMatureContent;
|
||||
|
||||
@@ -112,7 +112,7 @@ export class FolderBrowser {
|
||||
).join('');
|
||||
|
||||
// Set default lora root if available
|
||||
const defaultRoot = getStorageItem('settings', {}).default_loras_root;
|
||||
const defaultRoot = getStorageItem('settings', {}).default_lora_root;
|
||||
if (defaultRoot && rootsData.roots.includes(defaultRoot)) {
|
||||
loraRoot.value = defaultRoot;
|
||||
}
|
||||
|
||||
@@ -27,7 +27,7 @@ export class ImageProcessor {
|
||||
|
||||
async handleUrlInput() {
|
||||
const urlInput = document.getElementById('imageUrlInput');
|
||||
const errorElement = document.getElementById('urlError');
|
||||
const errorElement = document.getElementById('importUrlError');
|
||||
const input = urlInput.value.trim();
|
||||
|
||||
// Validate input
|
||||
|
||||
@@ -25,7 +25,7 @@
|
||||
<i class="fas fa-download"></i> Fetch Image
|
||||
</button>
|
||||
</div>
|
||||
<div class="error-message" id="urlError"></div>
|
||||
<div class="error-message" id="importUrlError"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
Reference in New Issue
Block a user