From 013a55edeb0d0ec7041464cf8c4c7b0529d531b6 Mon Sep 17 00:00:00 2001 From: Sergey Antropoff Date: Thu, 23 Jul 2026 12:25:29 +0300 Subject: [PATCH] =?UTF-8?q?=D0=94=D0=BE=D0=B1=D0=B0=D0=B2=D0=B8=D1=82?= =?UTF-8?q?=D1=8C=20=D0=BA=D0=BE=D0=BF=D0=B8=D1=80=D0=BE=D0=B2=D0=B0=D0=BD?= =?UTF-8?q?=D0=B8=D0=B5=20=D1=82=D0=B5=D0=BA=D1=81=D1=82=D0=B0=20=D0=B8=20?= =?UTF-8?q?=D0=B4=D0=B2=D1=83=D1=85=D0=BE=D1=81=D0=B5=D0=B2=D1=83=D1=8E=20?= =?UTF-8?q?=D0=BF=D1=80=D0=BE=D0=BA=D1=80=D1=83=D1=82=D0=BA=D1=83=20=D0=B2?= =?UTF-8?q?=20=D0=BF=D1=80=D0=B5=D0=B2=D1=8C=D1=8E=20unwrap.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 3 ++- app/static/css/app.css | 44 +++++++++++++++++++++++++++++++++++------ app/static/js/unwrap.js | 36 +++++++++++++++++++++++++++------ 3 files changed, 70 insertions(+), 13 deletions(-) diff --git a/README.md b/README.md index e46d232..3e9b3ad 100644 --- a/README.md +++ b/README.md @@ -8,6 +8,7 @@ - одноразовая расшифровка — ciphertext удаляется с сервера после открытия - опциональный пароль: Argon2-проверка до выдачи ciphertext, лимит попыток (по умолчанию 3), пустой пароль попытку не тратит - подсветка синтаксиса (highlight.js), ручной выбор языка +- после расшифровки: прокрутка текста по вертикали и горизонтали, копирование в буфер обмена - вложения: drag-and-drop, выбор файлов, вставка скриншота из буфера - спиннер с логотипом при создании и расшифровке - RU / EN, светлая и тёмная тема @@ -45,7 +46,7 @@ ### Расшифровка -Одноразовое открытие: ciphertext удаляется на сервере, превью — только в текущей сессии браузера. +Одноразовое открытие: ciphertext удаляется на сервере, превью — только в текущей сессии браузера. Текст с подсветкой прокручивается по обеим осям; рядом — кнопки копирования и скачивания. | Светлая тема | Тёмная тема | |:------------:|:-----------:| diff --git a/app/static/css/app.css b/app/static/css/app.css index cd90f57..ea20917 100644 --- a/app/static/css/app.css +++ b/app/static/css/app.css @@ -553,7 +553,15 @@ body.busy-open { overflow: hidden; } min-width: 0; font-size: 0.9rem; } -.btn.download-btn { +.item-card-actions { + display: flex; + flex-shrink: 0; + flex-wrap: wrap; + justify-content: flex-end; + gap: 0.4rem; +} +.btn.download-btn, +.btn.copy-btn { flex-shrink: 0; gap: 0.35rem; padding: 0.35rem 0.65rem; @@ -561,7 +569,8 @@ body.busy-open { overflow: hidden; } font-size: 0.78rem; font-weight: 600; } -.btn.download-btn i { +.btn.download-btn i, +.btn.copy-btn i { font-size: 0.72rem; opacity: 0.9; } @@ -961,15 +970,38 @@ html[data-theme="light"] .footer-pillars li { padding: 1rem; background: var(--input); } -.item-card pre { +.item-card pre, +.item-card .item-text-pre { margin: 0.6rem 0 0; - overflow: auto; - max-height: 420px; + max-height: min(60vh, 28rem); padding: 0.8rem; border-radius: 8px; background: rgba(0,0,0,0.25); + overflow-x: auto; + overflow-y: auto; + -webkit-overflow-scrolling: touch; + overscroll-behavior: contain; + tab-size: 2; +} +.item-card pre code, +.item-card .item-text-pre code { + display: block; + width: max-content; + min-width: 100%; + margin: 0; + padding: 0; + background: transparent !important; + white-space: pre; + word-break: normal; + overflow-wrap: normal; + font-family: var(--font-mono); + font-size: 0.85rem; + line-height: 1.45; +} +html[data-theme="light"] .item-card pre, +html[data-theme="light"] .item-card .item-text-pre { + background: rgba(16, 32, 56, 0.05); } -html[data-theme="light"] .item-card pre { background: rgba(16, 32, 56, 0.05); } .item-card img { max-width: 100%; border-radius: 8px; diff --git a/app/static/js/unwrap.js b/app/static/js/unwrap.js index 4892e77..3b8a0f0 100644 --- a/app/static/js/unwrap.js +++ b/app/static/js/unwrap.js @@ -120,6 +120,27 @@ return btn; } + function makeCopyBtn(getText) { + const btn = document.createElement("button"); + btn.className = "btn copy-btn"; + btn.type = "button"; + const label = () => + `${t("common.copy")}`; + btn.innerHTML = label(); + btn.addEventListener("click", async () => { + try { + await navigator.clipboard.writeText(getText() || ""); + btn.innerHTML = `${t("common.copied")}`; + setTimeout(() => { + btn.innerHTML = label(); + }, 1200); + } catch { + /* ignore */ + } + }); + return btn; + } + function renderPackage(pack) { els.items.innerHTML = ""; for (const item of pack.items || []) { @@ -127,23 +148,26 @@ card.className = "item-card"; if (item.type === "text") { const lang = item.language || "plaintext"; + const text = item.content || ""; const head = document.createElement("div"); head.className = "item-card-head"; head.innerHTML = `
text · ${lang}
`; - head.appendChild( + const actions = document.createElement("div"); + actions.className = "item-card-actions"; + actions.appendChild(makeCopyBtn(() => text)); + actions.appendChild( makeDownloadBtn(() => { - downloadBlob( - `wrapped-${lang}.txt`, - new Blob([item.content || ""], { type: "text/plain" }) - ); + downloadBlob(`wrapped-${lang}.txt`, new Blob([text], { type: "text/plain" })); }) ); + head.appendChild(actions); card.appendChild(head); const pre = document.createElement("pre"); + pre.className = "item-text-pre"; const code = document.createElement("code"); pre.appendChild(code); card.appendChild(pre); - window.WrappedHighlight.highlightElement(code, item.content || "", lang); + window.WrappedHighlight.highlightElement(code, text, lang); } else if (item.type === "file") { const bytes = window.WrappedCrypto.base64ToBytes(item.data_b64); const blob = new Blob([bytes], { type: item.mime || "application/octet-stream" });