feat: add drag & drop queue reordering and expandable queue item details

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
xRangerDE 2026-03-20 09:42:35 +01:00
parent 2481230983
commit fbcf3935d0
3 changed files with 75 additions and 2 deletions

View File

@ -198,6 +198,49 @@ function updateQueueItemProgress(progress: DownloadProgress): void {
if (meta) meta.textContent = getQueueMetaText(queue[idx]);
}
function toggleQueueDetails(id: string): void {
const el = document.getElementById(`details-${id}`);
if (el) el.style.display = el.style.display === 'none' ? 'block' : 'none';
}
function initQueueDragDrop(): void {
const list = byId('queueList');
list.addEventListener('dragstart', (e: DragEvent) => {
const el = (e.target as HTMLElement).closest('.queue-item') as HTMLElement;
if (!el) return;
draggedQueueItemId = el.dataset.id || null;
el.classList.add('dragging');
if (e.dataTransfer) e.dataTransfer.effectAllowed = 'move';
});
list.addEventListener('dragover', (e: DragEvent) => {
e.preventDefault();
if (e.dataTransfer) e.dataTransfer.dropEffect = 'move';
});
list.addEventListener('drop', (e: DragEvent) => {
e.preventDefault();
const target = (e.target as HTMLElement).closest('.queue-item') as HTMLElement;
if (!target || !draggedQueueItemId) return;
const targetId = target.dataset.id;
if (!targetId || targetId === draggedQueueItemId) return;
const fromIdx = queue.findIndex(i => i.id === draggedQueueItemId);
const toIdx = queue.findIndex(i => i.id === targetId);
if (fromIdx < 0 || toIdx < 0) return;
const [moved] = queue.splice(fromIdx, 1);
queue.splice(toIdx, 0, moved);
window.api.reorderQueue(queue.map(i => i.id));
renderQueue();
});
list.addEventListener('dragend', () => {
draggedQueueItemId = null;
document.querySelectorAll('.queue-item.dragging').forEach(el => el.classList.remove('dragging'));
});
}
function renderQueue(): void {
if (!Array.isArray(queue)) {
queue = [];
@ -244,7 +287,7 @@ function renderQueue(): void {
: '';
return `
<div class="queue-item${isMergeGroup ? ' merge-group' : ''}">
<div class="queue-item${isMergeGroup ? ' merge-group' : ''}" draggable="${item.status === 'pending' ? 'true' : 'false'}" data-id="${item.id}">
${showSelector
? `<div class="queue-selector${isSelected ? ' selected' : ''}" onclick="toggleQueueSelection('${item.id}')">${isSelected ? selectionIndex + 1 : ''}</div>`
: ''
@ -252,7 +295,7 @@ function renderQueue(): void {
<div class="status ${item.status}"></div>
<div class="queue-main">
<div class="queue-title-row">
<div class="title" title="${safeTitle}">${mergeIcon}${isClip}${safeTitle}</div>
<div class="title" title="${safeTitle}" onclick="toggleQueueDetails('${item.id}')" style="cursor:pointer">${mergeIcon}${isClip}${safeTitle}</div>
<div class="queue-status-label">${safeStatusLabel}</div>
</div>
<div class="queue-meta">${safeMeta}${mergeMetaExtra}</div>
@ -260,6 +303,12 @@ function renderQueue(): void {
<div class="queue-progress-bar${progressClass}" style="width: ${progressValue}%;"></div>
</div>
<div class="queue-progress-text">${safeProgressText}</div>
<div class="queue-details" id="details-${item.id}" style="display:none">
<div>URL: ${escapeHtml(item.url)}</div>
<div>Streamer: ${escapeHtml(item.streamer)}</div>
<div>Dauer: ${escapeHtml(item.duration_str)}</div>
<div>Datum: ${escapeHtml(new Date(item.date).toLocaleString())}</div>
</div>
</div>
<span class="remove" onclick="removeFromQueue('${item.id}')">x</span>
</div>

View File

@ -42,6 +42,7 @@ async function init(): Promise<void> {
changeTheme(config.theme ?? 'twitch');
renderStreamers();
renderQueue();
initQueueDragDrop();
updateDownloadButtonState();
window.api.onQueueUpdated((q: QueueItem[]) => {

View File

@ -334,6 +334,29 @@ body {
opacity: 1;
}
.queue-item[draggable="true"] {
cursor: grab;
}
.queue-item[draggable="true"]:active {
cursor: grabbing;
}
.queue-item.dragging {
opacity: 0.4;
}
.queue-details {
font-size: 10px;
color: var(--text-secondary);
padding: 4px 0;
word-break: break-all;
}
.queue-details div {
margin-bottom: 2px;
}
.queue-selector {
width: 22px;
height: 22px;