From bb30b580371fa7767159abdf598eae9dc4f66f68 Mon Sep 17 00:00:00 2001 From: Administrator Date: Wed, 11 Mar 2026 19:52:24 +0100 Subject: [PATCH] feat: sticky tab bar, improved context menu, instant retry - Sticky tab bar: stays fixed at top when scrolling settings/history - Context menu improvements: - Click on empty queue area deselects all selected jobs - Dynamic labels with selection count (e.g. "Links kopieren (3)") - Singular/plural for single selection ("Link kopieren" vs "Links kopieren") - "Alle entfernen" to clear entire queue - Reorganized menu items into logical groups with separators - Instant retry: "Erneut versuchen" now immediately starts uploading the selected files instead of just resetting status to preview Co-Authored-By: Claude Opus 4.6 --- renderer/app.js | 41 ++++++++++++++++++++++++++++++++++++++++- renderer/index.html | 8 ++++++-- renderer/styles.css | 6 +++++- 3 files changed, 51 insertions(+), 4 deletions(-) diff --git a/renderer/app.js b/renderer/app.js index f68aa2f..31f99ee 100644 --- a/renderer/app.js +++ b/renderer/app.js @@ -768,6 +768,17 @@ function showContextMenu(x, y) { // Update "Always on top" text const aotItem = menu.querySelector('[data-action="always-on-top"]'); if (aotItem) aotItem.textContent = alwaysOnTopState ? 'Immer im Vordergrund ✓' : 'Immer im Vordergrund'; + // Update labels with selection count + const n = selectedJobIds.size; + const delItem = menu.querySelector('[data-action="delete-selected"]'); + if (delItem) delItem.textContent = n > 1 ? `Entfernen (${n})` : 'Entfernen'; + const copyItem = menu.querySelector('[data-action="copy-links"]'); + if (copyItem) copyItem.textContent = n > 1 ? `Links kopieren (${n})` : 'Link kopieren'; + menu.querySelectorAll('[data-action="retry-selected"]').forEach(el => { + el.textContent = n > 1 ? `Erneut versuchen (${n})` : 'Erneut versuchen'; + }); + const startItem = menu.querySelector('[data-action="start-selected"]'); + if (startItem) startItem.textContent = n > 1 ? `Ausgewählte starten (${n})` : 'Ausgewählte starten'; menu.style.display = 'block'; const menuX = Math.min(x, window.innerWidth - menu.offsetWidth - 5); @@ -953,6 +964,16 @@ async function handleContextAction(action) { persistQueueStateSoon(); } else if (action === 'copy-all-links') { copyAllLinks(); + } else if (action === 'delete-all') { + queueJobs.forEach(j => removeJobFromIndex(j)); + queueJobs = []; + selectedJobIds.clear(); + selectedFiles = []; + syncSelectedFilesFromQueue(); + renderQueueTable(); + updateUploadView(); + updateStatusBar(); + persistQueueStateSoon(); } else if (action === 'always-on-top') { alwaysOnTopState = !alwaysOnTopState; await window.api.setAlwaysOnTop(alwaysOnTopState); @@ -1237,7 +1258,8 @@ function handleStats(data) { } // --- Retry --- -function retrySelectedJobs() { +async function retrySelectedJobs() { + const retryJobs = []; queueJobs.forEach(j => { if (selectedJobIds.has(j.id) && ['error', 'done', 'aborted', 'skipped'].includes(j.status)) { j.status = 'preview'; @@ -1249,16 +1271,22 @@ function retrySelectedJobs() { j.remaining = 0; j.progress = 0; j.uploadId = null; + retryJobs.push(j); if (!selectedFiles.find(f => f.path === j.file || f.name === j.fileName)) { selectedFiles.push({ path: j.file, name: j.fileName, size: j.bytesTotal }); } } }); + if (retryJobs.length === 0) return; + + // Select the retry jobs and start them immediately selectedJobIds.clear(); + retryJobs.forEach(j => selectedJobIds.add(j.id)); renderQueueTable(); updateQueueActionButtons(); updateStatusBar(); persistQueueStateSoon(); + await startSelectedUpload(); } async function abortSelectedJobs() { @@ -2314,6 +2342,17 @@ function setupListeners() { document.getElementById('shutdownOverlay').style.display = 'none'; }); + // Click on empty area in queue → deselect all + document.getElementById('upload-view').addEventListener('click', (e) => { + if (!e.target.closest('.queue-row') && !e.target.closest('.btn') && !e.target.closest('.context-menu') && !e.target.closest('.recent-files-panel')) { + if (selectedJobIds.size > 0) { + selectedJobIds.clear(); + renderQueueTable(); + updateQueueActionButtons(); + } + } + }); + // Right-click on upload view background document.getElementById('upload-view').addEventListener('contextmenu', (e) => { if (e.target.closest('.queue-row')) return; // handled per row diff --git a/renderer/index.html b/renderer/index.html index 5478107..9f0e83d 100644 --- a/renderer/index.html +++ b/renderer/index.html @@ -266,11 +266,15 @@