Files
jellybit/openspec/changes/archive/2026-07-04-htmx-action-swap/proposal.md
T
avandClaude Opus 4.8 576fc4e6c0 OpenSpec: влить дельты htmx-action-swap в спеки, архив change
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>
2026-07-04 14:42:06 +03:00

6.1 KiB
Raw Blame History

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) не трогаем; клиентских фреймворков не добавляем.