mirror of
https://github.com/willmiao/ComfyUI-Lora-Manager.git
synced 2026-03-26 07:35:44 -03:00
feat(sidebar): implement display mode toggle and update sidebar actions for improved navigation. See #389
This commit is contained in:
@@ -400,7 +400,11 @@
|
|||||||
"sidebar": {
|
"sidebar": {
|
||||||
"modelRoot": "Modell-Stammverzeichnis",
|
"modelRoot": "Modell-Stammverzeichnis",
|
||||||
"collapseAll": "Alle Ordner einklappen",
|
"collapseAll": "Alle Ordner einklappen",
|
||||||
"pinToggle": "Seitenleiste anheften/lösen"
|
"pinSidebar": "Sidebar anheften",
|
||||||
|
"unpinSidebar": "Sidebar lösen",
|
||||||
|
"switchToListView": "Zur Listenansicht wechseln",
|
||||||
|
"switchToTreeView": "Zur Baumansicht wechseln",
|
||||||
|
"collapseAllDisabled": "Im Listenmodus nicht verfügbar"
|
||||||
},
|
},
|
||||||
"statistics": {
|
"statistics": {
|
||||||
"title": "Statistiken",
|
"title": "Statistiken",
|
||||||
|
|||||||
@@ -400,7 +400,11 @@
|
|||||||
"sidebar": {
|
"sidebar": {
|
||||||
"modelRoot": "Model Root",
|
"modelRoot": "Model Root",
|
||||||
"collapseAll": "Collapse All Folders",
|
"collapseAll": "Collapse All Folders",
|
||||||
"pinToggle": "Pin/Unpin Sidebar"
|
"pinSidebar": "Pin Sidebar",
|
||||||
|
"unpinSidebar": "Unpin Sidebar",
|
||||||
|
"switchToListView": "Switch to List View",
|
||||||
|
"switchToTreeView": "Switch to Tree View",
|
||||||
|
"collapseAllDisabled": "Not available in list view"
|
||||||
},
|
},
|
||||||
"statistics": {
|
"statistics": {
|
||||||
"title": "Statistics",
|
"title": "Statistics",
|
||||||
|
|||||||
@@ -400,7 +400,11 @@
|
|||||||
"sidebar": {
|
"sidebar": {
|
||||||
"modelRoot": "Raíz del modelo",
|
"modelRoot": "Raíz del modelo",
|
||||||
"collapseAll": "Colapsar todas las carpetas",
|
"collapseAll": "Colapsar todas las carpetas",
|
||||||
"pinToggle": "Anclar/Desanclar barra lateral"
|
"pinSidebar": "Fijar barra lateral",
|
||||||
|
"unpinSidebar": "Desfijar barra lateral",
|
||||||
|
"switchToListView": "Cambiar a vista de lista",
|
||||||
|
"switchToTreeView": "Cambiar a vista de árbol",
|
||||||
|
"collapseAllDisabled": "No disponible en vista de lista"
|
||||||
},
|
},
|
||||||
"statistics": {
|
"statistics": {
|
||||||
"title": "Estadísticas",
|
"title": "Estadísticas",
|
||||||
|
|||||||
@@ -400,7 +400,11 @@
|
|||||||
"sidebar": {
|
"sidebar": {
|
||||||
"modelRoot": "Racine du modèle",
|
"modelRoot": "Racine du modèle",
|
||||||
"collapseAll": "Réduire tous les dossiers",
|
"collapseAll": "Réduire tous les dossiers",
|
||||||
"pinToggle": "Épingler/Désépingler la barre latérale"
|
"pinSidebar": "Épingler la barre latérale",
|
||||||
|
"unpinSidebar": "Désépingler la barre latérale",
|
||||||
|
"switchToListView": "Passer en vue liste",
|
||||||
|
"switchToTreeView": "Passer en vue arborescence",
|
||||||
|
"collapseAllDisabled": "Non disponible en vue liste"
|
||||||
},
|
},
|
||||||
"statistics": {
|
"statistics": {
|
||||||
"title": "Statistiques",
|
"title": "Statistiques",
|
||||||
|
|||||||
@@ -400,7 +400,11 @@
|
|||||||
"sidebar": {
|
"sidebar": {
|
||||||
"modelRoot": "モデルルート",
|
"modelRoot": "モデルルート",
|
||||||
"collapseAll": "すべてのフォルダを折りたたむ",
|
"collapseAll": "すべてのフォルダを折りたたむ",
|
||||||
"pinToggle": "サイドバーの固定/固定解除"
|
"pinSidebar": "サイドバーを固定",
|
||||||
|
"unpinSidebar": "サイドバーの固定を解除",
|
||||||
|
"switchToListView": "リストビューに切り替え",
|
||||||
|
"switchToTreeView": "ツリービューに切り替え",
|
||||||
|
"collapseAllDisabled": "リストビューでは利用できません"
|
||||||
},
|
},
|
||||||
"statistics": {
|
"statistics": {
|
||||||
"title": "統計",
|
"title": "統計",
|
||||||
|
|||||||
@@ -400,7 +400,11 @@
|
|||||||
"sidebar": {
|
"sidebar": {
|
||||||
"modelRoot": "모델 루트",
|
"modelRoot": "모델 루트",
|
||||||
"collapseAll": "모든 폴더 접기",
|
"collapseAll": "모든 폴더 접기",
|
||||||
"pinToggle": "사이드바 고정/해제"
|
"pinSidebar": "사이드바 고정",
|
||||||
|
"unpinSidebar": "사이드바 고정 해제",
|
||||||
|
"switchToListView": "목록 보기로 전환",
|
||||||
|
"switchToTreeView": "트리 보기로 전환",
|
||||||
|
"collapseAllDisabled": "목록 보기에서는 사용할 수 없습니다"
|
||||||
},
|
},
|
||||||
"statistics": {
|
"statistics": {
|
||||||
"title": "통계",
|
"title": "통계",
|
||||||
|
|||||||
@@ -400,7 +400,11 @@
|
|||||||
"sidebar": {
|
"sidebar": {
|
||||||
"modelRoot": "Корень моделей",
|
"modelRoot": "Корень моделей",
|
||||||
"collapseAll": "Свернуть все папки",
|
"collapseAll": "Свернуть все папки",
|
||||||
"pinToggle": "Закрепить/Открепить боковую панель"
|
"pinSidebar": "Закрепить боковую панель",
|
||||||
|
"unpinSidebar": "Открепить боковую панель",
|
||||||
|
"switchToListView": "Переключить на вид списка",
|
||||||
|
"switchToTreeView": "Переключить на древовидный вид",
|
||||||
|
"collapseAllDisabled": "Недоступно в виде списка"
|
||||||
},
|
},
|
||||||
"statistics": {
|
"statistics": {
|
||||||
"title": "Статистика",
|
"title": "Статистика",
|
||||||
|
|||||||
@@ -400,7 +400,11 @@
|
|||||||
"sidebar": {
|
"sidebar": {
|
||||||
"modelRoot": "模型根目录",
|
"modelRoot": "模型根目录",
|
||||||
"collapseAll": "折叠所有文件夹",
|
"collapseAll": "折叠所有文件夹",
|
||||||
"pinToggle": "固定/取消固定侧边栏"
|
"pinSidebar": "固定侧边栏",
|
||||||
|
"unpinSidebar": "取消固定侧边栏",
|
||||||
|
"switchToListView": "切换到列表视图",
|
||||||
|
"switchToTreeView": "切换到树状视图",
|
||||||
|
"collapseAllDisabled": "列表视图下不可用"
|
||||||
},
|
},
|
||||||
"statistics": {
|
"statistics": {
|
||||||
"title": "统计",
|
"title": "统计",
|
||||||
|
|||||||
@@ -400,7 +400,11 @@
|
|||||||
"sidebar": {
|
"sidebar": {
|
||||||
"modelRoot": "模型根目錄",
|
"modelRoot": "模型根目錄",
|
||||||
"collapseAll": "全部摺疊資料夾",
|
"collapseAll": "全部摺疊資料夾",
|
||||||
"pinToggle": "釘選/取消釘選側邊欄"
|
"pinSidebar": "固定側邊欄",
|
||||||
|
"unpinSidebar": "取消固定側邊欄",
|
||||||
|
"switchToListView": "切換至列表檢視",
|
||||||
|
"switchToTreeView": "切換至樹狀檢視",
|
||||||
|
"collapseAllDisabled": "列表檢視下不可用"
|
||||||
},
|
},
|
||||||
"statistics": {
|
"statistics": {
|
||||||
"title": "統計",
|
"title": "統計",
|
||||||
|
|||||||
@@ -114,18 +114,18 @@ class BaseModelRoutes(ABC):
|
|||||||
if not self.template_env or not template_name:
|
if not self.template_env or not template_name:
|
||||||
return web.Response(text="Template environment or template name not set", status=500)
|
return web.Response(text="Template environment or template name not set", status=500)
|
||||||
|
|
||||||
# 获取用户语言设置
|
# Get user's language setting
|
||||||
user_language = settings.get('language', 'en')
|
user_language = settings.get('language', 'en')
|
||||||
|
|
||||||
# 设置服务端i18n语言
|
# Set server-side i18n locale
|
||||||
server_i18n.set_locale(user_language)
|
server_i18n.set_locale(user_language)
|
||||||
|
|
||||||
# 为模板环境添加i18n过滤器
|
# Add i18n filter to the template environment if not already added
|
||||||
if not hasattr(self.template_env, '_i18n_filter_added'):
|
if not hasattr(self.template_env, '_i18n_filter_added'):
|
||||||
self.template_env.filters['t'] = server_i18n.create_template_filter()
|
self.template_env.filters['t'] = server_i18n.create_template_filter()
|
||||||
self.template_env._i18n_filter_added = True
|
self.template_env._i18n_filter_added = True
|
||||||
|
|
||||||
# 准备模板上下文
|
# Prepare template context
|
||||||
template_context = {
|
template_context = {
|
||||||
'is_initializing': is_initializing,
|
'is_initializing': is_initializing,
|
||||||
'settings': settings,
|
'settings': settings,
|
||||||
|
|||||||
@@ -132,6 +132,11 @@
|
|||||||
color: var(--lora-accent);
|
color: var(--lora-accent);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.sidebar-action-btn.disabled {
|
||||||
|
opacity: 0.3;
|
||||||
|
cursor: not-allowed;
|
||||||
|
}
|
||||||
|
|
||||||
/* Remove old close button styles */
|
/* Remove old close button styles */
|
||||||
.sidebar-toggle-close {
|
.sidebar-toggle-close {
|
||||||
display: none;
|
display: none;
|
||||||
@@ -365,6 +370,60 @@
|
|||||||
font-weight: 500;
|
font-weight: 500;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Folder List Mode Styles */
|
||||||
|
.sidebar-folder-item {
|
||||||
|
position: relative;
|
||||||
|
user-select: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sidebar-node-content {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
padding: 8px 16px;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: all 0.2s ease;
|
||||||
|
font-size: 0.85em;
|
||||||
|
border-left: 3px solid transparent;
|
||||||
|
color: var(--text-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
.sidebar-node-content:hover {
|
||||||
|
background: var(--lora-surface);
|
||||||
|
color: var(--text-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
.sidebar-folder-item.selected .sidebar-node-content {
|
||||||
|
background: oklch(var(--lora-accent-l) var(--lora-accent-c) var(--lora-accent-h) / 0.1);
|
||||||
|
color: var(--lora-accent);
|
||||||
|
border-left-color: var(--lora-accent);
|
||||||
|
font-weight: 500;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sidebar-folder-icon {
|
||||||
|
margin-right: 8px;
|
||||||
|
color: var(--text-muted);
|
||||||
|
opacity: 0.7;
|
||||||
|
width: 16px;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sidebar-folder-item.selected .sidebar-folder-icon {
|
||||||
|
color: var(--lora-accent);
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sidebar-node-content:hover .sidebar-folder-icon {
|
||||||
|
color: var(--text-color);
|
||||||
|
opacity: 0.9;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sidebar-folder-name {
|
||||||
|
flex: 1;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
|
||||||
/* Responsive Design */
|
/* Responsive Design */
|
||||||
@media (min-width: 2150px) {
|
@media (min-width: 2150px) {
|
||||||
.folder-sidebar {
|
.folder-sidebar {
|
||||||
|
|||||||
@@ -668,6 +668,8 @@ export class BaseModelApiClient {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
params.append('recursive', pageState.searchOptions.recursive ? 'true' : 'false');
|
||||||
|
|
||||||
if (pageState.filters) {
|
if (pageState.filters) {
|
||||||
if (pageState.filters.tags && pageState.filters.tags.length > 0) {
|
if (pageState.filters.tags && pageState.filters.tags.length > 0) {
|
||||||
pageState.filters.tags.forEach(tag => {
|
pageState.filters.tags.forEach(tag => {
|
||||||
|
|||||||
@@ -3,6 +3,7 @@
|
|||||||
*/
|
*/
|
||||||
import { getStorageItem, setStorageItem } from '../utils/storageHelpers.js';
|
import { getStorageItem, setStorageItem } from '../utils/storageHelpers.js';
|
||||||
import { getModelApiClient } from '../api/modelApiFactory.js';
|
import { getModelApiClient } from '../api/modelApiFactory.js';
|
||||||
|
import { translate } from '../utils/i18nHelpers.js';
|
||||||
|
|
||||||
export class SidebarManager {
|
export class SidebarManager {
|
||||||
constructor() {
|
constructor() {
|
||||||
@@ -18,6 +19,8 @@ export class SidebarManager {
|
|||||||
this.hoverTimeout = null;
|
this.hoverTimeout = null;
|
||||||
this.isHovering = false;
|
this.isHovering = false;
|
||||||
this.isInitialized = false;
|
this.isInitialized = false;
|
||||||
|
this.displayMode = 'tree'; // 'tree' or 'list'
|
||||||
|
this.foldersList = [];
|
||||||
|
|
||||||
// Bind methods
|
// Bind methods
|
||||||
this.handleTreeClick = this.handleTreeClick.bind(this);
|
this.handleTreeClick = this.handleTreeClick.bind(this);
|
||||||
@@ -31,6 +34,8 @@ export class SidebarManager {
|
|||||||
this.handleHoverAreaEnter = this.handleHoverAreaEnter.bind(this);
|
this.handleHoverAreaEnter = this.handleHoverAreaEnter.bind(this);
|
||||||
this.handleHoverAreaLeave = this.handleHoverAreaLeave.bind(this);
|
this.handleHoverAreaLeave = this.handleHoverAreaLeave.bind(this);
|
||||||
this.updateContainerMargin = this.updateContainerMargin.bind(this);
|
this.updateContainerMargin = this.updateContainerMargin.bind(this);
|
||||||
|
this.handleDisplayModeToggle = this.handleDisplayModeToggle.bind(this);
|
||||||
|
this.handleFolderListClick = this.handleFolderListClick.bind(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
async initialize(pageControls) {
|
async initialize(pageControls) {
|
||||||
@@ -105,6 +110,7 @@ export class SidebarManager {
|
|||||||
const sidebarHeader = document.getElementById('sidebarHeader');
|
const sidebarHeader = document.getElementById('sidebarHeader');
|
||||||
const sidebar = document.getElementById('folderSidebar');
|
const sidebar = document.getElementById('folderSidebar');
|
||||||
const hoverArea = document.getElementById('sidebarHoverArea');
|
const hoverArea = document.getElementById('sidebarHoverArea');
|
||||||
|
const displayModeToggleBtn = document.getElementById('sidebarDisplayModeToggle');
|
||||||
|
|
||||||
if (pinToggleBtn) {
|
if (pinToggleBtn) {
|
||||||
pinToggleBtn.removeEventListener('click', this.handlePinToggle);
|
pinToggleBtn.removeEventListener('click', this.handlePinToggle);
|
||||||
@@ -135,6 +141,10 @@ export class SidebarManager {
|
|||||||
|
|
||||||
// Remove resize event handler
|
// Remove resize event handler
|
||||||
window.removeEventListener('resize', this.updateContainerMargin);
|
window.removeEventListener('resize', this.updateContainerMargin);
|
||||||
|
|
||||||
|
if (displayModeToggleBtn) {
|
||||||
|
displayModeToggleBtn.removeEventListener('click', this.handleDisplayModeToggle);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async init() {
|
async init() {
|
||||||
@@ -258,6 +268,12 @@ export class SidebarManager {
|
|||||||
|
|
||||||
// Add dedicated resize listener for container margin updates
|
// Add dedicated resize listener for container margin updates
|
||||||
window.addEventListener('resize', this.updateContainerMargin);
|
window.addEventListener('resize', this.updateContainerMargin);
|
||||||
|
|
||||||
|
// Display mode toggle button
|
||||||
|
const displayModeToggleBtn = document.getElementById('sidebarDisplayModeToggle');
|
||||||
|
if (displayModeToggleBtn) {
|
||||||
|
displayModeToggleBtn.addEventListener('click', this.handleDisplayModeToggle);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
handleDocumentClick(event) {
|
handleDocumentClick(event) {
|
||||||
@@ -270,7 +286,7 @@ export class SidebarManager {
|
|||||||
handleSidebarHeaderClick(event) {
|
handleSidebarHeaderClick(event) {
|
||||||
// Only trigger root selection if clicking on the title area, not the buttons
|
// Only trigger root selection if clicking on the title area, not the buttons
|
||||||
if (!event.target.closest('.sidebar-header-actions')) {
|
if (!event.target.closest('.sidebar-header-actions')) {
|
||||||
this.selectFolder('');
|
this.selectFolder(null);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -286,7 +302,7 @@ export class SidebarManager {
|
|||||||
handleCollapseAll(event) {
|
handleCollapseAll(event) {
|
||||||
event.stopPropagation();
|
event.stopPropagation();
|
||||||
this.expandedNodes.clear();
|
this.expandedNodes.clear();
|
||||||
this.renderTree();
|
this.renderFolderDisplay();
|
||||||
this.saveExpandedState();
|
this.saveExpandedState();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -407,21 +423,36 @@ export class SidebarManager {
|
|||||||
const pinBtn = document.getElementById('sidebarPinToggle');
|
const pinBtn = document.getElementById('sidebarPinToggle');
|
||||||
if (pinBtn) {
|
if (pinBtn) {
|
||||||
pinBtn.classList.toggle('active', this.isPinned);
|
pinBtn.classList.toggle('active', this.isPinned);
|
||||||
pinBtn.title = this.isPinned ? 'Unpin Sidebar' : 'Pin Sidebar';
|
pinBtn.title = this.isPinned
|
||||||
|
? translate('sidebar.unpinSidebar')
|
||||||
|
: translate('sidebar.pinSidebar');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async loadFolderTree() {
|
async loadFolderTree() {
|
||||||
try {
|
try {
|
||||||
const response = await this.apiClient.fetchUnifiedFolderTree();
|
if (this.displayMode === 'tree') {
|
||||||
this.treeData = response.tree || {};
|
const response = await this.apiClient.fetchUnifiedFolderTree();
|
||||||
this.renderTree();
|
this.treeData = response.tree || {};
|
||||||
|
} else {
|
||||||
|
const response = await this.apiClient.fetchModelFolders();
|
||||||
|
this.foldersList = response.folders || [];
|
||||||
|
}
|
||||||
|
this.renderFolderDisplay();
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Failed to load folder tree:', error);
|
console.error('Failed to load folder data:', error);
|
||||||
this.renderEmptyState();
|
this.renderEmptyState();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
renderFolderDisplay() {
|
||||||
|
if (this.displayMode === 'tree') {
|
||||||
|
this.renderTree();
|
||||||
|
} else {
|
||||||
|
this.renderFolderList();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
renderTree() {
|
renderTree() {
|
||||||
const folderTree = document.getElementById('sidebarFolderTree');
|
const folderTree = document.getElementById('sidebarFolderTree');
|
||||||
if (!folderTree) return;
|
if (!folderTree) return;
|
||||||
@@ -476,7 +507,38 @@ export class SidebarManager {
|
|||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
renderFolderList() {
|
||||||
|
const folderTree = document.getElementById('sidebarFolderTree');
|
||||||
|
if (!folderTree) return;
|
||||||
|
|
||||||
|
if (!this.foldersList || this.foldersList.length === 0) {
|
||||||
|
this.renderEmptyState();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const foldersHtml = this.foldersList.map(folder => {
|
||||||
|
const displayName = folder === '' ? '/' : folder;
|
||||||
|
const isSelected = this.selectedPath === folder;
|
||||||
|
|
||||||
|
return `
|
||||||
|
<div class="sidebar-folder-item ${isSelected ? 'selected' : ''}" data-path="${folder}">
|
||||||
|
<div class="sidebar-node-content">
|
||||||
|
<i class="fas fa-folder sidebar-folder-icon"></i>
|
||||||
|
<div class="sidebar-folder-name" title="${displayName}">${displayName}</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
}).join('');
|
||||||
|
|
||||||
|
folderTree.innerHTML = foldersHtml;
|
||||||
|
}
|
||||||
|
|
||||||
handleTreeClick(event) {
|
handleTreeClick(event) {
|
||||||
|
if (this.displayMode === 'list') {
|
||||||
|
this.handleFolderListClick(event);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
const expandIcon = event.target.closest('.sidebar-tree-expand-icon');
|
const expandIcon = event.target.closest('.sidebar-tree-expand-icon');
|
||||||
const nodeContent = event.target.closest('.sidebar-tree-node-content');
|
const nodeContent = event.target.closest('.sidebar-tree-node-content');
|
||||||
|
|
||||||
@@ -516,7 +578,7 @@ export class SidebarManager {
|
|||||||
this.closeDropdown();
|
this.closeDropdown();
|
||||||
} else if (breadcrumbItem) {
|
} else if (breadcrumbItem) {
|
||||||
// Handle breadcrumb item click
|
// Handle breadcrumb item click
|
||||||
const path = breadcrumbItem.dataset.path || '';
|
const path = breadcrumbItem.dataset.path || null; // null for showing all models
|
||||||
const isPlaceholder = breadcrumbItem.classList.contains('placeholder');
|
const isPlaceholder = breadcrumbItem.classList.contains('placeholder');
|
||||||
const isActive = breadcrumbItem.classList.contains('active');
|
const isActive = breadcrumbItem.classList.contains('active');
|
||||||
const dropdown = breadcrumbItem.closest('.breadcrumb-dropdown');
|
const dropdown = breadcrumbItem.closest('.breadcrumb-dropdown');
|
||||||
@@ -557,8 +619,8 @@ export class SidebarManager {
|
|||||||
this.updateSidebarHeader();
|
this.updateSidebarHeader();
|
||||||
|
|
||||||
// Update page state
|
// Update page state
|
||||||
this.pageControls.pageState.activeFolder = path || null;
|
this.pageControls.pageState.activeFolder = path;
|
||||||
setStorageItem(`${this.pageType}_activeFolder`, path || null);
|
setStorageItem(`${this.pageType}_activeFolder`, path);
|
||||||
|
|
||||||
// Reload models with new filter
|
// Reload models with new filter
|
||||||
await this.pageControls.resetAndReload();
|
await this.pageControls.resetAndReload();
|
||||||
@@ -569,23 +631,87 @@ export class SidebarManager {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
handleFolderListClick(event) {
|
||||||
|
const folderItem = event.target.closest('.sidebar-folder-item');
|
||||||
|
|
||||||
|
if (folderItem) {
|
||||||
|
const path = folderItem.dataset.path;
|
||||||
|
this.selectFolder(path);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
handleDisplayModeToggle(event) {
|
||||||
|
event.stopPropagation();
|
||||||
|
this.displayMode = this.displayMode === 'tree' ? 'list' : 'tree';
|
||||||
|
this.updateDisplayModeButton();
|
||||||
|
this.updateCollapseAllButton();
|
||||||
|
this.updateSearchRecursiveOption();
|
||||||
|
this.saveDisplayMode();
|
||||||
|
this.loadFolderTree(); // Reload with new display mode
|
||||||
|
}
|
||||||
|
|
||||||
|
updateDisplayModeButton() {
|
||||||
|
const displayModeBtn = document.getElementById('sidebarDisplayModeToggle');
|
||||||
|
if (displayModeBtn) {
|
||||||
|
const icon = displayModeBtn.querySelector('i');
|
||||||
|
if (this.displayMode === 'tree') {
|
||||||
|
icon.className = 'fas fa-sitemap';
|
||||||
|
displayModeBtn.title = translate('sidebar.switchToListView');
|
||||||
|
} else {
|
||||||
|
icon.className = 'fas fa-list';
|
||||||
|
displayModeBtn.title = translate('sidebar.switchToTreeView');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
updateCollapseAllButton() {
|
||||||
|
const collapseAllBtn = document.getElementById('sidebarCollapseAll');
|
||||||
|
if (collapseAllBtn) {
|
||||||
|
if (this.displayMode === 'list') {
|
||||||
|
collapseAllBtn.disabled = true;
|
||||||
|
collapseAllBtn.classList.add('disabled');
|
||||||
|
collapseAllBtn.title = translate('sidebar.collapseAllDisabled');
|
||||||
|
} else {
|
||||||
|
collapseAllBtn.disabled = false;
|
||||||
|
collapseAllBtn.classList.remove('disabled');
|
||||||
|
collapseAllBtn.title = translate('sidebar.collapseAll');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
updateSearchRecursiveOption() {
|
||||||
|
this.pageControls.pageState.searchOptions.recursive = this.displayMode === 'tree';
|
||||||
|
}
|
||||||
|
|
||||||
updateTreeSelection() {
|
updateTreeSelection() {
|
||||||
const folderTree = document.getElementById('sidebarFolderTree');
|
const folderTree = document.getElementById('sidebarFolderTree');
|
||||||
if (!folderTree) return;
|
if (!folderTree) return;
|
||||||
|
|
||||||
// Remove all selections
|
if (this.displayMode === 'list') {
|
||||||
folderTree.querySelectorAll('.sidebar-tree-node-content').forEach(node => {
|
// Remove all selections in list mode
|
||||||
node.classList.remove('selected');
|
folderTree.querySelectorAll('.sidebar-folder-item').forEach(item => {
|
||||||
});
|
item.classList.remove('selected');
|
||||||
|
});
|
||||||
|
|
||||||
// Add selection to current path
|
// Add selection to current path
|
||||||
if (this.selectedPath) {
|
if (this.selectedPath !== null) {
|
||||||
const selectedNode = folderTree.querySelector(`[data-path="${this.selectedPath}"] .sidebar-tree-node-content`);
|
const selectedItem = folderTree.querySelector(`[data-path="${this.selectedPath}"]`);
|
||||||
if (selectedNode) {
|
if (selectedItem) {
|
||||||
selectedNode.classList.add('selected');
|
selectedItem.classList.add('selected');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// ...existing tree selection logic...
|
||||||
|
folderTree.querySelectorAll('.sidebar-tree-node-content').forEach(node => {
|
||||||
|
node.classList.remove('selected');
|
||||||
|
});
|
||||||
|
|
||||||
// Expand parents to show selection
|
if (this.selectedPath) {
|
||||||
this.expandPathParents(this.selectedPath);
|
const selectedNode = folderTree.querySelector(`[data-path="${this.selectedPath}"] .sidebar-tree-node-content`);
|
||||||
|
if (selectedNode) {
|
||||||
|
selectedNode.classList.add('selected');
|
||||||
|
this.expandPathParents(this.selectedPath);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -799,11 +925,16 @@ export class SidebarManager {
|
|||||||
restoreSidebarState() {
|
restoreSidebarState() {
|
||||||
const isPinned = getStorageItem(`${this.pageType}_sidebarPinned`, false);
|
const isPinned = getStorageItem(`${this.pageType}_sidebarPinned`, false);
|
||||||
const expandedPaths = getStorageItem(`${this.pageType}_expandedNodes`, []);
|
const expandedPaths = getStorageItem(`${this.pageType}_expandedNodes`, []);
|
||||||
|
const displayMode = getStorageItem(`${this.pageType}_displayMode`, 'tree');
|
||||||
|
|
||||||
this.isPinned = isPinned;
|
this.isPinned = isPinned;
|
||||||
this.expandedNodes = new Set(expandedPaths);
|
this.expandedNodes = new Set(expandedPaths);
|
||||||
|
this.displayMode = displayMode;
|
||||||
|
|
||||||
this.updatePinButton();
|
this.updatePinButton();
|
||||||
|
this.updateDisplayModeButton();
|
||||||
|
this.updateCollapseAllButton();
|
||||||
|
this.updateSearchRecursiveOption();
|
||||||
}
|
}
|
||||||
|
|
||||||
restoreSelectedFolder() {
|
restoreSelectedFolder() {
|
||||||
@@ -829,6 +960,10 @@ export class SidebarManager {
|
|||||||
setStorageItem(`${this.pageType}_expandedNodes`, Array.from(this.expandedNodes));
|
setStorageItem(`${this.pageType}_expandedNodes`, Array.from(this.expandedNodes));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
saveDisplayMode() {
|
||||||
|
setStorageItem(`${this.pageType}_displayMode`, this.displayMode);
|
||||||
|
}
|
||||||
|
|
||||||
async refresh() {
|
async refresh() {
|
||||||
await this.loadFolderTree();
|
await this.loadFolderTree();
|
||||||
this.restoreSelectedFolder();
|
this.restoreSelectedFolder();
|
||||||
|
|||||||
@@ -6,6 +6,9 @@
|
|||||||
<div class="sidebar-header" id="sidebarHeader">
|
<div class="sidebar-header" id="sidebarHeader">
|
||||||
<h3><i class="fas fa-home"></i> <span id="sidebarTitle">{{ t('sidebar.modelRoot') }}</span></h3>
|
<h3><i class="fas fa-home"></i> <span id="sidebarTitle">{{ t('sidebar.modelRoot') }}</span></h3>
|
||||||
<div class="sidebar-header-actions">
|
<div class="sidebar-header-actions">
|
||||||
|
<button class="sidebar-action-btn" id="sidebarDisplayModeToggle" title="{{ t('sidebar.switchToListView') }}">
|
||||||
|
<i class="fas fa-sitemap"></i>
|
||||||
|
</button>
|
||||||
<button class="sidebar-action-btn" id="sidebarCollapseAll" title="{{ t('sidebar.collapseAll') }}">
|
<button class="sidebar-action-btn" id="sidebarCollapseAll" title="{{ t('sidebar.collapseAll') }}">
|
||||||
<i class="fas fa-compress-alt"></i>
|
<i class="fas fa-compress-alt"></i>
|
||||||
</button>
|
</button>
|
||||||
@@ -17,7 +20,7 @@
|
|||||||
<div class="sidebar-content">
|
<div class="sidebar-content">
|
||||||
<div class="sidebar-tree-container">
|
<div class="sidebar-tree-container">
|
||||||
<div class="sidebar-tree" id="sidebarFolderTree">
|
<div class="sidebar-tree" id="sidebarFolderTree">
|
||||||
<!-- Tree will be populated by JavaScript -->
|
<!-- Tree/List will be populated by JavaScript -->
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
Reference in New Issue
Block a user