Files
willmiao-ComfyUI-Lora-Manager/templates/components/controls.html
T
Will Miao 965bfd33cd feat(controls): keep model counts readable in large libraries
Counts in the filter panel chips (base models, tags, model types), the All Types
dropdown and the Refresh scope rows were rendered as raw digits, so a library with
10k+ models turned every chip into a five digit pill that dominated the row it sat
in — and the Refresh menu grouped its numbers while the filter panel did not.

One formatter now renders all of them (`formatCount`): exact and locale-grouped
below 10,000, abbreviated to three significant digits above it, and abbreviated
only where the locale's Intl compact form is a real unit and narrower than the
plain number — German keeps 18.475 instead of the rounded 18.500, Russian keeps
18 475 instead of the wider 18,5 тыс.

Their hover text is always present and always exact ({name} — {count} models, and
... recipes on the Recipes page): an affordance that only exists above a threshold
is undiscoverable, and the tooltip is also what reveals a chip label that CSS has
ellipsized.

Chips gain a two-slot layout (the name ellipsizes, the count keeps a tabular
right-aligned slot) so a long tag name can no longer push the count out of the
chip; Refresh rows move from float:right to a flex row, which keeps the counts in
a right-hand column and lets a long root label ellipsize. The All Types group
tooltip moves onto its trigger so it stops leaking onto the menu options.

The two new strings ship as [TODO: Translate] placeholders in the nine locales;
the translation pass is the next commit.
2026-10-18 18:34:16 +08:00

172 lines
12 KiB
HTML

<div class="controls">
{% if page_id != 'recipes' %}
<div id="excludedViewBanner" class="excluded-view-banner hidden">
<div class="excluded-view-banner__content">
<div class="excluded-view-banner__title">
<i class="fas fa-eye-slash"></i>
<span>{{ t('globalContextMenu.manageExcludedModels.label', default='Excluded Models') }}</span>
</div>
<button id="excludedViewBackBtn" class="excluded-view-banner__back">
<i class="fas fa-arrow-left"></i>
<span>{{ t('common.actions.back', default='Back') }}</span>
</button>
</div>
</div>
{% endif %}
<div class="actions">
<div class="action-buttons">
<div title="{% if page_id == 'recipes' %}{{ t('recipes.controls.sort.title') }}{% else %}{{ t('loras.controls.sort.title') }}{% endif %}" class="control-group sort-dropdown-group dropdown-group" data-sort-dropdown>
<button type="button" class="sort-trigger" aria-haspopup="listbox" aria-expanded="false">
<span class="sort-trigger__label"></span>
<i class="fas fa-caret-down sort-trigger__caret" aria-hidden="true"></i>
</button>
<div class="dropdown-menu sort-dropdown-menu" role="listbox"></div>
<select id="sortSelect" class="sort-select-native" tabindex="-1" aria-hidden="true">
<optgroup label="{{ t('loras.controls.sort.name') }}">
<option value="name:asc">{{ t('loras.controls.sort.nameAsc') }}</option>
<option value="name:desc">{{ t('loras.controls.sort.nameDesc') }}</option>
</optgroup>
<optgroup label="{% if page_id == 'recipes' %}{{ t('recipes.controls.sort.date') }}{% else %}{{ t('loras.controls.sort.date') }}{% endif %}">
<option value="date:desc">{{ t('loras.controls.sort.dateDesc') }}</option>
<option value="date:asc">{{ t('loras.controls.sort.dateAsc') }}</option>
</optgroup>
{% if page_id != 'recipes' %}
<optgroup label="{{ t('loras.controls.sort.size') }}">
<option value="size:desc">{{ t('loras.controls.sort.sizeDesc') }}</option>
<option value="size:asc">{{ t('loras.controls.sort.sizeAsc') }}</option>
</optgroup>
{% endif %}
{% if page_id != 'embeddings' and page_id != 'recipes' and page_id != 'other' %}
<optgroup label="{{ t('loras.controls.sort.usage', default='Usage') }}">
<option value="usage:desc">{{ t('loras.controls.sort.usageDesc', default='Times used (high to low)') }}</option>
<option value="usage:asc">{{ t('loras.controls.sort.usageAsc', default='Times used (low to high)') }}</option>
</optgroup>
{% endif %}
{% if page_id != 'recipes' %}
<optgroup class="sort-option-versions-count" label="{{ t('loras.controls.sort.versionsCount', default='Local Versions') }}">
<option value="versions_count:desc">{{ t('loras.controls.sort.versionsCountDesc', default='Most versions first') }}</option>
<option value="versions_count:asc">{{ t('loras.controls.sort.versionsCountAsc', default='Fewest versions first') }}</option>
</optgroup>
{% endif %}
{% if page_id == 'recipes' %}
<optgroup label="{{ t('recipes.controls.sort.lorasCount') }}">
<option value="loras_count:desc">{{ t('recipes.controls.sort.lorasCountDesc') }}</option>
<option value="loras_count:asc">{{ t('recipes.controls.sort.lorasCountAsc') }}</option>
</optgroup>
{% endif %}
{% if page_id == 'recipes' %}
<optgroup label="{{ t('recipes.controls.sort.opened', default='Recently Opened') }}">
<option value="opened:desc">{{ t('recipes.controls.sort.openedDesc', default='Recently opened') }}</option>
</optgroup>
{% endif %}
<optgroup label="{{ t('loras.controls.sort.random', default='Random') }}">
<option value="random">{{ t('loras.controls.sort.randomAction', default='Randomize (shuffle)') }}</option>
</optgroup>
</select>
</div>
{% if page_id in ['loras', 'checkpoints', 'other'] %}
{# The tooltip sits on the trigger, not the group: a group-level title
leaks onto every menu option that has no title of its own. #}
<div class="control-group model-type-dropdown-group dropdown-group hidden" id="modelTypeDropdown">
<button type="button" class="model-type-trigger" aria-haspopup="listbox" aria-expanded="false" title="{{ t('loras.controls.modelType.title') }}">
<span class="model-type-trigger__label"></span>
<i class="fas fa-caret-down model-type-trigger__caret" aria-hidden="true"></i>
</button>
<div class="dropdown-menu model-type-dropdown-menu" role="listbox"></div>
</div>
{% endif %}
<div title="{% if page_id == 'recipes' %}{{ t('recipes.controls.refresh.title') }}{% else %}{{ t('loras.controls.refresh.title') }}{% endif %}" class="control-group dropdown-group">
<button data-action="refresh" class="dropdown-main"><span><span>{{ t('common.actions.refresh') }}</span> <kbd class="shortcut-key">R</kbd></span></button>
<button class="dropdown-toggle" aria-label="Show refresh options">
<i class="fas fa-caret-down"></i>
</button>
<div class="dropdown-menu">
<div class="dropdown-item" data-action="full-rebuild" title="{% if page_id == 'recipes' %}{{ t('recipes.controls.refresh.fullTooltip', default='Rebuild cache - full rescan of all recipe files') }}{% else %}{{ t('loras.controls.refresh.fullTooltip') }}{% endif %}">
<i class="fas fa-tools"></i> <span>{% if page_id == 'recipes' %}{{ t('loras.controls.refresh.full', default='Rebuild Cache') }}{% else %}{{ t('loras.controls.refresh.full') }}{% endif %}</span>
</div>
{% if page_id != 'recipes' %}
<!-- Per-root scan scope, filled by PageControls.loadScanScopeMenu() -->
<div class="dropdown-separator"></div>
<div class="dropdown-section-title">{{ t('loras.controls.refresh.scopeSection') }}</div>
<div id="refreshScopeMenu" class="dropdown-scope-list"></div>
{% endif %}
</div>
</div>
{% if page_id != 'recipes' %}
<div class="control-group">
<button data-action="fetch" title="{{ t('loras.controls.fetch.title') }}"><span><span>{{ t('loras.controls.fetch.action') }}</span> <kbd class="shortcut-key">F</kbd></span></button>
</div>
<div class="control-group">
<button data-action="download" title="{{ t('loras.controls.download.title') }}">
<span><span>{{ t('loras.controls.download.action') }}</span> <kbd class="shortcut-key">D</kbd></span>
</button>
</div>
{% endif %}
{% if page_id == 'recipes' %}
<div title="{{ t('recipes.controls.import.title') }}" class="control-group">
<button onclick="importManager.showImportModal()"><span>{{ t('recipes.controls.import.action') }}</span></button>
</div>
<div title="{{ t('recipes.batchImport.title') }}" class="control-group">
<button onclick="batchImportManager.showModal()"><span>{{ t('recipes.batchImport.action') }}</span></button>
</div>
{% endif %}
<div class="control-group">
<button id="bulkOperationsBtn" data-action="bulk" title="{{ t('loras.controls.bulk.title') }}">
<span><span>{{ t('loras.controls.bulk.action') }}</span> <kbd class="shortcut-key">B</kbd></span>
</button>
</div>
<div class="control-group">
<button id="findDuplicatesBtn" data-action="find-duplicates" title="{{ t('loras.controls.duplicates.title') }}">
<span>{{ t('loras.controls.duplicates.action') }}</span>
<span id="duplicatesBadge" class="badge"></span>
</button>
</div>
<div class="control-group">
<button id="favoriteFilterBtn" data-action="toggle-favorites" class="favorite-filter" title="{{ t('loras.controls.favorites.title') }}">
<i class="fas fa-star"></i> <span>{{ t('loras.controls.favorites.action') }}</span>
</button>
</div>
{% if page_id != 'recipes' %}
<div class="control-group dropdown-group update-filter-group">
<button id="updateFilterBtn" data-action="toggle-updates" class="dropdown-main update-filter" title="{{ t('loras.controls.updates.title') }}">
<i class="fas fa-exclamation-circle"></i> <span>{{ t('loras.controls.updates.action') }}</span>
</button>
<button id="updateFilterMenuToggle" class="dropdown-toggle" aria-label="{{ t('loras.controls.updates.menuLabel') }}">
<i class="fas fa-caret-down"></i>
</button>
<div class="dropdown-menu">
<div id="checkUpdatesMenuItem" class="dropdown-item" data-action="check-updates" title="{{ t('loras.controls.updates.checkTooltip') }}">
<i class="fas fa-sync-alt"></i> <span>{{ t('loras.controls.updates.check') }}</span>
</div>
</div>
</div>
{% endif %}
<div id="customFilterIndicator" class="control-group hidden">
<div class="filter-active">
<i class="fas fa-filter"></i> <span class="customFilterText" title=""></span>
<i class="fas fa-times-circle clear-filter"></i>
</div>
</div>
</div>
<div class="controls-right">
{% if page_id == 'recipes' %}
<div class="control-group layout-toggle-group" role="group" aria-label="{{ t('recipes.controls.layout.title') }}" title="{{ t('recipes.controls.layout.title') }}">
<button type="button" class="layout-toggle-btn" data-recipes-layout="grid" aria-pressed="false" title="{{ t('recipes.controls.layout.grid') }}" aria-label="{{ t('recipes.controls.layout.grid') }}">
<i class="fas fa-th-large" aria-hidden="true"></i>
</button>
<button type="button" class="layout-toggle-btn" data-recipes-layout="masonry" aria-pressed="false" title="{{ t('recipes.controls.layout.masonry') }}" aria-label="{{ t('recipes.controls.layout.masonry') }}">
<i class="fas fa-columns" aria-hidden="true"></i>
</button>
</div>
{% endif %}
<div class="control-group doctor-control-group">
<button id="doctorTriggerBtn" class="doctor-trigger" title="{{ t('doctor.buttonTitle', default='Run diagnostics and common fixes') }}">
<span>{{ t('doctor.title', default='Doctor') }}</span>
<span id="doctorStatusBadge" class="doctor-status-badge hidden" aria-hidden="true"></span>
</button>
</div>
</div>
</div>
</div>