uploadkro2 / templates /index.html
triflix's picture
Update templates/index.html
cec4e62 verified
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0, viewport-fit=cover">
<title>Minimalist File Uploader</title>
<link href="https://fonts.googleapis.com/css2?family=Noto+Sans:wght@400;500;700&family=Poppins:wght@400;500;600&display=swap" rel="stylesheet" />
<link href="https://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet">
<style>
:root {
/* Core Palette - Nothing OS Inspired */
--primary-dark-ref: #1B1B1D;
--accent-red: #D71921;
--accent-red-hover: #b01017;
--text-on-accent: #FFFFFF;
/* Light Theme */
--bg-main: #F7F7F7;
--surface-primary: #FFFFFF;
--surface-secondary: #ECECEC;
--border-light: #E0E0E0;
--border-medium: #C2C2C2;
--text-primary: var(--primary-dark-ref);
--text-secondary: #666666;
--text-placeholder: #999999;
--text-email-on-light: rgba(27, 27, 29, 0.7);
/* Typography */
--font-main: 'Noto Sans', 'Poppins', sans-serif;
--font-mono: 'ui-monospace', 'SFMono-Regular', Consolas, 'Liberation Mono', Menlo, Courier, monospace;
/* UI Elements */
--radius-sharp: 4px;
--radius-softer: 6px;
--radius-profile-card: 12px;
--shadow-none: none;
--shadow-profile-card: 0 4px 10px rgba(0,0,0,0.1);
--nav-height: 60px;
}
* { box-sizing: border-box; margin: 0; padding: 0; }
body {
font-family: var(--font-main);
background: var(--bg-main);
color: var(--text-primary);
padding-bottom: calc(var(--nav-height) + env(safe-area-inset-bottom));
min-height: 100vh;
display: flex;
flex-direction: column;
overflow-x: hidden;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
main {
flex: 1;
display: flex;
flex-direction: column;
padding: 16px;
}
section {
display: none;
flex: 1;
overflow-y: auto;
animation: fadeIn 0.2s ease-out;
}
section.active { display: flex; flex-direction: column; }
button, .button-like {
background: var(--accent-red);
color: var(--text-on-accent);
border: 1px solid var(--accent-red);
border-radius: var(--radius-sharp);
padding: 10px 18px;
font-weight: 500;
font-size: 0.9rem;
text-transform: uppercase;
letter-spacing: 0.5px;
box-shadow: var(--shadow-none);
transition: background-color 0.15s ease, border-color 0.15s ease;
cursor: pointer;
display: inline-flex;
align-items: center;
justify-content: center;
outline: none;
}
button:hover, .button-like:hover {
background-color: var(--accent-red-hover);
border-color: var(--accent-red-hover);
}
button:focus-visible {
outline: 2px solid var(--accent-red);
outline-offset: 2px;
}
button:disabled {
background-color: var(--surface-secondary);
color: var(--text-placeholder);
border-color: var(--border-light);
cursor: not-allowed;
}
.uploader { display: flex; flex-direction: column; gap: 16px; width: 100%; max-width: 480px; margin: auto; }
.file-input-area { display: flex; flex-direction: column; align-items: center; justify-content: center; padding: 25px 20px; border: 2px dashed var(--border-medium); border-radius: var(--radius-softer); background-color: var(--surface-primary); cursor: pointer; transition: border-color 0.2s, background-color 0.2s; text-align: center; }
.file-input-area:hover, .file-input-area.dragover { border-color: var(--accent-red); background-color: #fff5f5; }
.file-input-area input[type=file] { display: none; }
.file-input-area .material-icons { font-size: 40px; color: var(--text-secondary); margin-bottom: 10px; transition: color 0.2s ease; }
.file-input-area:hover .material-icons { color: var(--accent-red); }
.file-input-label { color: var(--text-primary); font-weight: 500; font-size: 1rem; margin-bottom: 4px; }
.file-input-hint, #fileNameDisplay { font-size: 0.8rem; color: var(--text-secondary); }
#fileNameDisplay { margin-top: 6px; font-weight: 500; color: var(--text-primary); word-break: break-all; font-family: var(--font-mono); }
#uploadBtn { width: 100%; padding: 12px 18px; }
.loader { width: 28px; height: 28px; border: 3px solid var(--surface-secondary); border-top: 3px solid var(--accent-red); border-radius: 50%; animation: spin 0.8s linear infinite; margin: 16px auto; display: none; }
.progress-container { width: 100%; margin-top: 8px; display: none; }
.progress { width: 100%; height: 6px; background: var(--surface-secondary); border-radius: var(--radius-sharp); overflow: hidden; }
.progress-bar { height: 100%; width: 0; background: var(--accent-red); transition: width 0.2s; }
.info { text-align: center; margin-top: 8px; font-size: 0.85em; color: var(--text-secondary); min-height: 1.2em; font-family: var(--font-mono); }
.info.error-message { color: var(--accent-red); font-weight: 500; }
.info.success-message { color: #28a745; font-weight: 500; }
#shareLinkContainer { margin-top: 16px; padding: 12px; background-color: var(--surface-primary); border: 1px solid var(--border-light); border-radius: var(--radius-softer); display: none; }
#shareLinkContainer p { margin-bottom: 8px; font-weight: 500; font-size: 0.9rem; color: var(--text-primary); }
.share-link-display { display: flex; gap: 8px; align-items: center; }
.share-link-display input[type="text"] { flex-grow: 1; padding: 8px 10px; border: 1px solid var(--border-medium); border-radius: var(--radius-sharp); font-size: 0.85em; background-color: var(--bg-main); color: var(--text-primary); font-family: var(--font-mono); outline: none; }
.share-link-display input[type="text"]:focus { border-color: var(--accent-red); }
.share-link-display button { padding: 8px 12px; font-size: 0.8rem; }
.history-list { display: flex; flex-direction: column; gap: 10px; }
.history-item { background: var(--surface-primary); padding: 12px 16px; border-radius: var(--radius-softer); border: 1px solid var(--border-light); display: flex; justify-content: space-between; align-items: center; transition: border-color 0.2s ease; }
.history-item:hover { border-color: var(--border-medium); }
.history-item-info { flex-grow: 1; margin-right: 12px; overflow: hidden; }
.history-item-name { font-weight: 500; font-size: 0.9rem; color: var(--text-primary); margin-bottom: 3px; font-family: var(--font-mono); letter-spacing: 0.2px; word-break: break-all; }
.history-item-url { font-size: 0.75em; color: var(--text-secondary); word-break: break-all; display: block; text-decoration: none; font-family: var(--font-mono); }
.history-item-url:hover { color: var(--accent-red); text-decoration: underline;}
.history-item button { padding: 7px 10px; font-size: 0.75rem; min-width: 80px; }
.profile-container {
display: flex;
flex-direction: column;
align-items: center;
gap: 24px;
padding: 16px 0;
width: 100%;
max-width: 450px;
margin: auto;
}
.profile-header { text-align: center; margin-bottom: 16px; }
.profile-avatar-wrapper { position: relative; width: 120px; height: 120px; margin: 0 auto 20px auto; }
.profile-avatar { width: 100%; height: 100%; border-radius: 50%; object-fit: cover; border: 3px solid var(--surface-primary); box-shadow: 0 2px 4px rgba(0,0,0,0.1); position: relative; z-index: 2; }
.profile-avatar-ring { position: absolute; top: -5px; left: -5px; right: -5px; bottom: -5px; border-radius: 50%; border: 2px dotted var(--accent-red); animation: rotateRing 20s linear infinite; z-index: 1; }
@keyframes rotateRing { from { transform: rotate(0deg); } to { transform: rotate(360deg); } }
.profile-name { font-family: var(--font-mono); font-size: 1.8rem; color: var(--text-primary); margin-bottom: 4px; letter-spacing: 0.5px; }
.profile-email { font-family: var(--font-main); font-size: 0.95rem; color: var(--text-email-on-light); }
.profile-action-card { background-color: var(--surface-primary); border-radius: var(--radius-profile-card); padding: 16px; width: 100%; box-shadow: var(--shadow-profile-card); text-align: center; }
.portfolio-button { display: inline-flex; align-items: center; justify-content: center; gap: 8px; background-color: var(--accent-red); color: var(--text-on-accent); font-family: var(--font-main); font-size: 1rem; font-weight: 500; padding: 12px 24px; border-radius: var(--radius-sharp); text-decoration: none; text-transform: uppercase; letter-spacing: 0.5px; transition: background-color 0.15s ease; width: auto; min-width: 200px; }
.portfolio-button:hover { background-color: var(--accent-red-hover); }
.portfolio-button .material-icons { font-size: 20px; }
.profile-social-links { display: grid; grid-template-columns: repeat(auto-fit, minmax(100px, 1fr)); gap: 16px; width: 100%; padding: 0 8px; }
.social-link-card { background-color: var(--surface-primary); border-radius: var(--radius-profile-card); padding: 16px; box-shadow: var(--shadow-profile-card); display: flex; flex-direction: column; align-items: center; justify-content: center; text-decoration: none; color: var(--text-primary); gap: 8px; transition: transform 0.2s ease, box-shadow 0.2s ease, color 0.2s ease; min-height: 100px; }
.social-link-card:hover { transform: translateY(-3px) scale(1.03); box-shadow: 0 6px 12px rgba(0,0,0,0.12); color: var(--accent-red); }
.social-link-card .social-icon {
width: 28px; /* SVG size */
height: 28px;
fill: currentColor; /* Inherits color from .social-link-card for hover effect */
transition: fill 0.2s ease;
}
.social-link-card span { font-family: var(--font-main); font-size: 0.8rem; font-weight: 500; text-align: center; }
.bottom-nav { position: fixed; bottom: 0; left: 0; right: 0; width: 100%; background: var(--surface-primary); display: flex; justify-content: space-around; align-items: stretch; border-top: 1px solid var(--border-light); padding-bottom: env(safe-area-inset-bottom); height: calc(var(--nav-height) + env(safe-area-inset-bottom)); box-shadow: var(--shadow-none); }
.nav-item { flex: 1; display: flex; flex-direction: column; justify-content: center; align-items: center; font-size: 0.7rem; color: var(--text-secondary); cursor: pointer; transition: color 0.15s ease; padding-top: 5px; padding-bottom: 3px; position: relative; }
.nav-item:hover { color: var(--text-primary); }
.nav-item.active { color: var(--accent-red); font-weight: 700; }
.nav-item.active::after { content: ''; position: absolute; bottom: 5px; left: 50%; transform: translateX(-50%); width: 5px; height: 5px; background-color: var(--accent-red); border-radius: 50%; }
.nav-item .material-icons { font-size: 24px; margin-bottom: 2px; }
@keyframes spin { from { transform: rotate(0deg); } to { transform: rotate(360deg); } }
@keyframes fadeIn { from { opacity: 0; transform: translateY(5px); } to { opacity: 1; transform: translateY(0); } }
</style>
</head>
<body>
<main>
<section id="uploadSection" class="active">
<div class="uploader">
<label for="fileInput" class="file-input-area">
<i class="material-icons">upload_file</i>
<span class="file-input-label">Choose File</span>
<input type="file" id="fileInput" />
<span class="file-input-hint">Max. 100MB</span>
<div id="fileNameDisplay">No file selected</div>
</label>
<button id="uploadBtn" disabled>Upload</button>
<div class="loader" id="loader"></div>
<div class="progress-container" id="progressContainer">
<div class="progress"><div id="progressBar" class="progress-bar"></div></div>
</div>
<div class="info" id="uploadInfo"></div>
<div id="shareLinkContainer"></div>
</div>
</section>
<section id="historySection">
<div class="history-list" id="historyList"></div>
</section>
<section id="profileSection">
<div class="profile-container">
<div class="profile-header">
<div class="profile-avatar-wrapper">
<div class="profile-avatar-ring"></div>
<img src="https://www.adityadevarshi.online/static/media/hrishi2.26eee295fbf7bdd9fb2d.png" alt="Aditya Devarshi" class="profile-avatar">
</div>
<h3 class="profile-name">Aditya Devarshi</h3>
<p class="profile-email">devarshia5@gmail.com</p>
</div>
<div class="profile-action-card portfolio-cta-card">
<a href="https://www.adityadevarshi.online/" target="_blank" rel="noopener noreferrer" class="portfolio-button">
<i class="material-icons">visibility</i>
<span>View My Work</span>
</a>
</div>
<div class="profile-social-links">
<a href="https://github.com/devarshiadi/" target="_blank" rel="noopener noreferrer" class="social-link-card" aria-label="GitHub Profile">
<svg class="social-icon" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><path d="M12 .297c-6.63 0-12 5.373-12 12 0 5.303 3.438 9.8 8.205 11.385.6.113.82-.258.82-.577 0-.285-.01-1.04-.015-2.04-3.338.724-4.042-1.61-4.042-1.61C4.422 18.07 3.633 17.7 3.633 17.7c-1.087-.744.084-.729.084-.729 1.205.084 1.838 1.236 1.838 1.236 1.07 1.835 2.809 1.305 3.495.998.108-.776.417-1.305.76-1.605-2.665-.3-5.466-1.332-5.466-5.93 0-1.31.465-2.38 1.235-3.22-.135-.303-.54-1.523.105-3.176 0 0 1.005-.322 3.3 1.23.96-.267 1.98-.399 3-.405 1.02.006 2.04.138 3 .405 2.28-1.552 3.285-1.23 3.285-1.23.645 1.653.24 2.873.12 3.176.765.84 1.23 1.91 1.23 3.22 0 4.61-2.805 5.625-5.475 5.92.42.36.81 1.096.81 2.22 0 1.606-.015 2.896-.015 3.286 0 .315.21.69.825.57C20.565 22.092 24 17.592 24 12.297c0-6.627-5.373-12-12-12"/></svg>
<span>GitHub</span>
</a>
<a href="https://www.linkedin.com/in/aditya-devarshi/" target="_blank" rel="noopener noreferrer" class="social-link-card" aria-label="LinkedIn Profile">
<svg class="social-icon" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><path d="M19 0h-14c-2.761 0-5 2.239-5 5v14c0 2.761 2.239 5 5 5h14c2.762 0 5-2.239 5-5v-14c0-2.761-2.238-5-5-5zm-11 19h-3v-11h3v11zm-1.5-12.268c-.966 0-1.75-.79-1.75-1.764s.784-1.764 1.75-1.764 1.75.79 1.75 1.764-.783 1.764-1.75 1.764zm13.5 12.268h-3v-5.604c0-3.368-4-3.113-4 0v5.604h-3v-11h3v1.765c1.396-2.586 7-2.777 7 2.476v6.759z"/></svg>
<span>LinkedIn</span>
</a>
<a href="https://www.instagram.com/curseofwitcher/" target="_blank" rel="noopener noreferrer" class="social-link-card" aria-label="Instagram Profile">
<svg class="social-icon" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><path d="M12 2.163c3.204 0 3.584.012 4.85.07 3.252.148 4.771 1.691 4.919 4.919.058 1.265.069 1.645.069 4.849 0 3.205-.012 3.584-.069 4.849-.149 3.225-1.664 4.771-4.919 4.919-1.266.058-1.644.07-4.85.07-3.204 0-3.584-.012-4.849-.07-3.26-.149-4.771-1.699-4.919-4.92-.058-1.265-.07-1.644-.07-4.849 0-3.204.013-3.583.07-4.849.149-3.227 1.664-4.771 4.919-4.919 1.266-.057 1.645-.069 4.849-.069zm0-2.163c-3.259 0-3.667.014-4.947.072-4.358.2-6.78 2.618-6.98 6.98-.059 1.281-.073 1.689-.073 4.948 0 3.259.014 3.668.072 4.948.2 4.358 2.618 6.78 6.98 6.98 1.281.058 1.689.072 4.948.072 3.259 0 3.668-.014 4.948-.072 4.354-.2 6.782-2.618 6.979-6.98.059-1.28.073-1.689.073-4.948 0-3.259-.014-3.667-.072-4.947-.196-4.354-2.617-6.78-6.979-6.98-1.281-.059-1.69-.073-4.949-.073zm0 5.838c-3.403 0-6.162 2.759-6.162 6.162s2.759 6.163 6.162 6.163 6.162-2.759 6.162-6.163c.001-3.403-2.758-6.162-6.162-6.162zm0 10.162c-2.209 0-4-1.79-4-4 0-2.209 1.791-4 4-4s4 1.791 4 4c0 2.21-1.791 4-4 4zm6.406-11.845c-.796 0-1.441.645-1.441 1.44s.645 1.44 1.441 1.44c.795 0 1.439-.645 1.439-1.44s-.644-1.44-1.439-1.44z"/></svg>
<span>Instagram</span>
</a>
</div>
</div>
</section>
</main>
<nav class="bottom-nav">
<div class="nav-item active" data-target="uploadSection">
<i class="material-icons">home</i> Home
</div>
<div class="nav-item" data-target="historySection">
<i class="material-icons">history</i> History
</div>
<div class="nav-item" data-target="profileSection">
<i class="material-icons">person</i> Profile
</div>
</nav>
<script>
const BASE_URL = 'https://triflix-uploadkro2.hf.space'; // Define Base URL
const navItems = document.querySelectorAll('.nav-item');
const sections = document.querySelectorAll('section');
const uploadBtn = document.getElementById('uploadBtn');
const fileInput = document.getElementById('fileInput');
const fileInputArea = document.querySelector('.file-input-area');
const fileNameDisplay = document.getElementById('fileNameDisplay');
const loader = document.getElementById('loader');
const progressContainer = document.getElementById('progressContainer');
const progressBar = document.getElementById('progressBar');
const uploadInfo = document.getElementById('uploadInfo');
const shareLinkContainer = document.getElementById('shareLinkContainer');
const historyList = document.getElementById('historyList');
const STORAGE_KEY = 'upload_history';
let infoTimeout = null;
function displayInfoMessage(message, type = 'info', persistent = false) {
if (uploadInfo) {
if (infoTimeout && !persistent) clearTimeout(infoTimeout);
uploadInfo.textContent = message;
uploadInfo.className = 'info';
if (type === 'error') {
uploadInfo.classList.add('error-message');
} else if (type === 'success') {
uploadInfo.classList.add('success-message');
}
if (message && !persistent && (type === 'error' || message === "Please select a file first.")) {
infoTimeout = setTimeout(() => {
if (uploadInfo.textContent === message) {
uploadInfo.textContent = "";
uploadInfo.className = 'info';
}
}, 3000);
}
}
}
function resetUploadUI(clearMessage = true) {
if (fileInput) fileInput.value = '';
if (fileNameDisplay) fileNameDisplay.textContent = 'No file selected';
if (uploadBtn) uploadBtn.disabled = true;
if (progressContainer) progressContainer.style.display = 'none';
if (progressBar) progressBar.style.width = '0%';
if (clearMessage) {
displayInfoMessage("");
}
if (loader) loader.style.display = 'none';
if (shareLinkContainer) {
shareLinkContainer.style.display = 'none';
shareLinkContainer.innerHTML = '';
}
if (progressBar) progressBar.style.background = 'var(--accent-red)';
}
if (navItems.length) {
navItems.forEach(item => {
item.addEventListener('click', () => {
navItems.forEach(i => i.classList.remove('active'));
item.classList.add('active');
const targetId = item.dataset.target;
sections.forEach(sec => {
sec.classList.toggle('active', sec.id === targetId);
});
if (targetId === 'historySection') {
loadHistory();
}
});
});
}
if (fileInput && fileInputArea && fileNameDisplay && uploadBtn && progressContainer && progressBar && uploadInfo && shareLinkContainer && loader) {
fileInput.addEventListener('change', () => {
if (fileInput.files.length > 0) {
fileNameDisplay.textContent = fileInput.files[0].name;
uploadBtn.disabled = false;
progressContainer.style.display = 'none';
progressBar.style.width = '0%';
if (uploadInfo.textContent === "Please select a file first.") {
displayInfoMessage("");
}
shareLinkContainer.style.display = 'none';
loader.style.display = 'none';
} else {
fileNameDisplay.textContent = 'No file selected';
uploadBtn.disabled = true;
}
});
['dragenter', 'dragover', 'dragleave', 'drop'].forEach(eventName => {
fileInputArea.addEventListener(eventName, preventDefaults, false);
});
function preventDefaults(e) {
e.preventDefault();
e.stopPropagation();
}
['dragenter', 'dragover'].forEach(eventName => {
fileInputArea.addEventListener(eventName, () => fileInputArea.classList.add('dragover'), false);
});
['dragleave', 'drop'].forEach(eventName => {
fileInputArea.addEventListener(eventName, () => fileInputArea.classList.remove('dragover'), false);
});
fileInputArea.addEventListener('drop', (e) => {
fileInput.files = e.dataTransfer.files;
fileInput.dispatchEvent(new Event('change'));
}, false);
}
function copyToClipboard(text, buttonElement) {
navigator.clipboard.writeText(text).then(() => {
const originalText = buttonElement ? buttonElement.textContent : null;
const originalBg = buttonElement ? buttonElement.style.backgroundColor : null;
if (buttonElement) {
buttonElement.textContent = 'Copied!';
buttonElement.style.backgroundColor = '#28a745';
setTimeout(() => {
if (buttonElement) {
buttonElement.textContent = originalText;
buttonElement.style.backgroundColor = originalBg;
}
}, 2000);
}
}).catch(err => {
console.error('Failed to copy: ', err);
displayInfoMessage('Copy failed.', 'error');
});
}
function loadHistory() {
if (!historyList) return;
const data = JSON.parse(localStorage.getItem(STORAGE_KEY) || '[]');
historyList.innerHTML = '';
if (data.length === 0) {
historyList.innerHTML = '<p style="text-align:center; color: var(--text-secondary); padding: 20px 0;">No upload history.</p>';
return;
}
data.forEach(entry => {
const el = document.createElement('div');
el.className = 'history-item';
const dateStr = entry.timestamp ? new Date(entry.timestamp).toLocaleDateString() : '';
// Ensure entry.url is a full URL (new entries will be, old ones might not if not migrated)
const fullUrl = entry.url.startsWith('http') ? entry.url : `${BASE_URL}${entry.url}`;
el.innerHTML = `
<div class="history-item-info">
<div class="history-item-name">${entry.name}</div>
<a href="${fullUrl}" target="_blank" rel="noopener noreferrer" class="history-item-url">${fullUrl.length > 50 ? fullUrl.substring(0,50)+'...' : fullUrl}</a>
${dateStr ? `<span style="font-size:0.7em; color:var(--text-placeholder); display:block; margin-top:2px;">${dateStr}</span>` : ''}
</div>
<button data-url="${fullUrl}">Copy</button>
`;
historyList.appendChild(el);
el.querySelector('button').addEventListener('click', function() {
copyToClipboard(this.dataset.url, this);
});
});
}
if (uploadBtn && fileInput && loader && progressContainer && progressBar && uploadInfo && shareLinkContainer) {
uploadBtn.addEventListener('click', () => {
const file = fileInput.files[0];
if (!file) {
displayInfoMessage("Please select a file first.", "error");
return;
}
const xhr = new XMLHttpRequest();
const formData = new FormData();
formData.append('file', file);
loader.style.display = 'block';
progressContainer.style.display = 'block';
progressBar.style.width = '0%';
progressBar.style.background = 'var(--accent-red)';
displayInfoMessage('0%');
uploadBtn.disabled = true;
shareLinkContainer.style.display = 'none';
xhr.upload.onprogress = event => {
if (event.lengthComputable) {
const percentComplete = Math.round((event.loaded / event.total) * 100);
progressBar.style.width = percentComplete + '%';
displayInfoMessage(percentComplete + '%');
}
};
xhr.onload = () => {
loader.style.display = 'none';
if (xhr.status === 200) {
try {
const response = JSON.parse(xhr.responseText);
// Prepend BASE_URL if download_url is relative
const fullDownloadUrl = response.download_url.startsWith('http') ? response.download_url : `${BASE_URL}${response.download_url}`;
progressBar.style.width = '100%';
displayInfoMessage('Upload Complete!', 'success', true);
shareLinkContainer.innerHTML = `
<p>Share Link:</p>
<div class="share-link-display">
<input type="text" value="${fullDownloadUrl}" id="shareableLinkInput-${Date.now()}" readonly>
<button data-url="${fullDownloadUrl}">Copy</button>
</div>
`;
shareLinkContainer.style.display = 'block';
shareLinkContainer.querySelector('button').addEventListener('click', function() {
const inputId = this.previousElementSibling.id;
const linkInput = document.getElementById(inputId);
if (linkInput) {
linkInput.select();
linkInput.setSelectionRange(0, 99999);
copyToClipboard(this.dataset.url, this);
}
});
const historyData = JSON.parse(localStorage.getItem(STORAGE_KEY) || '[]');
historyData.unshift({
name: response.filename || file.name,
url: fullDownloadUrl, // Store the full URL
timestamp: Date.now()
});
localStorage.setItem(STORAGE_KEY, JSON.stringify(historyData.slice(0, 20)));
fileInput.value = '';
fileNameDisplay.textContent = 'No file selected';
uploadBtn.disabled = true;
} catch (e) {
console.error("Error parsing server response:", e);
displayInfoMessage('Error processing response.', 'error', true);
if(progressBar) progressBar.style.width = '0%';
}
} else {
displayInfoMessage(`Upload failed. Status: ${xhr.status}`, 'error', true);
if(progressBar) progressBar.style.background = '#dc3545';
}
};
xhr.onerror = () => {
if(loader) loader.style.display = 'none';
displayInfoMessage('Upload error. Check connection.', 'error', true);
if(progressBar) progressBar.style.background = '#dc3545';
};
xhr.open('POST', '/upload/', true); // DO NOT CHANGE BACKEND
xhr.send(formData);
});
}
// Initial load
if (typeof resetUploadUI === 'function') resetUploadUI();
document.querySelector('.nav-item[data-target="uploadSection"]')?.click();
</script>
</body>
</html>