diff --git a/app.py b/app.py index b4adcb4..f784c19 100644 --- a/app.py +++ b/app.py @@ -113,6 +113,37 @@ def delete_file(): except Exception as e: return jsonify({"error": str(e)}), 500 +@app.route('/api/delete_multiple', methods=['DELETE']) +def delete_multiple_files(): + """Deletes multiple files.""" + data = request.get_json() + paths = data.get('paths', []) + + if not paths: + return jsonify({"error": "No file paths provided"}), 400 + + errors = [] + success_count = 0 + + for path in paths: + if not path or not os.path.exists(path): + errors.append({"path": path, "error": "File not found"}) + continue + + try: + if os.path.isdir(path): + errors.append({"path": path, "error": "Cannot delete directories"}) + else: + os.remove(path) + success_count += 1 + except Exception as e: + errors.append({"path": path, "error": str(e)}) + + if not errors: + return jsonify({"success": True, "message": f"{success_count} files deleted."}) + else: + return jsonify({"success": False, "error": "Some files could not be deleted", "details": errors}), 500 + if __name__ == '__main__': # Run on localhost print("Starting File Explorer on http://127.0.0.1:5005") diff --git a/templates/index.html b/templates/index.html index 7e1d4ce..ccec983 100644 --- a/templates/index.html +++ b/templates/index.html @@ -40,6 +40,9 @@ + @@ -49,6 +52,7 @@ + @@ -86,8 +90,27 @@ const API_BASE = '/api'; let currentPath = ""; - // Initial Load - document.addEventListener('DOMContentLoaded', () => loadFiles()); + document.addEventListener('DOMContentLoaded', () => { + // Initial Load + loadFiles(); + + const selectAllCheckbox = document.getElementById('selectAllCheckbox'); + const fileTableBody = document.getElementById('fileTableBody'); + + selectAllCheckbox.addEventListener('change', (e) => { + const checkboxes = fileTableBody.querySelectorAll('.file-checkbox'); + checkboxes.forEach(checkbox => { + checkbox.checked = e.target.checked; + }); + updateDeleteButtonVisibility(); + }); + + fileTableBody.addEventListener('change', (e) => { + if (e.target.classList.contains('file-checkbox')) { + updateDeleteButtonVisibility(); + } + }); + }); async function loadFiles() { const path = document.getElementById('pathInput').value; @@ -127,12 +150,11 @@ if (parentPath === '') parentPath = '/'; const tr = document.createElement('tr'); - tr.className = 'file-row'; - tr.onclick = () => enterDir(parentPath); tr.innerHTML = ` + `; tbody.appendChild(tr); @@ -141,17 +163,13 @@ if (files.length === 0) { // If we haven't added the "up" directory, show no files. if (tbody.childElementCount === 0) { - tbody.innerHTML = ''; + tbody.innerHTML = ''; } return; } files.forEach(file => { const tr = document.createElement('tr'); - tr.className = 'file-row'; - if (file.is_dir) { - tr.onclick = () => enterDir(file.path); - } // Icon logic let icon = 'fa-file'; @@ -164,13 +182,29 @@ const size = file.is_dir ? '-' : (file.size / 1024).toFixed(1) + ' KB'; tr.innerHTML = ` + - + `; + + // Row click should not trigger when clicking on interactive elements + tr.addEventListener('click', (e) => { + if (e.target.matches('input, button, a, i')) return; // Ignore clicks on actions/checkboxes + if (file.is_dir) { + enterDir(file.path); + } + }); + + // Make the text part of the cell clickable for directory navigation + const fileNameCell = tr.querySelector('.file-name-cell'); + if(file.is_dir) { + fileNameCell.classList.add('file-row'); + fileNameCell.onclick = () => enterDir(file.path); + } tbody.appendChild(tr); }); } @@ -232,7 +266,47 @@ alert("Network error"); } } + + async function deleteSelected() { + const selectedCheckboxes = document.querySelectorAll('.file-checkbox:checked'); + if (selectedCheckboxes.length === 0) { + alert("Please select files to delete."); + return; + } + + if (!confirm(`Are you sure you want to delete ${selectedCheckboxes.length} selected files? This cannot be undone.`)) return; + + const paths = Array.from(selectedCheckboxes).map(cb => cb.dataset.path); + + try { + const res = await fetch(`${API_BASE}/delete_multiple`, { + method: 'DELETE', + headers: { + 'Content-Type': 'application/json' + }, + body: JSON.stringify({ paths: paths }) + }); + + const data = await res.json(); + + if (data.success) { + loadFiles(); // Refresh list + } else { + alert("Error deleting files: " + (data.error || "Unknown error")); + } + } catch (err) { + alert("Network error during multiple delete."); + } + } + + function updateDeleteButtonVisibility() { + const selectedCheckboxes = document.querySelectorAll('.file-checkbox:checked'); + const deleteBtn = document.getElementById('deleteSelectedBtn'); + if (selectedCheckboxes.length > 0) { + deleteBtn.classList.remove('d-none'); + } else { + deleteBtn.classList.add('d-none'); + } + } - -
Name Size ..
No files found.
No files found.
${file.name}${file.name} ${size} ${getActions(file)}