docker.dateiverwaltung/frontend/templates/preview.html
data 21e1ffe9e2 Version 1.1: Dateimanager mit 3-Panel Layout
Neue Features:
- 3-Panel Dateimanager (Ordnerbaum, Dateiliste, Vorschau)
- Separates Vorschau-Fenster für zweiten Monitor
- Resize-Handles für flexible Panel-Größen (horizontal & vertikal)
- Vorschau-Panel ausblendbar wenn externes Fenster aktiv
- Natürliche Sortierung (Sonderzeichen → Zahlen → Buchstaben)
- PDF-Vorschau mit Fit-to-Page
- Email-Attachment Abruf erweitert

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-02 12:51:40 +01:00

376 lines
12 KiB
HTML

<!DOCTYPE html>
<html lang="de">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Vorschau - Dateiverwaltung</title>
<link rel="stylesheet" href="/static/css/style.css">
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
background: var(--bg);
color: var(--text);
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
height: 100vh;
overflow: hidden;
display: flex;
flex-direction: column;
}
/* Header */
.preview-header {
display: flex;
justify-content: space-between;
align-items: center;
padding: 0.5rem 1rem;
background: var(--bg-secondary);
border-bottom: 1px solid var(--border);
flex-shrink: 0;
}
.preview-header h1 {
font-size: 0.9rem;
font-weight: 500;
display: flex;
align-items: center;
gap: 0.5rem;
}
.preview-header .filename {
font-weight: normal;
color: var(--text-secondary);
max-width: 400px;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.preview-header .controls {
display: flex;
gap: 0.5rem;
align-items: center;
}
.status-indicator {
width: 10px;
height: 10px;
border-radius: 50%;
background: var(--danger);
}
.status-indicator.connected {
background: var(--success);
}
.btn {
padding: 0.375rem 0.75rem;
font-size: 0.8rem;
border: 1px solid var(--border);
border-radius: var(--radius);
background: var(--bg);
color: var(--text);
cursor: pointer;
}
.btn:hover {
background: var(--bg-tertiary);
}
/* Preview Container */
.preview-container {
flex: 1;
overflow: auto;
display: flex;
align-items: center;
justify-content: center;
background: var(--bg-tertiary);
padding: 1rem;
}
.preview-placeholder {
text-align: center;
color: var(--text-secondary);
}
.preview-placeholder .icon {
font-size: 4rem;
margin-bottom: 1rem;
}
.preview-placeholder p {
font-size: 1rem;
}
.preview-placeholder .hint {
font-size: 0.85rem;
margin-top: 0.5rem;
opacity: 0.7;
}
/* PDF Preview */
.preview-pdf {
width: 100%;
height: 100%;
border: none;
background: white;
}
/* Image Preview */
.preview-image {
max-width: 100%;
max-height: 100%;
object-fit: contain;
border-radius: var(--radius);
box-shadow: var(--shadow);
}
/* Text Preview */
.preview-text {
width: 100%;
height: 100%;
background: var(--bg);
color: var(--text);
padding: 1rem;
font-family: 'Consolas', 'Monaco', monospace;
font-size: 0.85rem;
overflow: auto;
border-radius: var(--radius);
white-space: pre-wrap;
word-wrap: break-word;
}
/* No Preview */
.preview-unavailable {
text-align: center;
padding: 2rem;
}
.preview-unavailable .file-type-icon {
font-size: 5rem;
margin-bottom: 1rem;
}
.preview-unavailable p {
color: var(--text-secondary);
margin-bottom: 1rem;
}
.preview-unavailable .btn {
margin-top: 1rem;
}
/* Theme Select */
.theme-select {
padding: 0.25rem 0.5rem;
font-size: 0.8rem;
border: 1px solid var(--border);
border-radius: var(--radius);
background: var(--bg);
color: var(--text);
}
</style>
</head>
<body>
<header class="preview-header">
<h1>
👁 Vorschau
<span id="filename" class="filename">Keine Datei ausgewählt</span>
</h1>
<div class="controls">
<div id="status" class="status-indicator" title="Nicht verbunden"></div>
<select id="theme-select" class="theme-select" onchange="wechsleTheme(this.value)">
<option value="auto">🎨 Auto</option>
<option value="dark">🌙 Dark</option>
<option value="breeze-dark">🌙 Breeze Dark</option>
<option value="breeze-light">☀️ Breeze Light</option>
</select>
<button class="btn" onclick="window.close()">✕ Schließen</button>
</div>
</header>
<div id="preview-container" class="preview-container">
<div class="preview-placeholder">
<div class="icon">📂</div>
<p>Warte auf Dateiauswahl...</p>
<p class="hint">Wähle eine Datei im Hauptfenster aus</p>
</div>
</div>
<script>
// ============ BroadcastChannel für Kommunikation ============
const channel = new BroadcastChannel('dateiverwaltung-preview');
let currentFile = null;
// Status anzeigen
function setConnected(connected) {
const status = document.getElementById('status');
if (connected) {
status.classList.add('connected');
status.title = 'Verbunden mit Hauptfenster';
} else {
status.classList.remove('connected');
status.title = 'Nicht verbunden';
}
}
// Nachricht vom Hauptfenster empfangen
channel.onmessage = (event) => {
const { type, data } = event.data;
switch (type) {
case 'preview':
ladeVorschau(data.path, data.name);
setConnected(true);
break;
case 'clear':
zeigeWartePlaceholder();
break;
case 'ping':
// Bestätigung senden
channel.postMessage({ type: 'pong' });
setConnected(true);
break;
}
};
// Beim Start Ping senden
channel.postMessage({ type: 'preview-window-ready' });
// ============ Theme ============
function ladeTheme() {
const gespeichertesTheme = localStorage.getItem('theme') || 'auto';
wendeThemeAn(gespeichertesTheme);
document.getElementById('theme-select').value = gespeichertesTheme;
}
function wendeThemeAn(theme) {
const html = document.documentElement;
if (theme === 'auto') {
html.removeAttribute('data-theme');
} else {
html.setAttribute('data-theme', theme);
}
}
function wechsleTheme(theme) {
wendeThemeAn(theme);
localStorage.setItem('theme', theme);
}
// ============ Vorschau ============
async function ladeVorschau(pfad, name) {
currentFile = { path: pfad, name: name };
document.getElementById('filename').textContent = name;
document.title = `${name} - Vorschau`;
const container = document.getElementById('preview-container');
const ext = name.split('.').pop().toLowerCase();
// PDF Vorschau - mit fit-to-page für erste Seite
if (ext === 'pdf') {
container.innerHTML = `<iframe class="preview-pdf" src="/api/file/preview?path=${encodeURIComponent(pfad)}&t=${Date.now()}#page=1&view=FitV"></iframe>`;
return;
}
// Bild Vorschau
if (['jpg', 'jpeg', 'png', 'gif', 'bmp', 'webp', 'tiff'].includes(ext)) {
container.innerHTML = `<img class="preview-image" src="/api/file/preview?path=${encodeURIComponent(pfad)}&t=${Date.now()}" alt="${name}">`;
return;
}
// Text-basierte Dateien
if (['txt', 'md', 'log', 'xml', 'json', 'csv', 'html', 'css', 'js'].includes(ext)) {
try {
const response = await fetch(`/api/file/text?path=${encodeURIComponent(pfad)}`);
const result = await response.json();
if (result.content) {
container.innerHTML = `<pre class="preview-text">${escapeHtml(result.content)}</pre>`;
} else {
zeigeKeineVorschau(name, ext);
}
} catch (e) {
zeigeKeineVorschau(name, ext);
}
return;
}
// Keine Vorschau verfügbar
zeigeKeineVorschau(name, ext);
}
function zeigeKeineVorschau(name, ext) {
const icon = getFileIcon(name);
document.getElementById('preview-container').innerHTML = `
<div class="preview-unavailable">
<div class="file-type-icon">${icon}</div>
<p>Keine Vorschau für .${ext} Dateien</p>
<button class="btn" onclick="dateiExternOeffnen()">🔗 Extern öffnen</button>
</div>
`;
}
function zeigeWartePlaceholder() {
document.getElementById('filename').textContent = 'Keine Datei ausgewählt';
document.title = 'Vorschau - Dateiverwaltung';
document.getElementById('preview-container').innerHTML = `
<div class="preview-placeholder">
<div class="icon">📂</div>
<p>Warte auf Dateiauswahl...</p>
<p class="hint">Wähle eine Datei im Hauptfenster aus</p>
</div>
`;
}
function getFileIcon(name) {
const ext = name.split('.').pop().toLowerCase();
const icons = {
'pdf': '📄',
'jpg': '🖼️', 'jpeg': '🖼️', 'png': '🖼️', 'gif': '🖼️', 'bmp': '🖼️', 'tiff': '🖼️', 'webp': '🖼️',
'doc': '📝', 'docx': '📝', 'odt': '📝',
'xls': '📊', 'xlsx': '📊', 'ods': '📊', 'csv': '📊',
'zip': '📦', 'rar': '📦', '7z': '📦', 'tar': '📦', 'gz': '📦',
'txt': '📃', 'md': '📃', 'log': '📃',
'mp3': '🎵', 'wav': '🎵', 'flac': '🎵',
'mp4': '🎬', 'avi': '🎬', 'mkv': '🎬',
'xml': '📋', 'json': '📋', 'html': '📋'
};
return icons[ext] || '📎';
}
function escapeHtml(text) {
const div = document.createElement('div');
div.textContent = text;
return div.innerHTML;
}
function dateiExternOeffnen() {
if (currentFile) {
window.open(`/api/file/download?path=${encodeURIComponent(currentFile.path)}`, '_blank');
}
}
// ============ Init ============
document.addEventListener('DOMContentLoaded', () => {
ladeTheme();
// Falls Pfad als URL-Parameter übergeben wurde
const params = new URLSearchParams(window.location.search);
const path = params.get('path');
if (path) {
const name = path.split('/').pop();
ladeVorschau(path, name);
}
});
// Fenster-Schließen mitteilen
window.addEventListener('beforeunload', () => {
channel.postMessage({ type: 'preview-window-closed' });
});
</script>
</body>
</html>