Merge pull request #426 from christianlouis/copilot/support-directory-upload

feat(upload): directory drag-and-drop, adaptive 429 throttling, full Gotenberg file type support
This commit is contained in:
Christian Krakau-Louis
2026-02-27 00:56:17 +01:00
committed by GitHub
15 changed files with 900 additions and 278 deletions
+13 -6
View File
@@ -3,6 +3,12 @@
{% block head_extra %}
<script src="/static/js/common.js"></script>
<script>
window.uploadConfig = {
concurrency: {{ upload_concurrency }},
queueDelayMs: {{ upload_queue_delay_ms }}
};
</script>
<script src="/static/js/upload.js"></script>
<style>
/* Drop overlay styles - covers entire page for drag-and-drop anywhere */
@@ -342,8 +348,8 @@
<div id="dropOverlay" class="drop-overlay">
<div class="drop-message">
<i class="fas fa-cloud-upload-alt"></i>
<p>Drop files anywhere to upload</p>
<div class="drop-hint">Supports PDF, Office docs, images, and more</div>
<p>Drop files or folders anywhere to upload</p>
<div class="drop-hint">Supports PDF, Office docs, images, HTML, Markdown, and more folders are processed recursively</div>
</div>
</div>
@@ -902,18 +908,19 @@
e.dataTransfer.dropEffect = 'copy';
});
window.addEventListener('drop', (e) => {
window.addEventListener('drop', async (e) => {
e.preventDefault();
dragCounter = 0;
dropOverlay.classList.remove('active');
if (e.dataTransfer.files.length > 0) {
const files = await getFilesFromDataTransfer(e.dataTransfer);
if (files.length > 0) {
// Show upload modal
uploadModal.classList.add('active');
uploadProgressContainer.innerHTML = '';
// Process the dropped files
processFiles(e.dataTransfer.files, uploadProgressContainer, uploadStatusMessage);
// Process the dropped files (queue-based, with adaptive throttling)
processFiles(files, uploadProgressContainer, uploadStatusMessage);
}
});
+17 -9
View File
@@ -12,12 +12,11 @@
<div
id="dropZone"
class="border-4 border-dashed border-gray-300 rounded-lg p-6 sm:p-8 bg-white text-center w-full"
ondrop="handleDrop(event)"
ondragover="handleDragOver(event)"
ondragleave="handleDragLeave(event)"
>
<p class="text-gray-500 mb-4 hidden sm:block">
Drag &amp; drop files here, or click to select files.
Drag &amp; drop files or folders here, or click to select files.
</p>
<p class="text-gray-500 mb-4 sm:hidden">
Tap to select files or use the camera button below.
@@ -42,7 +41,7 @@
</button>
<div class="text-sm text-gray-500 mt-2">
<p>Allowed types: PDF, Office documents (Word, Excel, PowerPoint, etc.), Images</p>
<p>Maximum size: 500MB per file</p>
<p>Maximum size: 500MB per file &bull; Directories are uploaded recursively</p>
</div>
</div>
@@ -122,6 +121,13 @@
{% endblock %}
{% block scripts %}
<script>
// Expose server-side upload throttling configuration to upload.js
window.uploadConfig = {
concurrency: {{ upload_concurrency }},
queueDelayMs: {{ upload_queue_delay_ms }}
};
</script>
<script src="/static/js/upload.js"></script>
<script>
// Drag-and-drop / file input logic
@@ -143,10 +149,12 @@
dropZone.classList.remove("bg-gray-100");
}
function handleDrop(e) {
dropZone.addEventListener("drop", async (e) => {
e.preventDefault();
dropZone.classList.remove("bg-gray-100");
if (e.dataTransfer.files.length) {
const files = await getFilesFromDataTransfer(e.dataTransfer);
if (files.length) {
// Clear previous upload progress
uploadProgress.innerHTML = "";
@@ -155,10 +163,10 @@
progressContainer.className = "space-y-2";
uploadProgress.appendChild(progressContainer);
// Use the shared processFiles function
processFiles(e.dataTransfer.files, progressContainer, statusMessage);
// Use the shared processFiles function (queue-based)
processFiles(files, progressContainer, statusMessage);
}
}
});
function handleFileSelect(e) {
if (e.target.files.length) {
@@ -171,7 +179,7 @@
uploadProgress.appendChild(progressContainer);
// Use the shared processFiles function
processFiles(e.target.files, progressContainer, statusMessage);
processFiles(Array.from(e.target.files), progressContainer, statusMessage);
}
}