diff --git a/public/app.js b/public/app.js index fdefa1b6..8f9619ff 100644 --- a/public/app.js +++ b/public/app.js @@ -71,6 +71,8 @@ const fileSidebar = document.getElementById('file-sidebar'); const fileSidebarToggle = document.getElementById('file-sidebar-toggle'); const fileSidebarClose = document.getElementById('file-sidebar-close'); const fileSidebarUp = document.getElementById('file-sidebar-up'); +const fileSidebarSort = document.getElementById('file-sidebar-sort'); +const fileSidebarSearch = document.getElementById('file-sidebar-search'); const fileList = document.getElementById('file-list'); const fileSidebarPath = document.getElementById('file-sidebar-path'); const fileBrowser = new FileBrowser(fileList, fileSidebarPath, messageInput, (filePath) => { @@ -78,6 +80,50 @@ const fileBrowser = new FileBrowser(fileList, fileSidebarPath, messageInput, (fi const ext = name.split('.').pop()?.toLowerCase() || ''; pendingFilePaths.push({ path: filePath, name, ext }); renderAttachmentPreviews(); +}, fileSidebarSearch); + +const fileSortDropdown = document.getElementById('file-sort-dropdown'); +const fileSortMenu = document.getElementById('file-sort-menu'); +const SORT_LABELS = { name: 'Name', date: 'Date', size: 'Size' }; + +function updateSortMenuActive() { + fileSortMenu.querySelectorAll('.model-dropdown-item').forEach(el => { + el.classList.toggle('active', el.dataset.sort === fileBrowser.sortBy); + }); +} + +function closeSortMenu() { + fileSortMenu.classList.add('hidden'); + fileSortDropdown.classList.remove('open'); +} + +fileSidebarSort.title = `Sort: ${SORT_LABELS[fileBrowser.sortBy]}`; +fileSidebarSort.addEventListener('click', (e) => { + e.stopPropagation(); + const isOpen = !fileSortMenu.classList.contains('hidden'); + if (isOpen) { + closeSortMenu(); + } else { + updateSortMenuActive(); + fileSortMenu.classList.remove('hidden'); + fileSortDropdown.classList.add('open'); + } +}); + +fileSortMenu.querySelectorAll('.model-dropdown-item').forEach(el => { + el.addEventListener('click', () => { + fileBrowser.setSortBy(el.dataset.sort); + fileSidebarSort.title = `Sort: ${SORT_LABELS[el.dataset.sort]}`; + closeSortMenu(); + }); +}); + +document.addEventListener('click', (e) => { + if (!fileSortDropdown.contains(e.target)) closeSortMenu(); +}); + +fileSidebarSearch.addEventListener('input', () => { + fileBrowser.setSearchQuery(fileSidebarSearch.value); }); fileSidebarToggle.addEventListener('click', () => { diff --git a/public/file-browser.js b/public/file-browser.js index efd73805..6a27efe9 100644 --- a/public/file-browser.js +++ b/public/file-browser.js @@ -37,19 +37,29 @@ function formatSize(bytes) { return `${(bytes / (1024 * 1024)).toFixed(1)}M`; } +const SORT_MODES = ['name', 'date', 'size']; + export class FileBrowser { - constructor(container, pathEl, messageInput, onFileInserted = null) { + constructor(container, pathEl, messageInput, onFileInserted = null, searchInput = null) { this.container = container; this.pathEl = pathEl; this.messageInput = messageInput; this.onFileInserted = onFileInserted; + this.searchInput = searchInput; this.currentPath = null; + this.items = []; + this.searchQuery = ''; + this.sortBy = SORT_MODES.includes(localStorage.getItem('tau-file-sort')) + ? localStorage.getItem('tau-file-sort') + : 'name'; this.setupDropTarget(); } async load(dirPath) { this.container.innerHTML = '