diff --git a/public/app.js b/public/app.js index 35d14f5..e6f4659 100644 --- a/public/app.js +++ b/public/app.js @@ -34,6 +34,7 @@ const resultLabel = document.querySelector("#result-label"); const jobStatus = document.querySelector("#job-status"); const jobStatusText = document.querySelector("#job-status-text"); const downloadLink = document.querySelector("#download-link"); +const downloadCurrent = document.querySelector("#download-current"); let currentVersionId = null; let historyVersions = []; @@ -61,6 +62,37 @@ function formatFileSize(bytes) { return `${(bytes / (1024 * 1024)).toFixed(1)} МБ`; } +function versionFileName(version) { + const kindName = { + original: "original", + edit: "edited", + upscale: "upscaled" + }[version.kind] || "photo"; + + let stamp = ""; + try { + const d = new Date(version.createdAt); + stamp = `${d.getFullYear()}-${String(d.getMonth() + 1).padStart(2, "0")}-${String(d.getDate()).padStart(2, "0")}`; + } catch { + stamp = "photo"; + } + + return `${kindName}-${stamp}.${version.ext || "jpg"}`; +} + +function updateDownloadCurrent(version) { + if (!version) { + downloadCurrent.hidden = true; + downloadCurrent.removeAttribute("href"); + downloadCurrent.removeAttribute("download"); + return; + } + + downloadCurrent.href = version.url; + downloadCurrent.download = versionFileName(version); + downloadCurrent.hidden = false; +} + function setHealthState(state, message, title = "") { healthStatus.className = `health-status health-${state}`; healthText.textContent = message; @@ -201,6 +233,7 @@ async function selectFile(file) { file.name, `${formatFileSize(file.size)} · готово к обработке` ); + updateDownloadCurrent(data.version); setJobStatus("", ""); setBusy(false); } catch (error) { @@ -299,6 +332,7 @@ async function runJob(kind) { if (version) { historyVersions.push(version); currentVersionId = version.id; + updateDownloadCurrent(version); renderHistory(); } @@ -428,6 +462,7 @@ function selectHistoryVersion(version) { currentVersionId = version.id; resetResult(); + updateDownloadCurrent(version); showSourcePreviewFromUrl( version.url, version.label || "Версия", @@ -455,6 +490,7 @@ async function loadHistoryOnStart() { if (historyVersions.length > 0) { const latest = historyVersions[historyVersions.length - 1]; currentVersionId = latest.id; + updateDownloadCurrent(latest); showSourcePreviewFromUrl( latest.url, latest.label || "Текущая фотография", diff --git a/public/index.html b/public/index.html index e08970c..a60678c 100644 --- a/public/index.html +++ b/public/index.html @@ -15,7 +15,7 @@ rel="icon" href="data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 64 64'%3E%3Crect width='64' height='64' rx='16' fill='%23202321'/%3E%3Ctext x='32' y='45' font-size='38' font-weight='bold' text-anchor='middle' fill='%23ffffff' font-family='sans-serif'%3E%D0%9A%3C/text%3E%3C/svg%3E" > - +
@@ -129,7 +129,19 @@

Предпросмотр

До и после

- LAN · локально +
+ + LAN · локально +
@@ -224,6 +236,6 @@
- + diff --git a/public/style.css b/public/style.css index 1efd4be..fcf1d75 100644 --- a/public/style.css +++ b/public/style.css @@ -445,6 +445,41 @@ textarea:disabled { text-transform: uppercase; } +.workspace-head-actions { + display: flex; + align-items: center; + gap: 10px; +} + +.download-current { + display: inline-flex; + align-items: center; + gap: 8px; + padding: 9px 13px; + border: 1px solid var(--dark); + border-radius: 999px; + color: var(--dark); + background: transparent; + font-size: 12px; + font-weight: 740; + text-decoration: none; + transition: + transform 150ms ease, + background 150ms ease, + color 150ms ease; +} + +.download-current:hover { + background: var(--dark); + color: white; + transform: translateY(-1px); +} + +.download-current:focus-visible { + outline: 3px solid rgba(108, 92, 231, 0.28); + outline-offset: 2px; +} + .comparison { display: grid; grid-template-columns: 1fr 1fr; @@ -854,7 +889,7 @@ textarea:disabled { } } -/* -- Èñòîðèÿ âåðñèé ------------------------------------------- */ +/* -- История версий ------------------------------------------------- */ .history-section { margin-top: 30px;