mirror of
https://github.com/willmiao/ComfyUI-Lora-Manager.git
synced 2026-08-21 13:01:27 -03:00
refactor(ui): split settings modal into section templates with shared macros
This commit is contained in:
@@ -904,6 +904,9 @@ export class SettingsManager {
|
|||||||
// Helper to update model Combobox presets from catalog / Ollama API
|
// Helper to update model Combobox presets from catalog / Ollama API
|
||||||
const llmModelInput = document.getElementById('llmModel');
|
const llmModelInput = document.getElementById('llmModel');
|
||||||
this._llmModelCombobox = null;
|
this._llmModelCombobox = null;
|
||||||
|
if (llmModelInput) {
|
||||||
|
llmModelInput.value = state.global.settings.llm_model || '';
|
||||||
|
}
|
||||||
if (llmModelInput && typeof Combobox !== 'undefined') {
|
if (llmModelInput && typeof Combobox !== 'undefined') {
|
||||||
const currentProvider = llmProviderSelect ? llmProviderSelect.value : 'openai';
|
const currentProvider = llmProviderSelect ? llmProviderSelect.value : 'openai';
|
||||||
const fallbackModels = currentProvider === 'ollama' ? [] : (this._providerModels[currentProvider] || []);
|
const fallbackModels = currentProvider === 'ollama' ? [] : (this._providerModels[currentProvider] || []);
|
||||||
|
|||||||
@@ -0,0 +1,69 @@
|
|||||||
|
{# Shared building blocks for the settings modal sections. #}
|
||||||
|
{# Usage: {% import 'components/modals/settings/_macros.html' as sm with context %} #}
|
||||||
|
{# `with context` is required so macros can call the `t()` translation function. #}
|
||||||
|
|
||||||
|
{% macro setting_toggle(id, key, label, help='') %}
|
||||||
|
<div class="setting-item">
|
||||||
|
<div class="setting-row">
|
||||||
|
<div class="setting-info">
|
||||||
|
<label for="{{ id }}">
|
||||||
|
{{ t(label) }}
|
||||||
|
{% if help %}<i class="fas fa-info-circle info-icon" data-tooltip="{{ t(help) }}"></i>{% endif %}
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
<div class="setting-control">
|
||||||
|
<label class="toggle-switch">
|
||||||
|
<input type="checkbox" id="{{ id }}" onchange="settingsManager.saveToggleSetting('{{ id }}', '{{ key }}')">
|
||||||
|
<span class="toggle-slider"></span>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% endmacro %}
|
||||||
|
|
||||||
|
{% macro setting_select(id, key, label, options, help='') %}
|
||||||
|
<div class="setting-item">
|
||||||
|
<div class="setting-row">
|
||||||
|
<div class="setting-info">
|
||||||
|
<label for="{{ id }}">
|
||||||
|
{{ t(label) }}
|
||||||
|
{% if help %}<i class="fas fa-info-circle info-icon" data-tooltip="{{ t(help) }}"></i>{% endif %}
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
<div class="setting-control select-control">
|
||||||
|
<select id="{{ id }}" onchange="settingsManager.saveSelectSetting('{{ id }}', '{{ key }}')">
|
||||||
|
{% for value, option_label in options %}
|
||||||
|
<option value="{{ value }}">{{ t(option_label) }}</option>
|
||||||
|
{% endfor %}
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% endmacro %}
|
||||||
|
|
||||||
|
{% macro setting_input(id, key, label, placeholder, help='') %}
|
||||||
|
<div class="setting-item">
|
||||||
|
<div class="setting-row">
|
||||||
|
<div class="setting-info">
|
||||||
|
<label for="{{ id }}">
|
||||||
|
{{ t(label) }}
|
||||||
|
{% if help %}<i class="fas fa-info-circle info-icon" data-tooltip="{{ t(help) }}"></i>{% endif %}
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
<div class="setting-control">
|
||||||
|
<div class="text-input-wrapper">
|
||||||
|
<input type="text" id="{{ id }}"
|
||||||
|
placeholder="{{ t(placeholder) }}"
|
||||||
|
onblur="settingsManager.saveInputSetting('{{ id }}', '{{ key }}')"
|
||||||
|
onkeydown="if(event.key === 'Enter') { this.blur(); }" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% endmacro %}
|
||||||
|
|
||||||
|
{% macro subsection_header(title) %}
|
||||||
|
<div class="settings-subsection-header">
|
||||||
|
<h4>{{ t(title) }}</h4>
|
||||||
|
</div>
|
||||||
|
{% endmacro %}
|
||||||
@@ -0,0 +1,326 @@
|
|||||||
|
{% import 'components/modals/settings/_macros.html' as sm with context %}
|
||||||
|
<!-- Section 1: General -->
|
||||||
|
<div id="section-general" class="settings-section active" data-section="general">
|
||||||
|
<!-- Language -->
|
||||||
|
<div class="setting-item">
|
||||||
|
<div class="setting-row">
|
||||||
|
<div class="setting-info">
|
||||||
|
<label for="languageSelect">
|
||||||
|
{{ t('common.language.select') }}
|
||||||
|
<i class="fas fa-info-circle info-icon" data-tooltip="{{ t('common.language.select_help') }}"></i>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
<div class="setting-control select-control">
|
||||||
|
<select id="languageSelect" onchange="settingsManager.saveLanguageSetting()">
|
||||||
|
<option value="en">{{ t('common.language.english') }}</option>
|
||||||
|
<option value="zh-CN">{{ t('common.language.chinese_simplified') }}</option>
|
||||||
|
<option value="zh-TW">{{ t('common.language.chinese_traditional') }}</option>
|
||||||
|
<option value="ru">{{ t('common.language.russian') }}</option>
|
||||||
|
<option value="de">{{ t('common.language.german') }}</option>
|
||||||
|
<option value="ja">{{ t('common.language.japanese') }}</option>
|
||||||
|
<option value="ko">{{ t('common.language.korean') }}</option>
|
||||||
|
<option value="fr">{{ t('common.language.french') }}</option>
|
||||||
|
<option value="es">{{ t('common.language.spanish') }}</option>
|
||||||
|
<option value="he">{{ t('common.language.Hebrew') }}</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Storage Location -->
|
||||||
|
{{ sm.setting_toggle('usePortableSettings', 'use_portable_settings', 'settings.storage.locationLabel', 'settings.storage.locationHelp') }}
|
||||||
|
|
||||||
|
<!-- API Configuration -->
|
||||||
|
<div class="setting-item api-key-item">
|
||||||
|
<div class="setting-row">
|
||||||
|
<div class="setting-info">
|
||||||
|
<label>{{ t('settings.civitaiApiKey') }}</label>
|
||||||
|
<i class="fas fa-info-circle info-icon" data-tooltip="{{ t('settings.civitaiApiKeyHelp') }}"></i>
|
||||||
|
</div>
|
||||||
|
<div class="setting-control">
|
||||||
|
<!-- Status display (shown when not editing) -->
|
||||||
|
<div id="civitaiApiKeyStatus" class="api-key-status">
|
||||||
|
<span id="civitaiApiKeyStatusText" class="api-key-status-text api-key-status--unconfigured">
|
||||||
|
<i class="fas fa-times-circle text-error"></i>
|
||||||
|
{{ t('settings.civitaiApiKeyNotConfigured') }}
|
||||||
|
</span>
|
||||||
|
<button type="button" class="secondary-btn" id="civitaiApiKeyActionBtn" onclick="settingsManager.editApiKey()">
|
||||||
|
{{ t('settings.civitaiApiKeySet') }}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<!-- Inline edit view (shown when editing) -->
|
||||||
|
<div id="civitaiApiKeyEdit" class="api-key-edit is-hidden">
|
||||||
|
<div class="api-key-input">
|
||||||
|
<input type="text"
|
||||||
|
id="civitaiApiKey"
|
||||||
|
class="api-key-masked"
|
||||||
|
placeholder="{{ t('settings.civitaiApiKeyPlaceholder') }}"
|
||||||
|
autocomplete="off"
|
||||||
|
data-mask="css" />
|
||||||
|
<button type="button" class="toggle-visibility">
|
||||||
|
<i class="fas fa-eye"></i>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<button type="button" class="primary-btn" onclick="settingsManager.saveApiKey()">{{ t('common.actions.save') }}</button>
|
||||||
|
<button type="button" class="secondary-btn" onclick="settingsManager.cancelEditApiKey()">{{ t('common.actions.cancel') }}</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{{ sm.setting_select('civitaiHost', 'civitai_host', 'settings.civitaiHost.label', [
|
||||||
|
('civitai.com', 'settings.civitaiHost.options.com'),
|
||||||
|
('civitai.red', 'settings.civitaiHost.options.red'),
|
||||||
|
], 'settings.civitaiHost.help') }}
|
||||||
|
|
||||||
|
<div class="settings-subsection">
|
||||||
|
{{ sm.subsection_header('settings.sections.downloads') }}
|
||||||
|
<div class="setting-item">
|
||||||
|
<div class="setting-row">
|
||||||
|
<div class="setting-info">
|
||||||
|
<label for="downloadBackend">{{ t('settings.downloadBackend.label') }}</label>
|
||||||
|
<i class="fas fa-info-circle info-icon" data-tooltip="{{ t('settings.downloadBackend.help') }}"></i>
|
||||||
|
<a class="settings-action-link" href="https://github.com/willmiao/ComfyUI-Lora-Manager/wiki/Aria2-Download-Backend-(Experimental)" target="_blank" rel="noopener" aria-label="{{ t('settings.aria2HelpLink') }}" title="{{ t('settings.aria2HelpLink') }}">
|
||||||
|
<i class="fas fa-question-circle" aria-hidden="true"></i>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
<div class="setting-control select-control">
|
||||||
|
<select id="downloadBackend" onchange="settingsManager.saveSelectSetting('downloadBackend', 'download_backend')">
|
||||||
|
<option value="python">{{ t('settings.downloadBackend.options.python') }}</option>
|
||||||
|
<option value="aria2">{{ t('settings.downloadBackend.options.aria2') }}</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="setting-item" id="aria2PathSetting" style="display: none;">
|
||||||
|
<div class="setting-row">
|
||||||
|
<div class="setting-info">
|
||||||
|
<label for="aria2cPath">{{ t('settings.aria2cPath.label') }}</label>
|
||||||
|
<i class="fas fa-info-circle info-icon" data-tooltip="{{ t('settings.aria2cPath.help') }}"></i>
|
||||||
|
</div>
|
||||||
|
<div class="setting-control">
|
||||||
|
<div class="text-input-wrapper">
|
||||||
|
<input type="text"
|
||||||
|
id="aria2cPath"
|
||||||
|
placeholder="{{ t('settings.aria2cPath.placeholder') }}"
|
||||||
|
onblur="settingsManager.saveInputSetting('aria2cPath', 'aria2c_path')"
|
||||||
|
onkeydown="if(event.key === 'Enter') { this.blur(); }" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- AI Provider Configuration (BYOK) -->
|
||||||
|
<div class="settings-subsection">
|
||||||
|
{{ sm.subsection_header('settings.aiProvider.title') }}
|
||||||
|
{{ sm.setting_select('llmProvider', 'llm_provider', 'settings.aiProvider.provider', [
|
||||||
|
('openai', 'settings.aiProvider.providerOptions.openai'),
|
||||||
|
('ollama', 'settings.aiProvider.providerOptions.ollama'),
|
||||||
|
('deepseek', 'settings.aiProvider.providerOptions.deepseek'),
|
||||||
|
('groq', 'settings.aiProvider.providerOptions.groq'),
|
||||||
|
('openrouter', 'settings.aiProvider.providerOptions.openrouter'),
|
||||||
|
('google', 'settings.aiProvider.providerOptions.google'),
|
||||||
|
('opencode-go', 'settings.aiProvider.providerOptions.opencode-go'),
|
||||||
|
('custom', 'settings.aiProvider.providerOptions.custom'),
|
||||||
|
], 'settings.aiProvider.providerHelp') }}
|
||||||
|
<div class="setting-item">
|
||||||
|
<div class="setting-row">
|
||||||
|
<div class="setting-info">
|
||||||
|
<label for="llmApiBase">{{ t('settings.aiProvider.apiBase') }}</label>
|
||||||
|
<i class="fas fa-info-circle info-icon" data-tooltip="{{ t('settings.aiProvider.apiBaseHelp') }}"></i>
|
||||||
|
</div>
|
||||||
|
<div class="setting-control">
|
||||||
|
<div class="text-input-wrapper lm-combobox-container">
|
||||||
|
<input type="text" id="llmApiBase"
|
||||||
|
class="lm-combobox-input"
|
||||||
|
placeholder="{{ t('settings.aiProvider.apiBasePlaceholder') }}"
|
||||||
|
autocomplete="off"
|
||||||
|
onblur="settingsManager.saveInputSetting('llmApiBase', 'llm_api_base')"
|
||||||
|
onkeydown="if(event.key === 'Enter') { this.blur(); }" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="setting-item api-key-item">
|
||||||
|
<div class="setting-row">
|
||||||
|
<div class="setting-info">
|
||||||
|
<label>{{ t('settings.aiProvider.apiKey') }}</label>
|
||||||
|
<i class="fas fa-info-circle info-icon" data-tooltip="{{ t('settings.aiProvider.apiKeyHelp') }}"></i>
|
||||||
|
</div>
|
||||||
|
<div class="setting-control">
|
||||||
|
<div id="llmApiKeyStatus" class="api-key-status">
|
||||||
|
<span id="llmApiKeyStatusText" class="api-key-status-text api-key-status--unconfigured">
|
||||||
|
<i class="fas fa-times-circle text-error"></i>
|
||||||
|
{{ t('settings.aiProvider.apiKeyNotSet') }}
|
||||||
|
</span>
|
||||||
|
<button type="button" class="secondary-btn" id="llmApiKeyActionBtn" onclick="settingsManager.editApiKey('llm_api_key', 'llmApiKey')">
|
||||||
|
{{ t('settings.aiProvider.apiKeySet') }}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<div id="llmApiKeyEdit" class="api-key-edit is-hidden">
|
||||||
|
<div class="api-key-input">
|
||||||
|
<input type="text"
|
||||||
|
id="llmApiKey"
|
||||||
|
class="api-key-masked"
|
||||||
|
placeholder="{{ t('settings.aiProvider.apiKeyPlaceholder') }}"
|
||||||
|
autocomplete="off"
|
||||||
|
data-mask="css" />
|
||||||
|
<button type="button" class="toggle-visibility">
|
||||||
|
<i class="fas fa-eye"></i>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<button type="button" class="primary-btn" onclick="settingsManager.saveApiKey('llm_api_key', 'llmApiKey')">{{ t('common.actions.save') }}</button>
|
||||||
|
<button type="button" class="secondary-btn" onclick="settingsManager.cancelEditApiKey(true, 'llmApiKey')">{{ t('common.actions.cancel') }}</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="setting-item">
|
||||||
|
<div class="setting-row">
|
||||||
|
<div class="setting-info">
|
||||||
|
<label for="llmModel">{{ t('settings.aiProvider.model') }}</label>
|
||||||
|
<i class="fas fa-info-circle info-icon" data-tooltip="{{ t('settings.aiProvider.modelHelp') }}"></i>
|
||||||
|
</div>
|
||||||
|
<div class="setting-control">
|
||||||
|
<div class="text-input-wrapper lm-combobox-container">
|
||||||
|
<input type="text" id="llmModel"
|
||||||
|
class="lm-combobox-input"
|
||||||
|
placeholder="{{ t('settings.aiProvider.modelPlaceholder') }}"
|
||||||
|
autocomplete="off"
|
||||||
|
onblur="settingsManager.saveInputSetting('llmModel', 'llm_model')"
|
||||||
|
onkeydown="if(event.key === 'Enter') { this.blur(); }" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Backup -->
|
||||||
|
<div class="settings-subsection">
|
||||||
|
{{ sm.subsection_header('settings.sections.backup') }}
|
||||||
|
<div class="settings-help-text subtle">
|
||||||
|
{{ t('settings.backup.scopeHelp') }}
|
||||||
|
</div>
|
||||||
|
{{ sm.setting_toggle('backupAutoEnabled', 'backup_auto_enabled', 'settings.backup.autoEnabled', 'settings.backup.autoEnabledHelp') }}
|
||||||
|
<div class="setting-item">
|
||||||
|
<div class="setting-row">
|
||||||
|
<div class="setting-info">
|
||||||
|
<label for="backupRetentionCount">
|
||||||
|
{{ t('settings.backup.retention') }}
|
||||||
|
<i class="fas fa-info-circle info-icon" data-tooltip="{{ t('settings.backup.retentionHelp') }}"></i>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
<div class="setting-control">
|
||||||
|
<div class="text-input-wrapper">
|
||||||
|
<input
|
||||||
|
type="number"
|
||||||
|
id="backupRetentionCount"
|
||||||
|
min="1"
|
||||||
|
step="1"
|
||||||
|
onblur="settingsManager.saveInputSetting('backupRetentionCount', 'backup_retention_count')"
|
||||||
|
onkeydown="if(event.key === 'Enter') { this.blur(); }"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="setting-item">
|
||||||
|
<div class="setting-row">
|
||||||
|
<div class="setting-info">
|
||||||
|
<label>
|
||||||
|
{{ t('settings.backup.management') }}
|
||||||
|
<i class="fas fa-info-circle info-icon" data-tooltip="{{ t('settings.backup.managementHelp') }}"></i>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
<div class="setting-control">
|
||||||
|
<button type="button" class="secondary-btn" onclick="settingsManager.exportBackup()">
|
||||||
|
{{ t('settings.backup.exportButton') }}
|
||||||
|
</button>
|
||||||
|
<button type="button" class="secondary-btn" onclick="settingsManager.triggerBackupImport()" style="margin-left: 10px;">
|
||||||
|
{{ t('settings.backup.importButton') }}
|
||||||
|
</button>
|
||||||
|
<input
|
||||||
|
type="file"
|
||||||
|
id="backupImportInput"
|
||||||
|
accept=".zip,application/zip"
|
||||||
|
style="display: none;"
|
||||||
|
onchange="settingsManager.handleBackupImportFile(this)"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="setting-item">
|
||||||
|
<details class="backup-location-details">
|
||||||
|
<summary>{{ t('settings.backup.locationSummary') }}</summary>
|
||||||
|
<div class="backup-location-panel">
|
||||||
|
<code id="backupLocationPath" class="backup-location-path"></code>
|
||||||
|
<button type="button" class="secondary-btn" id="backupOpenLocationBtn">
|
||||||
|
{{ t('settings.backup.openFolderButton') }}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</details>
|
||||||
|
</div>
|
||||||
|
<div class="setting-item">
|
||||||
|
<div class="backup-status" id="backupStatus">
|
||||||
|
<!-- Status will be populated by JavaScript -->
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Proxy Settings -->
|
||||||
|
<div class="settings-subsection">
|
||||||
|
{{ sm.subsection_header('settings.sections.proxySettings') }}
|
||||||
|
{{ sm.setting_toggle('proxyEnabled', 'proxy_enabled', 'settings.proxySettings.enableProxy', 'settings.proxySettings.enableProxyHelp') }}
|
||||||
|
|
||||||
|
<div id="proxySettingsGroup" class="proxy-settings-group" style="display: none;">
|
||||||
|
<div class="setting-item">
|
||||||
|
<div class="setting-row">
|
||||||
|
<div class="setting-info">
|
||||||
|
<label for="proxyType">
|
||||||
|
{{ t('settings.proxySettings.proxyType') }}
|
||||||
|
<i class="fas fa-info-circle info-icon" data-tooltip="{{ t('settings.proxySettings.proxyTypeHelp') }}"></i>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
<div class="setting-control select-control">
|
||||||
|
<select id="proxyType" onchange="settingsManager.saveSelectSetting('proxyType', 'proxy_type')">
|
||||||
|
<option value="http">HTTP</option>
|
||||||
|
<option value="https">HTTPS</option>
|
||||||
|
<option value="socks4">SOCKS4</option>
|
||||||
|
<option value="socks5">SOCKS5</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{{ sm.setting_input('proxyHost', 'proxy_host', 'settings.proxySettings.proxyHost', 'settings.proxySettings.proxyHostPlaceholder', 'settings.proxySettings.proxyHostHelp') }}
|
||||||
|
|
||||||
|
{{ sm.setting_input('proxyPort', 'proxy_port', 'settings.proxySettings.proxyPort', 'settings.proxySettings.proxyPortPlaceholder', 'settings.proxySettings.proxyPortHelp') }}
|
||||||
|
|
||||||
|
{{ sm.setting_input('proxyUsername', 'proxy_username', 'settings.proxySettings.proxyUsername', 'settings.proxySettings.proxyUsernamePlaceholder', 'settings.proxySettings.proxyUsernameHelp') }}
|
||||||
|
|
||||||
|
<div class="setting-item">
|
||||||
|
<div class="setting-row">
|
||||||
|
<div class="setting-info">
|
||||||
|
<label for="proxyPassword">
|
||||||
|
{{ t('settings.proxySettings.proxyPassword') }}
|
||||||
|
<i class="fas fa-info-circle info-icon" data-tooltip="{{ t('settings.proxySettings.proxyPasswordHelp') }}"></i>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
<div class="setting-control">
|
||||||
|
<div class="api-key-input">
|
||||||
|
<input type="password" id="proxyPassword"
|
||||||
|
placeholder="{{ t('settings.proxySettings.proxyPasswordPlaceholder') }}"
|
||||||
|
autocomplete="new-password"
|
||||||
|
onblur="settingsManager.saveInputSetting('proxyPassword', 'proxy_password')"
|
||||||
|
onkeydown="if(event.key === 'Enter') { this.blur(); }" />
|
||||||
|
<button class="toggle-visibility">
|
||||||
|
<i class="fas fa-eye"></i>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
@@ -0,0 +1,127 @@
|
|||||||
|
{% import 'components/modals/settings/_macros.html' as sm with context %}
|
||||||
|
<!-- Section 2: Interface -->
|
||||||
|
<div id="section-interface" class="settings-section" data-section="interface">
|
||||||
|
<!-- Content Filtering -->
|
||||||
|
<div class="settings-subsection">
|
||||||
|
{{ sm.subsection_header('settings.sections.contentFiltering') }}
|
||||||
|
{{ sm.setting_toggle('blurMatureContent', 'blur_mature_content', 'settings.contentFiltering.blurNsfwContent', 'settings.contentFiltering.blurNsfwContentHelp') }}
|
||||||
|
{{ sm.setting_toggle('showOnlySFW', 'show_only_sfw', 'settings.contentFiltering.showOnlySfw', 'settings.contentFiltering.showOnlySfwHelp') }}
|
||||||
|
{{ sm.setting_select('matureBlurLevel', 'mature_blur_level', 'settings.contentFiltering.matureBlurThreshold', [
|
||||||
|
('PG13', 'settings.contentFiltering.matureBlurThresholdOptions.pg13'),
|
||||||
|
('R', 'settings.contentFiltering.matureBlurThresholdOptions.r'),
|
||||||
|
('X', 'settings.contentFiltering.matureBlurThresholdOptions.x'),
|
||||||
|
('XXX', 'settings.contentFiltering.matureBlurThresholdOptions.xxx'),
|
||||||
|
], 'settings.contentFiltering.matureBlurThresholdHelp') }}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Video Settings -->
|
||||||
|
<div class="settings-subsection">
|
||||||
|
{{ sm.subsection_header('settings.sections.videoSettings') }}
|
||||||
|
{{ sm.setting_toggle('autoplayOnHover', 'autoplay_on_hover', 'settings.videoSettings.autoplayOnHover', 'settings.videoSettings.autoplayOnHoverHelp') }}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Layout Settings -->
|
||||||
|
<div class="settings-subsection">
|
||||||
|
{{ sm.subsection_header('settings.sections.layoutSettings') }}
|
||||||
|
<div class="setting-item">
|
||||||
|
<div class="setting-row">
|
||||||
|
<div class="setting-info">
|
||||||
|
<label for="displayDensity">
|
||||||
|
{{ t('settings.layoutSettings.displayDensity') }}
|
||||||
|
<i class="fas fa-info-circle info-icon" data-tooltip="{{ t('settings.layoutSettings.displayDensityHelp') }}"></i>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
<div class="setting-control select-control">
|
||||||
|
<select id="displayDensity" onchange="settingsManager.saveSelectSetting('displayDensity', 'display_density')">
|
||||||
|
<option value="default">{{ t('settings.layoutSettings.displayDensityOptions.default') }}</option>
|
||||||
|
<option value="medium">{{ t('settings.layoutSettings.displayDensityOptions.medium') }}</option>
|
||||||
|
<option value="compact">{{ t('settings.layoutSettings.displayDensityOptions.compact') }}</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="input-help"><ul class="list-description">
|
||||||
|
<li><strong>{{ t('settings.layoutSettings.displayDensityOptions.default') }}:</strong> {{ t('settings.layoutSettings.displayDensityDetails.default') }}</li>
|
||||||
|
<li><strong>{{ t('settings.layoutSettings.displayDensityOptions.medium') }}:</strong> {{ t('settings.layoutSettings.displayDensityDetails.medium') }}</li>
|
||||||
|
<li><strong>{{ t('settings.layoutSettings.displayDensityOptions.compact') }}:</strong> {{ t('settings.layoutSettings.displayDensityDetails.compact') }}</li>
|
||||||
|
</ul>
|
||||||
|
<span class="warning-text">{{ t('settings.layoutSettings.displayDensityWarning') }}</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="setting-item">
|
||||||
|
<div class="setting-row">
|
||||||
|
<div class="setting-info">
|
||||||
|
<label id="recipesLayoutLabel">
|
||||||
|
{{ t('settings.layoutSettings.recipesLayout') }}
|
||||||
|
<i class="fas fa-info-circle info-icon" data-tooltip="{{ t('settings.layoutSettings.recipesLayoutHelp') }}"></i>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
<div class="setting-control layout-options-control">
|
||||||
|
<div id="recipesLayoutOptions" class="layout-options" role="radiogroup" aria-label="{{ t('settings.layoutSettings.recipesLayout') }}" aria-labelledby="recipesLayoutLabel">
|
||||||
|
<button type="button" class="layout-option" data-recipes-layout="grid" onclick="settingsManager.saveRecipesLayout('grid')" role="radio" aria-checked="true">
|
||||||
|
<span class="layout-option-preview layout-preview-grid" aria-hidden="true"><span></span><span></span><span></span><span></span></span>
|
||||||
|
<span class="layout-option-label">{{ t('settings.layoutSettings.recipesLayoutOptions.grid') }}</span>
|
||||||
|
</button>
|
||||||
|
<button type="button" class="layout-option" data-recipes-layout="masonry" onclick="settingsManager.saveRecipesLayout('masonry')" role="radio" aria-checked="false">
|
||||||
|
<span class="layout-option-preview layout-preview-masonry" aria-hidden="true"><span></span><span></span><span></span></span>
|
||||||
|
<span class="layout-option-label">{{ t('settings.layoutSettings.recipesLayoutOptions.masonry') }}</span>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{{ sm.setting_select('modelNameDisplay', 'model_name_display', 'settings.layoutSettings.modelNameDisplay', [
|
||||||
|
('model_name', 'settings.layoutSettings.modelNameDisplayOptions.modelName'),
|
||||||
|
('file_name', 'settings.layoutSettings.modelNameDisplayOptions.fileName'),
|
||||||
|
], 'settings.layoutSettings.modelNameDisplayHelp') }}
|
||||||
|
|
||||||
|
<!-- Group by model toggle -->
|
||||||
|
{{ sm.setting_toggle('groupByModel', 'group_by_model', 'settings.layoutSettings.groupByModel', 'settings.layoutSettings.groupByModelHelp') }}
|
||||||
|
|
||||||
|
{{ sm.setting_select('cardInfoDisplay', 'card_info_display', 'settings.layoutSettings.cardInfoDisplay', [
|
||||||
|
('always', 'settings.layoutSettings.cardInfoDisplayOptions.always'),
|
||||||
|
('hover', 'settings.layoutSettings.cardInfoDisplayOptions.hover'),
|
||||||
|
], 'settings.layoutSettings.cardInfoDisplayHelp') }}
|
||||||
|
|
||||||
|
<div class="setting-item">
|
||||||
|
<div class="setting-row">
|
||||||
|
<div class="setting-info">
|
||||||
|
<label for="cardBlurAmount">
|
||||||
|
{{ t('settings.layoutSettings.cardBlurAmount') }}
|
||||||
|
<i class="fas fa-info-circle info-icon" data-tooltip="{{ t('settings.layoutSettings.cardBlurAmountHelp') }}"></i>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
<div class="setting-control range-control">
|
||||||
|
<input type="range" id="cardBlurAmount" min="0" max="20" value="8" step="1"
|
||||||
|
oninput="var pct = (this.value / 20) * 100; this.style.setProperty('--range-fill', pct + '%'); document.getElementById('cardBlurAmountValue').textContent = this.value + 'px'"
|
||||||
|
onchange="settingsManager.saveRangeSetting('cardBlurAmount', 'cardBlurAmountValue', 'card_blur_amount')">
|
||||||
|
<span id="cardBlurAmountValue" class="range-value">8px</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{{ sm.setting_toggle('showVersionOnCard', 'show_version_on_card', 'settings.layoutSettings.showVersionOnCard', 'settings.layoutSettings.showVersionOnCardHelp') }}
|
||||||
|
|
||||||
|
{{ sm.setting_select('modelCardFooterAction', 'model_card_footer_action', 'settings.layoutSettings.modelCardFooterAction', [
|
||||||
|
('example_images', 'settings.layoutSettings.modelCardFooterActionOptions.exampleImages'),
|
||||||
|
('replace_preview', 'settings.layoutSettings.modelCardFooterActionOptions.replacePreview'),
|
||||||
|
], 'settings.layoutSettings.modelCardFooterActionHelp') }}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- License Icons -->
|
||||||
|
<div class="settings-subsection">
|
||||||
|
{{ sm.subsection_header('settings.sections.licenseIcons') }}
|
||||||
|
{{ sm.setting_toggle('useNewLicenseIcons', 'use_new_license_icons', 'settings.licenseIcons.useNewStyle', 'settings.licenseIcons.useNewStyleHelp') }}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Miscellaneous -->
|
||||||
|
<div class="settings-subsection">
|
||||||
|
{{ sm.subsection_header('settings.sections.misc') }}
|
||||||
|
{{ sm.setting_select('loraSyntaxFormat', 'lora_syntax_format', 'settings.misc.loraSyntaxFormat', [
|
||||||
|
('full', 'settings.misc.loraSyntaxFormatOptions.full'),
|
||||||
|
('legacy', 'settings.misc.loraSyntaxFormatOptions.legacy'),
|
||||||
|
], 'settings.misc.loraSyntaxFormatHelp') }}
|
||||||
|
{{ sm.setting_toggle('includeTriggerWords', 'include_trigger_words', 'settings.misc.includeTriggerWords', 'settings.misc.includeTriggerWordsHelp') }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
@@ -0,0 +1,482 @@
|
|||||||
|
{% import 'components/modals/settings/_macros.html' as sm with context %}
|
||||||
|
{% set template_preset_options = [
|
||||||
|
('', 'settings.downloadPathTemplates.templateOptions.flatStructure'),
|
||||||
|
('{base_model}', 'settings.downloadPathTemplates.templateOptions.byBaseModel'),
|
||||||
|
('{author}', 'settings.downloadPathTemplates.templateOptions.byAuthor'),
|
||||||
|
('{first_tag}', 'settings.downloadPathTemplates.templateOptions.byFirstTag'),
|
||||||
|
('{base_model}/{first_tag}', 'settings.downloadPathTemplates.templateOptions.baseModelFirstTag'),
|
||||||
|
('{base_model}/{author}', 'settings.downloadPathTemplates.templateOptions.baseModelAuthor'),
|
||||||
|
('{author}/{first_tag}', 'settings.downloadPathTemplates.templateOptions.authorFirstTag'),
|
||||||
|
('{base_model}/{author}/{first_tag}', 'settings.downloadPathTemplates.templateOptions.baseModelAuthorFirstTag'),
|
||||||
|
('custom', 'settings.downloadPathTemplates.templateOptions.customTemplate'),
|
||||||
|
] %}
|
||||||
|
<!-- Section 3: Library -->
|
||||||
|
<div id="section-library" class="settings-section" data-section="library">
|
||||||
|
<!-- Folder Settings -->
|
||||||
|
<div class="settings-subsection">
|
||||||
|
{{ sm.subsection_header('settings.sections.folderSettings') }}
|
||||||
|
<div class="setting-item">
|
||||||
|
<div class="setting-row">
|
||||||
|
<div class="setting-info">
|
||||||
|
<label for="librarySelect">
|
||||||
|
{{ t('settings.folderSettings.activeLibrary') }}
|
||||||
|
<i class="fas fa-info-circle info-icon" data-tooltip="{{ t('settings.folderSettings.activeLibraryHelp') }}"></i>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
<div class="setting-control select-control">
|
||||||
|
<select id="librarySelect" onchange="settingsManager.handleLibraryChange()">
|
||||||
|
<option value="">{{ t('settings.folderSettings.loadingLibraries') }}</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{{ sm.setting_select('defaultLoraRoot', 'default_lora_root', 'settings.folderSettings.defaultLoraRoot', [], 'settings.folderSettings.defaultLoraRootHelp') }}
|
||||||
|
|
||||||
|
{{ sm.setting_select('defaultCheckpointRoot', 'default_checkpoint_root', 'settings.folderSettings.defaultCheckpointRoot', [], 'settings.folderSettings.defaultCheckpointRootHelp') }}
|
||||||
|
|
||||||
|
{{ sm.setting_select('defaultUnetRoot', 'default_unet_root', 'settings.folderSettings.defaultUnetRoot', [], 'settings.folderSettings.defaultUnetRootHelp') }}
|
||||||
|
|
||||||
|
{{ sm.setting_select('defaultEmbeddingRoot', 'default_embedding_root', 'settings.folderSettings.defaultEmbeddingRoot', [], 'settings.folderSettings.defaultEmbeddingRootHelp') }}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Recipe Settings -->
|
||||||
|
<div class="settings-subsection">
|
||||||
|
{{ sm.subsection_header('settings.sections.recipeSettings') }}
|
||||||
|
{{ sm.setting_input('recipesPath', 'recipes_path', 'settings.folderSettings.recipesPath', 'settings.folderSettings.recipesPathPlaceholder', 'settings.folderSettings.recipesPathHelp') }}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Extra Folder Paths -->
|
||||||
|
<div class="settings-subsection">
|
||||||
|
<div class="settings-subsection-header">
|
||||||
|
<h4>
|
||||||
|
{{ t('settings.extraFolderPaths.title') }}
|
||||||
|
<i class="fas fa-sync-alt restart-required-icon" title="{{ t('settings.extraFolderPaths.restartRequired') }}"></i>
|
||||||
|
</h4>
|
||||||
|
</div>
|
||||||
|
<div class="setting-item">
|
||||||
|
<div class="input-help">
|
||||||
|
{{ t('settings.extraFolderPaths.description') }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- LoRA Paths -->
|
||||||
|
<div class="setting-item">
|
||||||
|
<div class="setting-row">
|
||||||
|
<div class="setting-info">
|
||||||
|
<label>{{ t('settings.extraFolderPaths.modelTypes.lora') }}</label>
|
||||||
|
</div>
|
||||||
|
<div class="setting-control">
|
||||||
|
<button type="button" class="add-mapping-btn" onclick="settingsManager.addExtraFolderPathRow('loras')">
|
||||||
|
<i class="fas fa-plus"></i>
|
||||||
|
<span>{{ t('common.actions.add') }}</span>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="extra-folder-paths-container" id="extraFolderPaths-loras">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Checkpoint Paths -->
|
||||||
|
<div class="setting-item">
|
||||||
|
<div class="setting-row">
|
||||||
|
<div class="setting-info">
|
||||||
|
<label>{{ t('settings.extraFolderPaths.modelTypes.checkpoint') }}</label>
|
||||||
|
</div>
|
||||||
|
<div class="setting-control">
|
||||||
|
<button type="button" class="add-mapping-btn" onclick="settingsManager.addExtraFolderPathRow('checkpoints')">
|
||||||
|
<i class="fas fa-plus"></i>
|
||||||
|
<span>{{ t('common.actions.add') }}</span>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="extra-folder-paths-container" id="extraFolderPaths-checkpoints">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Diffusion Model (Unet) Paths -->
|
||||||
|
<div class="setting-item">
|
||||||
|
<div class="setting-row">
|
||||||
|
<div class="setting-info">
|
||||||
|
<label>{{ t('settings.extraFolderPaths.modelTypes.unet') }}</label>
|
||||||
|
</div>
|
||||||
|
<div class="setting-control">
|
||||||
|
<button type="button" class="add-mapping-btn" onclick="settingsManager.addExtraFolderPathRow('unet')">
|
||||||
|
<i class="fas fa-plus"></i>
|
||||||
|
<span>{{ t('common.actions.add') }}</span>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="extra-folder-paths-container" id="extraFolderPaths-unet">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Embedding Paths -->
|
||||||
|
<div class="setting-item">
|
||||||
|
<div class="setting-row">
|
||||||
|
<div class="setting-info">
|
||||||
|
<label>{{ t('settings.extraFolderPaths.modelTypes.embedding') }}</label>
|
||||||
|
</div>
|
||||||
|
<div class="setting-control">
|
||||||
|
<button type="button" class="add-mapping-btn" onclick="settingsManager.addExtraFolderPathRow('embeddings')">
|
||||||
|
<i class="fas fa-plus"></i>
|
||||||
|
<span>{{ t('common.actions.add') }}</span>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="extra-folder-paths-container" id="extraFolderPaths-embeddings">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Download Path Templates -->
|
||||||
|
<div class="settings-subsection">
|
||||||
|
<div class="settings-subsection-header">
|
||||||
|
<h4>
|
||||||
|
{{ t('settings.downloadPathTemplates.title') }}
|
||||||
|
<i class="fas fa-info-circle info-icon" data-tooltip="{{ t('settings.downloadPathTemplates.help') }}"></i>
|
||||||
|
</h4>
|
||||||
|
</div>
|
||||||
|
<div class="setting-item">
|
||||||
|
<div class="input-help">
|
||||||
|
<div class="placeholder-info">
|
||||||
|
<strong>{{ t('settings.downloadPathTemplates.availablePlaceholders') }}</strong>
|
||||||
|
<span class="placeholder-tag">{base_model}</span>
|
||||||
|
<span class="placeholder-tag">{author}</span>
|
||||||
|
<span class="placeholder-tag">{first_tag}</span>
|
||||||
|
<span class="placeholder-tag">{model_name}</span>
|
||||||
|
<span class="placeholder-tag">{version_name}</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="setting-item">
|
||||||
|
<div class="setting-row">
|
||||||
|
<div class="setting-info">
|
||||||
|
<label for="loraTemplatePreset">{{ t('settings.downloadPathTemplates.modelTypes.lora') }}</label>
|
||||||
|
</div>
|
||||||
|
<div class="setting-control select-control">
|
||||||
|
<select id="loraTemplatePreset" onchange="settingsManager.updateTemplatePreset('lora', this.value)">
|
||||||
|
{% for value, option_label in template_preset_options %}
|
||||||
|
<option value="{{ value }}">{{ t(option_label) }}</option>
|
||||||
|
{% endfor %}
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="template-custom-row" id="loraCustomRow" style="display: none;">
|
||||||
|
<input type="text" id="loraCustomTemplate" class="template-custom-input" placeholder="{{ t('settings.downloadPathTemplates.customTemplatePlaceholder') }}" />
|
||||||
|
<div class="template-validation" id="loraValidation"></div>
|
||||||
|
</div>
|
||||||
|
<div class="template-preview" id="loraPreview"></div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="setting-item">
|
||||||
|
<div class="setting-row">
|
||||||
|
<div class="setting-info">
|
||||||
|
<label for="checkpointTemplatePreset">{{ t('settings.downloadPathTemplates.modelTypes.checkpoint') }}</label>
|
||||||
|
</div>
|
||||||
|
<div class="setting-control select-control">
|
||||||
|
<select id="checkpointTemplatePreset" onchange="settingsManager.updateTemplatePreset('checkpoint', this.value)">
|
||||||
|
{% for value, option_label in template_preset_options %}
|
||||||
|
<option value="{{ value }}">{{ t(option_label) }}</option>
|
||||||
|
{% endfor %}
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="template-custom-row" id="checkpointCustomRow" style="display: none;">
|
||||||
|
<input type="text" id="checkpointCustomTemplate" class="template-custom-input" placeholder="{{ t('settings.downloadPathTemplates.customTemplatePlaceholder') }}" />
|
||||||
|
<div class="template-validation" id="checkpointValidation"></div>
|
||||||
|
</div>
|
||||||
|
<div class="template-preview" id="checkpointPreview"></div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="setting-item">
|
||||||
|
<div class="setting-row">
|
||||||
|
<div class="setting-info">
|
||||||
|
<label for="embeddingTemplatePreset">{{ t('settings.downloadPathTemplates.modelTypes.embedding') }}</label>
|
||||||
|
</div>
|
||||||
|
<div class="setting-control select-control">
|
||||||
|
<select id="embeddingTemplatePreset" onchange="settingsManager.updateTemplatePreset('embedding', this.value)">
|
||||||
|
{% for value, option_label in template_preset_options %}
|
||||||
|
<option value="{{ value }}">{{ t(option_label) }}</option>
|
||||||
|
{% endfor %}
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="template-custom-row" id="embeddingCustomRow" style="display: none;">
|
||||||
|
<input type="text" id="embeddingCustomTemplate" class="template-custom-input" placeholder="{{ t('settings.downloadPathTemplates.customTemplatePlaceholder') }}" />
|
||||||
|
<div class="template-validation" id="embeddingValidation"></div>
|
||||||
|
</div>
|
||||||
|
<div class="template-preview" id="embeddingPreview"></div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="setting-item">
|
||||||
|
<div class="setting-row">
|
||||||
|
<div class="setting-info">
|
||||||
|
<label>
|
||||||
|
{{ t('settings.downloadPathTemplates.baseModelPathMappings') }}
|
||||||
|
<i class="fas fa-info-circle info-icon" data-tooltip="{{ t('settings.downloadPathTemplates.baseModelPathMappingsHelp') }}"></i>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
<div class="setting-control">
|
||||||
|
<button type="button" class="add-mapping-btn" onclick="settingsManager.addMappingRow()">
|
||||||
|
<i class="fas fa-plus"></i>
|
||||||
|
<span>{{ t('settings.downloadPathTemplates.addMapping') }}</span>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="mappings-container">
|
||||||
|
<div id="baseModelMappingsContainer">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{{ sm.setting_toggle('skipPreviouslyDownloadedModelVersions', 'skip_previously_downloaded_model_versions', 'settings.skipPreviouslyDownloadedModelVersions.label', 'settings.skipPreviouslyDownloadedModelVersions.help') }}
|
||||||
|
|
||||||
|
<div class="setting-item">
|
||||||
|
<div class="setting-row">
|
||||||
|
<div class="setting-info">
|
||||||
|
<label for="downloadSkipBaseModelsToggle">
|
||||||
|
{{ t('settings.downloadSkipBaseModels.label') }}
|
||||||
|
<i class="fas fa-info-circle info-icon" data-tooltip="{{ t('settings.downloadSkipBaseModels.help') }}"></i>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
<div class="setting-control">
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
id="downloadSkipBaseModelsToggle"
|
||||||
|
class="secondary-btn base-model-skip-toggle"
|
||||||
|
aria-expanded="false"
|
||||||
|
>
|
||||||
|
<span id="downloadSkipBaseModelsSummary">{{ t('settings.downloadSkipBaseModels.summary.none') }}</span>
|
||||||
|
<span class="base-model-skip-toggle-label">{{ t('settings.downloadSkipBaseModels.actions.edit') }}</span>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div id="downloadSkipBaseModelsPanel" class="base-model-skip-panel" hidden>
|
||||||
|
<div class="base-model-skip-toolbar">
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
id="downloadSkipBaseModelsSearch"
|
||||||
|
class="base-model-skip-search"
|
||||||
|
placeholder="{{ t('settings.downloadSkipBaseModels.searchPlaceholder') }}"
|
||||||
|
/>
|
||||||
|
<button type="button" class="text-btn base-model-skip-clear" id="downloadSkipBaseModelsClear">
|
||||||
|
{{ t('settings.downloadSkipBaseModels.actions.clear') }}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<div id="downloadSkipBaseModelsContainer" class="base-model-skip-list"></div>
|
||||||
|
<div id="downloadSkipBaseModelsEmpty" class="base-model-skip-empty" hidden>
|
||||||
|
{{ t('settings.downloadSkipBaseModels.empty') }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="settings-input-error-message" id="downloadSkipBaseModelsError"></div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Priority Tags -->
|
||||||
|
<div class="setting-item priority-tags-item">
|
||||||
|
<div class="setting-row priority-tags-header-row">
|
||||||
|
<div class="setting-info priority-tags-header">
|
||||||
|
<label>
|
||||||
|
{{ t('settings.priorityTags.title') }}
|
||||||
|
<i class="fas fa-info-circle info-icon" data-tooltip="{{ t('settings.priorityTags.description') }}"></i>
|
||||||
|
</label>
|
||||||
|
<a class="settings-action-link priority-tags-help-link" href="https://github.com/willmiao/ComfyUI-Lora-Manager/wiki/Priority-Tags-Configuration-Guide" target="_blank" rel="noopener" aria-label="{{ t('settings.priorityTags.helpLinkLabel') }}" title="{{ t('settings.priorityTags.helpLinkLabel') }}">
|
||||||
|
<i class="fas fa-question-circle" aria-hidden="true"></i>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="priority-tags-tabs">
|
||||||
|
<input type="radio" id="priority-tags-tab-lora" name="priority-tags-tab" class="priority-tags-tab-input" checked>
|
||||||
|
<input type="radio" id="priority-tags-tab-checkpoint" name="priority-tags-tab" class="priority-tags-tab-input">
|
||||||
|
<input type="radio" id="priority-tags-tab-embedding" name="priority-tags-tab" class="priority-tags-tab-input">
|
||||||
|
|
||||||
|
<div class="priority-tags-tablist">
|
||||||
|
<label class="priority-tags-tab-label" for="priority-tags-tab-lora" id="priority-tags-tab-lora-label">{{ t('settings.priorityTags.modelTypes.lora') }}</label>
|
||||||
|
<label class="priority-tags-tab-label" for="priority-tags-tab-checkpoint" id="priority-tags-tab-checkpoint-label">{{ t('settings.priorityTags.modelTypes.checkpoint') }}</label>
|
||||||
|
<label class="priority-tags-tab-label" for="priority-tags-tab-embedding" id="priority-tags-tab-embedding-label">{{ t('settings.priorityTags.modelTypes.embedding') }}</label>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="priority-tags-panels">
|
||||||
|
<div class="priority-tags-panel" id="priority-tags-panel-lora">
|
||||||
|
<textarea id="loraPriorityTagsInput" class="priority-tags-input" placeholder="{{ t('settings.priorityTags.placeholder') }}"></textarea>
|
||||||
|
<div class="settings-input-error-message" id="loraPriorityTagsError"></div>
|
||||||
|
</div>
|
||||||
|
<div class="priority-tags-panel" id="priority-tags-panel-checkpoint">
|
||||||
|
<textarea id="checkpointPriorityTagsInput" class="priority-tags-input" placeholder="{{ t('settings.priorityTags.placeholder') }}"></textarea>
|
||||||
|
<div class="settings-input-error-message" id="checkpointPriorityTagsError"></div>
|
||||||
|
</div>
|
||||||
|
<div class="priority-tags-panel" id="priority-tags-panel-embedding">
|
||||||
|
<textarea id="embeddingPriorityTagsInput" class="priority-tags-input" placeholder="{{ t('settings.priorityTags.placeholder') }}"></textarea>
|
||||||
|
<div class="settings-input-error-message" id="embeddingPriorityTagsError"></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Version Scope -->
|
||||||
|
<div class="settings-subsection">
|
||||||
|
{{ sm.subsection_header('settings.sections.versionScope') }}
|
||||||
|
{{ sm.setting_select('versionGrouping', 'version_grouping', 'settings.versionGrouping.label', [
|
||||||
|
('same_base', 'settings.versionGrouping.options.sameBase'),
|
||||||
|
('any', 'settings.versionGrouping.options.any'),
|
||||||
|
], 'settings.versionGrouping.help') }}
|
||||||
|
{{ sm.setting_toggle('hideEarlyAccessUpdates', 'hide_early_access_updates', 'settings.hideEarlyAccessUpdates.label', 'settings.hideEarlyAccessUpdates.help') }}
|
||||||
|
{{ sm.setting_toggle('hidePaidUpdates', 'hide_paid_updates', 'settings.hidePaidUpdates.label', 'settings.hidePaidUpdates.help') }}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Example Images -->
|
||||||
|
<div class="settings-subsection">
|
||||||
|
{{ sm.subsection_header('settings.sections.exampleImages') }}
|
||||||
|
<div class="setting-item">
|
||||||
|
<div class="setting-row">
|
||||||
|
<div class="setting-info">
|
||||||
|
<label for="exampleImagesPath">{{ t('settings.exampleImages.downloadLocation') }} <i class="fas fa-sync-alt restart-required-icon" title="{{ t('settings.exampleImages.restartRequired') }}"></i></label>
|
||||||
|
</div>
|
||||||
|
<div class="setting-control path-control">
|
||||||
|
<input type="text" id="exampleImagesPath" placeholder="{{ t('settings.exampleImages.downloadLocationPlaceholder') }}" />
|
||||||
|
<button id="exampleImagesDownloadBtn" class="primary-btn">
|
||||||
|
<i class="fas fa-download"></i> <span id="exampleDownloadBtnText">{{ t('settings.exampleImages.download') }}</span>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{{ sm.setting_toggle('autoDownloadExampleImages', 'auto_download_example_images', 'settings.exampleImages.autoDownload', 'settings.exampleImages.autoDownloadHelp') }}
|
||||||
|
|
||||||
|
{{ sm.setting_toggle('optimizeExampleImages', 'optimize_example_images', 'settings.exampleImages.optimizeImages', 'settings.exampleImages.optimizeImagesHelp') }}
|
||||||
|
|
||||||
|
<div class="setting-item">
|
||||||
|
<div class="setting-row">
|
||||||
|
<div class="setting-info">
|
||||||
|
<label for="exampleImagesOpenMode">
|
||||||
|
{{ t('settings.exampleImages.openMode') }}
|
||||||
|
<i class="fas fa-info-circle info-icon" data-tooltip="{{ t('settings.exampleImages.openModeHelp') }}"></i>
|
||||||
|
<a class="settings-action-link" href="https://github.com/willmiao/ComfyUI-Lora-Manager/wiki/Remote-Open-for-Example-Images" target="_blank" rel="noopener" title="{{ t('settings.exampleImages.openModeWikiLink') }}">
|
||||||
|
<i class="fas fa-question-circle" aria-hidden="true"></i>
|
||||||
|
</a>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
<div class="setting-control select-control">
|
||||||
|
<select id="exampleImagesOpenMode" onchange="settingsManager.handleExampleImagesOpenModeChange()">
|
||||||
|
<option value="system">{{ t('settings.exampleImages.openModeOptions.system') }}</option>
|
||||||
|
<option value="clipboard">{{ t('settings.exampleImages.openModeOptions.clipboard') }}</option>
|
||||||
|
<option value="uri_template">{{ t('settings.exampleImages.openModeOptions.uriTemplate') }}</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="setting-item" id="exampleImagesLocalRootSetting" style="display: none;">
|
||||||
|
<div class="setting-row">
|
||||||
|
<div class="setting-info">
|
||||||
|
<label for="exampleImagesLocalRoot">
|
||||||
|
{{ t('settings.exampleImages.localRoot') }}
|
||||||
|
<i class="fas fa-info-circle info-icon" data-tooltip="{{ t('settings.exampleImages.localRootHelp') }}"></i>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
<div class="setting-control path-control">
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
id="exampleImagesLocalRoot"
|
||||||
|
placeholder="{{ t('settings.exampleImages.localRootPlaceholder') }}"
|
||||||
|
onchange="settingsManager.saveInputSetting('exampleImagesLocalRoot', 'example_images_local_root')" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="setting-item" id="exampleImagesUriTemplateSetting" style="display: none;">
|
||||||
|
<div class="setting-row">
|
||||||
|
<div class="setting-info">
|
||||||
|
<label for="exampleImagesOpenUriTemplate">
|
||||||
|
{{ t('settings.exampleImages.uriTemplate') }}
|
||||||
|
<i class="fas fa-info-circle info-icon" data-tooltip="{{ t('settings.exampleImages.uriTemplateHelp') }} {{ t('settings.exampleImages.uriTemplatePlaceholders') }}"></i>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
<div class="setting-control path-control">
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
id="exampleImagesOpenUriTemplate"
|
||||||
|
placeholder="{{ t('settings.exampleImages.uriTemplatePlaceholder') }}"
|
||||||
|
onchange="settingsManager.saveInputSetting('exampleImagesOpenUriTemplate', 'example_images_open_uri_template')" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Auto-organize -->
|
||||||
|
<div class="settings-subsection">
|
||||||
|
{{ sm.subsection_header('settings.sections.autoOrganize') }}
|
||||||
|
|
||||||
|
<!-- Auto-organize Exclusions -->
|
||||||
|
<div class="setting-item auto-organize-exclusions-item">
|
||||||
|
<div class="setting-row">
|
||||||
|
<div class="setting-info">
|
||||||
|
<label for="autoOrganizeExclusions">
|
||||||
|
{{ t('settings.autoOrganizeExclusions.label') }}
|
||||||
|
<i class="fas fa-info-circle info-icon" data-tooltip="{{ t('settings.autoOrganizeExclusions.help') }}"></i>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<textarea id="autoOrganizeExclusions" class="priority-tags-input auto-organize-exclusions-input" placeholder="{{ t('settings.autoOrganizeExclusions.placeholder') }}"></textarea>
|
||||||
|
<div class="settings-input-error-message" id="autoOrganizeExclusionsError"></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Metadata -->
|
||||||
|
<div class="settings-subsection">
|
||||||
|
{{ sm.subsection_header('settings.sections.metadata') }}
|
||||||
|
|
||||||
|
<!-- Metadata Refresh Skip Paths -->
|
||||||
|
<div class="setting-item">
|
||||||
|
<div class="setting-row">
|
||||||
|
<div class="setting-info">
|
||||||
|
<label for="metadataRefreshSkipPaths">
|
||||||
|
{{ t('settings.metadataRefreshSkipPaths.label') }}
|
||||||
|
<i class="fas fa-info-circle info-icon" data-tooltip="{{ t('settings.metadataRefreshSkipPaths.help') }}"></i>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<textarea id="metadataRefreshSkipPaths" class="priority-tags-input auto-organize-exclusions-input" placeholder="{{ t('settings.metadataRefreshSkipPaths.placeholder') }}"></textarea>
|
||||||
|
<div class="settings-input-error-message" id="metadataRefreshSkipPathsError"></div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- CivArchive API provider toggle -->
|
||||||
|
{{ sm.setting_toggle('enableCivarchiveApi', 'enable_civarchive_api', 'settings.metadataArchive.enableCivarchiveApi', 'settings.metadataArchive.enableCivarchiveApiHelp') }}
|
||||||
|
|
||||||
|
<!-- Metadata Archive DB -->
|
||||||
|
{{ sm.setting_toggle('enableMetadataArchive', 'enable_metadata_archive_db', 'settings.metadataArchive.enableArchiveDb', 'settings.metadataArchive.enableArchiveDbHelp') }}
|
||||||
|
|
||||||
|
<div class="setting-item">
|
||||||
|
<div class="metadata-archive-status" id="metadataArchiveStatus">
|
||||||
|
<!-- Status will be populated by JavaScript -->
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="setting-item">
|
||||||
|
<div class="setting-row">
|
||||||
|
<div class="setting-info">
|
||||||
|
<label>
|
||||||
|
{{ t('settings.metadataArchive.management') }}
|
||||||
|
<i class="fas fa-info-circle info-icon" data-tooltip="{{ t('settings.metadataArchive.managementHelp') }}"></i>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
<div class="setting-control">
|
||||||
|
<button type="button" id="downloadMetadataArchiveBtn" class="primary-btn" onclick="settingsManager.downloadMetadataArchive()">
|
||||||
|
{{ t('settings.metadataArchive.downloadButton') }}
|
||||||
|
</button>
|
||||||
|
<button type="button" id="removeMetadataArchiveBtn" class="danger-btn" onclick="settingsManager.removeMetadataArchive()" style="margin-left: 10px;">
|
||||||
|
{{ t('settings.metadataArchive.removeButton') }}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Metadata provider fallback order -->
|
||||||
|
{{ sm.setting_select('metadataProviderOrder', 'metadata_provider_order', 'settings.metadataArchive.providerOrder', [
|
||||||
|
('civitai_archive_sqlite', 'settings.metadataArchive.providerOrderCivitaiArchiveSqlite'),
|
||||||
|
('civitai_sqlite_archive', 'settings.metadataArchive.providerOrderCivitaiSqliteArchive'),
|
||||||
|
], 'settings.metadataArchive.providerOrderHelp') }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user