# web-ui Specification (delta)

## ADDED Requirements

### Requirement: Обзор жизненного цикла в карточке списка

Карточка загрузки в списке SHALL показывать обзорную мета-строку для решения о
судьбе раздачи: метку `ID:` перед копируемым идентификатором загрузки, дату
добавления раздачи (всегда), размер раздачи и рейтинг отдачи. Контекст загрузки
MUST NOT показываться в карточке списка — он доступен на странице `/download/{id}`.

Дата добавления SHALL показываться всегда как абсолютная дата и относительная
давность (например «`2026-06-30 · 5 дней назад`»); источником SHALL быть время
добавления раздачи в источник (`source_added_at`, qBittorrent `added_on`) с
фолбэком на время создания загрузки (`created_at`), согласованным с порядком
списка.

Рейтинг отдачи SHALL браться из живого снимка телеметрии; если торрента нет в
снимке (источник ушёл из qBittorrent), рейтинг SHALL отображаться прочерком «—».

Размер раздачи SHALL браться из живого снимка (общий размер торрента), а при
отсутствии торрента в снимке — из суммарного размера разложенных файлов загрузки;
если неизвестно ни то, ни другое — прочерк «—».

#### Scenario: Метка идентификатора

- **WHEN** рендерится карточка загрузки в списке
- **THEN** перед значением `download.id` показана метка «ID:», а кнопка
  копирования копирует именно `download.id`

#### Scenario: Дата добавления показана всегда

- **WHEN** рендерится любая карточка списка
- **THEN** в ней показана дата добавления раздачи абсолютной датой и
  относительной давностью
- **AND** если `source_added_at` неизвестно, используется `created_at`

#### Scenario: Рейтинг из живого снимка

- **WHEN** торрент загрузки присутствует в живом снимке
- **THEN** в карточке показан его рейтинг отдачи
- **AND** если торрента в снимке нет, рейтинг показан прочерком «—»

#### Scenario: Размер с фолбэком на разложенные файлы

- **WHEN** торрент загрузки присутствует в живом снимке
- **THEN** размер раздачи в карточке берётся из общего размера торрента
- **AND** если торрента в снимке нет, но у загрузки есть разложенные файлы —
  размер берётся из суммарного размера этих файлов

#### Scenario: Контекст не в карточке

- **WHEN** у загрузки есть переданный контекст
- **THEN** он не показывается в карточке списка, но доступен на странице
  `/download/{id}`

## MODIFIED Requirements

### Requirement: Клиентские взаимодействия без сборки

Веб-UI SHALL реализовывать клиентскую логику без шага сборки и без реактивных
фреймворков: копирование идентификатора загрузки (vanilla JS). Основной
копируемый идентификатор в карточке списка и в шапке страницы просмотра SHALL
быть `download.id` (ULID) — тот же ключ, что пишется в логи (`download_id`). Все
действия над загрузкой SHALL выполняться через формы/htmx (раундтрип на сервер),
без клиентского пересчёта доменного состояния.

#### Scenario: Копирование идентификатора загрузки

- **WHEN** пользователь нажимает кнопку копирования рядом с идентификатором
  загрузки (`download.id`) в карточке списка или шапке страницы просмотра
- **THEN** значение `download.id` копируется в буфер обмена без перезагрузки
  страницы

#### Scenario: Действия только через раундтрип

- **WHEN** пользователь выполняет действие над загрузкой
- **THEN** оно исполняется формой/htmx-запросом на сервер, без клиентского
  пересчёта доменного состояния
