mirror of
https://github.com/willmiao/ComfyUI-Lora-Manager.git
synced 2026-09-21 03:01:27 -03:00
c6c44b741a
The sidebar used the models-only folder list while the download and move destination pickers list every directory, so a model downloaded into an empty category folder did not appear in the sidebar at all. Empty folders are also deliberate organization on disk, and a file-manager-shaped tree that hides them is surprising. Default the preference to on. Because the preference no longer gates fetching, both folder lists are always loaded: the full list is the tree's single source of truth and the empty-folder count, the models-only list is what "empty" is measured against. That makes the view-options toggle a pure re-render, and lets the new count decorate the "..." menu so the preference's effect is visible without scanning the tree: - empty-folder count shown next to the menu label, cleared when unknown - the toggle is hidden entirely when there are no empty folders - list view filters empty entries itself (the tree gets that for free from the backend, the flat list is now always loaded in full) - creating a folder still re-enables the preference, since the folder the user just asked for would otherwise be invisible Dim styling is decided by _isRenderedEmptyFolder() at render time; the models-only set keeps its ancestor-expanded semantics for the delete guard.
272 lines
15 KiB
HTML
272 lines
15 KiB
HTML
<div id="loraContextMenu" class="context-menu">
|
|
<!-- <div class="context-menu-item" data-action="detail">
|
|
<i class="fas fa-info-circle"></i> Show Details
|
|
</div> -->
|
|
<!-- <div class="context-menu-item" data-action="civitai">
|
|
<i class="fas fa-external-link-alt"></i> View on Civitai
|
|
</div> -->
|
|
<!-- Metadata -->
|
|
<div class="context-menu-item" data-action="refresh-metadata">
|
|
<i class="fas fa-sync"></i> <span>{{ t('loras.contextMenu.refreshMetadata') }}</span>
|
|
</div>
|
|
<div class="context-menu-item" data-action="check-updates">
|
|
<i class="fas fa-bell"></i> <span>{{ t('loras.contextMenu.checkUpdates') }}</span>
|
|
</div>
|
|
<div class="context-menu-item has-submenu" data-has-submenu="link-model">
|
|
<i class="fas fa-link"></i>
|
|
<span>{{ t('loras.contextMenu.linkModel') }}</span>
|
|
<i class="fas fa-chevron-right submenu-arrow"></i>
|
|
<div class="context-submenu">
|
|
<div class="context-menu-item" data-action="relink-civitai">
|
|
<i class="fas fa-external-link-alt"></i> <span>{{ t('loras.contextMenu.linkCivitai') }}</span>
|
|
</div>
|
|
<div class="context-menu-item" data-action="link-hf">
|
|
<i class="fas fa-robot"></i> <span>{{ t('loras.contextMenu.linkModelSource') }}</span>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="context-menu-item" data-action="enrich-hf-llm">
|
|
<i class="fas fa-wand-magic-sparkles"></i> <span>{{ t('loras.contextMenu.enrichHfAgent') }}</span>
|
|
</div>
|
|
<div class="context-menu-separator menu-section-break"></div>
|
|
<!-- Workflow -->
|
|
<div class="context-menu-item" data-action="copyname">
|
|
<i class="fas fa-copy"></i> <span>{{ t('loras.contextMenu.copySyntax') }}</span>
|
|
</div>
|
|
<div class="context-menu-item" data-action="sendappend">
|
|
<i class="fas fa-paper-plane"></i> <span>{{ t('loras.contextMenu.sendToWorkflowAppend') }}</span>
|
|
</div>
|
|
<div class="context-menu-item" data-action="sendreplace">
|
|
<i class="fas fa-exchange-alt"></i> <span>{{ t('loras.contextMenu.sendToWorkflowReplace') }}</span>
|
|
</div>
|
|
<div class="context-menu-separator menu-section-break"></div>
|
|
<!-- Media / Preview -->
|
|
<div class="context-menu-item" data-action="preview">
|
|
<i class="fas fa-folder-open"></i> <span>{{ t('loras.contextMenu.openExamples') }}</span>
|
|
</div>
|
|
<div class="context-menu-item has-submenu" data-has-submenu="download-examples">
|
|
<i class="fas fa-download"></i>
|
|
<span>{{ t('loras.contextMenu.downloadExamples') }}</span>
|
|
<i class="fas fa-chevron-right submenu-arrow"></i>
|
|
<div class="context-submenu">
|
|
<div class="context-menu-item" data-action="download-examples">
|
|
<i class="fas fa-download"></i> <span>{{ t('loras.contextMenu.downloadMissingExamples') }}</span>
|
|
</div>
|
|
<div class="context-menu-item" data-action="download-examples-force">
|
|
<i class="fas fa-redo-alt"></i> <span>{{ t('loras.contextMenu.reprocessExamples') }}</span>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="context-menu-item" data-action="replace-preview">
|
|
<i class="fas fa-image"></i> <span>{{ t('loras.contextMenu.replacePreview') }}</span>
|
|
</div>
|
|
<div class="context-menu-separator menu-section-break"></div>
|
|
<!-- Attributes -->
|
|
<div class="context-menu-item" data-action="set-nsfw">
|
|
<i class="fas fa-exclamation-triangle"></i> <span>{{ t('loras.contextMenu.setContentRating') }}</span>
|
|
</div>
|
|
<div class="context-menu-separator menu-section-break"></div>
|
|
<!-- Organization -->
|
|
<div class="context-menu-item" data-action="move">
|
|
<i class="fas fa-folder-open"></i> <span>{{ t('loras.contextMenu.moveToFolder') }}</span>
|
|
</div>
|
|
<div class="context-menu-separator"></div>
|
|
<!-- Destructive -->
|
|
<div class="context-menu-item" data-action="exclude">
|
|
<i class="fas fa-eye-slash"></i> <span>{{ t('loras.contextMenu.excludeModel') }}</span>
|
|
</div>
|
|
<div class="context-menu-item delete-item" data-action="delete">
|
|
<i class="fas fa-trash"></i> <span>{{ t('loras.contextMenu.deleteModel') }}</span>
|
|
</div>
|
|
</div>
|
|
|
|
<div id="bulkContextMenu" class="context-menu">
|
|
<div class="bulk-context-header">
|
|
<i class="fas fa-th-large"></i>
|
|
<span>{{ t('loras.bulkOperations.selected', {'count': 0}) }}</span>
|
|
</div>
|
|
<div class="context-menu-separator"></div>
|
|
<div class="context-menu-section" data-section="metadata">
|
|
<div class="context-menu-section-header">{{ t('loras.bulkOperations.sections.metadata') }}</div>
|
|
<div class="context-menu-item" data-action="refresh-all">
|
|
<i class="fas fa-sync-alt"></i> <span>{{ t('loras.bulkOperations.refreshAll') }}</span>
|
|
</div>
|
|
<div class="context-menu-item" data-action="check-updates">
|
|
<i class="fas fa-bell"></i> <span>{{ t('loras.bulkOperations.checkUpdates') }}</span>
|
|
</div>
|
|
<div class="context-menu-item" data-action="rematch-metadata">
|
|
<i class="fas fa-link"></i> <span>{{ t('loras.bulkOperations.rematchMetadata') }}</span>
|
|
</div>
|
|
<div class="context-menu-item" data-action="reimport-metadata">
|
|
<i class="fas fa-undo-alt"></i> <span>{{ t('loras.bulkOperations.reimportMetadata') }}</span>
|
|
</div>
|
|
<div class="context-menu-item" data-action="skip-metadata-refresh">
|
|
<i class="fas fa-ban"></i> <span>{{ t('loras.bulkOperations.skipMetadataRefresh') }}</span>
|
|
</div>
|
|
<div class="context-menu-item" data-action="resume-metadata-refresh">
|
|
<i class="fas fa-redo"></i> <span>{{ t('loras.bulkOperations.resumeMetadataRefresh') }}</span>
|
|
</div>
|
|
<div class="context-menu-item" data-action="enrich-hf-llm-bulk">
|
|
<i class="fas fa-wand-magic-sparkles"></i> <span>{{ t('loras.bulkOperations.enrichHfAgent') }}</span>
|
|
</div>
|
|
</div>
|
|
<div class="context-menu-section" data-section="workflow">
|
|
<div class="context-menu-section-header">{{ t('loras.bulkOperations.sections.workflow') }}</div>
|
|
<div class="context-menu-item has-submenu" data-has-submenu="send-to-workflow">
|
|
<i class="fas fa-paper-plane"></i>
|
|
<span>{{ t('loras.bulkOperations.sendToWorkflow') }}</span>
|
|
<i class="fas fa-chevron-right submenu-arrow"></i>
|
|
<div class="context-submenu">
|
|
<div class="context-menu-item" data-action="send-to-workflow-append">
|
|
<i class="fas fa-paper-plane"></i> <span>{{ t('loras.contextMenu.sendToWorkflowAppend') }}</span>
|
|
</div>
|
|
<div class="context-menu-item" data-action="send-to-workflow-replace">
|
|
<i class="fas fa-exchange-alt"></i> <span>{{ t('loras.contextMenu.sendToWorkflowReplace') }}</span>
|
|
</div>
|
|
<div class="context-menu-item" data-action="copy-all">
|
|
<i class="fas fa-copy"></i> <span>{{ t('loras.bulkOperations.copyAll') }}</span>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="context-menu-section" data-section="attributes">
|
|
<div class="context-menu-section-header">{{ t('loras.bulkOperations.sections.attributes') }}</div>
|
|
<div class="context-menu-item" data-action="add-tags">
|
|
<i class="fas fa-tags"></i> <span>{{ t('loras.bulkOperations.addTags') }}</span>
|
|
</div>
|
|
<div class="context-menu-item" data-action="set-base-model">
|
|
<i class="fas fa-layer-group"></i> <span>{{ t('loras.bulkOperations.setBaseModel') }}</span>
|
|
</div>
|
|
<div class="context-menu-item" data-action="set-favorite">
|
|
<i class="fas fa-star"></i> <span>{{ t('loras.bulkOperations.setFavorite') }}</span>
|
|
</div>
|
|
<div class="context-menu-item" data-action="set-content-rating">
|
|
<i class="fas fa-exclamation-triangle"></i> <span>{{ t('loras.bulkOperations.setContentRating') }}</span>
|
|
</div>
|
|
</div>
|
|
<div class="context-menu-section" data-section="download">
|
|
<div class="context-menu-section-header">{{ t('loras.bulkOperations.sections.download') }}</div>
|
|
<div class="context-menu-item has-submenu" data-has-submenu="download-example-images">
|
|
<i class="fas fa-download"></i>
|
|
<span>{{ t('loras.bulkOperations.downloadExamples') }}</span>
|
|
<i class="fas fa-chevron-right submenu-arrow"></i>
|
|
<div class="context-submenu">
|
|
<div class="context-menu-item" data-action="download-missing-example-images">
|
|
<i class="fas fa-download"></i> <span>{{ t('loras.bulkOperations.downloadMissingExamples') }}</span>
|
|
</div>
|
|
<div class="context-menu-item" data-action="download-example-images">
|
|
<i class="fas fa-redo-alt"></i> <span>{{ t('loras.bulkOperations.reprocessExamples') }}</span>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="context-menu-item" data-action="download-missing-loras">
|
|
<i class="fas fa-download"></i> <span>{{ t('loras.bulkOperations.downloadMissingLoras') }}</span>
|
|
</div>
|
|
</div>
|
|
<div class="context-menu-section" data-section="organize">
|
|
<div class="context-menu-section-header">{{ t('loras.bulkOperations.sections.organize') }}</div>
|
|
<div class="context-menu-item" data-action="auto-organize">
|
|
<i class="fas fa-magic"></i> <span>{{ t('loras.bulkOperations.autoOrganize') }}</span>
|
|
</div>
|
|
<div class="context-menu-item" data-action="move-all">
|
|
<i class="fas fa-folder-open"></i> <span>{{ t('loras.bulkOperations.moveAll') }}</span>
|
|
</div>
|
|
</div>
|
|
<div class="context-menu-separator"></div>
|
|
<div class="context-menu-item delete-item" data-action="delete-all">
|
|
<i class="fas fa-trash"></i> <span>{{ t('loras.bulkOperations.deleteAll') }}</span>
|
|
</div>
|
|
</div>
|
|
|
|
<div id="globalContextMenu" class="context-menu">
|
|
<div class="context-menu-item" data-action="download-example-images">
|
|
<i class="fas fa-download"></i> <span>{{ t('globalContextMenu.downloadExampleImages.label') }}</span>
|
|
</div>
|
|
<div class="context-menu-item" data-action="check-model-updates">
|
|
<i class="fas fa-sync-alt"></i> <span>{{ t('globalContextMenu.checkModelUpdates.label') }}</span>
|
|
</div>
|
|
<div class="context-menu-item" data-action="fetch-missing-licenses">
|
|
<i class="fas fa-shield-alt"></i> <span>{{ t('globalContextMenu.fetchMissingLicenses.label') }}</span>
|
|
</div>
|
|
<div class="context-menu-item" data-action="cleanup-example-images-folders">
|
|
<i class="fas fa-trash-restore"></i> <span>{{ t('globalContextMenu.cleanupExampleImages.label') }}</span>
|
|
</div>
|
|
<div class="context-menu-item" data-action="manage-excluded-models">
|
|
<i class="fas fa-eye-slash"></i> <span>{{ t('globalContextMenu.manageExcludedModels.label', default='Manage Excluded Models') }}</span>
|
|
</div>
|
|
<div class="context-menu-separator"></div>
|
|
<div class="context-menu-item" data-action="toggle-group-by-model">
|
|
<i class="fas fa-layer-group"></i> <span>{{ t('globalContextMenu.groupByModel.label') }}</span>
|
|
<i class="fas fa-check check-indicator" style="margin-left:auto;display:none"></i>
|
|
</div>
|
|
<div class="context-menu-item" data-action="rematch-recipes">
|
|
<i class="fas fa-link"></i> <span>{{ t('globalContextMenu.rematchRecipes.label') }}</span>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Sidebar Folder Context Menu -->
|
|
<!-- Order: the content action (update check) first, then the folder operations
|
|
as one group, then the destructive action behind its own divider. The
|
|
dividers are collapsed by SidebarManager when a group is hidden on the
|
|
current page (recipes keep only the update check). -->
|
|
<div id="sidebarFolderContextMenu" class="context-menu">
|
|
<div class="context-menu-item" data-action="check-folder-updates">
|
|
<i class="fas fa-bell"></i> <span>{{ t('sidebar.folderUpdateCheck.label') }}</span>
|
|
</div>
|
|
<div class="context-menu-separator"></div>
|
|
<div class="context-menu-item" data-action="create-subfolder">
|
|
<i class="fas fa-folder-plus"></i> <span>{{ t('sidebar.newSubfolder') }}</span>
|
|
</div>
|
|
<div class="context-menu-item" data-action="rename-folder">
|
|
<i class="fas fa-i-cursor"></i> <span>{{ t('sidebar.renameFolder') }}</span>
|
|
</div>
|
|
<div class="context-menu-separator"></div>
|
|
<div class="context-menu-item delete-item" data-action="delete-folder">
|
|
<i class="fas fa-trash"></i> <span>{{ t('sidebar.deleteFolder') }}</span>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Sidebar View Options Menu -->
|
|
<div id="sidebarViewOptionsMenu" class="context-menu">
|
|
<div class="context-menu-item" data-action="view-mode-tree">
|
|
<i class="fas fa-sitemap"></i> <span>{{ t('sidebar.treeView') }}</span>
|
|
<i class="fas fa-check check-indicator" style="margin-left:auto;display:none"></i>
|
|
</div>
|
|
<div class="context-menu-item" data-action="view-mode-list">
|
|
<i class="fas fa-list"></i> <span>{{ t('sidebar.listView') }}</span>
|
|
<i class="fas fa-check check-indicator" style="margin-left:auto;display:none"></i>
|
|
</div>
|
|
<div class="context-menu-separator"></div>
|
|
<div class="context-menu-item" data-action="toggle-recursive">
|
|
<i class="fas fa-code-branch"></i> <span>{{ t('sidebar.recursiveOn') }}</span>
|
|
<i class="fas fa-check check-indicator" style="margin-left:auto;display:none"></i>
|
|
</div>
|
|
<div class="context-menu-item" data-action="toggle-empty-folders">
|
|
<i class="fas fa-folder-open"></i> <span>{{ t('sidebar.showEmptyFolders') }}</span>
|
|
<span id="sidebarEmptyFoldersCount" class="context-menu-count"></span>
|
|
<i class="fas fa-check check-indicator" style="margin-left:auto;display:none"></i>
|
|
</div>
|
|
</div>
|
|
|
|
<div id="nsfwLevelSelector" class="nsfw-level-selector">
|
|
<div class="nsfw-level-header">
|
|
<h3>{{ t('modals.contentRating.title') }}</h3>
|
|
<button class="close-nsfw-selector"><i class="fas fa-times"></i></button>
|
|
</div>
|
|
<div class="nsfw-level-content">
|
|
<div class="current-level"><span>{{ t('modals.contentRating.current') }}:</span> <span id="currentNSFWLevel">{{
|
|
t('common.status.unknown') }}</span></div>
|
|
<div class="nsfw-level-options">
|
|
<button class="nsfw-level-btn" data-level="1">{{ t('modals.contentRating.levels.pg') }}</button>
|
|
<button class="nsfw-level-btn" data-level="2">{{ t('modals.contentRating.levels.pg13') }}</button>
|
|
<button class="nsfw-level-btn" data-level="4">{{ t('modals.contentRating.levels.r') }}</button>
|
|
<button class="nsfw-level-btn" data-level="8">{{ t('modals.contentRating.levels.x') }}</button>
|
|
<button class="nsfw-level-btn" data-level="16">{{ t('modals.contentRating.levels.xxx') }}</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div id="nodeSelector" class="node-selector">
|
|
<!-- Dynamic node list will be populated here -->
|
|
</div>
|