diff --git a/locales/de.json b/locales/de.json index 402fceba..1b7386a0 100644 --- a/locales/de.json +++ b/locales/de.json @@ -241,6 +241,10 @@ } }, "folderSettings": { + "activeLibrary": "Active Library", + "activeLibraryHelp": "Switch between configured libraries to update default folders. Changing the selection reloads the page.", + "loadingLibraries": "Loading libraries...", + "noLibraries": "No libraries configured", "defaultLoraRoot": "Standard-LoRA-Stammordner", "defaultLoraRootHelp": "Legen Sie den Standard-LoRA-Stammordner für Downloads, Importe und Verschiebungen fest", "defaultCheckpointRoot": "Standard-Checkpoint-Stammordner", @@ -1125,6 +1129,8 @@ "compactModeToggled": "Kompakt-Modus {state}", "settingSaveFailed": "Fehler beim Speichern der Einstellung: {message}", "displayDensitySet": "Anzeige-Dichte auf {density} gesetzt", + "libraryLoadFailed": "Failed to load libraries: {message}", + "libraryActivateFailed": "Failed to activate library: {message}", "languageChangeFailed": "Fehler beim Ändern der Sprache: {message}", "cacheCleared": "Cache-Dateien wurden erfolgreich gelöscht. Cache wird bei der nächsten Aktion neu aufgebaut.", "cacheClearFailed": "Fehler beim Löschen des Caches: {error}", diff --git a/locales/en.json b/locales/en.json index 278aab0d..828ac4f8 100644 --- a/locales/en.json +++ b/locales/en.json @@ -241,6 +241,10 @@ } }, "folderSettings": { + "activeLibrary": "Active Library", + "activeLibraryHelp": "Switch between configured libraries to update default folders. Changing the selection reloads the page.", + "loadingLibraries": "Loading libraries...", + "noLibraries": "No libraries configured", "defaultLoraRoot": "Default LoRA Root", "defaultLoraRootHelp": "Set the default LoRA root directory for downloads, imports and moves", "defaultCheckpointRoot": "Default Checkpoint Root", @@ -1125,6 +1129,8 @@ "compactModeToggled": "Compact Mode {state}", "settingSaveFailed": "Failed to save setting: {message}", "displayDensitySet": "Display Density set to {density}", + "libraryLoadFailed": "Failed to load libraries: {message}", + "libraryActivateFailed": "Failed to activate library: {message}", "languageChangeFailed": "Failed to change language: {message}", "cacheCleared": "Cache files have been cleared successfully. Cache will rebuild on next action.", "cacheClearFailed": "Failed to clear cache: {error}", diff --git a/locales/es.json b/locales/es.json index e4ab8994..80d1be69 100644 --- a/locales/es.json +++ b/locales/es.json @@ -241,6 +241,10 @@ } }, "folderSettings": { + "activeLibrary": "Active Library", + "activeLibraryHelp": "Switch between configured libraries to update default folders. Changing the selection reloads the page.", + "loadingLibraries": "Loading libraries...", + "noLibraries": "No libraries configured", "defaultLoraRoot": "Raíz predeterminada de LoRA", "defaultLoraRootHelp": "Establecer el directorio raíz predeterminado de LoRA para descargas, importaciones y movimientos", "defaultCheckpointRoot": "Raíz predeterminada de checkpoint", @@ -1125,6 +1129,8 @@ "compactModeToggled": "Modo compacto {state}", "settingSaveFailed": "Error al guardar configuración: {message}", "displayDensitySet": "Densidad de visualización establecida a {density}", + "libraryLoadFailed": "Failed to load libraries: {message}", + "libraryActivateFailed": "Failed to activate library: {message}", "languageChangeFailed": "Error al cambiar idioma: {message}", "cacheCleared": "Archivos de caché limpiados exitosamente. La caché se reconstruirá en la próxima acción.", "cacheClearFailed": "Error al limpiar caché: {error}", diff --git a/locales/fr.json b/locales/fr.json index ec3f3a4e..4049b1fa 100644 --- a/locales/fr.json +++ b/locales/fr.json @@ -241,6 +241,10 @@ } }, "folderSettings": { + "activeLibrary": "Active Library", + "activeLibraryHelp": "Switch between configured libraries to update default folders. Changing the selection reloads the page.", + "loadingLibraries": "Loading libraries...", + "noLibraries": "No libraries configured", "defaultLoraRoot": "Racine LoRA par défaut", "defaultLoraRootHelp": "Définir le répertoire racine LoRA par défaut pour les téléchargements, imports et déplacements", "defaultCheckpointRoot": "Racine Checkpoint par défaut", @@ -1125,6 +1129,8 @@ "compactModeToggled": "Mode compact {state}", "settingSaveFailed": "Échec de la sauvegarde du paramètre : {message}", "displayDensitySet": "Densité d'affichage définie sur {density}", + "libraryLoadFailed": "Failed to load libraries: {message}", + "libraryActivateFailed": "Failed to activate library: {message}", "languageChangeFailed": "Échec du changement de langue : {message}", "cacheCleared": "Les fichiers de cache ont été vidés avec succès. Le cache sera reconstruit à la prochaine action.", "cacheClearFailed": "Échec du vidage du cache : {error}", diff --git a/locales/he.json b/locales/he.json index 8bfb805f..814d6f10 100644 --- a/locales/he.json +++ b/locales/he.json @@ -241,6 +241,10 @@ } }, "folderSettings": { + "activeLibrary": "Active Library", + "activeLibraryHelp": "Switch between configured libraries to update default folders. Changing the selection reloads the page.", + "loadingLibraries": "Loading libraries...", + "noLibraries": "No libraries configured", "defaultLoraRoot": "תיקיית שורש ברירת מחדל של LoRA", "defaultLoraRootHelp": "הגדר את ספריית השורש המוגדרת כברירת מחדל של LoRA להורדות, ייבוא והעברות", "defaultCheckpointRoot": "תיקיית שורש ברירת מחדל של Checkpoint", @@ -1125,6 +1129,8 @@ "compactModeToggled": "מצב קומפקטי {state}", "settingSaveFailed": "שמירת ההגדרה נכשלה: {message}", "displayDensitySet": "צפיפות התצוגה הוגדרה ל-{density}", + "libraryLoadFailed": "Failed to load libraries: {message}", + "libraryActivateFailed": "Failed to activate library: {message}", "languageChangeFailed": "שינוי השפה נכשל: {message}", "cacheCleared": "קבצי המטמון נוקו בהצלחה. המטמון ייבנה מחדש בפעולה הבאה.", "cacheClearFailed": "ניקוי המטמון נכשל: {error}", diff --git a/locales/ja.json b/locales/ja.json index 4d0a3577..832664fb 100644 --- a/locales/ja.json +++ b/locales/ja.json @@ -241,6 +241,10 @@ } }, "folderSettings": { + "activeLibrary": "Active Library", + "activeLibraryHelp": "Switch between configured libraries to update default folders. Changing the selection reloads the page.", + "loadingLibraries": "Loading libraries...", + "noLibraries": "No libraries configured", "defaultLoraRoot": "デフォルトLoRAルート", "defaultLoraRootHelp": "ダウンロード、インポート、移動用のデフォルトLoRAルートディレクトリを設定", "defaultCheckpointRoot": "デフォルトCheckpointルート", @@ -1125,6 +1129,8 @@ "compactModeToggled": "コンパクトモード {state}", "settingSaveFailed": "設定の保存に失敗しました:{message}", "displayDensitySet": "表示密度が {density} に設定されました", + "libraryLoadFailed": "Failed to load libraries: {message}", + "libraryActivateFailed": "Failed to activate library: {message}", "languageChangeFailed": "言語の変更に失敗しました:{message}", "cacheCleared": "キャッシュファイルが正常にクリアされました。次回のアクションでキャッシュが再構築されます。", "cacheClearFailed": "キャッシュのクリアに失敗しました:{error}", diff --git a/locales/ko.json b/locales/ko.json index 882d1d4c..da128cc2 100644 --- a/locales/ko.json +++ b/locales/ko.json @@ -241,6 +241,10 @@ } }, "folderSettings": { + "activeLibrary": "Active Library", + "activeLibraryHelp": "Switch between configured libraries to update default folders. Changing the selection reloads the page.", + "loadingLibraries": "Loading libraries...", + "noLibraries": "No libraries configured", "defaultLoraRoot": "기본 LoRA 루트", "defaultLoraRootHelp": "다운로드, 가져오기 및 이동을 위한 기본 LoRA 루트 디렉토리를 설정합니다", "defaultCheckpointRoot": "기본 Checkpoint 루트", @@ -1125,6 +1129,8 @@ "compactModeToggled": "컴팩트 모드 {state}", "settingSaveFailed": "설정 저장 실패: {message}", "displayDensitySet": "표시 밀도가 {density}로 설정되었습니다", + "libraryLoadFailed": "Failed to load libraries: {message}", + "libraryActivateFailed": "Failed to activate library: {message}", "languageChangeFailed": "언어 변경 실패: {message}", "cacheCleared": "캐시 파일이 성공적으로 지워졌습니다. 다음 작업 시 캐시가 재구축됩니다.", "cacheClearFailed": "캐시 지우기 실패: {error}", diff --git a/locales/ru.json b/locales/ru.json index c062220e..9f11fe94 100644 --- a/locales/ru.json +++ b/locales/ru.json @@ -241,6 +241,10 @@ } }, "folderSettings": { + "activeLibrary": "Active Library", + "activeLibraryHelp": "Switch between configured libraries to update default folders. Changing the selection reloads the page.", + "loadingLibraries": "Loading libraries...", + "noLibraries": "No libraries configured", "defaultLoraRoot": "Корневая папка LoRA по умолчанию", "defaultLoraRootHelp": "Установить корневую папку LoRA по умолчанию для загрузок, импорта и перемещений", "defaultCheckpointRoot": "Корневая папка Checkpoint по умолчанию", @@ -1125,6 +1129,8 @@ "compactModeToggled": "Компактный режим {state}", "settingSaveFailed": "Не удалось сохранить настройку: {message}", "displayDensitySet": "Плотность отображения установлена на {density}", + "libraryLoadFailed": "Failed to load libraries: {message}", + "libraryActivateFailed": "Failed to activate library: {message}", "languageChangeFailed": "Не удалось изменить язык: {message}", "cacheCleared": "Файлы кэша успешно очищены. Кэш будет пересобран при следующем действии.", "cacheClearFailed": "Не удалось очистить кэш: {error}", diff --git a/locales/zh-CN.json b/locales/zh-CN.json index 9a653b54..aad48214 100644 --- a/locales/zh-CN.json +++ b/locales/zh-CN.json @@ -247,6 +247,10 @@ } }, "folderSettings": { + "activeLibrary": "Active Library", + "activeLibraryHelp": "Switch between configured libraries to update default folders. Changing the selection reloads the page.", + "loadingLibraries": "Loading libraries...", + "noLibraries": "No libraries configured", "defaultLoraRoot": "默认 LoRA 根目录", "defaultLoraRootHelp": "设置下载、导入和移动时的默认 LoRA 根目录", "defaultCheckpointRoot": "默认 Checkpoint 根目录", @@ -1131,6 +1135,8 @@ "compactModeToggled": "紧凑模式 {state}", "settingSaveFailed": "保存设置失败:{message}", "displayDensitySet": "显示密度已设置为 {density}", + "libraryLoadFailed": "Failed to load libraries: {message}", + "libraryActivateFailed": "Failed to activate library: {message}", "languageChangeFailed": "切换语言失败:{message}", "cacheCleared": "缓存文件已成功清除。下次操作将重建缓存。", "cacheClearFailed": "清除缓存失败:{error}", diff --git a/locales/zh-TW.json b/locales/zh-TW.json index e9692aa4..f2b8f468 100644 --- a/locales/zh-TW.json +++ b/locales/zh-TW.json @@ -241,6 +241,10 @@ } }, "folderSettings": { + "activeLibrary": "Active Library", + "activeLibraryHelp": "Switch between configured libraries to update default folders. Changing the selection reloads the page.", + "loadingLibraries": "Loading libraries...", + "noLibraries": "No libraries configured", "defaultLoraRoot": "預設 LoRA 根目錄", "defaultLoraRootHelp": "設定下載、匯入和移動時的預設 LoRA 根目錄", "defaultCheckpointRoot": "預設 Checkpoint 根目錄", @@ -1125,6 +1129,8 @@ "compactModeToggled": "緊湊模式已{state}", "settingSaveFailed": "儲存設定失敗:{message}", "displayDensitySet": "顯示密度已設為 {density}", + "libraryLoadFailed": "Failed to load libraries: {message}", + "libraryActivateFailed": "Failed to activate library: {message}", "languageChangeFailed": "切換語言失敗:{message}", "cacheCleared": "快取檔案已成功清除。快取將於下次操作時重建。", "cacheClearFailed": "清除快取失敗:{error}", diff --git a/static/js/managers/SettingsManager.js b/static/js/managers/SettingsManager.js index 99e64f0a..34bad67d 100644 --- a/static/js/managers/SettingsManager.js +++ b/static/js/managers/SettingsManager.js @@ -12,6 +12,8 @@ export class SettingsManager { this.initialized = false; this.isOpen = false; this.initializationPromise = null; + this.availableLibraries = {}; + this.activeLibrary = ''; // Add initialization to sync with modal state this.currentPage = document.body.dataset.page || 'loras'; @@ -301,6 +303,9 @@ export class SettingsManager { // Load base model path mappings this.loadBaseModelMappings(); + // Load library options + await this.loadLibraries(); + // Load default lora root await this.loadLoraRoots(); @@ -372,6 +377,147 @@ export class SettingsManager { } } + async loadLibraries() { + const librarySelect = document.getElementById('librarySelect'); + if (!librarySelect) { + return; + } + + const setPlaceholderOption = (textKey, fallback) => { + librarySelect.innerHTML = ''; + const option = document.createElement('option'); + option.value = ''; + option.textContent = translate(textKey, {}, fallback); + librarySelect.appendChild(option); + }; + + setPlaceholderOption('settings.folderSettings.loadingLibraries', 'Loading libraries...'); + librarySelect.disabled = true; + + try { + const response = await fetch('/api/lm/settings/libraries'); + if (!response.ok) { + throw new Error('Failed to fetch library registry'); + } + + const data = await response.json(); + if (data.success === false) { + throw new Error(data.error || 'Failed to fetch library registry'); + } + + const libraries = data.libraries && typeof data.libraries === 'object' + ? data.libraries + : {}; + + this.availableLibraries = libraries; + + const entries = Object.entries(libraries); + if (entries.length === 0) { + this.activeLibrary = ''; + setPlaceholderOption('settings.folderSettings.noLibraries', 'No libraries configured'); + return; + } + + const activeName = data.active_library && libraries[data.active_library] + ? data.active_library + : entries[0][0]; + + this.activeLibrary = activeName; + + librarySelect.innerHTML = ''; + const fragment = document.createDocumentFragment(); + entries + .sort((a, b) => { + const nameA = this.getLibraryDisplayName(a[0], a[1]).toLowerCase(); + const nameB = this.getLibraryDisplayName(b[0], b[1]).toLowerCase(); + return nameA.localeCompare(nameB); + }) + .forEach(([name, info]) => { + const option = document.createElement('option'); + option.value = name; + option.textContent = this.getLibraryDisplayName(name, info); + fragment.appendChild(option); + }); + + librarySelect.appendChild(fragment); + librarySelect.value = activeName; + librarySelect.disabled = entries.length <= 1; + } catch (error) { + console.error('Error loading libraries:', error); + setPlaceholderOption('settings.folderSettings.noLibraries', 'No libraries configured'); + this.availableLibraries = {}; + this.activeLibrary = ''; + librarySelect.disabled = true; + showToast('toast.settings.libraryLoadFailed', { message: error.message }, 'error'); + } + } + + getLibraryDisplayName(libraryName, libraryData = {}) { + if (libraryData && typeof libraryData === 'object') { + const metadata = libraryData.metadata; + if (metadata && typeof metadata === 'object' && metadata.display_name) { + return metadata.display_name; + } + + if (libraryData.display_name) { + return libraryData.display_name; + } + } + + return libraryName; + } + + async handleLibraryChange() { + const librarySelect = document.getElementById('librarySelect'); + if (!librarySelect) { + return; + } + + const selectedLibrary = librarySelect.value; + if (!selectedLibrary || selectedLibrary === this.activeLibrary) { + librarySelect.value = this.activeLibrary; + return; + } + + librarySelect.disabled = true; + + try { + await this.activateLibrary(selectedLibrary); + window.location.reload(); + } catch (error) { + console.error('Failed to activate library:', error); + showToast('toast.settings.libraryActivateFailed', { message: error.message }, 'error'); + await this.loadLibraries(); + } finally { + if (!document.hidden) { + librarySelect.disabled = librarySelect.options.length <= 1; + } + } + } + + async activateLibrary(libraryName) { + const response = await fetch('/api/lm/settings/libraries/activate', { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + }, + body: JSON.stringify({ library: libraryName }), + }); + + if (!response.ok) { + throw new Error(`Request failed with status ${response.status}`); + } + + const data = await response.json(); + if (data.success === false) { + throw new Error(data.error || 'Failed to activate library'); + } + + const activeName = data.active_library || libraryName; + this.activeLibrary = activeName; + return data; + } + async loadLoraRoots() { try { const defaultLoraRootSelect = document.getElementById('defaultLoraRoot'); diff --git a/templates/components/modals/settings_modal.html b/templates/components/modals/settings_modal.html index e036dc56..9282fb57 100644 --- a/templates/components/modals/settings_modal.html +++ b/templates/components/modals/settings_modal.html @@ -181,7 +181,23 @@