|
856 | 856 | // ============================================================================ |
857 | 857 |
|
858 | 858 | const loadFiles = async list => { |
859 | | - // Loader is already shown by inp.onchange handler |
860 | | - |
861 | 859 | try { |
862 | | - // Update loading message |
863 | 860 | const loadingText = D.load.querySelector('p'); |
864 | | - if (loadingText) loadingText.textContent = `Processing ${list.length} files...`; |
865 | | - |
866 | | - // Convert FileList to Array in small chunks to avoid blocking |
867 | | - const all = []; |
868 | | - const batchSize = 1000; |
869 | | - |
870 | | - for (let i = 0; i < list.length; i += batchSize) { |
871 | | - const end = Math.min(i + batchSize, list.length); |
872 | | - for (let j = i; j < end; j++) { |
873 | | - all.push(list[j]); |
874 | | - } |
875 | | - |
876 | | - // Update progress |
877 | | - if (loadingText) { |
878 | | - const progress = Math.round((i / list.length) * 100); |
879 | | - loadingText.textContent = `Processing files... ${progress}%`; |
880 | | - } |
881 | | - |
882 | | - // Let UI breathe |
883 | | - await new Promise(r => setTimeout(r, 0)); |
884 | | - } |
| 861 | + if (loadingText) loadingText.textContent = `Loading ${list.length} files...`; |
885 | 862 |
|
| 863 | + // Fast path: convert FileList to Array |
| 864 | + const all = Array.from(list); |
| 865 | + |
886 | 866 | if (loadingText) loadingText.textContent = 'Filtering files...'; |
887 | | - S.files = []; |
888 | | - |
889 | | - // Process in chunks to avoid blocking |
890 | | - for (let i = 0; i < all.length; i += 500) { |
891 | | - const chunk = all.slice(i, i + 500); |
892 | | - chunk.forEach(f => { |
893 | | - const path = f.webkitRelativePath; |
894 | | - if (!ign(path)) { |
895 | | - S.files.push({ path, name: f.name, size: f.size, file: f }); |
896 | | - } |
897 | | - }); |
898 | | - |
899 | | - // Update progress |
900 | | - if (loadingText) { |
901 | | - const progress = Math.round((i / all.length) * 100); |
902 | | - loadingText.textContent = `Filtering files... ${progress}%`; |
903 | | - } |
| 867 | + await new Promise(r => setTimeout(r, 0)); |
904 | 868 |
|
905 | | - await new Promise(r => setTimeout(r, 0)); // Let UI breathe |
906 | | - } |
| 869 | + // Filter files |
| 870 | + S.files = all.filter(f => !ign(f.webkitRelativePath)) |
| 871 | + .map(f => ({ |
| 872 | + path: f.webkitRelativePath, |
| 873 | + name: f.name, |
| 874 | + size: f.size, |
| 875 | + file: f |
| 876 | + })); |
907 | 877 |
|
908 | 878 | if (S.files.length === 0) { toast('No valid files', 'warning'); load(false); return; } |
909 | 879 | if (S.files.length > 5000) { toast(`Large directory (${S.files.length} files) - rendering first 1000`, 'warning'); } |
910 | 880 |
|
911 | 881 | if (loadingText) loadingText.textContent = 'Building file tree...'; |
912 | | - S.root = list[0].webkitRelativePath.split('/')[0]; |
| 882 | + S.root = S.files[0].path.split('/')[0]; |
913 | 883 |
|
914 | 884 | // Let UI update before building tree |
915 | | - await new Promise(r => setTimeout(r, 10)); |
| 885 | + await new Promise(r => setTimeout(r, 0)); |
916 | 886 |
|
917 | 887 | S.tree = build(S.files.slice(0, 3000)); // Limit tree size |
918 | 888 |
|
919 | 889 | if (loadingText) loadingText.textContent = 'Rendering tree...'; |
920 | | - await new Promise(r => setTimeout(r, 10)); |
| 890 | + await new Promise(r => setTimeout(r, 0)); |
921 | 891 |
|
922 | 892 | // Render tree |
923 | 893 | const items = render(S.tree); |
|
926 | 896 |
|
927 | 897 | stats(); |
928 | 898 |
|
929 | | - // Reset loading message |
930 | | - if (loadingText) loadingText.textContent = 'Processing files...'; |
931 | | - |
932 | 899 | toast(`Loaded ${S.files.length} files`, 'success'); |
933 | 900 | } catch (e) { |
934 | 901 | console.error('Load error:', e); |
|
1166 | 1133 | D.sel.onclick = () => inp.click(); |
1167 | 1134 |
|
1168 | 1135 | // Optimize file input change handler |
1169 | | - inp.onchange = async e => { |
| 1136 | + inp.onchange = e => { |
1170 | 1137 | if (!e.target.files.length) return; |
1171 | | - |
1172 | | - // Show loader IMMEDIATELY before any processing |
| 1138 | + |
| 1139 | + // Show loader IMMEDIATELY |
1173 | 1140 | load(true); |
1174 | | - |
1175 | | - // Let the loader render before starting heavy work |
1176 | | - await new Promise(r => setTimeout(r, 50)); |
1177 | | - |
1178 | | - // Now load files |
1179 | | - loadFiles(e.target.files); |
| 1141 | + |
| 1142 | + // Process files in next tick to let loader render |
| 1143 | + setTimeout(() => loadFiles(e.target.files), 0); |
1180 | 1144 | }; |
1181 | 1145 |
|
1182 | 1146 | D.tree.onclick = e => { |
|
0 commit comments