Sync новых требований в openspec/specs/: web-ui («Действия обновляют интерфейс на месте») и review («Петлевые действия ревью обновляют экран на месте»). Change перемещён в changes/archive/2026-07-04-htmx-action-swap. Конвенция web-ui.md актуализирована: сняты маркеры «будем» по реализованному. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
74 lines
6.1 KiB
Markdown
74 lines
6.1 KiB
Markdown
## Why
|
||
|
||
Сейчас действия над загрузкой (откат, привязать заново, распознать заново,
|
||
уточнить, применить и т.д.) — это POST-формы с PRG-редиректом: после действия
|
||
браузер уходит на другую страницу и теряет контекст. Откат со страницы
|
||
загрузки `/download/{id}` и из карточки списка одинаково выкидывает на `/`, а
|
||
перераспознавание на `/review/{id}` перезагружает страницу с прыжком скролла
|
||
наверх. Пользователь хочет оставаться там, где действовал: действие со списка —
|
||
остаёшься в списке, со страницы загрузки/ревью — остаёшься на ней.
|
||
|
||
## What Changes
|
||
|
||
- Мутирующие UI-действия выполняются через **htmx-swap на месте** вместо
|
||
server-side PRG-редиректа: сервер отвечает обновлённым HTML-фрагментом той
|
||
области, которую затронуло действие, htmx подменяет её в DOM без навигации.
|
||
Паттерн уже применяется в `reviewBlockAction` (выбор источника) — расширяем
|
||
его на остальные действия.
|
||
- **Карточка в списке** (`/`): действия `undo`, `relink`, `retry`, `cancel`
|
||
подменяют карточку (`<article class="card">`) на месте — обновлённое
|
||
состояние, бейдж и набор кнопок. Фильтр/поиск/пагинация/прокрутка не
|
||
сбрасываются.
|
||
- **Страница загрузки** (`/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
|
||
<!-- Нет новых capability — меняется поведение существующих. -->
|
||
|
||
### 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`) не трогаем;
|
||
клиентских фреймворков не добавляем.
|