// Форматирование и работа с буфером/файлами. Чистые функции без зависимостей — // то, что во всех панелях уже совпадало побайтово. // ISO-дата → «2026-08-01 12:34». Без секунд и без часового пояса — // формат для таблиц, где важна разница между записями, а не точность до сек. export function fmtDateTime(iso: string | null | undefined): string { if (!iso) return '—'; return iso.replace('T', ' ').slice(0, 16); } // unix-секунды → «5 мин» / «3 ч» (без суффикса «назад» — его добавляет вызывающий) export function timeAgo(ts: number | undefined): string { if (!ts) return '—'; const sec = Math.floor(Date.now() / 1000) - ts; if (sec < 60) return sec + ' сек'; if (sec < 3600) return Math.floor(sec / 60) + ' мин'; return Math.floor(sec / 3600) + ' ч'; } export function bytes(n: number | undefined): string { if (!n) return '—'; if (n < 1024) return n + ' B'; if (n < 1_048_576) return (n / 1024).toFixed(1) + ' KB'; if (n < 1_073_741_824) return (n / 1_048_576).toFixed(1) + ' MB'; return (n / 1_073_741_824).toFixed(2) + ' GB'; } export function downloadFile(filename: string, text: string) { const url = URL.createObjectURL(new Blob([text], { type: 'text/plain;charset=utf-8' })); const a = document.createElement('a'); a.href = url; a.download = filename; document.body.appendChild(a); a.click(); a.remove(); URL.revokeObjectURL(url); } // navigator.clipboard недоступен вне https/localhost — отсюда фолбэк на execCommand. export async function copyText(text: string) { if (navigator.clipboard) { await navigator.clipboard.writeText(text); } else { const ta = document.createElement('textarea'); ta.value = text; ta.style.cssText = 'position:fixed;opacity:0'; document.body.appendChild(ta); ta.select(); document.execCommand('copy'); document.body.removeChild(ta); } }