## Why Сейчас действия над загрузкой (откат, привязать заново, распознать заново, уточнить, применить и т.д.) — это POST-формы с PRG-редиректом: после действия браузер уходит на другую страницу и теряет контекст. Откат со страницы загрузки `/download/{id}` и из карточки списка одинаково выкидывает на `/`, а перераспознавание на `/review/{id}` перезагружает страницу с прыжком скролла наверх. Пользователь хочет оставаться там, где действовал: действие со списка — остаёшься в списке, со страницы загрузки/ревью — остаёшься на ней. ## What Changes - Мутирующие UI-действия выполняются через **htmx-swap на месте** вместо server-side PRG-редиректа: сервер отвечает обновлённым HTML-фрагментом той области, которую затронуло действие, htmx подменяет её в DOM без навигации. Паттерн уже применяется в `reviewBlockAction` (выбор источника) — расширяем его на остальные действия. - **Карточка в списке** (`/`): действия `undo`, `relink`, `retry`, `cancel` подменяют карточку (`
`) на месте — обновлённое состояние, бейдж и набор кнопок. Фильтр/поиск/пагинация/прокрутка не сбрасываются. - **Страница загрузки** (`/download/{id}`): те же действия обновляют содержимое страницы на месте (без перехода и без прыжка скролла), отражая новое состояние. - **Страница ревью** (`/review/{id}`): **петлевые** действия распознавания — `rerecognize`, `refine` (и уже работающий выбор источника) — обновляют экран на месте (без перезагрузки и прыжка скролла). Так как они асинхронны (переводят загрузку в `recognizing`), своп отдаёт состояние `recognizing`, а экран сам **допалливает** готовый план htmx-фрагментом (`GET /fragments/downloads/{id}/review`, по образцу `progress`/`seeding`) и автоматически сменяется на план по завершении — без ручного обновления. **Выходы** из ревью (`apply` → done, `defer` → deferred, `cancel` → cancelled) уводят с экрана (загрузка покидает ревью), поэтому остаются навигацией/редиректом. Отдельная кнопка перераспознавания на `/download/{id}` НЕ добавляется — ревью считается частью «страницы загрузки», действие остаётся в ревью-потоке. - **Деградация без JS сохраняется**: формы остаются обычными POST; при отсутствии htmx (нет заголовка `HX-Request`) сервер отвечает прежним PRG-редиректом, поведение не ломается. - **Ошибки действий** показываются на месте (в подменённом фрагменте), а не только через `?err=` после редиректа. Новых зависимостей нет: Alpine.js/SPA не вводятся, стек остаётся htmx-first (см. решение `2026-06-30-web-ui-design-port`). ## Capabilities ### New Capabilities ### Modified Capabilities - `web-ui`: действия над карточкой/страницей загрузки выполняются htmx-swap'ом фрагмента на месте (без навигации и сброса контекста списка), с graceful degradation на PRG-редирект без htmx. - `review`: петлевые действия ревью (`rerecognize`, `refine`) обновляют экран на месте htmx-свопом, не перезагружая страницу и не сбрасывая прокрутку; выходы из ревью (`apply`/`defer`/`cancel`) остаются навигацией. ## Impact - **Код**: `internal/httpapi` — обработчики действий (`review.go`, `httpapi.go`): вместо `redirect*` отвечать фрагментом (HTTP 200) при `HX-Request`, сохранив редирект-ветку; извлечь `buildCardView`/ `buildDownloadView`; новый fragment-эндпоинт `handleFragReview` для авто-поллинга recognizing (`live.go`). - **Шаблоны** (`web/templates`): выделить переиспользуемые фрагменты — карточка списка (в партиал), «главная область» страницы загрузки, тело ревью; проставить `hx-post`/`hx-target`/`hx-swap` на формы действий, скрытое поле `surface`, поллер на блок `recognizing`. - **Зависимости**: без изменений (htmx уже вендорится). - **Тесты**: обработчики действий — проверка ветвления `HX-Request` → фрагмент vs редирект. - **Вне scope**: живой поллинг телеметрии (`live-status`) не трогаем; клиентских фреймворков не добавляем.