const fs = require('fs'); let content = fs.readFileSync('frontend/src/components/FileExplorer.tsx', 'utf-8'); // 1. Add ConflictState to the component state const stateInsertPos = content.indexOf(' const [deleteConfirm'); const conflictStateCode = ` const [conflictState, setConflictState] = useState<{ type: 'upload' | 'move' | 'copy'; files?: {file: File, path: string}[]; sourcePaths?: string[]; destPath: string; conflicts: string[]; } | null>(null);\n`; content = content.slice(0, stateInsertPos) + conflictStateCode + content.slice(stateInsertPos); // 2. Replace handleDrop and uploadFile with processUploadBatch and updated handleDrop const uploadFileStart = content.indexOf(' async function uploadFile'); const handleCreateFolderStart = content.indexOf(' async function handleCreateFolder'); const newUploadCode = ` async function processUploadBatch(entries: {file: File, path: string}[], destPath: string, overwrite: boolean = false) { if (!entries.length) return; const batchId = 'batch-' + Date.now(); const totalFiles = entries.length; const totalBytes = entries.reduce((acc, curr) => acc + curr.file.size, 0); const batchName = totalFiles === 1 ? entries[0].file.name : \`Uploading \${totalFiles} items\`; setUploads(prev => [...prev, { id: batchId, name: batchName, totalBytes, uploadedBytes: 0, totalFiles, filesUploaded: 0, status: 'uploading' }]); const MAX_CONCURRENT = 4; let currentIndex = 0; let completedFiles = 0; let currentUploadedBytes = 0; const fileProgressMap = new Map(); const updateProgress = () => { let totalUploaded = 0; fileProgressMap.forEach(bytes => { totalUploaded += bytes; }); setUploads(prev => prev.map(b => b.id === batchId ? { ...b, uploadedBytes: totalUploaded, filesUploaded: completedFiles, status: completedFiles === totalFiles ? 'complete' : 'uploading' } : b)); }; const worker = async () => { while (currentIndex < entries.length) { const index = currentIndex++; const { file, path } = entries[index]; const uploadPath = path ? (destPath === '.' ? path : \`\${destPath}/\${path}\`) : destPath; try { await api.upload(file, uploadPath, { overwrite, onProgress: (percent) => { fileProgressMap.set(file.name + path, (percent / 100) * file.size); updateProgress(); } }); completedFiles++; fileProgressMap.set(file.name + path, file.size); updateProgress(); } catch (err: any) { setUploads(prev => prev.map(b => b.id === batchId ? { ...b, status: 'error', error: err.message || 'Upload failed' } : b)); } } }; const workers = []; for (let i = 0; i < Math.min(MAX_CONCURRENT, entries.length); i++) { workers.push(worker()); } await Promise.all(workers); setTimeout(() => { setUploads(prev => prev.filter(u => u.id !== batchId)); loadFiles(undefined, true); }, 3000); } async function handleDrop(e: React.DragEvent) { e.preventDefault(); setDragOver(false); if (draggedFile) return; if (e.dataTransfer.items) { const filesToUpload: { file: File; path: string }[] = []; const promises: Promise[] = []; const processEntry = async (entry: any, path: string = '') => { if (entry.isFile) { const file = await new Promise((resolve) => entry.file(resolve)); filesToUpload.push({ file, path: path.replace(/\\/$/, '') }); } else if (entry.isDirectory) { const dirReader = entry.createReader(); let entries: any[] = []; const readAllEntries = async () => { const batch = await new Promise((resolve) => dirReader.readEntries(resolve)); if (batch.length > 0) { entries = entries.concat(batch); await readAllEntries(); } }; await readAllEntries(); for (const child of entries) { await processEntry(child, \`\${path}\${entry.name}/\`); } } }; for (let i = 0; i < e.dataTransfer.items.length; i++) { const item = e.dataTransfer.items[i]; if (item.kind === 'file') { const entry = item.webkitGetAsEntry(); if (entry) { promises.push(processEntry(entry)); } } } await Promise.all(promises); if (filesToUpload.length > 0) { // Collect destination paths to create folders const uniqueDirs = new Set(); filesToUpload.forEach(({path}) => { if (path) uniqueDirs.add(currentPath === '.' ? path : \`\${currentPath}/\${path}\`); }); for (const dir of Array.from(uniqueDirs)) { try { await api.createFolder(dir); } catch {} } const fileNames = filesToUpload.map(f => f.path ? \`\${f.path}/\${f.file.name}\` : f.file.name); try { const res = await api.checkConflicts(currentPath, fileNames); if (res.conflicts && res.conflicts.length > 0) { setConflictState({ type: 'upload', files: filesToUpload, destPath: currentPath, conflicts: res.conflicts }); return; } } catch {} processUploadBatch(filesToUpload, currentPath); } } } async function handleFileUpload(e: React.ChangeEvent) { const uploadFiles = e.target.files; if (!uploadFiles) return; const filesArray = Array.from(uploadFiles).map(f => ({ file: f, path: '' })); const fileNames = filesArray.map(f => f.file.name); try { const res = await api.checkConflicts(currentPath, fileNames); if (res.conflicts && res.conflicts.length > 0) { setConflictState({ type: 'upload', files: filesArray, destPath: currentPath, conflicts: res.conflicts }); return; } } catch {} processUploadBatch(filesArray, currentPath); } `; content = content.slice(0, uploadFileStart) + newUploadCode + content.slice(handleCreateFolderStart); // Add Conflict Modal rendering const renderModalsPos = content.lastIndexOf('{showMoveModal && ('); const conflictModalCode = ` {conflictState && (
setConflictState(null)}> e.stopPropagation()} >

Name Conflict Detected

The following files already exist in the destination. How would you like to proceed?

    {conflictState.conflicts.map(c =>
  • {c}
  • )}
)} `; content = content.slice(0, renderModalsPos) + conflictModalCode + content.slice(renderModalsPos); // Add executeConflictResolution function const execFunctionCode = ` async function executeConflictResolution(resolution: string) { if (!conflictState) return; const { type, destPath, conflicts } = conflictState; if (type === 'upload' && conflictState.files) { let filesToUpload = conflictState.files; if (resolution === 'skip') { filesToUpload = filesToUpload.filter(f => { const name = f.path ? \`\${f.path}/\${f.file.name}\` : f.file.name; return !conflicts.includes(name); }); } setConflictState(null); if (filesToUpload.length > 0) { processUploadBatch(filesToUpload, destPath, resolution === 'overwrite'); } } else if (type === 'move' && conflictState.sourcePaths) { let pathsToMove = conflictState.sourcePaths; if (resolution === 'skip') { pathsToMove = pathsToMove.filter(p => !conflicts.includes(p.split('/').pop()!)); } setConflictState(null); if (pathsToMove.length > 0) { try { await Promise.all(pathsToMove.map(src => api.move(src, destPath, resolution))); setShowMoveModal(false); setMovingFiles([]); loadFiles(undefined, true); setSelectedFiles(new Set()); showToast(\`Moved \${pathsToMove.length} item\${pathsToMove.length > 1 ? 's' : ''}\`); } catch (e) { console.error(e); } } } } `; const handleDropOnFolderStart = content.indexOf(' async function handleDropOnFolder'); content = content.slice(0, handleDropOnFolderStart) + execFunctionCode + content.slice(handleDropOnFolderStart); // Update handleMoveExecute content = content.replace( \` async function handleMoveExecute() { if (movingFiles.length === 0) return; try { await Promise.all(movingFiles.map(src => api.move(src, moveModalPath === '.' ? '.' : moveModalPath)));\`, \` async function handleMoveExecute() { if (movingFiles.length === 0) return; try { const destPath = moveModalPath === '.' ? '.' : moveModalPath; const fileNames = movingFiles.map(src => src.split('/').pop() || src); const res = await api.checkConflicts(destPath, fileNames); if (res.conflicts && res.conflicts.length > 0) { setConflictState({ type: 'move', sourcePaths: movingFiles, destPath, conflicts: res.conflicts }); return; } await Promise.all(movingFiles.map(src => api.move(src, destPath)));\` ); // Update handleDropOnFolder content = content.replace( \` async function handleDropOnFolder(file: FileItem, targetFolder: FileItem) { if (file.path === targetFolder.path) return; try { await api.move(file.path, targetFolder.path);\`, \` async function handleDropOnFolder(file: FileItem, targetFolder: FileItem) { if (file.path === targetFolder.path) return; try { const res = await api.checkConflicts(targetFolder.path, [file.name]); if (res.conflicts && res.conflicts.length > 0) { setConflictState({ type: 'move', sourcePaths: [file.path], destPath: targetFolder.path, conflicts: res.conflicts }); return; } await api.move(file.path, targetFolder.path);\` ); fs.writeFileSync('frontend/src/components/FileExplorer.tsx', content);