feat: add keyboard shortcuts (Del/S) and download statistics bar
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
c96fd13aff
commit
2481230983
@ -218,6 +218,7 @@
|
||||
<button class="btn btn-clear" id="btnClear" onclick="clearCompleted()">Leeren</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="stats-bar" id="statsBar"></div>
|
||||
</aside>
|
||||
|
||||
<main class="main">
|
||||
|
||||
@ -96,6 +96,10 @@ async function init(): Promise<void> {
|
||||
byId('mergeProgressText').textContent = Math.round(percent) + '%';
|
||||
});
|
||||
|
||||
// Update stats bar
|
||||
updateStatsBar();
|
||||
setInterval(updateStatsBar, 5000);
|
||||
|
||||
if (config.client_id && config.client_secret) {
|
||||
await connect();
|
||||
} else {
|
||||
@ -119,9 +123,56 @@ async function init(): Promise<void> {
|
||||
scheduleQueueSync(document.hidden ? 600 : 150);
|
||||
});
|
||||
|
||||
// Keyboard shortcuts
|
||||
document.addEventListener('keydown', (e) => {
|
||||
// Skip if user is typing in an input field
|
||||
if (e.target instanceof HTMLInputElement || e.target instanceof HTMLTextAreaElement || e.target instanceof HTMLSelectElement) return;
|
||||
|
||||
if (e.key === 'Delete' && selectedQueueIds.length > 0) {
|
||||
// Delete selected queue items
|
||||
const idsToRemove = [...selectedQueueIds];
|
||||
selectedQueueIds = [];
|
||||
(async () => {
|
||||
for (const id of idsToRemove) {
|
||||
queue = await window.api.removeFromQueue(id);
|
||||
}
|
||||
renderQueue();
|
||||
})();
|
||||
}
|
||||
|
||||
if ((e.key === 's' || e.key === 'S') && !e.ctrlKey && !e.altKey && !e.metaKey) {
|
||||
e.preventDefault();
|
||||
toggleDownload();
|
||||
}
|
||||
});
|
||||
|
||||
scheduleQueueSync(QUEUE_SYNC_DEFAULT_MS);
|
||||
}
|
||||
|
||||
function formatBytesRenderer(bytes: number): string {
|
||||
if (bytes < 1024) return `${bytes} B`;
|
||||
if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)} KB`;
|
||||
if (bytes < 1024 * 1024 * 1024) return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;
|
||||
return `${(bytes / (1024 * 1024 * 1024)).toFixed(2)} GB`;
|
||||
}
|
||||
|
||||
function formatSpeedRenderer(bytesPerSec: number): string {
|
||||
if (bytesPerSec < 1024) return `${bytesPerSec.toFixed(0)} B/s`;
|
||||
if (bytesPerSec < 1024 * 1024) return `${(bytesPerSec / 1024).toFixed(1)} KB/s`;
|
||||
return `${(bytesPerSec / (1024 * 1024)).toFixed(1)} MB/s`;
|
||||
}
|
||||
|
||||
async function updateStatsBar(): Promise<void> {
|
||||
try {
|
||||
const metrics = await window.api.getRuntimeMetrics();
|
||||
const bar = byId('statsBar');
|
||||
if (!bar) return;
|
||||
const totalDL = formatBytesRenderer(metrics.downloadedBytesTotal);
|
||||
const avgSpeed = metrics.avgSpeedBytesPerSec > 0 ? formatSpeedRenderer(metrics.avgSpeedBytesPerSec) : '-';
|
||||
bar.textContent = `${totalDL} | ${avgSpeed} avg | ${metrics.downloadsCompleted} done | ${metrics.downloadsFailed} failed`;
|
||||
} catch { }
|
||||
}
|
||||
|
||||
let toastHideTimer: number | null = null;
|
||||
let queueSyncTimer: number | null = null;
|
||||
let queueSyncInFlight = false;
|
||||
|
||||
@ -386,6 +386,14 @@ body {
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.stats-bar {
|
||||
padding: 6px 15px;
|
||||
font-size: 10px;
|
||||
color: var(--text-secondary);
|
||||
border-top: 1px solid rgba(255,255,255,0.1);
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.btn {
|
||||
flex: 1;
|
||||
padding: 10px;
|
||||
|
||||
Loading…
Reference in New Issue
Block a user