В карточке списка и шапке /download/{id} показываем и копируем download.id
(ULID) — тот же ключ, что в логах (download_id), удобно грепать. Infohash
остаётся в блоке «Информация о торренте». Поиск по списку расширен: матчит
любой идентификатор (download.id ИЛИ infohash), плюс название/контекст.
Удалены осиротевшие поля Infohash/InfohashShort и хелпер shortenHash.
Дельта web-ui влита в спеки, change заархивирован.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
46 lines
1.8 KiB
JavaScript
46 lines
1.8 KiB
JavaScript
// Клиентская мелочь без сборки и фреймворков.
|
||
// htmx тянет серверные фрагменты, здесь — только то, что серверу знать не нужно.
|
||
|
||
// Копирование идентификатора (download id / infohash) в буфер обмена. На целевом
|
||
// деплое (домашний LAN по HTTP) navigator.clipboard недоступен — secure context
|
||
// только на HTTPS/localhost, поэтому есть fallback на execCommand, а индикация
|
||
// честная: «✓» лишь при успехе.
|
||
function copyHash(btn, full) {
|
||
if (navigator.clipboard && window.isSecureContext) {
|
||
navigator.clipboard.writeText(full).then(
|
||
() => flashCopy(btn, true),
|
||
() => flashCopy(btn, legacyCopy(full)),
|
||
);
|
||
} else {
|
||
flashCopy(btn, legacyCopy(full));
|
||
}
|
||
}
|
||
|
||
function legacyCopy(text) {
|
||
try {
|
||
const ta = document.createElement('textarea');
|
||
ta.value = text;
|
||
ta.style.position = 'fixed';
|
||
ta.style.opacity = '0';
|
||
document.body.appendChild(ta);
|
||
ta.focus();
|
||
ta.select();
|
||
const ok = document.execCommand('copy');
|
||
document.body.removeChild(ta);
|
||
return ok;
|
||
} catch (e) {
|
||
return false;
|
||
}
|
||
}
|
||
|
||
function flashCopy(btn, ok) {
|
||
const old = btn.dataset.label || btn.textContent;
|
||
btn.dataset.label = old;
|
||
btn.textContent = ok ? '✓ скопировано' : '✗ не вышло';
|
||
btn.classList.toggle('copied', ok);
|
||
setTimeout(() => { btn.textContent = old; btn.classList.remove('copied'); }, 1400);
|
||
}
|
||
|
||
// Фильтр, поиск и пагинация списка — серверные (GET-параметры f/q/page/all),
|
||
// работают без JS. Здесь клиентской фильтрации нет намеренно.
|