|
@@ -9,6 +9,9 @@
|
|
|
<link rel="icon" type="image/svg+xml" href="/static/favicon.svg">
|
|
<link rel="icon" type="image/svg+xml" href="/static/favicon.svg">
|
|
|
|
|
|
|
|
<link rel="stylesheet" href="/static/css/styles.css">
|
|
<link rel="stylesheet" href="/static/css/styles.css">
|
|
|
|
|
+ <!-- Fancybox CSS and JS -->
|
|
|
|
|
+ <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@fancyapps/ui@5.0/dist/fancybox/fancybox.css">
|
|
|
|
|
+ <script src="https://cdn.jsdelivr.net/npm/@fancyapps/ui@5.0/dist/fancybox/fancybox.umd.js"></script>
|
|
|
<script src="https://cdn.tailwindcss.com"></script>
|
|
<script src="https://cdn.tailwindcss.com"></script>
|
|
|
<script>
|
|
<script>
|
|
|
(function() {
|
|
(function() {
|
|
@@ -99,11 +102,19 @@
|
|
|
<div class="bg-white dark:bg-gray-800 rounded-lg shadow-md p-6 transition-colors duration-300">
|
|
<div class="bg-white dark:bg-gray-800 rounded-lg shadow-md p-6 transition-colors duration-300">
|
|
|
<div class="flex items-center justify-between mb-4">
|
|
<div class="flex items-center justify-between mb-4">
|
|
|
<h3 class="text-lg font-semibold text-gray-900 dark:text-white">File Preview</h3>
|
|
<h3 class="text-lg font-semibold text-gray-900 dark:text-white">File Preview</h3>
|
|
|
- <span class="text-sm text-gray-500 dark:text-gray-400">Text file preview</span>
|
|
|
|
|
|
|
+ <div class="flex items-center space-x-2">
|
|
|
|
|
+ <button onclick="copyTextToClipboard()" class="inline-flex items-center px-3 py-1 text-xs font-medium text-white bg-blue-600 hover:bg-blue-700 rounded-md transition-colors duration-200">
|
|
|
|
|
+ <svg class="w-3 h-3 mr-1" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
|
|
|
+ <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M8 16H6a2 2 0 01-2-2V6a2 2 0 012-2h8a2 2 0 012 2v2m-6 12h8a2 2 0 002-2v-8a2 2 0 00-2-2h-8a2 2 0 00-2 2v8a2 2 0 002 2z"></path>
|
|
|
|
|
+ </svg>
|
|
|
|
|
+ Copy Text
|
|
|
|
|
+ </button>
|
|
|
|
|
+ <span class="text-sm text-gray-500 dark:text-gray-400">Text file preview</span>
|
|
|
|
|
+ </div>
|
|
|
</div>
|
|
</div>
|
|
|
|
|
|
|
|
<div class="bg-gray-50 dark:bg-gray-900 rounded-lg p-4 overflow-auto max-h-96">
|
|
<div class="bg-gray-50 dark:bg-gray-900 rounded-lg p-4 overflow-auto max-h-96">
|
|
|
- <pre class="text-sm text-gray-800 dark:text-gray-200 whitespace-pre-wrap font-mono">{{ file_content }}</pre>
|
|
|
|
|
|
|
+ <pre id="file-content" class="text-sm text-gray-800 dark:text-gray-200 whitespace-pre-wrap font-mono">{{ file_content }}</pre>
|
|
|
</div>
|
|
</div>
|
|
|
|
|
|
|
|
<div class="mt-4 text-xs text-gray-500 dark:text-gray-400">
|
|
<div class="mt-4 text-xs text-gray-500 dark:text-gray-400">
|
|
@@ -120,6 +131,66 @@
|
|
|
<p class="text-gray-600 dark:text-gray-400">This text file couldn't be previewed. Please download it to view the content.</p>
|
|
<p class="text-gray-600 dark:text-gray-400">This text file couldn't be previewed. Please download it to view the content.</p>
|
|
|
</div>
|
|
</div>
|
|
|
</div>
|
|
</div>
|
|
|
|
|
+ <!-- Media File Preview -->
|
|
|
|
|
+ {% elif is_media_file %}
|
|
|
|
|
+ <div class="bg-white dark:bg-gray-800 rounded-lg shadow-md p-6 transition-colors duration-300">
|
|
|
|
|
+ <div class="flex items-center justify-between mb-4">
|
|
|
|
|
+ <h3 class="text-lg font-semibold text-gray-900 dark:text-white">Media Preview</h3>
|
|
|
|
|
+ <span class="text-sm text-gray-500 dark:text-gray-400">{{ media_type|title }} file preview</span>
|
|
|
|
|
+ </div>
|
|
|
|
|
+
|
|
|
|
|
+ <div class="bg-gray-50 dark:bg-gray-900 rounded-lg p-4 flex justify-center">
|
|
|
|
|
+ {% if media_type == 'image' %}
|
|
|
|
|
+ <div class="relative group">
|
|
|
|
|
+ <a href="/preview/{{ file_id }}" data-fancybox="gallery" data-caption="{{ filename }}">
|
|
|
|
|
+ <img src="/preview/{{ file_id }}" alt="{{ filename }}" class="max-w-full max-h-96 rounded-lg shadow-sm cursor-pointer transition-transform duration-200 hover:scale-105" loading="lazy">
|
|
|
|
|
+ </a>
|
|
|
|
|
+ <div class="mt-3 flex flex-col items-center space-y-2">
|
|
|
|
|
+ <p class="text-xs text-gray-500 dark:text-gray-400">Click to view with zoom, fullscreen & advanced controls</p>
|
|
|
|
|
+ <div class="flex space-x-2">
|
|
|
|
|
+ <button onclick="downloadImage('{{ file_id }}', '{{ filename }}')" class="inline-flex items-center px-2 py-1 text-xs font-medium text-white bg-green-600 hover:bg-green-700 rounded transition-colors duration-200">
|
|
|
|
|
+ <svg class="w-3 h-3 mr-1" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
|
|
|
+ <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 10v6m0 0l-3-3m3 3l3-3M3 17V7a2 2 0 012-2h6l2 2h6a2 2 0 012 2v10a2 2 0 01-2 2H5a2 2 0 01-2-2z"></path>
|
|
|
|
|
+ </svg>
|
|
|
|
|
+ Save Image
|
|
|
|
|
+ </button>
|
|
|
|
|
+ <button onclick="showImageInfo('{{ filename }}', '{{ file_size }}', '{{ mime_type }}')" class="inline-flex items-center px-2 py-1 text-xs font-medium text-white bg-blue-600 hover:bg-blue-700 rounded transition-colors duration-200">
|
|
|
|
|
+ <svg class="w-3 h-3 mr-1" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
|
|
|
+ <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M13 16h-1v-4h-1m1-4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z"></path>
|
|
|
|
|
+ </svg>
|
|
|
|
|
+ Info
|
|
|
|
|
+ </button>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ {% elif media_type == 'video' %}
|
|
|
|
|
+ <video controls class="max-w-full max-h-96 rounded-lg shadow-sm">
|
|
|
|
|
+ <source src="/preview/{{ file_id }}" type="{{ mime_type }}">
|
|
|
|
|
+ Your browser does not support the video tag.
|
|
|
|
|
+ </video>
|
|
|
|
|
+ {% elif media_type == 'audio' %}
|
|
|
|
|
+ <div class="w-full max-w-md">
|
|
|
|
|
+ <div class="flex items-center space-x-4 mb-4">
|
|
|
|
|
+ <svg class="w-12 h-12 text-blue-500" fill="currentColor" viewBox="0 0 20 20">
|
|
|
|
|
+ <path fill-rule="evenodd" d="M9.383 3.076A1 1 0 0110 4v12a1 1 0 01-1.707.707L4.586 13H2a1 1 0 01-1-1V8a1 1 0 011-1h2.586l3.707-3.707a1 1 0 011.09-.217zM15.657 6.343a1 1 0 011.414 0A9.972 9.972 0 0119 12a9.972 9.972 0 01-1.929 5.657 1 1 0 11-1.414-1.414A7.971 7.971 0 0017 12c0-2.21-.895-4.21-2.343-5.657a1 1 0 010-1.414zm-2.829 2.828a1 1 0 011.415 0A5.983 5.983 0 0115 12a5.983 5.983 0 01-.757 2.829 1 1 0 01-1.415-1.415A3.987 3.987 0 0013 12a3.987 3.987 0 00-.172-1.414 1 1 0 010-1.415z" clip-rule="evenodd"></path>
|
|
|
|
|
+ </svg>
|
|
|
|
|
+ <div>
|
|
|
|
|
+ <h4 class="font-medium text-gray-900 dark:text-white">{{ filename }}</h4>
|
|
|
|
|
+ <p class="text-sm text-gray-500 dark:text-gray-400">Audio file</p>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ <audio controls class="w-full">
|
|
|
|
|
+ <source src="/preview/{{ file_id }}" type="{{ mime_type }}">
|
|
|
|
|
+ Your browser does not support the audio tag.
|
|
|
|
|
+ </audio>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ {% endif %}
|
|
|
|
|
+ </div>
|
|
|
|
|
+
|
|
|
|
|
+ <div class="mt-4 text-xs text-gray-500 dark:text-gray-400">
|
|
|
|
|
+ <p>Media preview is displayed above. Download the file for full quality or offline viewing.</p>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ </div>
|
|
|
<!-- Archive File Preview -->
|
|
<!-- Archive File Preview -->
|
|
|
{% elif is_archive_file and archive_contents %}
|
|
{% elif is_archive_file and archive_contents %}
|
|
|
<div class="bg-white dark:bg-gray-800 rounded-lg shadow-md p-6 transition-colors duration-300">
|
|
<div class="bg-white dark:bg-gray-800 rounded-lg shadow-md p-6 transition-colors duration-300">
|
|
@@ -241,11 +312,164 @@
|
|
|
}, 3000);
|
|
}, 3000);
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+ // Copy text to clipboard
|
|
|
|
|
+ function copyTextToClipboard() {
|
|
|
|
|
+ const fileContent = document.getElementById('file-content');
|
|
|
|
|
+ if (!fileContent) {
|
|
|
|
|
+ showToast('No content to copy', 'error');
|
|
|
|
|
+ return;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ const textToCopy = fileContent.textContent || fileContent.innerText;
|
|
|
|
|
+
|
|
|
|
|
+ if (navigator.clipboard && window.isSecureContext) {
|
|
|
|
|
+ // Use modern clipboard API
|
|
|
|
|
+ navigator.clipboard.writeText(textToCopy).then(() => {
|
|
|
|
|
+ showToast('Text copied to clipboard!', 'success');
|
|
|
|
|
+ }).catch(err => {
|
|
|
|
|
+ console.error('Failed to copy text: ', err);
|
|
|
|
|
+ fallbackCopyTextToClipboard(textToCopy);
|
|
|
|
|
+ });
|
|
|
|
|
+ } else {
|
|
|
|
|
+ // Fallback for older browsers
|
|
|
|
|
+ fallbackCopyTextToClipboard(textToCopy);
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ function fallbackCopyTextToClipboard(text) {
|
|
|
|
|
+ const textArea = document.createElement('textarea');
|
|
|
|
|
+ textArea.value = text;
|
|
|
|
|
+ textArea.style.position = 'fixed';
|
|
|
|
|
+ textArea.style.left = '-999999px';
|
|
|
|
|
+ textArea.style.top = '-999999px';
|
|
|
|
|
+ document.body.appendChild(textArea);
|
|
|
|
|
+ textArea.focus();
|
|
|
|
|
+ textArea.select();
|
|
|
|
|
+
|
|
|
|
|
+ try {
|
|
|
|
|
+ const successful = document.execCommand('copy');
|
|
|
|
|
+ if (successful) {
|
|
|
|
|
+ showToast('Text copied to clipboard!', 'success');
|
|
|
|
|
+ } else {
|
|
|
|
|
+ showToast('Failed to copy text', 'error');
|
|
|
|
|
+ }
|
|
|
|
|
+ } catch (err) {
|
|
|
|
|
+ console.error('Fallback: Could not copy text: ', err);
|
|
|
|
|
+ showToast('Failed to copy text', 'error');
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ document.body.removeChild(textArea);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // Image functions
|
|
|
|
|
+ function downloadImage(fileId, filename) {
|
|
|
|
|
+ const link = document.createElement('a');
|
|
|
|
|
+ link.href = `/download/${fileId}/direct`;
|
|
|
|
|
+ link.download = filename;
|
|
|
|
|
+ document.body.appendChild(link);
|
|
|
|
|
+ link.click();
|
|
|
|
|
+ document.body.removeChild(link);
|
|
|
|
|
+ showToast('Image download started!', 'success');
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ function showImageInfo(filename, fileSize, mimeType) {
|
|
|
|
|
+ const sizeInKB = Math.round(fileSize / 1024);
|
|
|
|
|
+ const sizeInMB = (fileSize / (1024 * 1024)).toFixed(2);
|
|
|
|
|
+ const sizeText = fileSize > 1024 * 1024 ? `${sizeInMB} MB` : `${sizeInKB} KB`;
|
|
|
|
|
+
|
|
|
|
|
+ const infoMessage = `File: ${filename}\nSize: ${sizeText}\nType: ${mimeType}`;
|
|
|
|
|
+
|
|
|
|
|
+ // Create a custom modal for image info
|
|
|
|
|
+ const modal = document.createElement('div');
|
|
|
|
|
+ modal.className = 'fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center z-50';
|
|
|
|
|
+ modal.innerHTML = `
|
|
|
|
|
+ <div class="bg-white dark:bg-gray-800 rounded-lg p-6 max-w-md mx-4 shadow-xl">
|
|
|
|
|
+ <div class="flex items-center justify-between mb-4">
|
|
|
|
|
+ <h3 class="text-lg font-semibold text-gray-900 dark:text-white">Image Information</h3>
|
|
|
|
|
+ <button onclick="this.closest('.fixed').remove()" class="text-gray-400 hover:text-gray-600 dark:hover:text-gray-300">
|
|
|
|
|
+ <svg class="w-6 h-6" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
|
|
|
+ <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12"></path>
|
|
|
|
|
+ </svg>
|
|
|
|
|
+ </button>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ <div class="space-y-3">
|
|
|
|
|
+ <div>
|
|
|
|
|
+ <span class="text-sm font-medium text-gray-500 dark:text-gray-400">Filename:</span>
|
|
|
|
|
+ <p class="text-gray-900 dark:text-white break-all">${filename}</p>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ <div>
|
|
|
|
|
+ <span class="text-sm font-medium text-gray-500 dark:text-gray-400">File Size:</span>
|
|
|
|
|
+ <p class="text-gray-900 dark:text-white">${sizeText}</p>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ <div>
|
|
|
|
|
+ <span class="text-sm font-medium text-gray-500 dark:text-gray-400">MIME Type:</span>
|
|
|
|
|
+ <p class="text-gray-900 dark:text-white">${mimeType}</p>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ <div class="mt-6 flex justify-end">
|
|
|
|
|
+ <button onclick="this.closest('.fixed').remove()" class="px-4 py-2 bg-blue-600 text-white rounded hover:bg-blue-700 transition-colors duration-200">
|
|
|
|
|
+ Close
|
|
|
|
|
+ </button>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ `;
|
|
|
|
|
+
|
|
|
|
|
+ document.body.appendChild(modal);
|
|
|
|
|
+
|
|
|
|
|
+ // Close modal when clicking outside
|
|
|
|
|
+ modal.addEventListener('click', function(e) {
|
|
|
|
|
+ if (e.target === modal) {
|
|
|
|
|
+ modal.remove();
|
|
|
|
|
+ }
|
|
|
|
|
+ });
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
// Initialize
|
|
// Initialize
|
|
|
document.addEventListener('DOMContentLoaded', function() {
|
|
document.addEventListener('DOMContentLoaded', function() {
|
|
|
initTheme();
|
|
initTheme();
|
|
|
document.getElementById('themeToggle').addEventListener('click', toggleTheme);
|
|
document.getElementById('themeToggle').addEventListener('click', toggleTheme);
|
|
|
});
|
|
});
|
|
|
</script>
|
|
</script>
|
|
|
|
|
+
|
|
|
|
|
+ <!-- Fancybox Configuration -->
|
|
|
|
|
+ <script>
|
|
|
|
|
+ // Initialize Fancybox
|
|
|
|
|
+ document.addEventListener('DOMContentLoaded', function() {
|
|
|
|
|
+ if (typeof Fancybox !== 'undefined') {
|
|
|
|
|
+ Fancybox.bind('[data-fancybox]', {
|
|
|
|
|
+ // UI options
|
|
|
|
|
+ animated: true,
|
|
|
|
|
+ showClass: 'f-fadeIn',
|
|
|
|
|
+ hideClass: 'f-fadeOut',
|
|
|
|
|
+
|
|
|
|
|
+ // Toolbar options
|
|
|
|
|
+ Toolbar: {
|
|
|
|
|
+ display: {
|
|
|
|
|
+ left: ['infobar'],
|
|
|
|
|
+ middle: ['zoomIn', 'zoomOut', 'toggle1to1', 'rotateCCW', 'rotateCW', 'flipX', 'flipY'],
|
|
|
|
|
+ right: ['slideshow', 'fullscreen', 'thumbs', 'close']
|
|
|
|
|
+ }
|
|
|
|
|
+ },
|
|
|
|
|
+
|
|
|
|
|
+ // Image options
|
|
|
|
|
+ Images: {
|
|
|
|
|
+ zoom: true,
|
|
|
|
|
+ wheel: 'zoom'
|
|
|
|
|
+ },
|
|
|
|
|
+
|
|
|
|
|
+ // Thumbs options
|
|
|
|
|
+ Thumbs: {
|
|
|
|
|
+ showOnStart: false
|
|
|
|
|
+ },
|
|
|
|
|
+
|
|
|
|
|
+ // Slideshow options
|
|
|
|
|
+ Slideshow: {
|
|
|
|
|
+ speed: 3000
|
|
|
|
|
+ }
|
|
|
|
|
+ });
|
|
|
|
|
+ console.log('Fancybox initialized successfully');
|
|
|
|
|
+ }
|
|
|
|
|
+ });
|
|
|
|
|
+ </script>
|
|
|
</body>
|
|
</body>
|
|
|
</html>
|
|
</html>
|