|
2030 | 2030 | }, false); |
2031 | 2031 | }); |
2032 | 2032 |
|
2033 | | - // Handle dropped files |
2034 | | - document.addEventListener('drop', e => { |
| 2033 | + // Handle dropped items (folders/files) |
| 2034 | + document.addEventListener('drop', async e => { |
2035 | 2035 | dragDropZone.classList.remove('drag-over'); |
2036 | 2036 |
|
2037 | | - const files = e.dataTransfer.files; |
2038 | | - if (files.length > 0) { |
2039 | | - // Hide drag-drop zone and show editor |
2040 | | - dragDropZone.style.display = 'none'; |
2041 | | - if (codeEditor) codeEditor.style.display = 'block'; |
2042 | | - |
2043 | | - // Show loader IMMEDIATELY |
2044 | | - load(true, 'Loading dropped files...'); |
2045 | | - // Process files in next tick to let loader render |
2046 | | - setTimeout(() => loadFiles(files), 0); |
2047 | | - } else { |
2048 | | - toast('Please drop a folder with files', 'warning'); |
| 2037 | + const items = e.dataTransfer.items; |
| 2038 | + if (!items || items.length === 0) { |
| 2039 | + toast('No items detected. Please try using "Select Directory" button', 'warning'); |
| 2040 | + return; |
| 2041 | + } |
| 2042 | + |
| 2043 | + // Hide drag-drop zone and show editor |
| 2044 | + dragDropZone.style.display = 'none'; |
| 2045 | + if (codeEditor) codeEditor.style.display = 'block'; |
| 2046 | + |
| 2047 | + // Show loader IMMEDIATELY |
| 2048 | + load(true, 'Processing dropped folder...'); |
| 2049 | + |
| 2050 | + // Process dropped items |
| 2051 | + const allFiles = []; |
| 2052 | + |
| 2053 | + // Use webkitGetAsEntry for folder support |
| 2054 | + for (let i = 0; i < items.length; i++) { |
| 2055 | + const item = items[i].webkitGetAsEntry(); |
| 2056 | + if (item) { |
| 2057 | + if (item.isDirectory) { |
| 2058 | + await traverseDirectory(item, '', allFiles); |
| 2059 | + } else if (item.isFile) { |
| 2060 | + const file = items[i].getAsFile(); |
| 2061 | + if (file) { |
| 2062 | + // Add webkitRelativePath property |
| 2063 | + Object.defineProperty(file, 'webkitRelativePath', { |
| 2064 | + value: file.name, |
| 2065 | + writable: false |
| 2066 | + }); |
| 2067 | + allFiles.push(file); |
| 2068 | + } |
| 2069 | + } |
| 2070 | + } |
| 2071 | + } |
| 2072 | + |
| 2073 | + if (allFiles.length === 0) { |
| 2074 | + toast('No files found in dropped folder', 'warning'); |
| 2075 | + load(false); |
| 2076 | + dragDropZone.style.display = 'flex'; |
| 2077 | + if (codeEditor) codeEditor.style.display = 'none'; |
| 2078 | + return; |
2049 | 2079 | } |
| 2080 | + |
| 2081 | + // Create a FileList-like object |
| 2082 | + const fileList = { |
| 2083 | + length: allFiles.length, |
| 2084 | + item: i => allFiles[i], |
| 2085 | + [Symbol.iterator]: function* () { |
| 2086 | + for (let i = 0; i < allFiles.length; i++) { |
| 2087 | + yield allFiles[i]; |
| 2088 | + } |
| 2089 | + } |
| 2090 | + }; |
| 2091 | + |
| 2092 | + // Add array access |
| 2093 | + allFiles.forEach((file, idx) => { |
| 2094 | + fileList[idx] = file; |
| 2095 | + }); |
| 2096 | + |
| 2097 | + // Process files |
| 2098 | + setTimeout(() => loadFiles(fileList), 0); |
2050 | 2099 | }, false); |
2051 | 2100 |
|
| 2101 | + // Recursive function to traverse directory structure |
| 2102 | + async function traverseDirectory(entry, path, files) { |
| 2103 | + if (entry.isFile) { |
| 2104 | + return new Promise((resolve) => { |
| 2105 | + entry.file(file => { |
| 2106 | + // Add webkitRelativePath property |
| 2107 | + const relativePath = path + file.name; |
| 2108 | + Object.defineProperty(file, 'webkitRelativePath', { |
| 2109 | + value: relativePath, |
| 2110 | + writable: false |
| 2111 | + }); |
| 2112 | + files.push(file); |
| 2113 | + resolve(); |
| 2114 | + }); |
| 2115 | + }); |
| 2116 | + } else if (entry.isDirectory) { |
| 2117 | + const dirReader = entry.createReader(); |
| 2118 | + return new Promise((resolve) => { |
| 2119 | + const readEntries = () => { |
| 2120 | + dirReader.readEntries(async entries => { |
| 2121 | + if (entries.length === 0) { |
| 2122 | + resolve(); |
| 2123 | + return; |
| 2124 | + } |
| 2125 | + for (const childEntry of entries) { |
| 2126 | + await traverseDirectory( |
| 2127 | + childEntry, |
| 2128 | + path + entry.name + '/', |
| 2129 | + files |
| 2130 | + ); |
| 2131 | + } |
| 2132 | + readEntries(); // Continue reading if there are more entries |
| 2133 | + }); |
| 2134 | + }; |
| 2135 | + readEntries(); |
| 2136 | + }); |
| 2137 | + } |
| 2138 | + } |
| 2139 | + |
2052 | 2140 | // Click on zone to trigger folder selection |
2053 | 2141 | dragDropZone.addEventListener('click', () => { |
2054 | 2142 | if (D.sel) D.sel.click(); |
|
0 commit comments