mirror of
https://github.com/willmiao/ComfyUI-Lora-Manager.git
synced 2026-03-21 21:22:11 -03:00
Add ability to save and manage filter presets for quick access to commonly used filter combinations. Features: - Save current active filters as named presets - Apply presets with one click (shows active state with checkmark) - Toggle presets on/off like regular filters - Delete presets - Presets stored in browser localStorage per page - Default "WAN Models" preset for LoRA page - Visual feedback: active preset highlighted, filter tags show blue outlines - Inline "+ Add" button flows with preset tags UI/UX improvements: - Preset tags use same compact style as filter tags - Active preset deactivates when filters manually changed - Missing tags from presets automatically added to tag list - Clear filters properly resets preset state
186 lines
8.7 KiB
HTML
186 lines
8.7 KiB
HTML
<header class="app-header">
|
|
<div class="header-container">
|
|
<div class="header-branding">
|
|
<a href="/loras" class="logo-link">
|
|
<img src="/loras_static/images/favicon-32x32.png" alt="LoRA Manager" class="app-logo">
|
|
<span class="app-title">{{ t('header.appTitle') }}</span>
|
|
</a>
|
|
</div>
|
|
{% set current_path = request.path %}
|
|
{% if current_path.startswith('/loras/recipes') %}
|
|
{% set current_page = 'recipes' %}
|
|
{% elif current_path.startswith('/checkpoints') %}
|
|
{% set current_page = 'checkpoints' %}
|
|
{% elif current_path.startswith('/embeddings') %}
|
|
{% set current_page = 'embeddings' %}
|
|
{% elif current_path.startswith('/statistics') %}
|
|
{% set current_page = 'statistics' %}
|
|
{% else %}
|
|
{% set current_page = 'loras' %}
|
|
{% endif %}
|
|
{% set search_disabled = current_page == 'statistics' %}
|
|
{% set search_placeholder_key = 'header.search.notAvailable' if search_disabled else 'header.search.placeholders.' ~
|
|
current_page %}
|
|
{% set header_search_class = 'header-search disabled' if search_disabled else 'header-search' %}
|
|
<nav class="main-nav">
|
|
<a href="/loras" class="nav-item{% if current_path == '/loras' %} active{% endif %}" id="lorasNavItem">
|
|
<i class="fas fa-layer-group"></i> <span>{{ t('header.navigation.loras') }}</span>
|
|
</a>
|
|
<a href="/loras/recipes" class="nav-item{% if current_path.startswith('/loras/recipes') %} active{% endif %}"
|
|
id="recipesNavItem">
|
|
<i class="fas fa-book-open"></i> <span>{{ t('header.navigation.recipes') }}</span>
|
|
</a>
|
|
<a href="/checkpoints" class="nav-item{% if current_path.startswith('/checkpoints') %} active{% endif %}"
|
|
id="checkpointsNavItem">
|
|
<i class="fas fa-check-circle"></i> <span>{{ t('header.navigation.checkpoints') }}</span>
|
|
</a>
|
|
<a href="/embeddings" class="nav-item{% if current_path.startswith('/embeddings') %} active{% endif %}"
|
|
id="embeddingsNavItem">
|
|
<i class="fas fa-code"></i> <span>{{ t('header.navigation.embeddings') }}</span>
|
|
</a>
|
|
<a href="/statistics" class="nav-item{% if current_path.startswith('/statistics') %} active{% endif %}"
|
|
id="statisticsNavItem">
|
|
<i class="fas fa-chart-bar"></i> <span>{{ t('header.navigation.statistics') }}</span>
|
|
</a>
|
|
</nav>
|
|
|
|
<!-- Context-aware search container -->
|
|
<div class="{{ header_search_class }}" id="headerSearch">
|
|
<div class="search-container">
|
|
<input type="text" id="searchInput" placeholder="{{ t(search_placeholder_key) }}" {% if search_disabled %}
|
|
disabled{% endif %} />
|
|
<i class="fas fa-search search-icon"></i>
|
|
<button class="search-options-toggle" id="searchOptionsToggle" title="{{ t('header.search.options') }}" {% if
|
|
search_disabled %} disabled aria-disabled="true" {% endif %}>
|
|
<i class="fas fa-sliders-h"></i>
|
|
</button>
|
|
<button class="search-filter-toggle" id="filterButton" title="{{ t('header.filter.title') }}" {% if
|
|
search_disabled %} disabled aria-disabled="true" {% endif %}>
|
|
<i class="fas fa-filter"></i>
|
|
<span class="filter-badge" id="activeFiltersCount" style="display: none">0</span>
|
|
</button>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="header-actions">
|
|
<!-- Integrated corner controls -->
|
|
<div class="header-controls">
|
|
<div class="theme-toggle" title="{{ t('header.theme.toggle') }}">
|
|
<i class="fas fa-moon dark-icon"></i>
|
|
<i class="fas fa-sun light-icon"></i>
|
|
<i class="fas fa-adjust auto-icon"></i>
|
|
</div>
|
|
<div class="settings-toggle" title="{{ t('common.actions.settings') }}">
|
|
<i class="fas fa-cog"></i>
|
|
</div>
|
|
<div class="help-toggle" id="helpToggleBtn" title="{{ t('common.actions.help') }}">
|
|
<i class="fas fa-question-circle"></i>
|
|
<span class="update-badge"></span>
|
|
</div>
|
|
<div class="update-toggle" id="updateToggleBtn" title="{{ t('header.actions.notifications') }}">
|
|
<i class="fas fa-bell"></i>
|
|
<span class="update-badge"></span>
|
|
</div>
|
|
<div class="support-toggle" id="supportToggleBtn" title="{{ t('header.actions.support') }}">
|
|
<i class="fas fa-heart"></i>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</header>
|
|
|
|
<!-- Add search options panel with context-aware options -->
|
|
<div id="searchOptionsPanel" class="search-options-panel hidden">
|
|
<div class="options-header">
|
|
<h3>{{ t('header.search.options') }}</h3>
|
|
<button class="close-options-btn" id="closeSearchOptions">
|
|
<i class="fas fa-times"></i>
|
|
</button>
|
|
</div>
|
|
<div class="options-section">
|
|
<h4>{{ t('header.search.searchIn') }}</h4>
|
|
<div class="search-option-tags">
|
|
{% if request.path == '/loras/recipes' %}
|
|
<div class="search-option-tag active" data-option="title">{{ t('header.search.filters.title') }}</div>
|
|
<div class="search-option-tag active" data-option="tags">{{ t('header.search.filters.tags') }}</div>
|
|
<div class="search-option-tag active" data-option="loraName">{{ t('header.search.filters.loraName') }}</div>
|
|
<div class="search-option-tag active" data-option="loraModel">{{ t('header.search.filters.loraModel') }}</div>
|
|
<div class="search-option-tag active" data-option="prompt">{{ t('header.search.filters.prompt') }}</div>
|
|
{% elif request.path == '/checkpoints' %}
|
|
<div class="search-option-tag active" data-option="filename">{{ t('header.search.filters.filename') }}</div>
|
|
<div class="search-option-tag active" data-option="modelname">{{ t('header.search.filters.modelname') }}</div>
|
|
<div class="search-option-tag active" data-option="tags">{{ t('header.search.filters.tags') }}</div>
|
|
<div class="search-option-tag" data-option="creator">{{ t('header.search.filters.creator') }}</div>
|
|
{% elif request.path == '/embeddings' %}
|
|
<div class="search-option-tag active" data-option="filename">{{ t('header.search.filters.filename') }}</div>
|
|
<div class="search-option-tag active" data-option="modelname">{{ t('header.search.filters.modelname') }}</div>
|
|
<div class="search-option-tag active" data-option="tags">{{ t('header.search.filters.tags') }}</div>
|
|
<div class="search-option-tag" data-option="creator">{{ t('header.search.filters.creator') }}</div>
|
|
{% else %}
|
|
<!-- Default options for LoRAs page -->
|
|
<div class="search-option-tag active" data-option="filename">{{ t('header.search.filters.filename') }}</div>
|
|
<div class="search-option-tag active" data-option="modelname">{{ t('header.search.filters.modelname') }}</div>
|
|
<div class="search-option-tag active" data-option="tags">{{ t('header.search.filters.tags') }}</div>
|
|
<div class="search-option-tag" data-option="creator">{{ t('header.search.filters.creator') }}</div>
|
|
{% endif %}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Add filter panel -->
|
|
<div id="filterPanel" class="filter-panel hidden">
|
|
<div class="filter-header">
|
|
<h3>{{ t('header.filter.title') }}</h3>
|
|
<button class="close-filter-btn" onclick="filterManager.closeFilterPanel()">
|
|
<i class="fas fa-times"></i>
|
|
</button>
|
|
</div>
|
|
|
|
<!-- Presets Section -->
|
|
<div class="filter-section presets-section">
|
|
<h4>{{ t('header.filter.presets') }}</h4>
|
|
<div class="filter-presets" id="filterPresets">
|
|
<div class="no-presets">{{ t('header.filter.noPresets') }}</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="filter-section">
|
|
<h4>{{ t('header.filter.baseModel') }}</h4>
|
|
<div class="filter-tags" id="baseModelTags">
|
|
<!-- Tags will be dynamically inserted here -->
|
|
</div>
|
|
</div>
|
|
<div class="filter-section">
|
|
<h4>{{ t('header.filter.modelTags') }}</h4>
|
|
<div class="filter-tags" id="modelTagsFilter">
|
|
<!-- Top tags will be dynamically inserted here -->
|
|
<div class="tags-loading">{{ t('common.status.loading') }}</div>
|
|
</div>
|
|
</div>
|
|
{% if current_page == 'loras' %}
|
|
<div class="filter-section">
|
|
<h4>{{ t('header.filter.modelTypes') }}</h4>
|
|
<div class="filter-tags" id="modelTypeTags">
|
|
<div class="tags-loading">{{ t('common.status.loading') }}</div>
|
|
</div>
|
|
</div>
|
|
{% endif %}
|
|
{% if current_page != 'recipes' %}
|
|
<div class="filter-section">
|
|
<h4>{{ t('header.filter.license') }}</h4>
|
|
<div class="filter-tags">
|
|
<div class="filter-tag license-tag" data-license="noCredit">
|
|
{{ t('header.filter.noCreditRequired') }}
|
|
</div>
|
|
<div class="filter-tag license-tag" data-license="allowSelling">
|
|
{{ t('header.filter.allowSellingGeneratedContent') }}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
{% endif %}
|
|
<div class="filter-actions">
|
|
<button class="clear-filters-btn" onclick="filterManager.clearFilters()">
|
|
{{ t('header.filter.clearAll') }}
|
|
</button>
|
|
</div>
|
|
</div> |