Batches panel: redesign expanded track rows + fix scrollbar clipping

The per-track list inside an expanded batch was a cramped flat row with a faint
title and a -2px progress-bar hack, and the nested scrollbar sat on top of the
text. Reworked:
- Each row is now a grid: track number · title (+ artist sub-line) · right-aligned
  state, with hover, tabular-aligned numbers, per-row state coloring (✓ green /
  ✗ red / % accent / dim queued / strikethrough cancelled), and a clean full-width
  progress bar beneath downloading rows.
- Track list gets right padding + a thin, subtle scrollbar so it no longer
  clips titles; same thin-scrollbar treatment on the panel itself.
- Panel widened 340->366 with rebalanced side padding for more readable content.
Collapsed-panel behavior unchanged.
This commit is contained in:
BoulderBadgeDad 2026-05-31 22:44:58 -07:00
parent f50e67ac9b
commit 227c9373fe
2 changed files with 84 additions and 41 deletions

View file

@ -2738,34 +2738,37 @@ function _adlRenderBatchPanel() {
let tracksHtml = ''; let tracksHtml = '';
if (isExpanded) { if (isExpanded) {
if (batchTracks.length > 0) { if (batchTracks.length > 0) {
tracksHtml = batchTracks.map(t => { tracksHtml = batchTracks.map((t, i) => {
const cls = _adlStatusClass(t.status); const cls = _adlStatusClass(t.status);
const progress = t.progress || 0; const progress = t.progress || 0;
const idx = (t.track_index != null ? t.track_index + 1 : i + 1);
// Status indicator with detail // Right-aligned state: % / spinner / \u2713 / \u2717 / \u00B7 \u2014 color via row class.
let statusHtml = ''; let stateHtml = '';
if (t.status === 'downloading' && progress > 0) { if (t.status === 'downloading' && progress > 0) {
statusHtml = `<span class="adl-batch-track-status active">${Math.round(progress)}%</span>`; stateHtml = `<span class="adl-batch-track-state">${Math.round(progress)}%</span>`;
} else if (t.status === 'searching') { } else if (t.status === 'searching') {
statusHtml = `<span class="adl-batch-track-status active"><span class="adl-spinner" style="width:8px;height:8px"></span></span>`; stateHtml = `<span class="adl-batch-track-state"><span class="adl-spinner" style="width:9px;height:9px"></span></span>`;
} else if (t.status === 'post_processing') { } else if (t.status === 'post_processing') {
statusHtml = `<span class="adl-batch-track-status active" title="Processing">proc</span>`; stateHtml = `<span class="adl-batch-track-state" title="Processing">proc</span>`;
} else if (cls === 'completed') { } else if (cls === 'completed') {
statusHtml = `<span class="adl-batch-track-status completed">\u2713</span>`; stateHtml = `<span class="adl-batch-track-state">\u2713</span>`;
} else if (cls === 'failed') { } else if (cls === 'failed') {
statusHtml = `<span class="adl-batch-track-status failed">\u2717</span>`; stateHtml = `<span class="adl-batch-track-state" title="${_adlEsc(t.error || 'Failed')}">\u2717</span>`;
} else { } else {
statusHtml = `<span class="adl-batch-track-status queued">\u00B7</span>`; stateHtml = `<span class="adl-batch-track-state">\u00B7</span>`;
} }
// Mini progress bar for downloading tracks const isDownloading = (t.status === 'downloading' && progress > 0);
const miniBar = t.status === 'downloading' && progress > 0 const miniBar = isDownloading
? `<div class="adl-batch-track-progress"><div class="adl-batch-track-progress-fill" style="width:${progress}%"></div></div>` ? `<div class="adl-batch-track-progress"><div class="adl-batch-track-progress-fill" style="width:${progress}%"></div></div>`
: ''; : '';
const sub = t.artist ? `<span class="adl-batch-track-sub">${_adlEsc(t.artist)}</span>` : '';
return `<div class="adl-batch-track-row"> return `<div class="adl-batch-track-row ${cls}${isDownloading ? ' downloading' : ''}">
<span class="adl-batch-track-title">${_adlEsc(t.title || 'Unknown')}</span> <span class="adl-batch-track-idx">${idx}</span>
${statusHtml} <span class="adl-batch-track-text"><span class="adl-batch-track-title">${_adlEsc(t.title || 'Unknown')}</span>${sub}</span>
${stateHtml}
${miniBar} ${miniBar}
</div>`; </div>`;
}).join(''); }).join('');

View file

@ -58939,17 +58939,23 @@ body.reduce-effects *::after {
/* ---- Batch Context Panel ---- */ /* ---- Batch Context Panel ---- */
.adl-batch-panel { .adl-batch-panel {
width: 340px; width: 366px;
flex-shrink: 0; flex-shrink: 0;
border-left: 1px solid rgba(255, 255, 255, 0.06); border-left: 1px solid rgba(255, 255, 255, 0.06);
padding: 0 12px 0 24px; padding: 0 14px 0 22px;
margin-left: 8px; margin-left: 8px;
overflow-y: auto; overflow-y: auto;
overflow-x: hidden; overflow-x: hidden;
max-height: calc(100vh - 120px); max-height: calc(100vh - 120px);
position: sticky; position: sticky;
top: 80px; top: 80px;
scrollbar-width: thin;
scrollbar-color: rgba(255, 255, 255, 0.16) transparent;
} }
.adl-batch-panel::-webkit-scrollbar { width: 7px; }
.adl-batch-panel::-webkit-scrollbar-track { background: transparent; }
.adl-batch-panel::-webkit-scrollbar-thumb { background: rgba(255, 255, 255, 0.14); border-radius: 4px; }
.adl-batch-panel::-webkit-scrollbar-thumb:hover { background: rgba(255, 255, 255, 0.24); }
.adl-batch-panel-header { .adl-batch-panel-header {
display: flex; display: flex;
@ -59229,13 +59235,20 @@ body.reduce-effects *::after {
/* Expanded tracks list */ /* Expanded tracks list */
.adl-batch-tracks { .adl-batch-tracks {
display: none; display: none;
margin-top: 8px; margin-top: 10px;
padding-top: 8px; /* right padding keeps the nested scrollbar off the track text */
border-top: 1px solid rgba(255, 255, 255, 0.04); padding: 8px 8px 2px 2px;
max-height: 400px; border-top: 1px solid rgba(255, 255, 255, 0.06);
max-height: 360px;
overflow-y: auto; overflow-y: auto;
overflow-x: hidden; overflow-x: hidden;
scrollbar-width: thin;
scrollbar-color: rgba(255, 255, 255, 0.16) transparent;
} }
.adl-batch-tracks::-webkit-scrollbar { width: 6px; }
.adl-batch-tracks::-webkit-scrollbar-track { background: transparent; }
.adl-batch-tracks::-webkit-scrollbar-thumb { background: rgba(255, 255, 255, 0.14); border-radius: 3px; }
.adl-batch-tracks::-webkit-scrollbar-thumb:hover { background: rgba(255, 255, 255, 0.24); }
.adl-batch-card.expanded .adl-batch-tracks { .adl-batch-card.expanded .adl-batch-tracks {
display: block; display: block;
@ -59254,34 +59267,61 @@ body.reduce-effects *::after {
} }
.adl-batch-track-row { .adl-batch-track-row {
display: flex; display: grid;
grid-template-columns: 18px 1fr auto;
align-items: center; align-items: center;
gap: 8px; column-gap: 10px;
padding: 4px 0; padding: 6px 8px;
font-size: 0.75rem; border-radius: 7px;
flex-wrap: wrap; font-size: 0.76rem;
transition: background 0.12s ease;
}
.adl-batch-track-row:hover { background: rgba(255, 255, 255, 0.04); }
.adl-batch-track-idx {
font-size: 0.66rem;
color: rgba(255, 255, 255, 0.28);
text-align: right;
font-variant-numeric: tabular-nums;
}
.adl-batch-track-text {
min-width: 0;
display: flex;
flex-direction: column;
gap: 1px;
} }
.adl-batch-track-title { .adl-batch-track-title {
flex: 1; color: rgba(255, 255, 255, 0.82);
color: rgba(255, 255, 255, 0.6);
white-space: nowrap; white-space: nowrap;
overflow: hidden; overflow: hidden;
text-overflow: ellipsis; text-overflow: ellipsis;
min-width: 0;
} }
.adl-batch-track-status { .adl-batch-track-sub {
flex-shrink: 0; font-size: 0.64rem;
font-size: 0.7rem; color: rgba(255, 255, 255, 0.4);
min-width: 24px; white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.adl-batch-track-state {
font-size: 0.68rem;
color: rgba(255, 255, 255, 0.35);
text-align: right; text-align: right;
white-space: nowrap;
font-variant-numeric: tabular-nums;
} }
.adl-batch-track-status.completed { color: #22c55e; } /* Row-state coloring (class from _adlStatusClass) */
.adl-batch-track-status.active { color: rgba(var(--accent-rgb), 1); } .adl-batch-track-row.completed .adl-batch-track-state { color: #22c55e; }
.adl-batch-track-status.failed { color: #ef4444; } .adl-batch-track-row.completed .adl-batch-track-idx { color: rgba(34, 197, 94, 0.45); }
.adl-batch-track-status.queued { color: rgba(255, 255, 255, 0.3); } .adl-batch-track-row.failed .adl-batch-track-state { color: #ef4444; }
.adl-batch-track-row.active .adl-batch-track-state { color: rgb(var(--accent-rgb)); }
.adl-batch-track-row.queued .adl-batch-track-title { color: rgba(255, 255, 255, 0.5); }
.adl-batch-track-row.cancelled .adl-batch-track-title { color: rgba(255, 255, 255, 0.4); text-decoration: line-through; }
.adl-batch-release-note { .adl-batch-release-note {
padding: 6px 0; padding: 6px 0;
@ -59290,19 +59330,19 @@ body.reduce-effects *::after {
line-height: 1.4; line-height: 1.4;
} }
/* Mini progress bar per track */ /* Per-track progress — spans the full row width under a downloading track */
.adl-batch-track-progress { .adl-batch-track-progress {
width: 100%; grid-column: 1 / -1;
height: 2px; height: 2px;
background: rgba(255, 255, 255, 0.04); margin-top: 5px;
background: rgba(255, 255, 255, 0.06);
border-radius: 1px; border-radius: 1px;
overflow: hidden; overflow: hidden;
margin-top: -2px;
} }
.adl-batch-track-progress-fill { .adl-batch-track-progress-fill {
height: 100%; height: 100%;
background: rgba(var(--accent-rgb), 0.5); background: rgba(var(--accent-rgb), 0.8);
border-radius: 1px; border-radius: 1px;
transition: width 0.4s ease; transition: width 0.4s ease;
} }