/** * ui.js — Утилиты форматирования, отображения статусов и DOM-элементов */ export const MAX_FILE_SIZE = 25 * 1024 * 1024; export const ACCEPTED_TYPES = new Set([ "image/jpeg", "image/jpg", "image/png", "image/webp", "image/heic", "image/heif", ]); export function isHeicFile(file) { const type = String(file?.type || "").toLowerCase(); const name = String(file?.name || "").toLowerCase(); return ( type === "image/heic" || type === "image/heif" || name.endsWith(".heic") || name.endsWith(".heif") ); } export function formatFileSize(bytes) { if (!bytes || bytes <= 0) return ""; const units = ["Б", "КБ", "МБ", "ГБ"]; let value = bytes; let unitIndex = 0; while (value >= 1024 && unitIndex < units.length - 1) { value /= 1024; unitIndex++; } return `${value.toFixed(value >= 10 || unitIndex === 0 ? 0 : 1)} ${units[unitIndex]}`; } export function versionFileName(version) { const dateStr = version?.createdAt ? new Date(version.createdAt).toISOString().replace(/[:.]/g, "-").slice(0, 19) : "photo"; const ext = version?.ext || "jpg"; const label = version?.label ? version.label.replace(/[^\p{L}\p{N}._-]+/gu, "-") : "version"; return `kadr-${label}-${dateStr}.${ext}`; } export function formatHistoryDate(iso) { if (!iso) return ""; try { const d = new Date(iso); const date = d.toLocaleDateString("ru-RU", { day: "numeric", month: "short", }); const time = d.toLocaleTimeString("ru-RU", { hour: "2-digit", minute: "2-digit", }); return `${date}, ${time}`; } catch { return iso; } } export function setJobStatus(kind, message) { const jobStatus = document.querySelector("#job-status"); const jobStatusText = document.querySelector("#job-status-text"); if (!jobStatus || !jobStatusText) return; if (kind === "idle") { jobStatus.hidden = true; jobStatus.classList.remove("status-error"); jobStatusText.textContent = ""; return; } jobStatus.hidden = false; jobStatus.classList.toggle("status-error", kind === "error"); jobStatusText.textContent = message; } export function resetResult() { const resultImage = document.querySelector("#result-image"); const resultPlaceholder = document.querySelector("#result-placeholder"); const resultLabel = document.querySelector("#result-label"); const downloadLink = document.querySelector("#download-link"); if (resultImage) { resultImage.hidden = true; resultImage.removeAttribute("src"); } if (resultPlaceholder) resultPlaceholder.hidden = false; if (resultLabel) resultLabel.textContent = ""; if (downloadLink) downloadLink.hidden = true; } export function clearSourcePreview() { const sourceImage = document.querySelector("#source-image"); const sourcePlaceholder = document.querySelector("#source-placeholder"); const downloadCurrent = document.querySelector("#download-current"); if (sourceImage) { sourceImage.hidden = true; sourceImage.removeAttribute("src"); } if (sourcePlaceholder) sourcePlaceholder.hidden = false; if (downloadCurrent) downloadCurrent.hidden = true; } export function showSourcePreview(url, title, meta) { const sourceImage = document.querySelector("#source-image"); const sourcePlaceholder = document.querySelector("#source-placeholder"); const dropTitle = document.querySelector("#drop-title"); const fileMeta = document.querySelector("#file-meta"); if (sourceImage) { sourceImage.src = url; sourceImage.hidden = false; } if (sourcePlaceholder) sourcePlaceholder.hidden = true; if (dropTitle && title) dropTitle.textContent = title; if (fileMeta && meta) fileMeta.textContent = meta; } export function updateDownloadCurrent(version) { const downloadCurrent = document.querySelector("#download-current"); if (!downloadCurrent) return; if (!version || !version.url) { downloadCurrent.hidden = true; return; } downloadCurrent.href = version.url; downloadCurrent.download = versionFileName(version); downloadCurrent.hidden = false; }