|
369 | 369 | }; |
370 | 370 | const D = { |
371 | 371 | side: $safe('sidebar'), tog: $safe('toggleSidebar'), tree: $safe('fileTree'), |
372 | | - search: $safe('fileSearch'), sel: $safe('selectDirBtn'), |
| 372 | + search: $safe('fileSearch'), searchMeta: $safe('searchMeta'), searchClear: $safe('searchClear'), |
| 373 | + sel: $safe('selectDirBtn'), |
373 | 374 | exp: $safe('expandAll'), col: $safe('collapseAll'), |
374 | 375 | all: $safe('selectAll'), none: $safe('deselectAll'), |
375 | 376 | gen: $safe('generateContextBtn'), ed: $safe('codeEditor'), |
|
867 | 868 | D.tok.textContent = '0'; |
868 | 869 | D.sz.textContent = '0 B'; |
869 | 870 | D.pron.textContent = 'zero tokens'; |
| 871 | + if (D.search) D.search.value = ''; |
| 872 | + updateTreeSearch(''); |
870 | 873 | D.lang.textContent = '-'; |
871 | 874 | // Clear search |
872 | 875 | D.search.value = ''; |
|
960 | 963 | }; |
961 | 964 | // Pass parents array for lines |
962 | 965 | const renderSub = (nodes, lv, parents) => render(nodes, lv, [], parents).map(x => x.html).join(''); |
| 966 | + |
| 967 | + const openTreeParents = item => { |
| 968 | + let current = item.parentElement; |
| 969 | + while (current) { |
| 970 | + if (current.classList && current.classList.contains('tree-children')) { |
| 971 | + current.classList.add('open'); |
| 972 | + const parentItem = current.parentElement; |
| 973 | + if (parentItem && parentItem.classList.contains('tree-item')) { |
| 974 | + const btn = parentItem.querySelector('.expand-btn'); |
| 975 | + if (btn) { |
| 976 | + btn.classList.add('expanded'); |
| 977 | + const arrowImg = btn.querySelector('.tree-arrow'); |
| 978 | + if (arrowImg) { |
| 979 | + arrowImg.src = 'public/arrowDown.svg'; |
| 980 | + arrowImg.setAttribute('data-arrow', 'down'); |
| 981 | + } |
| 982 | + } |
| 983 | + } |
| 984 | + current = parentItem ? parentItem.parentElement : null; |
| 985 | + continue; |
| 986 | + } |
| 987 | + current = current.parentElement; |
| 988 | + } |
| 989 | + }; |
| 990 | + |
| 991 | + const updateTreeSearch = query => { |
| 992 | + const term = (query || '').trim().toLowerCase(); |
| 993 | + const items = document.querySelectorAll('.tree-item'); |
| 994 | + let matches = 0; |
| 995 | + const total = items.length; |
| 996 | + items.forEach(item => { |
| 997 | + const nameEl = item.querySelector('.file-name'); |
| 998 | + const name = nameEl ? nameEl.textContent.toLowerCase() : ''; |
| 999 | + const path = (item.dataset.path || '').toLowerCase(); |
| 1000 | + const match = !term || name.includes(term) || path.includes(term); |
| 1001 | + item.style.display = match ? '' : 'none'; |
| 1002 | + item.classList.toggle('search-match', !!term && match); |
| 1003 | + if (term && match) { |
| 1004 | + matches += 1; |
| 1005 | + openTreeParents(item); |
| 1006 | + } |
| 1007 | + }); |
| 1008 | + if (D.searchMeta) { |
| 1009 | + D.searchMeta.textContent = term |
| 1010 | + ? `${matches} match${matches === 1 ? '' : 'es'} of ${total}` |
| 1011 | + : 'Search file and folder names (case-insensitive)'; |
| 1012 | + } |
| 1013 | + if (D.searchClear) { |
| 1014 | + D.searchClear.hidden = !term; |
| 1015 | + } |
| 1016 | + }; |
963 | 1017 | // ============================================================================ |
964 | 1018 | // FILE LOADING - ASYNC CHUNKS (ENHANCED FILTERING & UI UPDATE) |
965 | 1019 | // ============================================================================ |
|
1023 | 1077 | const items = render(S.tree); |
1024 | 1078 | D.tree.innerHTML = items.map(x => x.html).join(''); |
1025 | 1079 | S.rendered = items.length; |
| 1080 | + if (D.search) updateTreeSearch(D.search.value); |
1026 | 1081 | stats(); |
1027 | 1082 | toast(`Loaded ${S.files.length} files`, 'success'); |
1028 | 1083 | } catch (e) { |
|
1576 | 1631 | stats(); |
1577 | 1632 | } |
1578 | 1633 | }; |
1579 | | - if (D.search) D.search.addEventListener('input', e => { |
1580 | | - const q = e.target.value.toLowerCase(); |
1581 | | - document.querySelectorAll('.tree-item').forEach(item => { |
1582 | | - const n = item.querySelector('.file-name').textContent.toLowerCase(); |
1583 | | - item.style.display = n.includes(q) ? '' : 'none'; |
| 1634 | + if (D.search) { |
| 1635 | + D.search.addEventListener('input', e => updateTreeSearch(e.target.value)); |
| 1636 | + D.search.addEventListener('keydown', e => { |
| 1637 | + if (e.key === 'Escape') { |
| 1638 | + e.target.value = ''; |
| 1639 | + updateTreeSearch(''); |
| 1640 | + } |
1584 | 1641 | }); |
1585 | | - }); |
| 1642 | + } |
| 1643 | + if (D.searchClear) { |
| 1644 | + D.searchClear.hidden = true; |
| 1645 | + D.searchClear.addEventListener('click', () => { |
| 1646 | + if (D.search) { |
| 1647 | + D.search.value = ''; |
| 1648 | + updateTreeSearch(''); |
| 1649 | + D.search.focus(); |
| 1650 | + } |
| 1651 | + }); |
| 1652 | + } |
1586 | 1653 | const togFold = o => { |
1587 | 1654 | document.querySelectorAll('.expand-btn').forEach(btn => { |
1588 | 1655 | const item = btn.closest('.tree-item'); |
|
0 commit comments