diff --git a/web/src/app/_ui/job-row.tsx b/web/src/app/_ui/job-row.tsx index 4a8d5d6..63e7fb3 100644 --- a/web/src/app/_ui/job-row.tsx +++ b/web/src/app/_ui/job-row.tsx @@ -14,6 +14,7 @@ export function JobRow({ album, kind, label, + marker, meta, note, bar, @@ -22,6 +23,7 @@ export function JobRow({ album: string; kind: Kind; label: string; + marker?: string; meta?: ReactNode; note?: ReactNode; bar?: ReactNode; @@ -31,6 +33,7 @@ export function JobRow({

+ {marker ? {marker} : null} {album} · {artist}

{meta ?
{meta}
: null} diff --git a/web/src/app/design.css b/web/src/app/design.css index 0bc14a7..fcdd2b1 100644 --- a/web/src/app/design.css +++ b/web/src/app/design.css @@ -388,6 +388,13 @@ nav.contents .sep { flex: 1; } .tab .n { color: var(--rule-2); margin-left: 6px; } .tab.has-errors .n { color: var(--alert); } +/* "Next up" badge on the first queued row (the item the worker claims next) */ +.nextup { + display: inline-block; vertical-align: middle; margin-right: 9px; transform: translateY(-2px); + font-family: var(--mono); font-size: 0.56rem; letter-spacing: 0.13em; text-transform: uppercase; + color: var(--accent-ink); background: var(--accent); padding: 2px 7px; border-radius: 4px; +} + /* ── Kebab menu (⋮ popup for press actions) ───────────── */ .dept.has-actions { align-items: center; } .dept.has-actions .fill { transform: none; } diff --git a/web/src/app/queue.tsx b/web/src/app/queue.tsx index 3c060c0..b5066dd 100644 --- a/web/src/app/queue.tsx +++ b/web/src/app/queue.tsx @@ -189,6 +189,14 @@ export function Queue() { const j = jobOf(r); buckets[categoryOf(j.state, j.currentStage)].push(r); } + // The API returns rows newest-first, but the worker claims the OLDEST queued job next + // (claim_next: ORDER BY createdAt ASC). Order the queue tab to match, so the item that runs + // next sits at the top instead of the bottom. + const ts = (r: Row) => (r.createdAt ? new Date(r.createdAt).getTime() : 0); + buckets.queue.sort((a, b) => ts(a) - ts(b)); + // "Next up" = the first queued item the worker will actually claim (oldest, not individually + // paused) — mark it so the ordering reads clearly. + const nextUpId = buckets.queue.find((r) => !jobOf(r).paused)?.id ?? null; const queuedCount = buckets.queue.length; const downloading = buckets.active.length > 0; // Smart default: land on the tab that has something worth looking at. @@ -289,6 +297,7 @@ export function Queue() { album={r.album} kind={d.kind} label={d.label} + marker={r.id === nextUpId ? "Next up" : undefined} meta={meta} note={note} bar={bar}