feat(sidebar): add recursive search functionality and toggle button

This commit is contained in:
Will Miao
2025-10-09 17:07:10 +08:00
parent 2b6910bd55
commit d2c2bfbe6a
13 changed files with 124 additions and 26 deletions

View File

@@ -529,12 +529,15 @@
"title": "Embedding-Modelle" "title": "Embedding-Modelle"
}, },
"sidebar": { "sidebar": {
"modelRoot": "Modell-Stammverzeichnis", "modelRoot": "Stammverzeichnis",
"collapseAll": "Alle Ordner einklappen", "collapseAll": "Alle Ordner einklappen",
"pinSidebar": "Sidebar anheften", "pinSidebar": "Sidebar anheften",
"unpinSidebar": "Sidebar lösen", "unpinSidebar": "Sidebar lösen",
"switchToListView": "Zur Listenansicht wechseln", "switchToListView": "Zur Listenansicht wechseln",
"switchToTreeView": "Zur Baumansicht wechseln", "switchToTreeView": "Zur Baumansicht wechseln",
"recursiveOn": "Unterordner durchsuchen",
"recursiveOff": "Nur aktuellen Ordner durchsuchen",
"recursiveUnavailable": "Rekursive Suche ist nur in der Baumansicht verfügbar",
"collapseAllDisabled": "Im Listenmodus nicht verfügbar" "collapseAllDisabled": "Im Listenmodus nicht verfügbar"
}, },
"statistics": { "statistics": {

View File

@@ -529,12 +529,15 @@
"title": "Embedding Models" "title": "Embedding Models"
}, },
"sidebar": { "sidebar": {
"modelRoot": "Model Root", "modelRoot": "Root",
"collapseAll": "Collapse All Folders", "collapseAll": "Collapse All Folders",
"pinSidebar": "Pin Sidebar", "pinSidebar": "Pin Sidebar",
"unpinSidebar": "Unpin Sidebar", "unpinSidebar": "Unpin Sidebar",
"switchToListView": "Switch to List View", "switchToListView": "Switch to List View",
"switchToTreeView": "Switch to Tree View", "switchToTreeView": "Switch to Tree View",
"recursiveOn": "Search subfolders",
"recursiveOff": "Search current folder only",
"recursiveUnavailable": "Recursive search is available in tree view only",
"collapseAllDisabled": "Not available in list view" "collapseAllDisabled": "Not available in list view"
}, },
"statistics": { "statistics": {

View File

@@ -529,12 +529,15 @@
"title": "Modelos embedding" "title": "Modelos embedding"
}, },
"sidebar": { "sidebar": {
"modelRoot": "Raíz del modelo", "modelRoot": "Raíz",
"collapseAll": "Colapsar todas las carpetas", "collapseAll": "Colapsar todas las carpetas",
"pinSidebar": "Fijar barra lateral", "pinSidebar": "Fijar barra lateral",
"unpinSidebar": "Desfijar barra lateral", "unpinSidebar": "Desfijar barra lateral",
"switchToListView": "Cambiar a vista de lista", "switchToListView": "Cambiar a vista de lista",
"switchToTreeView": "Cambiar a vista de árbol", "switchToTreeView": "Cambiar a vista de árbol",
"recursiveOn": "Buscar en subcarpetas",
"recursiveOff": "Buscar solo en la carpeta actual",
"recursiveUnavailable": "La búsqueda recursiva solo está disponible en la vista en árbol",
"collapseAllDisabled": "No disponible en vista de lista" "collapseAllDisabled": "No disponible en vista de lista"
}, },
"statistics": { "statistics": {

View File

@@ -529,12 +529,15 @@
"title": "Modèles Embedding" "title": "Modèles Embedding"
}, },
"sidebar": { "sidebar": {
"modelRoot": "Racine du modèle", "modelRoot": "Racine",
"collapseAll": "Réduire tous les dossiers", "collapseAll": "Réduire tous les dossiers",
"pinSidebar": "Épingler la barre latérale", "pinSidebar": "Épingler la barre latérale",
"unpinSidebar": "Désépingler la barre latérale", "unpinSidebar": "Désépingler la barre latérale",
"switchToListView": "Passer en vue liste", "switchToListView": "Passer en vue liste",
"switchToTreeView": "Passer en vue arborescence", "switchToTreeView": "Passer en vue arborescence",
"recursiveOn": "Rechercher dans les sous-dossiers",
"recursiveOff": "Rechercher uniquement dans le dossier actuel",
"recursiveUnavailable": "La recherche récursive n'est disponible qu'en vue arborescente",
"collapseAllDisabled": "Non disponible en vue liste" "collapseAllDisabled": "Non disponible en vue liste"
}, },
"statistics": { "statistics": {

View File

@@ -529,12 +529,15 @@
"title": "מודלי Embedding" "title": "מודלי Embedding"
}, },
"sidebar": { "sidebar": {
"modelRoot": "שורש המודלים", "modelRoot": "שורש",
"collapseAll": "כווץ את כל התיקיות", "collapseAll": "כווץ את כל התיקיות",
"pinSidebar": "נעל סרגל צד", "pinSidebar": "נעל סרגל צד",
"unpinSidebar": "שחרר סרגל צד", "unpinSidebar": "שחרר סרגל צד",
"switchToListView": "עבור לתצוגת רשימה", "switchToListView": "עבור לתצוגת רשימה",
"switchToTreeView": "עבור לתצוגת עץ", "switchToTreeView": "תצוגת עץ",
"recursiveOn": "חיפוש בתיקיות משנה",
"recursiveOff": "חיפוש רק בתיקייה הנוכחית",
"recursiveUnavailable": "חיפוש רקורסיבי זמין רק בתצוגת עץ",
"collapseAllDisabled": "לא זמין בתצוגת רשימה" "collapseAllDisabled": "לא זמין בתצוגת רשימה"
}, },
"statistics": { "statistics": {

View File

@@ -529,12 +529,15 @@
"title": "Embeddingモデル" "title": "Embeddingモデル"
}, },
"sidebar": { "sidebar": {
"modelRoot": "モデルルート", "modelRoot": "ルート",
"collapseAll": "すべてのフォルダを折りたたむ", "collapseAll": "すべてのフォルダを折りたたむ",
"pinSidebar": "サイドバーを固定", "pinSidebar": "サイドバーを固定",
"unpinSidebar": "サイドバーの固定を解除", "unpinSidebar": "サイドバーの固定を解除",
"switchToListView": "リストビューに切り替え", "switchToListView": "リストビューに切り替え",
"switchToTreeView": "ツリービューに切り替え", "switchToTreeView": "ツリー表示に切り替え",
"recursiveOn": "サブフォルダーを検索",
"recursiveOff": "現在のフォルダーのみを検索",
"recursiveUnavailable": "再帰検索はツリービューでのみ利用できます",
"collapseAllDisabled": "リストビューでは利用できません" "collapseAllDisabled": "リストビューでは利用できません"
}, },
"statistics": { "statistics": {

View File

@@ -529,12 +529,15 @@
"title": "Embedding 모델" "title": "Embedding 모델"
}, },
"sidebar": { "sidebar": {
"modelRoot": "모델 루트", "modelRoot": "루트",
"collapseAll": "모든 폴더 접기", "collapseAll": "모든 폴더 접기",
"pinSidebar": "사이드바 고정", "pinSidebar": "사이드바 고정",
"unpinSidebar": "사이드바 고정 해제", "unpinSidebar": "사이드바 고정 해제",
"switchToListView": "목록 보기로 전환", "switchToListView": "목록 보기로 전환",
"switchToTreeView": "트리 보기로 전환", "switchToTreeView": "트리 보기로 전환",
"recursiveOn": "하위 폴더 검색",
"recursiveOff": "현재 폴더만 검색",
"recursiveUnavailable": "재귀 검색은 트리 보기에서만 사용할 수 있습니다",
"collapseAllDisabled": "목록 보기에서는 사용할 수 없습니다" "collapseAllDisabled": "목록 보기에서는 사용할 수 없습니다"
}, },
"statistics": { "statistics": {

View File

@@ -529,12 +529,15 @@
"title": "Модели Embedding" "title": "Модели Embedding"
}, },
"sidebar": { "sidebar": {
"modelRoot": "Корень моделей", "modelRoot": "Корень",
"collapseAll": "Свернуть все папки", "collapseAll": "Свернуть все папки",
"pinSidebar": "Закрепить боковую панель", "pinSidebar": "Закрепить боковую панель",
"unpinSidebar": "Открепить боковую панель", "unpinSidebar": "Открепить боковую панель",
"switchToListView": "Переключить на вид списка", "switchToListView": "Переключить на вид списка",
"switchToTreeView": "Переключить на древовидный вид", "switchToTreeView": "Переключить на древовидный вид",
"recursiveOn": "Искать во вложенных папках",
"recursiveOff": "Искать только в текущей папке",
"recursiveUnavailable": "Рекурсивный поиск доступен только в режиме дерева",
"collapseAllDisabled": "Недоступно в виде списка" "collapseAllDisabled": "Недоступно в виде списка"
}, },
"statistics": { "statistics": {

View File

@@ -535,12 +535,15 @@
"title": "Embedding 模型" "title": "Embedding 模型"
}, },
"sidebar": { "sidebar": {
"modelRoot": "模型根目录", "modelRoot": "根目录",
"collapseAll": "折叠所有文件夹", "collapseAll": "折叠所有文件夹",
"pinSidebar": "固定侧边栏", "pinSidebar": "固定侧边栏",
"unpinSidebar": "取消固定侧边栏", "unpinSidebar": "取消固定侧边栏",
"switchToListView": "切换到列表视图", "switchToListView": "切换到列表视图",
"switchToTreeView": "切换到树状视图", "switchToTreeView": "切换到树状视图",
"recursiveOn": "搜索子文件夹",
"recursiveOff": "仅搜索当前文件夹",
"recursiveUnavailable": "仅在树形视图中可使用递归搜索",
"collapseAllDisabled": "列表视图下不可用" "collapseAllDisabled": "列表视图下不可用"
}, },
"statistics": { "statistics": {

View File

@@ -529,12 +529,15 @@
"title": "Embedding 模型" "title": "Embedding 模型"
}, },
"sidebar": { "sidebar": {
"modelRoot": "模型根目錄", "modelRoot": "根目錄",
"collapseAll": "全部摺疊資料夾", "collapseAll": "全部摺疊資料夾",
"pinSidebar": "固定側邊欄", "pinSidebar": "固定側邊欄",
"unpinSidebar": "取消固定側邊欄", "unpinSidebar": "取消固定側邊欄",
"switchToListView": "切換至列表檢視", "switchToListView": "切換至列表檢視",
"switchToTreeView": "切換樹狀檢視", "switchToTreeView": "切換樹狀檢視",
"recursiveOn": "搜尋子資料夾",
"recursiveOff": "僅搜尋目前資料夾",
"recursiveUnavailable": "遞迴搜尋僅能在樹狀檢視中使用",
"collapseAllDisabled": "列表檢視下不可用" "collapseAllDisabled": "列表檢視下不可用"
}, },
"statistics": { "statistics": {

View File

@@ -21,6 +21,7 @@ export class SidebarManager {
this.isInitialized = false; this.isInitialized = false;
this.displayMode = 'tree'; // 'tree' or 'list' this.displayMode = 'tree'; // 'tree' or 'list'
this.foldersList = []; this.foldersList = [];
this.recursiveSearchEnabled = true;
// Bind methods // Bind methods
this.handleTreeClick = this.handleTreeClick.bind(this); this.handleTreeClick = this.handleTreeClick.bind(this);
@@ -36,6 +37,7 @@ export class SidebarManager {
this.updateContainerMargin = this.updateContainerMargin.bind(this); this.updateContainerMargin = this.updateContainerMargin.bind(this);
this.handleDisplayModeToggle = this.handleDisplayModeToggle.bind(this); this.handleDisplayModeToggle = this.handleDisplayModeToggle.bind(this);
this.handleFolderListClick = this.handleFolderListClick.bind(this); this.handleFolderListClick = this.handleFolderListClick.bind(this);
this.handleRecursiveToggle = this.handleRecursiveToggle.bind(this);
} }
async initialize(pageControls) { async initialize(pageControls) {
@@ -89,6 +91,7 @@ export class SidebarManager {
this.isHovering = false; this.isHovering = false;
this.apiClient = null; this.apiClient = null;
this.isInitialized = false; this.isInitialized = false;
this.recursiveSearchEnabled = true;
// Reset container margin // Reset container margin
const container = document.querySelector('.container'); const container = document.querySelector('.container');
@@ -111,6 +114,7 @@ export class SidebarManager {
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'); const displayModeToggleBtn = document.getElementById('sidebarDisplayModeToggle');
const recursiveToggleBtn = document.getElementById('sidebarRecursiveToggle');
if (pinToggleBtn) { if (pinToggleBtn) {
pinToggleBtn.removeEventListener('click', this.handlePinToggle); pinToggleBtn.removeEventListener('click', this.handlePinToggle);
@@ -145,6 +149,9 @@ export class SidebarManager {
if (displayModeToggleBtn) { if (displayModeToggleBtn) {
displayModeToggleBtn.removeEventListener('click', this.handleDisplayModeToggle); displayModeToggleBtn.removeEventListener('click', this.handleDisplayModeToggle);
} }
if (recursiveToggleBtn) {
recursiveToggleBtn.removeEventListener('click', this.handleRecursiveToggle);
}
} }
async init() { async init() {
@@ -197,7 +204,7 @@ export class SidebarManager {
updateSidebarTitle() { updateSidebarTitle() {
const sidebarTitle = document.getElementById('sidebarTitle'); const sidebarTitle = document.getElementById('sidebarTitle');
if (sidebarTitle) { if (sidebarTitle) {
sidebarTitle.textContent = `${this.apiClient.apiConfig.config.displayName} Root`; sidebarTitle.textContent = translate('sidebar.modelRoot');
} }
} }
@@ -220,6 +227,12 @@ export class SidebarManager {
collapseAllBtn.addEventListener('click', this.handleCollapseAll); collapseAllBtn.addEventListener('click', this.handleCollapseAll);
} }
// Recursive toggle button
const recursiveToggleBtn = document.getElementById('sidebarRecursiveToggle');
if (recursiveToggleBtn) {
recursiveToggleBtn.addEventListener('click', this.handleRecursiveToggle);
}
// Tree click handler // Tree click handler
const folderTree = document.getElementById('sidebarFolderTree'); const folderTree = document.getElementById('sidebarFolderTree');
if (folderTree) { if (folderTree) {
@@ -645,11 +658,33 @@ export class SidebarManager {
this.displayMode = this.displayMode === 'tree' ? 'list' : 'tree'; this.displayMode = this.displayMode === 'tree' ? 'list' : 'tree';
this.updateDisplayModeButton(); this.updateDisplayModeButton();
this.updateCollapseAllButton(); this.updateCollapseAllButton();
this.updateRecursiveToggleButton();
this.updateSearchRecursiveOption(); this.updateSearchRecursiveOption();
this.saveDisplayMode(); this.saveDisplayMode();
this.loadFolderTree(); // Reload with new display mode this.loadFolderTree(); // Reload with new display mode
} }
async handleRecursiveToggle(event) {
event.stopPropagation();
if (this.displayMode !== 'tree') {
return;
}
this.recursiveSearchEnabled = !this.recursiveSearchEnabled;
setStorageItem(`${this.pageType}_recursiveSearch`, this.recursiveSearchEnabled);
this.updateSearchRecursiveOption();
this.updateRecursiveToggleButton();
if (this.pageControls && typeof this.pageControls.resetAndReload === 'function') {
try {
await this.pageControls.resetAndReload(true);
} catch (error) {
console.error('Failed to reload models after toggling recursive search:', error);
}
}
}
updateDisplayModeButton() { updateDisplayModeButton() {
const displayModeBtn = document.getElementById('sidebarDisplayModeToggle'); const displayModeBtn = document.getElementById('sidebarDisplayModeToggle');
if (displayModeBtn) { if (displayModeBtn) {
@@ -679,8 +714,35 @@ export class SidebarManager {
} }
} }
updateRecursiveToggleButton() {
const recursiveToggleBtn = document.getElementById('sidebarRecursiveToggle');
if (!recursiveToggleBtn) return;
const icon = recursiveToggleBtn.querySelector('i');
const isTreeMode = this.displayMode === 'tree';
const isActive = isTreeMode && this.recursiveSearchEnabled;
recursiveToggleBtn.classList.toggle('active', isActive);
recursiveToggleBtn.classList.toggle('disabled', !isTreeMode);
recursiveToggleBtn.setAttribute('aria-pressed', isActive ? 'true' : 'false');
recursiveToggleBtn.setAttribute('aria-disabled', isTreeMode ? 'false' : 'true');
if (icon) {
icon.className = 'fas fa-code-branch';
}
if (!isTreeMode) {
recursiveToggleBtn.title = translate('sidebar.recursiveUnavailable');
} else if (this.recursiveSearchEnabled) {
recursiveToggleBtn.title = translate('sidebar.recursiveOn');
} else {
recursiveToggleBtn.title = translate('sidebar.recursiveOff');
}
}
updateSearchRecursiveOption() { updateSearchRecursiveOption() {
this.pageControls.pageState.searchOptions.recursive = this.displayMode === 'tree'; const isRecursive = this.displayMode === 'tree' && this.recursiveSearchEnabled;
this.pageControls.pageState.searchOptions.recursive = isRecursive;
} }
updateTreeSelection() { updateTreeSelection() {
@@ -925,15 +987,18 @@ export class SidebarManager {
const isPinned = getStorageItem(`${this.pageType}_sidebarPinned`, true); const isPinned = getStorageItem(`${this.pageType}_sidebarPinned`, true);
const expandedPaths = getStorageItem(`${this.pageType}_expandedNodes`, []); const expandedPaths = getStorageItem(`${this.pageType}_expandedNodes`, []);
const displayMode = getStorageItem(`${this.pageType}_displayMode`, 'tree'); // 'tree' or 'list', default to 'tree' const displayMode = getStorageItem(`${this.pageType}_displayMode`, 'tree'); // 'tree' or 'list', default to 'tree'
const recursiveSearchEnabled = getStorageItem(`${this.pageType}_recursiveSearch`, true);
this.isPinned = isPinned; this.isPinned = isPinned;
this.expandedNodes = new Set(expandedPaths); this.expandedNodes = new Set(expandedPaths);
this.displayMode = displayMode; this.displayMode = displayMode;
this.recursiveSearchEnabled = recursiveSearchEnabled;
this.updatePinButton(); this.updatePinButton();
this.updateDisplayModeButton(); this.updateDisplayModeButton();
this.updateCollapseAllButton(); this.updateCollapseAllButton();
this.updateSearchRecursiveOption(); this.updateSearchRecursiveOption();
this.updateRecursiveToggleButton();
} }
restoreSelectedFolder() { restoreSelectedFolder() {

View File

@@ -67,7 +67,7 @@ export const state = {
modelname: true, modelname: true,
tags: false, tags: false,
creator: false, creator: false,
recursive: true, recursive: getStorageItem(`${MODEL_TYPES.LORA}_recursiveSearch`, true),
}, },
filters: { filters: {
baseModel: [], baseModel: [],
@@ -116,7 +116,7 @@ export const state = {
filename: true, filename: true,
modelname: true, modelname: true,
creator: false, creator: false,
recursive: true, recursive: getStorageItem(`${MODEL_TYPES.CHECKPOINT}_recursiveSearch`, true),
}, },
filters: { filters: {
baseModel: [], baseModel: [],
@@ -144,7 +144,7 @@ export const state = {
modelname: true, modelname: true,
tags: false, tags: false,
creator: false, creator: false,
recursive: true, recursive: getStorageItem(`${MODEL_TYPES.EMBEDDING}_recursiveSearch`, true),
}, },
filters: { filters: {
baseModel: [], baseModel: [],

View File

@@ -9,6 +9,9 @@
<button class="sidebar-action-btn" id="sidebarDisplayModeToggle" title="{{ t('sidebar.switchToListView') }}"> <button class="sidebar-action-btn" id="sidebarDisplayModeToggle" title="{{ t('sidebar.switchToListView') }}">
<i class="fas fa-sitemap"></i> <i class="fas fa-sitemap"></i>
</button> </button>
<button class="sidebar-action-btn active" id="sidebarRecursiveToggle" title="{{ t('sidebar.recursiveOn') }}" aria-pressed="true">
<i class="fas fa-code-branch"></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>