From 00000de7980618a1d2acde52092c7d11e95db96b Mon Sep 17 00:00:00 2001
From: 4gray <4gray@users.noreply.github.com>
Date: Sat, 15 Aug 2026 13:59:28 +0200
Subject: [PATCH] feat(downloads): align manager cards, queue rows, and
headings with the rail design language (#1448)
---
.changes/components-confirm-dialog-width.md | 7 +
.changes/downloads-library-cards-cleanup.md | 10 +
CLAUDE.md | 10 +-
.../electron-backend-e2e/src/downloads.e2e.ts | 27 +-
docs/architecture/download-manager.md | 23 +-
.../src/lib/download-library.component.html | 527 +++++++-----------
.../src/lib/download-library.component.scss | 163 +++---
.../lib/download-library.component.spec.ts | 74 ++-
.../src/lib/download-library.component.ts | 53 +-
.../src/lib/download-queue.component.html | 120 ++--
.../src/lib/download-queue.component.scss | 72 +--
.../src/lib/download-queue.component.spec.ts | 105 ++--
.../src/lib/download-queue.component.ts | 22 +
.../src/lib/downloads.component.spec.ts | 15 +-
.../lib/confirm-dialog/dialog.service.spec.ts | 3 +-
.../src/lib/confirm-dialog/dialog.service.ts | 5 +-
16 files changed, 619 insertions(+), 617 deletions(-)
create mode 100644 .changes/components-confirm-dialog-width.md
create mode 100644 .changes/downloads-library-cards-cleanup.md
diff --git a/.changes/components-confirm-dialog-width.md b/.changes/components-confirm-dialog-width.md
new file mode 100644
index 000000000..3ed0d545a
--- /dev/null
+++ b/.changes/components-confirm-dialog-width.md
@@ -0,0 +1,7 @@
+---
+type: fix
+area: components
+---
+
+Confirmation dialogs are wider by default, so their action buttons sit side by
+side instead of wrapping into a vertical stack with longer translated labels.
diff --git a/.changes/downloads-library-cards-cleanup.md b/.changes/downloads-library-cards-cleanup.md
new file mode 100644
index 000000000..9f14fb53c
--- /dev/null
+++ b/.changes/downloads-library-cards-cleanup.md
@@ -0,0 +1,10 @@
+---
+type: feature
+area: downloads
+---
+
+The download manager got a visual cleanup: "Ready to watch" cards are compact
+poster cards — file actions live in the poster's ⋮ menu, and clicking any card
+(episodes included) opens its offline detail. Queue rows show one primary
+action (pause/resume/retry) with cancel and remove in the menu, and all
+sections share one heading style with item counts.
diff --git a/CLAUDE.md b/CLAUDE.md
index 8dbcf27f7..d3c44cd5e 100644
--- a/CLAUDE.md
+++ b/CLAUDE.md
@@ -1199,10 +1199,12 @@ engine` (restart required) or
active rows plus completed available/unknown rows are skipped; failed,
canceled, completed-missing, and unambiguous row-less episodes remain
eligible.
-- Ready movie and grouped-series cards open a focused local detail. Movies play
- the finalized local file; series list only locally available episode rows and
- every episode action targets its own downloaded file. Focused routes disable
- route search and use `contextPanel: 'none'`.
+- Ready cards (movies, grouped series, and standalone episodes) open a focused
+ local detail; local file actions (Play, Show in folder, Copy URL, Remove)
+ live in the poster's overflow menu. Movies play the finalized local file;
+ series list only locally available episode rows and every episode action
+ targets its own downloaded file. Focused routes disable route search and use
+ `contextPanel: 'none'`.
- Downloads capture a versioned metadata snapshot from the rendered Xtream or
Stalker movie/episode detail at start time, including already-merged TMDB
fields. Legacy, sparse, stale, or wrong-language snapshots are safely
diff --git a/apps/electron-backend-e2e/src/downloads.e2e.ts b/apps/electron-backend-e2e/src/downloads.e2e.ts
index 42ae1148b..75b91056c 100644
--- a/apps/electron-backend-e2e/src/downloads.e2e.ts
+++ b/apps/electron-backend-e2e/src/downloads.e2e.ts
@@ -291,15 +291,11 @@ test.describe('Electron Downloads', () => {
'e2e download payload'
);
- // Completed items expose play / reveal / remove actions.
+ // Completed items keep their file actions behind the poster's
+ // overflow menu; the card itself stays free of toolbar chrome.
await expect(
- card.getByRole('button', { name: 'Play: E2E Movie' }).last()
- ).toBeVisible();
- await expect(
- card.getByRole('button', {
- name: 'Show in Folder: E2E Movie',
- })
- ).toBeVisible();
+ card.getByRole('button', { name: 'Play: E2E Movie' })
+ ).toHaveCount(0);
await expect(
card.getByText('Offline', { exact: true })
).toHaveCount(0);
@@ -318,6 +314,16 @@ test.describe('Electron Downloads', () => {
sourceHeader.getByText('Source', { exact: true })
).toBeVisible();
await expect(sourceHeader).toContainText('Download Portal');
+ await expect(
+ app.mainWindow.getByRole('menuitem', {
+ name: 'Play: E2E Movie',
+ })
+ ).toBeVisible();
+ await expect(
+ app.mainWindow.getByRole('menuitem', {
+ name: 'Show in Folder: E2E Movie',
+ })
+ ).toBeVisible();
await app.mainWindow.keyboard.press('Escape');
// Removing the finalized file must move the persisted completed
@@ -1022,6 +1028,11 @@ test.describe('Electron Downloads', () => {
await expect(removeItem).toBeVisible();
await removeItem
.getByRole('button', {
+ name: 'More actions for Remove Retained Partial',
+ })
+ .click();
+ await app.mainWindow
+ .getByRole('menuitem', {
name: 'Remove Remove Retained Partial from manager',
})
.click();
diff --git a/docs/architecture/download-manager.md b/docs/architecture/download-manager.md
index 74e38c1cf..7b61c7085 100644
--- a/docs/architecture/download-manager.md
+++ b/docs/architecture/download-manager.md
@@ -150,8 +150,18 @@ variants, contextual buttons, and theme-aware styling.
`DownloadItemAction` values and do not inject the download service, router,
dialogs, or snackbars.
The fixed header and filter row sit above one vertical scroll owner. The
- queue uses compact progress rows with status text and icons; completed movies
- and grouped series reuse the portal's canonical content grid. Both completed
+ queue and library sections share one heading treatment (the dashboard-rail
+ title style with an `.app-count-badge` item count) so same-level sections
+ read alike; the queue's bordered panel wraps only the row list. Queue rows
+ expose exactly one visible primary action — pause, resume, or retry — while
+ cancel, copy-URL, and remove live in the row's overflow menu, so two
+ adjacent destructive icons never compete. Completed movies
+ and grouped series reuse the portal's canonical content grid as compact
+ poster cards borrowed from the dashboard-rail card language: a type badge
+ and an always-visible ⋮ trigger sit on the artwork, the title and series
+ facts sit below it, and Play / Show in folder / Copy URL / Remove live in
+ that overflow menu. File size is not repeated on the card; it belongs to the
+ focused offline detail. Both completed
cards and their loading skeleton consume the global
`--cover-grid-min-width` / `--cover-gap` tokens, so the Small, Medium, and
Large cover preference behaves like it does elsewhere in the app. All
@@ -170,10 +180,11 @@ variants, contextual buttons, and theme-aware styling.
playlist scope when the page is opened under a source route. VOD and episode
detail views continue to render a paused download as an active Resume button
(`DownloadsService.isPaused()` / `resumeDownloadByContent()`). Artwork and
- titles on completed movie and grouped-series cards open the focused offline
- detail for that download; the explicit card Play action still starts the
- local file. A legacy standalone episode without a usable series id stays
- directly playable because there is no reliable series detail to build.
+ titles on every completed card — movie, grouped series, and standalone
+ episode alike — open the focused offline detail for that download; the
+ explicit Play command in the poster's overflow menu still starts the local
+ file directly. A legacy standalone episode without a usable series id
+ resolves to a single-episode offline detail built from its own row.
Missing completed rows show `File missing` under Needs attention with
`Download again`; Play and Show in folder are withheld. A file-action race
that returns `File not found` refreshes the authoritative list and returns
diff --git a/libs/portal/downloads/feature/src/lib/download-library.component.html b/libs/portal/downloads/feature/src/lib/download-library.component.html
index 59171fd69..aab3ba201 100644
--- a/libs/portal/downloads/feature/src/lib/download-library.component.html
+++ b/libs/portal/downloads/feature/src/lib/download-library.component.html
@@ -6,81 +6,208 @@
>