Перенос дизайна в server-rendered веб-UI (web-ui)

Новая capability web-ui: презентационный перенос готового дизайна
(семантический HTML + единый jellybit.css, тёмная тема по настройке ОС)
в html/template без шага сборки.

- Встроенная (go:embed) отдача статики под /static с Cache-Control и
  cache-busting (?v=<hash> по содержимому css/js).
- Шрифты IBM Plex self-hosted (@font-face, cyrillic+latin), без CDN.
- Наколеночный менеджер зависимостей: вендор (htmx + шрифты) не хранится
  в репо (gitignore), идемпотентно добывается `task assets` по
  web/assets.manifest с проверкой sha256; task build/run зависят от assets.
- Общие партиалы: шапка, бейдж статуса (карта всех 14 состояний),
  виджет «файл источника → раскладка» (общий для review и download).
- Страницы: список с фильтром/поиском, ревью, новая страница просмотра
  загрузки (/download/{id}). deleted скрыт по умолчанию.
- Превью раскладки берётся из единой логики internal/layout
  (buildFileRows), без дублирования правил имён в шаблонах.
- Убраны meta-refresh и инлайн-стили; copyHash на vanilla с fallback
  на execCommand и честной индикацией (целевой деплой — HTTP LAN).

Вне scope (отдельный change): живые обновления прогресса и раздел
раздачи, клиентский режим ручной раскладки файл→серия (с Alpine.js).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
av
2026-06-30 19:59:19 +03:00
co-authored by Claude Opus 4.8
parent d190072647
commit 4e2593ac31
26 changed files with 1960 additions and 303 deletions
@@ -0,0 +1,63 @@
## Why
Текущий веб-UI — черновой: инлайн `<style>`, хардкод цветов, грубый
`<meta http-equiv="refresh" content="5">` для «живости». Готов полноценный
дизайн (`tmp/design/`) — семантический HTML + один CSS-файл, заточенный ровно
под нашу архитектуру (server-rendered Go, без сборки/npm/React). Пора перенести
его в прод, чтобы «единое окно» стало пригодным для ежедневного использования.
Это презентационный change: поведение ядра (приём, распознавание, раскладка,
петля ревью) не меняется — оформляем уже существующие действия и состояния.
## What Changes
- **Отдача статики:** роут `/static/*` через `http.FileServer` над `web.FS`,
`go:embed` для `web/static/{css,js,fonts,vendor}`, разумные `Cache-Control`.
- **Шрифты self-hosted:** IBM Plex Sans + IBM Plex Mono кладём локально и
подключаем через `@font-face` (без Google Fonts CDN — не зависим от внешней
сети, не светим referrer).
- **Дизайн-система:** `jellybit.css` (токены, тёмная тема по настройке ОС) как
единственный CSS; новые цвета не вводим — только из токенов.
- **Зависимости фронта:** вендорные ассеты (htmx + шрифты) **не храним в репо**
`web/static/vendor/` в gitignore. `task assets` идемпотентно добывает их по
манифесту (имя/версия/URL/sha256): нет файла или хеш не сошёлся — качает, иначе
берёт с диска. Без Node и бандлера. Авторские css/js — обычный коммит.
- **Общие партиалы:** шапка `.app-header`, бейдж статуса (карта `state→class`),
виджет «файл источника → раскладка» (общий для review и download).
- **Шаблоны:** переносим `index` и `review`, добавляем `download` (просмотр одной
загрузки). Референсные `states.html`/`directions.html` в прод не идут.
- **Убираем meta-refresh** и инлайн-стили; до отдельного change живых
обновлений список остаётся статичным (без авто-обновления прогресса).
- **Клиентский JS по минимуму:** `copyHash` (vanilla), спойлер контекста
(нативный `<details>`). Реактивных виджетов нет — все действия идут через
формы/htmx.
- **Превью путей** на экране ревью отображается из серверного расчёта
(`internal/layout`), без дублирования правил в шаблоне.
- **Формы:** проставить `name` полей, совпадающие с Go-структурами обработчиков.
## Capabilities
### New Capabilities
- `web-ui`: презентационный слой веб-интерфейса — встроенная отдача статики и
шрифтов, единая дизайн-система, рендеринг страниц (список/ревью/просмотр),
бейджи статусов и клиентские взаимодействия без сборки (копирование infohash,
спойлер, режим ручной раскладки с live-превью).
### Modified Capabilities
<!-- Нет. Доменное поведение приёма/распознавания/ревью не меняется; правила
ревью-поверхности живут в docs/specs/review-ux.md (ещё не перенесены в
OpenSpec). Этот change оформляет уже существующие действия и состояния. -->
## Impact
- **Код:** `internal/httpapi` (роут статики, новые/обновлённые хендлеры рендера,
партиалы), `web/web.go` (`go:embed` статики), новые `web/static/**` и
`web/templates/{index,review,download}.html`. Превью зовёт `internal/naming`.
- **Зависимости:** вендор (htmx + шрифты) добывается `task assets` по манифесту с
checksum, не npm/не Go-модуль; Node не вводим. `web/static/vendor/` в gitignore.
`go:embed` требует ассеты на сборке, поэтому `task build`/`task run` зависят от
`task assets` (идемпотентного) — бинарь остаётся самодостаточным, но голый
`go build` без фетча неполноценен (осознанный размен).
- **Поведение:** без изменений в API/ядре. Вне scope: живые обновления прогресса
(отдельный change `web-live-updates`) и клиентский режим ручной раскладки
файл→серия (отложен до реальной необходимости — операция редкая).