Мутирующие действия больше не уводят со страницы: undo/relink/retry/cancel
в списке свопят карточку (#card-{id}), на /download/{id} — #download-main;
петля ревью (refine/rerecognize) свопит #review-main и допалливает
recognizing до готового плана (fragment /fragments/downloads/{id}/review,
every 2s). Выходы ревью (apply/defer/cancel) остаются навигацией. Без htmx —
прежний PRG-редирект (деградация). Ошибка действия на htmx-пути — HTTP 200
с сообщением в фрагменте (ActionError), иначе htmx не свопит DOM.
Разметка вынесена в партиалы card/download_main/review_main (корень = элемент
с целевым id), различение поверхности — скрытым полем surface=list|download.
Извлечены buildCardView/buildDownloadView. handleSetProvider переведён на
reviewBlockAction (консистентность source-действий).
Реализация change htmx-action-swap (OpenSpec).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
6.1 KiB
6.1 KiB
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
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) не трогаем; клиентских фреймворков не добавляем.