From 9f15c1fc06b25260103f8802a1d72758e8da3551 Mon Sep 17 00:00:00 2001 From: Will Miao Date: Thu, 26 Feb 2026 19:31:10 +0800 Subject: [PATCH] feat: Add Extra Folder Paths feature with improved layout - Add Extra Folder Paths section in Library settings for configuring additional model folders (LoRA, Checkpoint, Diffusion Model, Embedding) - Implement dynamic path input rows with add/remove functionality - Add dedicated CSS styles with flex-based layout for better UX - Add translations for 10 languages (DE, EN, ES, FR, HE, JA, KO, RU, ZH-CN, ZH-TW) - Integrate settings loading and saving via SettingsManager Closes layout issues with single-input path rows --- locales/de.json | 20 ++- locales/en.json | 18 +++ locales/es.json | 20 ++- locales/fr.json | 20 ++- locales/he.json | 20 ++- locales/ja.json | 20 ++- locales/ko.json | 20 ++- locales/ru.json | 20 ++- locales/zh-CN.json | 20 ++- locales/zh-TW.json | 20 ++- .../css/components/modal/settings-modal.css | 58 +++++++++ static/js/managers/SettingsManager.js | 116 ++++++++++++++++++ .../components/modals/settings_modal.html | 85 ++++++++++++- 13 files changed, 447 insertions(+), 10 deletions(-) diff --git a/locales/de.json b/locales/de.json index 26f47669..f14d73e3 100644 --- a/locales/de.json +++ b/locales/de.json @@ -260,6 +260,7 @@ "layoutSettings": "Layout-Einstellungen", "misc": "Verschiedenes", "folderSettings": "Standard-Roots", + "extraFolderPaths": "Zusätzliche Ordnerpfade", "downloadPathTemplates": "Download-Pfad-Vorlagen", "priorityTags": "Prioritäts-Tags", "updateFlags": "Update-Markierungen", @@ -484,6 +485,23 @@ "proxyPassword": "Passwort (optional)", "proxyPasswordPlaceholder": "passwort", "proxyPasswordHelp": "Passwort für die Proxy-Authentifizierung (falls erforderlich)" + }, + "extraFolderPaths": { + "title": "Zusätzliche Ordnerpfade", + "help": "Fügen Sie zusätzliche Modellordner außerhalb der Standardpfade von ComfyUI hinzu. Diese Pfade werden separat gespeichert und zusammen mit den Standardordnern gescannt.", + "description": "Konfigurieren Sie zusätzliche Ordner zum Scannen von Modellen. Diese Pfade sind spezifisch für LoRA Manager und werden mit den Standardpfaden von ComfyUI zusammengeführt.", + "modelTypes": { + "lora": "LoRA-Pfade", + "checkpoint": "Checkpoint-Pfade", + "unet": "Diffusionsmodell-Pfade", + "embedding": "Embedding-Pfade" + }, + "pathPlaceholder": "/pfad/zu/extra/modellen", + "saveSuccess": "Zusätzliche Ordnerpfade aktualisiert.", + "saveError": "Fehler beim Aktualisieren der zusätzlichen Ordnerpfade: {message}", + "validation": { + "duplicatePath": "Dieser Pfad ist bereits konfiguriert" + } } }, "loras": { @@ -1633,4 +1651,4 @@ "retry": "Wiederholen" } } -} +} \ No newline at end of file diff --git a/locales/en.json b/locales/en.json index 91d88a7b..9d9e824c 100644 --- a/locales/en.json +++ b/locales/en.json @@ -260,6 +260,7 @@ "layoutSettings": "Layout Settings", "misc": "Miscellaneous", "folderSettings": "Default Roots", + "extraFolderPaths": "Extra Folder Paths", "downloadPathTemplates": "Download Path Templates", "priorityTags": "Priority Tags", "updateFlags": "Update Flags", @@ -360,6 +361,23 @@ "defaultEmbeddingRootHelp": "Set default embedding root directory for downloads, imports and moves", "noDefault": "No Default" }, + "extraFolderPaths": { + "title": "Extra Folder Paths", + "help": "Add additional model folders outside of ComfyUI's standard paths. These paths are stored separately and scanned alongside the default folders.", + "description": "Configure additional folders to scan for models. These paths are specific to LoRA Manager and will be merged with ComfyUI's default paths.", + "modelTypes": { + "lora": "LoRA Paths", + "checkpoint": "Checkpoint Paths", + "unet": "Diffusion Model Paths", + "embedding": "Embedding Paths" + }, + "pathPlaceholder": "/path/to/extra/models", + "saveSuccess": "Extra folder paths updated.", + "saveError": "Failed to update extra folder paths: {message}", + "validation": { + "duplicatePath": "This path is already configured" + } + }, "priorityTags": { "title": "Priority Tags", "description": "Customize the tag priority order for each model type (e.g., character, concept, style(toon|toon_style))", diff --git a/locales/es.json b/locales/es.json index c2548008..a7e96ff5 100644 --- a/locales/es.json +++ b/locales/es.json @@ -260,6 +260,7 @@ "layoutSettings": "Configuración de diseño", "misc": "Varios", "folderSettings": "Raíces predeterminadas", + "extraFolderPaths": "Rutas de carpetas adicionales", "downloadPathTemplates": "Plantillas de rutas de descarga", "priorityTags": "Etiquetas prioritarias", "updateFlags": "Indicadores de actualización", @@ -484,6 +485,23 @@ "proxyPassword": "Contraseña (opcional)", "proxyPasswordPlaceholder": "contraseña", "proxyPasswordHelp": "Contraseña para autenticación de proxy (si es necesario)" + }, + "extraFolderPaths": { + "title": "Rutas de carpetas adicionales", + "help": "Agregue carpetas de modelos adicionales fuera de las rutas estándar de ComfyUI. Estas rutas se almacenan por separado y se escanean junto con las carpetas predeterminadas.", + "description": "Configure carpetas adicionales para escanear modelos. Estas rutas son específicas de LoRA Manager y se fusionarán con las rutas predeterminadas de ComfyUI.", + "modelTypes": { + "lora": "Rutas de LoRA", + "checkpoint": "Rutas de Checkpoint", + "unet": "Rutas de modelo de difusión", + "embedding": "Rutas de Embedding" + }, + "pathPlaceholder": "/ruta/a/modelos/extra", + "saveSuccess": "Rutas de carpetas adicionales actualizadas.", + "saveError": "Error al actualizar las rutas de carpetas adicionales: {message}", + "validation": { + "duplicatePath": "Esta ruta ya está configurada" + } } }, "loras": { @@ -1633,4 +1651,4 @@ "retry": "Reintentar" } } -} +} \ No newline at end of file diff --git a/locales/fr.json b/locales/fr.json index 27cf1151..31ec9e15 100644 --- a/locales/fr.json +++ b/locales/fr.json @@ -260,6 +260,7 @@ "layoutSettings": "Paramètres d'affichage", "misc": "Divers", "folderSettings": "Racines par défaut", + "extraFolderPaths": "Chemins de dossiers supplémentaires", "downloadPathTemplates": "Modèles de chemin de téléchargement", "priorityTags": "Étiquettes prioritaires", "updateFlags": "Indicateurs de mise à jour", @@ -484,6 +485,23 @@ "proxyPassword": "Mot de passe (optionnel)", "proxyPasswordPlaceholder": "mot_de_passe", "proxyPasswordHelp": "Mot de passe pour l'authentification proxy (si nécessaire)" + }, + "extraFolderPaths": { + "title": "Chemins de dossiers supplémentaires", + "help": "Ajoutez des dossiers de modèles supplémentaires en dehors des chemins standard de ComfyUI. Ces chemins sont stockés séparément et analysés aux côtés des dossiers par défaut.", + "description": "Configurez des dossiers supplémentaires pour l'analyse de modèles. Ces chemins sont spécifiques à LoRA Manager et seront fusionnés avec les chemins par défaut de ComfyUI.", + "modelTypes": { + "lora": "Chemins LoRA", + "checkpoint": "Chemins Checkpoint", + "unet": "Chemins de modèle de diffusion", + "embedding": "Chemins Embedding" + }, + "pathPlaceholder": "/chemin/vers/modèles/supplémentaires", + "saveSuccess": "Chemins de dossiers supplémentaires mis à jour.", + "saveError": "Échec de la mise à jour des chemins de dossiers supplémentaires: {message}", + "validation": { + "duplicatePath": "Ce chemin est déjà configuré" + } } }, "loras": { @@ -1633,4 +1651,4 @@ "retry": "Réessayer" } } -} +} \ No newline at end of file diff --git a/locales/he.json b/locales/he.json index 6cbd707a..75d39a28 100644 --- a/locales/he.json +++ b/locales/he.json @@ -260,6 +260,7 @@ "layoutSettings": "הגדרות פריסה", "misc": "שונות", "folderSettings": "תיקיות ברירת מחדל", + "extraFolderPaths": "נתיבי תיקיות נוספים", "downloadPathTemplates": "תבניות נתיב הורדה", "priorityTags": "תגיות עדיפות", "updateFlags": "תגי עדכון", @@ -484,6 +485,23 @@ "proxyPassword": "סיסמה (אופציונלי)", "proxyPasswordPlaceholder": "password", "proxyPasswordHelp": "סיסמה לאימות מול הפרוקסי (אם נדרש)" + }, + "extraFolderPaths": { + "title": "נתיבי תיקיות נוספים", + "help": "הוסף תיקיות מודלים נוספות מחוץ לנתיבים הסטנדרטיים של ComfyUI. נתיבים אלה נשמרים בנפרד ונסרקים לצד תיקיות ברירת המחדל.", + "description": "הגדר תיקיות נוספות לסריקת מודלים. נתיבים אלה ספציפיים ל-LoRA Manager וימוזגו עם נתיבי ברירת המחדל של ComfyUI.", + "modelTypes": { + "lora": "נתיבי LoRA", + "checkpoint": "נתיבי Checkpoint", + "unet": "נתיבי מודל דיפוזיה", + "embedding": "נתיבי Embedding" + }, + "pathPlaceholder": "/נתיב/למודלים/נוספים", + "saveSuccess": "נתיבי תיקיות נוספים עודכנו.", + "saveError": "נכשל בעדכון נתיבי תיקיות נוספים: {message}", + "validation": { + "duplicatePath": "נתיב זה כבר מוגדר" + } } }, "loras": { @@ -1633,4 +1651,4 @@ "retry": "נסה שוב" } } -} +} \ No newline at end of file diff --git a/locales/ja.json b/locales/ja.json index c7dbe26c..0beb5231 100644 --- a/locales/ja.json +++ b/locales/ja.json @@ -260,6 +260,7 @@ "layoutSettings": "レイアウト設定", "misc": "その他", "folderSettings": "デフォルトルート", + "extraFolderPaths": "追加フォルダーパス", "downloadPathTemplates": "ダウンロードパステンプレート", "priorityTags": "優先タグ", "updateFlags": "アップデートフラグ", @@ -484,6 +485,23 @@ "proxyPassword": "パスワード(任意)", "proxyPasswordPlaceholder": "パスワード", "proxyPasswordHelp": "プロキシ認証用のパスワード(必要な場合)" + }, + "extraFolderPaths": { + "title": "追加フォルダーパス", + "help": "ComfyUIの標準パスの外部に追加のモデルフォルダを追加します。これらのパスは別々に保存され、デフォルトのフォルダと一緒にスキャンされます。", + "description": "モデルをスキャンするための追加フォルダを設定します。これらのパスはLoRA Manager固有であり、ComfyUIのデフォルトパスとマージされます。", + "modelTypes": { + "lora": "LoRAパス", + "checkpoint": "Checkpointパス", + "unet": "Diffusionモデルパス", + "embedding": "Embeddingパス" + }, + "pathPlaceholder": "/追加モデルへのパス", + "saveSuccess": "追加フォルダーパスを更新しました。", + "saveError": "追加フォルダーパスの更新に失敗しました: {message}", + "validation": { + "duplicatePath": "このパスはすでに設定されています" + } } }, "loras": { @@ -1633,4 +1651,4 @@ "retry": "再試行" } } -} +} \ No newline at end of file diff --git a/locales/ko.json b/locales/ko.json index 9ca18447..545d9dd2 100644 --- a/locales/ko.json +++ b/locales/ko.json @@ -260,6 +260,7 @@ "layoutSettings": "레이아웃 설정", "misc": "기타", "folderSettings": "기본 루트", + "extraFolderPaths": "추가 폴다 경로", "downloadPathTemplates": "다운로드 경로 템플릿", "priorityTags": "우선순위 태그", "updateFlags": "업데이트 표시", @@ -484,6 +485,23 @@ "proxyPassword": "비밀번호 (선택사항)", "proxyPasswordPlaceholder": "password", "proxyPasswordHelp": "프록시 인증에 필요한 비밀번호 (필요한 경우)" + }, + "extraFolderPaths": { + "title": "추가 폴다 경로", + "help": "ComfyUI의 표준 경로 외부에 추가 모델 폴드를 추가하세요. 이러한 경로는 별도로 저장되며 기본 폴와 함께 스캔됩니다.", + "description": "모델을 스캔하기 위한 추가 폴를 설정하세요. 이러한 경로는 LoRA Manager 특유의 것이며 ComfyUI의 기본 경로와 병합됩니다.", + "modelTypes": { + "lora": "LoRA 경로", + "checkpoint": "Checkpoint 경로", + "unet": "Diffusion 모델 경로", + "embedding": "Embedding 경로" + }, + "pathPlaceholder": "/추가/모델/경로", + "saveSuccess": "추가 폴다 경로가 업데이트되었습니다.", + "saveError": "추가 폴다 경로 업데이트 실패: {message}", + "validation": { + "duplicatePath": "이 경로는 이미 구성되어 있습니다" + } } }, "loras": { @@ -1633,4 +1651,4 @@ "retry": "다시 시도" } } -} +} \ No newline at end of file diff --git a/locales/ru.json b/locales/ru.json index f123f4d3..b2eaecbf 100644 --- a/locales/ru.json +++ b/locales/ru.json @@ -260,6 +260,7 @@ "layoutSettings": "Настройки макета", "misc": "Разное", "folderSettings": "Корневые папки", + "extraFolderPaths": "Дополнительные пути к папкам", "downloadPathTemplates": "Шаблоны путей загрузки", "priorityTags": "Приоритетные теги", "updateFlags": "Метки обновлений", @@ -484,6 +485,23 @@ "proxyPassword": "Пароль (необязательно)", "proxyPasswordPlaceholder": "пароль", "proxyPasswordHelp": "Пароль для аутентификации на прокси (если требуется)" + }, + "extraFolderPaths": { + "title": "Дополнительные пути к папкам", + "help": "Добавьте дополнительные папки моделей за пределами стандартных путей ComfyUI. Эти пути хранятся отдельно и сканируются вместе с папками по умолчанию.", + "description": "Настройте дополнительные папки для сканирования моделей. Эти пути специфичны для LoRA Manager и будут объединены с путями по умолчанию ComfyUI.", + "modelTypes": { + "lora": "Пути LoRA", + "checkpoint": "Пути Checkpoint", + "unet": "Пути моделей диффузии", + "embedding": "Пути Embedding" + }, + "pathPlaceholder": "/путь/к/дополнительным/моделям", + "saveSuccess": "Дополнительные пути к папкам обновлены.", + "saveError": "Не удалось обновить дополнительные пути к папкам: {message}", + "validation": { + "duplicatePath": "Этот путь уже настроен" + } } }, "loras": { @@ -1633,4 +1651,4 @@ "retry": "Повторить" } } -} +} \ No newline at end of file diff --git a/locales/zh-CN.json b/locales/zh-CN.json index 326ece38..5fbed5f7 100644 --- a/locales/zh-CN.json +++ b/locales/zh-CN.json @@ -260,6 +260,7 @@ "layoutSettings": "布局设置", "misc": "其他", "folderSettings": "默认根目录", + "extraFolderPaths": "额外文件夹路径", "downloadPathTemplates": "下载路径模板", "priorityTags": "优先标签", "updateFlags": "更新标记", @@ -484,6 +485,23 @@ "proxyPassword": "密码 (可选)", "proxyPasswordPlaceholder": "密码", "proxyPasswordHelp": "代理认证的密码 (如果需要)" + }, + "extraFolderPaths": { + "title": "额外文件夹路径", + "help": "在 ComfyUI 的标准路径之外添加额外的模型文件夹。这些路径单独存储,并与默认文件夹一起扫描。", + "description": "配置额外的文件夹以扫描模型。这些路径是 LoRA Manager 特有的,将与 ComfyUI 的默认路径合并。", + "modelTypes": { + "lora": "LoRA 路径", + "checkpoint": "Checkpoint 路径", + "unet": "Diffusion 模型路径", + "embedding": "Embedding 路径" + }, + "pathPlaceholder": "/额外/模型/路径", + "saveSuccess": "额外文件夹路径已更新。", + "saveError": "更新额外文件夹路径失败:{message}", + "validation": { + "duplicatePath": "此路径已配置" + } } }, "loras": { @@ -1633,4 +1651,4 @@ "retry": "重试" } } -} +} \ No newline at end of file diff --git a/locales/zh-TW.json b/locales/zh-TW.json index 3dd3225f..9dcf22ee 100644 --- a/locales/zh-TW.json +++ b/locales/zh-TW.json @@ -260,6 +260,7 @@ "layoutSettings": "版面設定", "misc": "其他", "folderSettings": "預設根目錄", + "extraFolderPaths": "額外資料夾路徑", "downloadPathTemplates": "下載路徑範本", "priorityTags": "優先標籤", "updateFlags": "更新標記", @@ -484,6 +485,23 @@ "proxyPassword": "密碼(選填)", "proxyPasswordPlaceholder": "password", "proxyPasswordHelp": "代理驗證所需的密碼(如有需要)" + }, + "extraFolderPaths": { + "title": "額外資料夾路徑", + "help": "在 ComfyUI 的標準路徑之外新增額外的模型資料夾。這些路徑單獨儲存,並與預設資料夾一起掃描。", + "description": "設定額外的資料夾以掃描模型。這些路徑是 LoRA Manager 特有的,將與 ComfyUI 的預設路徑合併。", + "modelTypes": { + "lora": "LoRA 路徑", + "checkpoint": "Checkpoint 路徑", + "unet": "Diffusion 模型路徑", + "embedding": "Embedding 路徑" + }, + "pathPlaceholder": "/額外/模型/路徑", + "saveSuccess": "額外資料夾路徑已更新。", + "saveError": "更新額外資料夾路徑失敗:{message}", + "validation": { + "duplicatePath": "此路徑已設定" + } } }, "loras": { @@ -1633,4 +1651,4 @@ "retry": "重試" } } -} +} \ No newline at end of file diff --git a/static/css/components/modal/settings-modal.css b/static/css/components/modal/settings-modal.css index ffd1a6ac..d897593e 100644 --- a/static/css/components/modal/settings-modal.css +++ b/static/css/components/modal/settings-modal.css @@ -1210,3 +1210,61 @@ input:checked + .toggle-slider:before { background: rgba(40, 40, 40, 0.95); box-shadow: 0 4px 12px rgba(0, 0, 0, 0.5); } + +/* Extra Folder Paths - Single input layout */ +.extra-folder-path-row { + margin-bottom: var(--space-2); +} + +.extra-folder-path-row:last-child { + margin-bottom: 0; +} + +.extra-folder-paths-container { + margin-top: var(--space-2); +} + +.extra-folder-path-row .path-controls { + display: flex; + gap: var(--space-2); + align-items: center; +} + +.extra-folder-path-row .path-controls .extra-folder-path-input { + flex: 1; + min-width: 0; + padding: 6px 10px; + border-radius: var(--border-radius-xs); + border: 1px solid var(--border-color); + background-color: var(--lora-surface); + color: var(--text-color); + font-size: 0.9em; + height: 32px; + box-sizing: border-box; +} + +.extra-folder-path-row .path-controls .extra-folder-path-input:focus { + border-color: var(--lora-accent); + outline: none; + box-shadow: 0 0 0 2px rgba(var(--lora-accent-rgb, 79, 70, 229), 0.1); +} + +.extra-folder-path-row .path-controls .remove-path-btn { + width: 32px; + height: 32px; + border-radius: var(--border-radius-xs); + border: 1px solid var(--lora-error); + background: transparent; + color: var(--lora-error); + cursor: pointer; + display: flex; + align-items: center; + justify-content: center; + transition: all 0.2s; + flex-shrink: 0; +} + +.extra-folder-path-row .path-controls .remove-path-btn:hover { + background: var(--lora-error); + color: white; +} diff --git a/static/js/managers/SettingsManager.js b/static/js/managers/SettingsManager.js index 195e5aa1..6a72c67c 100644 --- a/static/js/managers/SettingsManager.js +++ b/static/js/managers/SettingsManager.js @@ -799,6 +799,9 @@ export class SettingsManager { // Load default unet root await this.loadUnetRoots(); + // Load extra folder paths + this.loadExtraFolderPaths(); + // Load language setting const languageSelect = document.getElementById('languageSelect'); if (languageSelect) { @@ -1301,6 +1304,119 @@ export class SettingsManager { } } + loadExtraFolderPaths() { + const extraFolderPaths = state.global.settings.extra_folder_paths || {}; + + // Load paths for each model type + ['loras', 'checkpoints', 'unet', 'embeddings'].forEach((modelType) => { + const container = document.getElementById(`extraFolderPaths-${modelType}`); + if (!container) return; + + // Clear existing paths + container.innerHTML = ''; + + // Add existing paths + const paths = extraFolderPaths[modelType] || []; + paths.forEach((path) => { + this.addExtraFolderPathRow(modelType, path); + }); + + // Add empty row for new path if no paths exist + if (paths.length === 0) { + this.addExtraFolderPathRow(modelType, ''); + } + }); + } + + addExtraFolderPathRow(modelType, path = '') { + const container = document.getElementById(`extraFolderPaths-${modelType}`); + if (!container) return; + + const row = document.createElement('div'); + row.className = 'extra-folder-path-row mapping-row'; + + row.innerHTML = ` +
+ + +
+ `; + + container.appendChild(row); + + // Focus the input if it's empty (new row) + if (!path) { + const input = row.querySelector('.extra-folder-path-input'); + if (input) { + setTimeout(() => input.focus(), 0); + } + } + } + + async updateExtraFolderPaths(changedModelType) { + const extraFolderPaths = {}; + + // Collect paths for all model types + ['loras', 'checkpoints', 'unet', 'embeddings'].forEach((modelType) => { + const container = document.getElementById(`extraFolderPaths-${modelType}`); + if (!container) return; + + const inputs = container.querySelectorAll('.extra-folder-path-input'); + const paths = []; + + inputs.forEach((input) => { + const value = input.value.trim(); + if (value) { + paths.push(value); + } + }); + + extraFolderPaths[modelType] = paths; + }); + + // Check if paths have actually changed + const currentPaths = state.global.settings.extra_folder_paths || {}; + const pathsChanged = JSON.stringify(currentPaths) !== JSON.stringify(extraFolderPaths); + + if (!pathsChanged) { + return; + } + + // Update state + state.global.settings.extra_folder_paths = extraFolderPaths; + + try { + // Save to backend - this triggers path validation + await this.saveSetting('extra_folder_paths', extraFolderPaths); + showToast('toast.settings.settingsUpdated', { setting: 'Extra Folder Paths' }, 'success'); + + // Add empty row if no valid paths exist for the changed type + const container = document.getElementById(`extraFolderPaths-${changedModelType}`); + if (container) { + const inputs = container.querySelectorAll('.extra-folder-path-input'); + const hasEmptyRow = Array.from(inputs).some((input) => !input.value.trim()); + + if (!hasEmptyRow) { + this.addExtraFolderPathRow(changedModelType, ''); + } + } + } catch (error) { + console.error('Failed to save extra folder paths:', error); + showToast('toast.settings.settingSaveFailed', { message: error.message }, 'error'); + + // Restore previous state on error + state.global.settings.extra_folder_paths = currentPaths; + this.loadExtraFolderPaths(); + } + } + loadBaseModelMappings() { const mappingsContainer = document.getElementById('baseModelMappingsContainer'); if (!mappingsContainer) return; diff --git a/templates/components/modals/settings_modal.html b/templates/components/modals/settings_modal.html index 85bd4686..5305a619 100644 --- a/templates/components/modals/settings_modal.html +++ b/templates/components/modals/settings_modal.html @@ -519,7 +519,90 @@ - + + +
+
+

+ {{ t('settings.extraFolderPaths.title') }} + +

+
+
+
+ {{ t('settings.extraFolderPaths.description') }} +
+
+ + +
+
+
+ +
+
+ +
+
+
+
+
+ + +
+
+
+ +
+
+ +
+
+
+
+
+ + +
+
+
+ +
+
+ +
+
+
+
+
+ + +
+
+
+ +
+
+ +
+
+
+
+
+
+