feat(settings): split overloaded Library tab into a new Organization tab

Move download path templates, priority tags, and auto-organize
exclusions out of the Library settings section into a dedicated
Organization section, so Library keeps location-focused settings
(roots, extra paths, example images, metadata) and Organization holds
file-arrangement rules. Translated settings.nav.organization for all
locales.
This commit is contained in:
Will Miao
2026-09-19 07:38:07 +08:00
parent c8c84bfc54
commit 327da0465b
14 changed files with 257 additions and 216 deletions
@@ -1,15 +1,4 @@
{% 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 -->
@@ -187,192 +176,6 @@
</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') }}
@@ -463,25 +266,6 @@
</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') }}
@@ -0,0 +1,219 @@
{% 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 4: Organization -->
<div id="section-organization" class="settings-section" data-section="organization">
<!-- 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>
<!-- 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>
</div>
@@ -36,6 +36,7 @@
<button type="button" class="settings-nav-item active" data-section="general">{{ t('settings.nav.general') }}</button>
<button type="button" class="settings-nav-item" data-section="interface">{{ t('settings.nav.interface') }}</button>
<button type="button" class="settings-nav-item" data-section="library">{{ t('settings.nav.library') }}</button>
<button type="button" class="settings-nav-item" data-section="organization">{{ t('settings.nav.organization') }}</button>
</li>
</ul>
</nav>
@@ -46,6 +47,7 @@
{% include 'components/modals/settings/general.html' %}
{% include 'components/modals/settings/interface.html' %}
{% include 'components/modals/settings/library.html' %}
{% include 'components/modals/settings/organization.html' %}
</div>
</div>
</div>