Спеки+беклог: закрытие батча UI-задач (favicon, cancelled, значок типа, ellipsis, oob-панель)

Реализованное поведение перенесено в openspec/specs:
- web-ui: cancelled скрыт по умолчанию наравне с deleted; значок типа в карточке;
- review: панель действий обновляется oob-фрагментом при свопе источника.
Пять закрытых задач удалены из docs/backlog + индекса.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
av
2026-07-11 09:46:28 +03:00
co-authored by Claude Opus 4.8
parent 3f80d36727
commit 9472bfdd83
8 changed files with 21 additions and 114 deletions
-5
View File
@@ -40,12 +40,9 @@ Tududi (проект `jellybit`) больше **не** держит беклог
## Низкий ## Низкий
- [Значок типа (фильм/сериал) в списке загрузок](spisok-znachok-tipa.md) — показать 🎬/📺 в строке списка; media type сейчас не доходит до листинга (нужно протащить в запрос и card.html)
- [Ревью уведомлений в Telegram (аудит текстов и формата)](telegram-revyu-uvedomleniy.md) — зонтичный проход по всем текстам бота: полнота карточек, единый язык, оформление; порождает под-задачи - [Ревью уведомлений в Telegram (аудит текстов и формата)](telegram-revyu-uvedomleniy.md) — зонтичный проход по всем текстам бота: полнота карточек, единый язык, оформление; порождает под-задачи
- [Download id в Telegram моноширинным (code) для tap-to-copy](telegram-download-id-code.md) — слать id как `code`; требует включить parse mode (HTML) в send() + escape всех текстов - [Download id в Telegram моноширинным (code) для tap-to-copy](telegram-download-id-code.md) — слать id как `code`; требует включить parse mode (HTML) в send() + escape всех текстов
- [Кнопка «Обновить имя»: полный формат ярлыка (Название (режиссёр, год), сезон)](knopka-obnovit-imya-polnyj-format.md) — Перелив имени даёт усечённое «Title (Year)» вместо полного формата add-шага (режиссёр+сезон); нужный `render` уже есть - [Кнопка «Обновить имя»: полный формат ярлыка (Название (режиссёр, год), сезон)](knopka-obnovit-imya-polnyj-format.md) — Перелив имени даёт усечённое «Title (Year)» вместо полного формата add-шага (режиссёр+сезон); нужный `render` уже есть
- [Скрывать отменённые (cancelled) в общем списке наравне с удалёнными](skryvat-otmenennye-v-spiske.md) — cancelled шумит в ленте; тумблер «показать удалённые» должен раскрывать и cancelled, и deleted
- [Панель действий ревью вне htmx-свопа блока источника](panel-review-vne-swap.md) — При выборе источника одним кликом обновляется только блок источника (#source-block)…
- [Мгновенные обновления через SSE](sse-obnovleniya.md) — Живые обновления прогресса сейчас на htmx-поллинге (фаза 2 веб-UI) — просто и работает… - [Мгновенные обновления через SSE](sse-obnovleniya.md) — Живые обновления прогресса сейчас на htmx-поллинге (фаза 2 веб-UI) — просто и работает…
- [Шум ERROR фоновых циклов при недоступной зависимости](oshibki-klassifikaciya-i-konvencii-logirovaniya.md) — Остаток задачи логирования: ext.* ERROR-шторм при недоступном qBittorrent + эскалация устойчивого сбоя тика _(ревью Fable)_ - [Шум ERROR фоновых циклов при недоступной зависимости](oshibki-klassifikaciya-i-konvencii-logirovaniya.md) — Остаток задачи логирования: ext.* ERROR-шторм при недоступном qBittorrent + эскалация устойчивого сбоя тика _(ревью Fable)_
- [Версии/качество одного тайтла (репаки, апгрейд 1080p → 2160p)](versii-kachestvo-repaki.md) — По калибровке болей (2026-07-02) — не боль, из приоритета выпало - [Версии/качество одного тайтла (репаки, апгрейд 1080p → 2160p)](versii-kachestvo-repaki.md) — По калибровке болей (2026-07-02) — не боль, из приоритета выпало
@@ -53,7 +50,6 @@ Tududi (проект `jellybit`) больше **не** держит беклог
- [Выбор из нескольких находок метабазы в Telegram](telegram-vybor-nahodok.md) — Когда распознавание даёт несколько подходящих кандидатов в метабазе, предлагать их в… - [Выбор из нескольких находок метабазы в Telegram](telegram-vybor-nahodok.md) — Когда распознавание даёт несколько подходящих кандидатов в метабазе, предлагать их в…
- [Улучшения UI: показывать матч с записью метабазы в Telegram](telegram-match-metabazy.md) — Название/год/провайдер+id в боте уже выводятся; осталась кликабельная ссылка на запись - [Улучшения UI: показывать матч с записью метабазы в Telegram](telegram-match-metabazy.md) — Название/год/провайдер+id в боте уже выводятся; осталась кликабельная ссылка на запись
- [Добавление торрентов файлом/ссылкой — «единое окно» (остаток: URL)](dobavlenie-edinoe-okno.md) — magnet и .torrent-файл приняты; остался фетч .torrent по URL (нужен SSRF-гард) - [Добавление торрентов файлом/ссылкой — «единое окно» (остаток: URL)](dobavlenie-edinoe-okno.md) — magnet и .torrent-файл приняты; остался фетч .torrent по URL (нужен SSRF-гард)
- [Форма приёма: длинное имя .torrent-файла ломает верстку add-row](torrent-fayl-dlinnoe-imya.md) — длинное имя файла распирает лейбл файл-пикера, поле source сжимается — нужен ellipsis
- [Раздачи-копии диска (DVD/BluRay: VIDEO_TS/BDMV)](disk-kopii-video-ts-bdmv.md) — Иногда для очень редких фильмов скачивается не один видеофайл, а полная копия диска… - [Раздачи-копии диска (DVD/BluRay: VIDEO_TS/BDMV)](disk-kopii-video-ts-bdmv.md) — Иногда для очень редких фильмов скачивается не один видеофайл, а полная копия диска…
- [Проверка свободного места перед copy-fallback](svobodnoe-mesto-copy-fallback.md) — Когда хардлинк невозможен (EXDEV/ENOTSUP/…), layout копирует файл, дублируя место на диске - [Проверка свободного места перед copy-fallback](svobodnoe-mesto-copy-fallback.md) — Когда хардлинк невозможен (EXDEV/ENOTSUP/…), layout копирует файл, дублируя место на диске
- [Кэш метабаз (и опционально LLM)](kesh-metabaz.md) — Повторные и ретраящиеся прогоны распознавания бьют TMDB/TVDB/TVMaze одним и тем же… - [Кэш метабаз (и опционально LLM)](kesh-metabaz.md) — Повторные и ретраящиеся прогоны распознавания бьют TMDB/TVDB/TVMaze одним и тем же…
@@ -61,7 +57,6 @@ Tududi (проект `jellybit`) больше **не** держит беклог
- [[идея] Завершение загрузки через webhook](webhook-zavershenie-zagruzki.md) — ИДЕЯ (решим по опыту эксплуатации) - [[идея] Завершение загрузки через webhook](webhook-zavershenie-zagruzki.md) — ИДЕЯ (решим по опыту эксплуатации)
- [Авторизация веб-UI (на будущее)](avtorizaciya-web-ui.md) — Решено для v1: без авторизации в доверенной LAN, опц - [Авторизация веб-UI (на будущее)](avtorizaciya-web-ui.md) — Решено для v1: без авторизации в доверенной LAN, опц
- [Современный Web-UI как PWA](web-ui-pwa.md) — Переделать веб-интерфейс в современное PWA-приложение (устанавливаемое, отзывчивое… - [Современный Web-UI как PWA](web-ui-pwa.md) — Переделать веб-интерфейс в современное PWA-приложение (устанавливаемое, отзывчивое…
- [Сделать фавиконку для jellybit](favicon.md) — мелкая косметика: иконка вкладки/PWA для веб-UI
- [Идентичность инфохэшей: split v1/v2 одного торрента + крафт-магнет отравляет владение (F4, F5)](review-f4-f5-infohash-identity.md) — split v1/v2 идентичность и крафт-магнет отравляют владение инфохэшами _(ревью 2026-07-08)_ - [Идентичность инфохэшей: split v1/v2 одного торрента + крафт-магнет отравляет владение (F4, F5)](review-f4-f5-infohash-identity.md) — split v1/v2 идентичность и крафт-магнет отравляют владение инфохэшами _(ревью 2026-07-08)_
- [Приём/UI: мелкие фиксы границ и парсинга (F7, F8, F9, F10, N2)](review-f7-f10-ingest-ui-fixes.md) — мелочи приёма/UI: oversized→500, гонки дедупа, парсинг magnet, cap контекста, руны _(ревью 2026-07-08)_ - [Приём/UI: мелкие фиксы границ и парсинга (F7, F8, F9, F10, N2)](review-f7-f10-ingest-ui-fixes.md) — мелочи приёма/UI: oversized→500, гонки дедупа, парсинг magnet, cap контекста, руны _(ревью 2026-07-08)_
- [Жизненный цикл: мелкие находки (MINOR-8, MINOR-9, NIT-11, NIT-12)](review-lifecycle-minor.md) — claim-token распознавания, I/O под глобальным mutex, мелкие lookup/retry _(ревью 2026-07-08)_ - [Жизненный цикл: мелкие находки (MINOR-8, MINOR-9, NIT-11, NIT-12)](review-lifecycle-minor.md) — claim-token распознавания, I/O под глобальным mutex, мелкие lookup/retry _(ревью 2026-07-08)_
-5
View File
@@ -1,5 +0,0 @@
# Сделать фавиконку для jellybit
**Приоритет:** низкий
_Описание не заполнено._
-7
View File
@@ -1,7 +0,0 @@
# Панель действий ревью вне htmx-свопа блока источника
**Приоритет:** низкий
При выборе источника одним кликом обновляется только блок источника (#source-block) htmx-свопом, а нижняя панель действий (кнопка «Применить», завязанная на HasLinks) — вне блока и не обновляется до полной перезагрузки. Практически не мешает (хардлинки только по явному «Применить», Apply без плана вернёт ошибку), но в краевом случае (источник с пустым предпросмотром из-за коллизии) кнопка может остаться/пропасть не синхронно. Решение намечено в дизайне review-unified-source-block: обновлять панель hx-swap-oob из того же партиала.
Связано: openspec/specs/review, openspec/specs/web-ui, пакет httpapi.
@@ -1,35 +0,0 @@
# Скрывать отменённые (cancelled) в общем списке наравне с удалёнными
**Приоритет:** низкий · **Теги:** web-ui
Отменённые загрузки (`cancelled`) должны по умолчанию скрываться в общем списке
так же, как удалённые (`deleted`), а тумблер «Показать всё, включая удалённые»
(`web/templates/index.html:52`) должен показывать **и те, и те**. Сейчас под
группой `all` скрыт только `deleted`, а `cancelled` всегда виден и шумит в ленте
— тем более что новый стоп-кран «Закрыть» будет плодить `cancelled`-записи.
Где править:
- `internal/store/list.go:62-66` — ветка `else if !f.IncludeDeleted` под группой
`all` скрывает только `StateDeleted` (`state <> ?`). Расширить до
`state NOT IN (deleted, cancelled)`.
- Осмыслить семантику флага `ListFilter.IncludeDeleted` (`list.go:45`): теперь он
«показывать скрытые терминальные» (deleted **и** cancelled). Либо переименовать
(`IncludeHidden`/`IncludeTerminal`), либо оставить имя и уточнить комментарий.
- Метка тумблера (`index.html:52`) и текст — при желании подправить («…включая
отменённые и удалённые»); `ShowAll`/`all=1` уже управляет показом.
- Комментарии-инварианты про «под all видны» в `internal/store/list.go:22-26` и
`statesInGroup` (cancelled/reverted/deleted не имеют чипа) — привести в
соответствие.
Замечания:
- `cancelled` своей группы-чипа не имеет (`statesInGroup`, `list.go:29-40`) — под
группами `review/active/done/problem` он и так не появляется, правка нужна
только в ветке `all`.
- `reverted` пользователь не упоминал — по умолчанию оставляем видимым; при
желании обсудить симметрию отдельно.
- Обновить тест `TestListDownloadsPageFilterAndDeleted`
(`internal/store/list_test.go`) — добавить `cancelled` в скрытые по умолчанию и
видимые под `IncludeDeleted`.
Связано: `openspec/specs/web-ui`, пакеты `internal/store`, `internal/httpapi`,
конвенция [web-ui](../conventions/web-ui.md).
-25
View File
@@ -1,25 +0,0 @@
# Значок типа (фильм/сериал) в списке загрузок
**Приоритет:** низкий
В строке списка загрузок показывать значок типа контента — 🎬 фильм / 📺 сериал,
как уже делается на экране деталей/ревью и в карточке бота
(`internal/tgbot/render.go:231-233`). Сейчас партиал строки списка
`web/templates/partials/card.html` (define `"card"`) типа не показывает — только
заголовок, ID, дату/размер, статус-бейдж и прогресс.
Что учесть:
- Media type в модели строки списка отсутствует: `downloadView`
(`internal/httpapi/httpapi.go:200`) и `store.Download`
(`internal/store/download.go:175`) его не несут. В detail/review он берётся из
`rd.Plan.Type` (`internal/httpapi/download.go:139`). Каноничный тип —
`recognize.MediaType` (`movie`/`series`).
- Нужно протащить `MediaType`/`IsSeries` в листинг: расширить запрос
`ListDownloadsPage` (сейчас через LEFT JOIN тянет только `rec_title`) и сборку
`buildCardView`, затем добавить значок в `card.html`.
- Тип может быть ещё не распознан (загрузка на раннем шаге) — предусмотреть
отсутствие значка/нейтральный плейсхолдер.
Связано: `internal/httpapi`, `web/templates/partials/card.html`,
[docs/specs/architecture.md](../specs/architecture.md).
-30
View File
@@ -1,30 +0,0 @@
# Форма приёма: длинное имя .torrent-файла ломает верстку add-row
**Приоритет:** низкий · **Теги:** web-ui, ingest
При выборе `.torrent`-файла с длинным именем (типичный релиз с трекера, напр.
«Бухта вдов Widow's Bay Сезон 1 Серии 1-10 из 10 (Хиро Мурай) [2026, США, …,
WEB-DL 1080p] 7 x MVO (HDr [rutracker-6869937].torrent») кнопка-лейбл файл-пикера
раздувается на всю ширину и ломает раскладку строки добавления: поле `source`
сжимается/прячется, «Добавить» уезжает.
Причина: onchange пишет полное `files[0].name` в `[data-file-label]`
(`web/templates/index.html:26-30`), а у `.btn-file` нет ограничения ширины
(`web/static/css/jellybit.css:194` — только цвет). В `.add-row`
(`display:flex`, `.input{flex:1}`) лейбл распирается по контенту и съедает строку.
Фикс (косметика, без домена):
- CSS `.btn-file`: `max-width` + `overflow:hidden; text-overflow:ellipsis;
white-space:nowrap` — имя усекается многоточием, а не растягивает строку.
- Опц.: показывать базовое имя без `.torrent` и класть полное в `title`
(тултип). Полное имя всё равно уходит на сабмит через сам file-input
(`header.Filename`), правка чисто визуальная.
Смежное (проверить заодно, не обязательно тем же фиксом): на приёме `source_ref`
выводится из имени раздачи/файла и может быть очень длинным. В списке/карточке он
усечён (`shorten(oneLine(d.SourceRef), 120)` — `internal/httpapi/download.go:67`),
так что отображение ок; убедиться лишь, что нет жёсткого лимита колонки в БД,
роняющего сам приём длинного имени.
Связано: `openspec/specs/ingest`, `openspec/specs/web-ui`, пакет `httpapi`,
конвенция [web-ui](../conventions/web-ui.md).
+7 -1
View File
@@ -117,7 +117,11 @@ qBittorrent, но и **готов к раскладке** — раздача в
выполняться через раундтрип на сервер (форма/htmx), без клиентского пересчёта выполняться через раундтрип на сервер (форма/htmx), без клиентского пересчёта
доменного состояния; при этом инфо-часть и предпросмотр раскладки SHALL доменного состояния; при этом инфо-часть и предпросмотр раскладки SHALL
немедленно обновляться под выбранный источник (частичный своп блока, без полной немедленно обновляться под выбранный источник (частичный своп блока, без полной
перезагрузки страницы). Экран SHALL позволять операции над этим списком: перезагрузки страницы). Тем же ответом свопа SHALL синхронно обновляться панель
действий — в частности доступность команды **Применить**, зависящая от наличия
предпросмотра раскладки, — через out-of-band-фрагмент, чтобы кнопка не
рассинхронизировалась с блоком источника (например при пустом предпросмотре
из-за коллизии путей). Экран SHALL позволять операции над этим списком:
выбрать кандидата базы, переключиться на другого кандидата и снять матч с базы выбрать кандидата базы, переключиться на другого кандидата и снять матч с базы
обратно на нейронку («без базы»). Список источников SHALL показываться только обратно на нейронку («без базы»). Список источников SHALL показываться только
при наличии плана распознавания. при наличии плана распознавания.
@@ -139,6 +143,8 @@ qBittorrent, но и **готов к раскладке** — раздача в
отдельного нажатия кнопки «выбрать» отдельного нажатия кнопки «выбрать»
- **AND** инфо-часть и предпросмотр раскладки сразу обновляются под выбранного - **AND** инфо-часть и предпросмотр раскладки сразу обновляются под выбранного
кандидата без полной перезагрузки страницы кандидата без полной перезагрузки страницы
- **AND** панель действий обновляется тем же ответом (out-of-band): доступность
«Применить» синхронна наличию предпросмотра раскладки
#### Scenario: Снятие матча в пользу нейронки #### Scenario: Снятие матча в пользу нейронки
+14 -6
View File
@@ -93,10 +93,12 @@ self-hosted шрифтов, единая дизайн-система (`jellybit.
и страницу просмотра одной загрузки (`/download/{id}`) с распознаванием, и страницу просмотра одной загрузки (`/download/{id}`) с распознаванием,
файлами→раскладкой, историей, блоком информации о торренте и — для сидирующих файлами→раскладкой, историей, блоком информации о торренте и — для сидирующих
задач — секцией живой статистики раздачи. Карточки активных (downloading) задач — секцией живой статистики раздачи. Карточки активных (downloading)
загрузок в списке SHALL содержать индикатор прогресса. Фильтр, поиск и номер загрузок в списке SHALL содержать индикатор прогресса. Карточка загрузки с
распознанным типом SHALL нести значок типа (фильм/сериал). Фильтр, поиск и номер
страницы SHALL передаваться GET-параметрами запроса (например `f`, `q`, `page`) страницы SHALL передаваться GET-параметрами запроса (например `f`, `q`, `page`)
и SHALL работать без клиентского JavaScript. Состояние `deleted` SHALL быть и SHALL работать без клиентского JavaScript. Терминальные состояния `deleted` и
скрыто в списке по умолчанию (с переключателем «показать всё»). Механика живого `cancelled` SHALL быть скрыты в списке по умолчанию (переключатель «показать
всё» раскрывает оба). Механика живого
обновления прогресса и наполнение секции раздачи определяются capability обновления прогресса и наполнение секции раздачи определяются capability
`live-status`. `live-status`.
@@ -112,12 +114,18 @@ self-hosted шрифтов, единая дизайн-система (`jellybit.
- **THEN** её карточка содержит индикатор прогресса (прогресс-бар со скоростью - **THEN** её карточка содержит индикатор прогресса (прогресс-бар со скоростью
и ETA) и ETA)
#### Scenario: Удалённые скрыты по умолчанию #### Scenario: Отменённые и удалённые скрыты по умолчанию
- **WHEN** в списке есть загрузки в состоянии `deleted` и фильтр «показать - **WHEN** в списке есть загрузки в состоянии `deleted` или `cancelled` и фильтр
всё» не включён «показать всё» не включён
- **THEN** они не отображаются, но доступны при включённом переключателе - **THEN** они не отображаются, но доступны при включённом переключателе
#### Scenario: Значок типа в карточке списка
- **WHEN** загрузка в списке имеет распознанный тип (`movie` или `series`)
- **THEN** её карточка показывает значок типа (🎬 фильм / 📺 сериал); при
отсутствии распознанного типа значок не показывается
#### Scenario: Пагинация списка #### Scenario: Пагинация списка
- **WHEN** загрузок под текущим фильтром больше, чем помещается на одну - **WHEN** загрузок под текущим фильтром больше, чем помещается на одну