Мутирующие действия больше не уводят со страницы: 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>
75 lines
3.1 KiB
HTML
75 lines
3.1 KiB
HTML
<!doctype html>
|
|
<html lang="ru">
|
|
<head>
|
|
<meta charset="utf-8">
|
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
<title>jellybit — загрузки</title>
|
|
<link rel="stylesheet" href="{{asset "css/jellybit.css"}}">
|
|
<script src="{{asset "vendor/htmx.min.js"}}" defer></script>
|
|
<script src="{{asset "js/app.js"}}" defer></script>
|
|
</head>
|
|
<body>
|
|
|
|
{{template "header" "downloads"}}
|
|
|
|
<main class="main">
|
|
<h1 class="page-title">Загрузки</h1>
|
|
<p class="page-sub">Кинь magnet, ссылку, .torrent или сообщение бота — остальное jellybit сделает сам.</p>
|
|
|
|
{{if .Error}}<div class="result-banner" style="background:var(--st-err-bg);border-color:color-mix(in srgb,var(--st-err) 35%,transparent)"><h2 style="color:var(--st-err)">{{.Error}}</h2></div>{{end}}
|
|
|
|
<!-- Единое окно добавления -->
|
|
<form class="add-box" method="post" action="/ui/downloads">
|
|
<div class="add-row">
|
|
<input class="input" type="text" name="source" autocomplete="off" required
|
|
placeholder="magnet:?xt=urn:btih:… · ссылка · .torrent · или вставь текст бота">
|
|
<button class="btn btn-primary btn-lg" type="submit">Добавить</button>
|
|
</div>
|
|
<p class="add-hint">Можно вставить целиком сообщение трекер-бота — magnet и контекст разберутся автоматически.</p>
|
|
</form>
|
|
|
|
{{if or .Downloads .Searching}}
|
|
<!-- Панель фильтров (серверные: фильтр/поиск/пагинация в query) -->
|
|
<div class="toolbar" id="list-toolbar">
|
|
<form class="search" method="get" action="/">
|
|
<span class="ico">⌕</span>
|
|
<input class="input" type="search" name="q" value="{{.Query}}"
|
|
placeholder="Поиск по названию, id или infohash…">
|
|
{{if ne .Filter "all"}}<input type="hidden" name="f" value="{{.Filter}}">{{end}}
|
|
{{if .ShowAll}}<input type="hidden" name="all" value="1">{{end}}
|
|
</form>
|
|
<div class="filter-chips" id="chips">
|
|
{{range .Chips}}<a class="chip{{if .Active}} active{{end}}" href="{{.URL}}">{{.Label}}</a>{{end}}
|
|
</div>
|
|
</div>
|
|
|
|
<div class="toggle-line">
|
|
<a href="{{.ShowURL}}">{{if .ShowAll}}☑{{else}}☐{{end}} Показать всё, включая удалённые</a>
|
|
</div>
|
|
{{end}}
|
|
|
|
{{if .Downloads}}
|
|
<!-- Список карточек -->
|
|
<div id="list">
|
|
{{range .Downloads}}{{template "card" .}}{{end}}
|
|
</div>
|
|
|
|
{{if gt .Pages 1}}
|
|
<nav class="pager">
|
|
{{if .PrevURL}}<a class="btn btn-sm" href="{{.PrevURL}}">←</a>{{end}}
|
|
{{range .PageLinks}}<a class="btn btn-sm{{if .Active}} btn-primary{{end}}" href="{{.URL}}">{{.Num}}</a>{{end}}
|
|
{{if .NextURL}}<a class="btn btn-sm" href="{{.NextURL}}">→</a>{{end}}
|
|
</nav>
|
|
{{end}}
|
|
|
|
{{else}}
|
|
<div class="empty-state">
|
|
<div class="big">🍿</div>
|
|
{{if .Searching}}Ничего не найдено. Поменяй фильтр или запрос.{{else}}Пока пусто. Добавь первую загрузку выше.{{end}}
|
|
</div>
|
|
{{end}}
|
|
|
|
</main>
|
|
</body>
|
|
</html>
|