feat: add drop target overlay and statusbar colons

- Full-window drop overlay with large "+" icon when dragging files over the app
- Works from any tab, not just the upload view
- Added colons to all statusbar labels for consistency

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Administrator 2026-03-12 04:01:09 +01:00
parent a5c1ec362d
commit c0b9ec9d17
4 changed files with 73 additions and 7 deletions

View File

@ -1,6 +1,6 @@
{ {
"name": "multi-hoster-uploader", "name": "multi-hoster-uploader",
"version": "1.9.5", "version": "1.9.6",
"description": "Upload files to doodstream, voe, vidmoly, byse simultaneously", "description": "Upload files to doodstream, voe, vidmoly, byse simultaneously",
"main": "main.js", "main": "main.js",
"scripts": { "scripts": {

View File

@ -414,11 +414,39 @@ function setupDragDrop() {
const dropZone = document.getElementById('dropZone'); const dropZone = document.getElementById('dropZone');
// Allow drop on the entire upload view // Allow drop on the entire upload view
const uploadView = document.getElementById('upload-view'); const uploadView = document.getElementById('upload-view');
const dropOverlay = document.getElementById('dropOverlay');
// Window-level drag overlay
let dragCounter = 0;
window.addEventListener('dragenter', (e) => {
e.preventDefault();
if (!e.dataTransfer.types.includes('Files')) return;
dragCounter++;
dropOverlay.classList.add('visible');
});
window.addEventListener('dragleave', (e) => {
e.preventDefault();
dragCounter--;
if (dragCounter <= 0) { dragCounter = 0; dropOverlay.classList.remove('visible'); }
});
window.addEventListener('dragover', (e) => { e.preventDefault(); });
let _dropHandled = false;
window.addEventListener('drop', (e) => {
e.preventDefault();
dragCounter = 0;
dropOverlay.classList.remove('visible');
// Process files dropped anywhere (fallback if no specific handler caught it)
if (!_dropHandled && e.dataTransfer.files.length > 0) {
addDroppedFiles(e.dataTransfer.files).catch(console.error);
}
_dropHandled = false;
});
dropZone.addEventListener('dragover', (e) => { e.preventDefault(); e.stopPropagation(); dropZone.classList.add('drag-over'); }); dropZone.addEventListener('dragover', (e) => { e.preventDefault(); e.stopPropagation(); dropZone.classList.add('drag-over'); });
dropZone.addEventListener('dragleave', (e) => { e.preventDefault(); dropZone.classList.remove('drag-over'); }); dropZone.addEventListener('dragleave', (e) => { e.preventDefault(); dropZone.classList.remove('drag-over'); });
dropZone.addEventListener('drop', (e) => { dropZone.addEventListener('drop', (e) => {
e.preventDefault(); e.stopPropagation(); dropZone.classList.remove('drag-over'); e.preventDefault(); e.stopPropagation(); dropZone.classList.remove('drag-over');
_dropHandled = true;
addDroppedFiles(e.dataTransfer.files).catch(console.error); addDroppedFiles(e.dataTransfer.files).catch(console.error);
}); });
dropZone.addEventListener('click', () => pickFiles()); dropZone.addEventListener('click', () => pickFiles());
@ -428,6 +456,7 @@ function setupDragDrop() {
uploadView.addEventListener('drop', (e) => { uploadView.addEventListener('drop', (e) => {
e.preventDefault(); e.preventDefault();
if (e.target.closest('.drop-zone')) return; // handled above if (e.target.closest('.drop-zone')) return; // handled above
_dropHandled = true;
addDroppedFiles(e.dataTransfer.files).catch(console.error); addDroppedFiles(e.dataTransfer.files).catch(console.error);
}); });
} }
@ -1636,12 +1665,12 @@ function updateStatusBar() {
const uploadedSize = Math.max(0, stats.totalSize - stats.remainingSize); const uploadedSize = Math.max(0, stats.totalSize - stats.remainingSize);
document.getElementById('sbTotal').textContent = `${formatSize(uploadedSize)} / ${formatSize(stats.totalSize)}`; document.getElementById('sbTotal').textContent = `${formatSize(uploadedSize)} / ${formatSize(stats.totalSize)}`;
document.getElementById('sbEta').textContent = `ETA ${etaSeconds > 0 ? formatTime(etaSeconds) : '--:--'}`; document.getElementById('sbEta').textContent = `ETA ${etaSeconds > 0 ? formatTime(etaSeconds) : '--:--'}`;
document.getElementById('sbConnections').textContent = `Aktive Verbindungen ${lastUploadStats.activeJobs || 0}`; document.getElementById('sbConnections').textContent = `Aktive Verbindungen: ${lastUploadStats.activeJobs || 0}`;
document.getElementById('sbQueueCount').textContent = `Gesamt ${stats.total}`; document.getElementById('sbQueueCount').textContent = `Gesamt: ${stats.total}`;
document.getElementById('sbRemainingCount').textContent = `Remaining ${stats.remaining}`; document.getElementById('sbRemainingCount').textContent = `Remaining: ${stats.remaining}`;
document.getElementById('sbInProgressCount').textContent = `In Progress ${stats.inProgress}`; document.getElementById('sbInProgressCount').textContent = `In Progress: ${stats.inProgress}`;
document.getElementById('sbDoneCount').textContent = `Done ${stats.done}`; document.getElementById('sbDoneCount').textContent = `Done: ${stats.done}`;
document.getElementById('sbErrorCount').textContent = `Error ${stats.errors}`; document.getElementById('sbErrorCount').textContent = `Error: ${stats.errors}`;
} }
// --- Health Check --- // --- Health Check ---

View File

@ -7,6 +7,13 @@
<link rel="stylesheet" href="styles.css"> <link rel="stylesheet" href="styles.css">
</head> </head>
<body> <body>
<div id="dropOverlay" class="drop-overlay">
<div class="drop-overlay-content">
<div class="drop-overlay-icon">+</div>
<p>Dateien hier ablegen</p>
</div>
</div>
<nav class="tab-bar"> <nav class="tab-bar">
<button class="tab active" data-view="upload">Upload</button> <button class="tab active" data-view="upload">Upload</button>
<button class="tab" data-view="accounts">Accounts</button> <button class="tab" data-view="accounts">Accounts</button>

View File

@ -169,6 +169,36 @@ body {
box-shadow: var(--panel-shadow); box-shadow: var(--panel-shadow);
} }
.drop-zone:hover, .drop-zone.drag-over { border-color: rgba(126, 220, 255, 0.6); background-color: rgba(62, 167, 255, 0.06); } .drop-zone:hover, .drop-zone.drag-over { border-color: rgba(126, 220, 255, 0.6); background-color: rgba(62, 167, 255, 0.06); }
/* Drop overlay (full-window drop target) */
.drop-overlay {
display: none;
position: fixed;
inset: 0;
z-index: 9999;
background: rgba(16, 18, 22, 0.92);
backdrop-filter: blur(6px);
align-items: center;
justify-content: center;
pointer-events: none;
}
.drop-overlay.visible { display: flex; }
.drop-overlay-content {
text-align: center;
color: rgba(126, 220, 255, 0.9);
}
.drop-overlay-icon {
font-size: 120px;
font-weight: 200;
line-height: 1;
margin-bottom: 8px;
color: rgba(126, 220, 255, 0.8);
}
.drop-overlay-content p {
font-size: 16px;
color: var(--text-muted);
margin: 0;
}
.drop-icon { font-size: 40px; margin-bottom: 8px; } .drop-icon { font-size: 40px; margin-bottom: 8px; }
/* Queue Container */ /* Queue Container */