From 128d294ea102df6cfc4df38a90de2d80f35ddae8 Mon Sep 17 00:00:00 2001 From: Gregor Lohaus Date: Wed, 25 Feb 2026 19:26:28 +0100 Subject: [PATCH] file previews --- Backend/src/main/resources/static/download.js | 100 ++++++++++++++---- Backend/src/main/resources/static/style.css | 34 ++++++ 2 files changed, 116 insertions(+), 18 deletions(-) diff --git a/Backend/src/main/resources/static/download.js b/Backend/src/main/resources/static/download.js index e93be04..2e23859 100644 --- a/Backend/src/main/resources/static/download.js +++ b/Backend/src/main/resources/static/download.js @@ -25,22 +25,7 @@ if (!fragment) { setStatus('Decrypting\u2026'); const plaintext = await decryptFile(await response.arrayBuffer(), key); - const url = URL.createObjectURL(new Blob([plaintext])); - const a = document.createElement('a'); - a.href = url; - a.download = filename; - - htmx.swap(htmx.find('#download-state'), ` -
🔒
-
${filename}
- `, - { swapStyle: 'innerHTML' }); - - htmx.on(htmx.find('#download-btn'), 'click', () => { - a.click(); - URL.revokeObjectURL(url); - showSuccess(filename); - }); + showPreview(filename, plaintext); } } catch (err) { showError('Decryption failed: ' + err.message); @@ -51,10 +36,89 @@ function setStatus(msg) { if (el) el.textContent = msg; } +function escapeHtml(str) { + return str.replace(/&/g, '&').replace(//g, '>').replace(/"/g, '"'); +} + +function getMimeType(filename) { + const ext = filename.split('.').pop().toLowerCase(); + const types = { + jpg: 'image/jpeg', jpeg: 'image/jpeg', png: 'image/png', gif: 'image/gif', + webp: 'image/webp', svg: 'image/svg+xml', bmp: 'image/bmp', ico: 'image/x-icon', + mp4: 'video/mp4', webm: 'video/webm', ogv: 'video/ogg', mov: 'video/quicktime', + mp3: 'audio/mpeg', wav: 'audio/wav', ogg: 'audio/ogg', flac: 'audio/flac', + aac: 'audio/aac', m4a: 'audio/mp4', + txt: 'text/plain', md: 'text/plain', csv: 'text/csv', log: 'text/plain', + json: 'application/json', xml: 'text/xml', + js: 'text/plain', ts: 'text/plain', py: 'text/plain', java: 'text/plain', + c: 'text/plain', cpp: 'text/plain', h: 'text/plain', sh: 'text/plain', + yaml: 'text/plain', yml: 'text/plain', toml: 'text/plain', ini: 'text/plain', + pdf: 'application/pdf', + }; + return types[ext] || 'application/octet-stream'; +} + +function showPreview(filename, plaintext) { + const mimeType = getMimeType(filename); + const blob = new Blob([plaintext], { type: mimeType }); + const url = URL.createObjectURL(blob); + window.addEventListener('unload', () => URL.revokeObjectURL(url)); + + const name = escapeHtml(filename); + const downloadBtn = ``; + let previewHtml; + + if (mimeType.startsWith('image/')) { + previewHtml = ` + ${name} +
${name}
+ ${downloadBtn}`; + } else if (mimeType.startsWith('video/')) { + previewHtml = ` + +
${name}
+ ${downloadBtn}`; + } else if (mimeType.startsWith('audio/')) { + previewHtml = ` +
🎵
+
${name}
+ + ${downloadBtn}`; + } else if (mimeType === 'application/pdf') { + previewHtml = ` +
${name}
+ + ${downloadBtn}`; + } else if (mimeType.startsWith('text/') || mimeType === 'application/json') { + const text = new TextDecoder().decode(plaintext); + const preview = text.length > 10000 ? text.slice(0, 10000) + '\n\u2026' : text; + previewHtml = ` +
${name}
+
${escapeHtml(preview)}
+ ${downloadBtn}`; + } else { + previewHtml = ` +
📄
+
${name}
+ ${downloadBtn}`; + } + + htmx.swap(htmx.find('#download-state'), previewHtml, { swapStyle: 'innerHTML' }); + + htmx.on(htmx.find('#download-btn'), 'click', () => { + const a = document.createElement('a'); + a.href = url; + a.download = filename; + a.click(); + showSuccess(filename); + }); +} + function showSuccess(filename) { + const name = escapeHtml(filename); htmx.swap(htmx.find('#download-state'), `
-
${filename}
+
${name}
Your download has started.
Send a file`, { swapStyle: 'innerHTML' }); @@ -63,7 +127,7 @@ function showSuccess(filename) { function showError(msg) { htmx.swap(htmx.find('#download-state'), `
-
${msg}
+
${escapeHtml(msg)}
Go home`, { swapStyle: 'innerHTML' }); } diff --git a/Backend/src/main/resources/static/style.css b/Backend/src/main/resources/static/style.css index 0ffbbc0..5557647 100644 --- a/Backend/src/main/resources/static/style.css +++ b/Backend/src/main/resources/static/style.css @@ -65,3 +65,37 @@ footer, footer a { footer a:hover { color: #8b949e; } + +.preview-media { + max-width: 100%; + border-radius: 8px; + display: block; + margin-left: auto; + margin-right: auto; +} + +.preview-audio { + width: 100%; +} + +.preview-pdf { + width: 100%; + height: 420px; + border: none; + border-radius: 8px; + display: block; +} + +.preview-text { + text-align: left; + max-height: 320px; + overflow: auto; + background: #0d1117; + padding: 1rem; + border-radius: 8px; + font-size: 0.78rem; + color: #e6edf3; + border: 1px solid #30363d; + white-space: pre-wrap; + word-break: break-word; +}