Spaces:
Runtime error
Runtime error
Update templates/index.html
Browse files- templates/index.html +107 -256
templates/index.html
CHANGED
|
@@ -24,7 +24,7 @@
|
|
| 24 |
--text-primary: var(--primary-dark-ref);
|
| 25 |
--text-secondary: #666666;
|
| 26 |
--text-placeholder: #999999;
|
| 27 |
-
--text-email-on-light: rgba(27, 27, 29, 0.7);
|
| 28 |
|
| 29 |
/* Typography */
|
| 30 |
--font-main: 'Noto Sans', 'Poppins', sans-serif;
|
|
@@ -33,10 +33,9 @@
|
|
| 33 |
/* UI Elements */
|
| 34 |
--radius-sharp: 4px;
|
| 35 |
--radius-softer: 6px;
|
| 36 |
-
--radius-profile-card: 12px;
|
| 37 |
--shadow-none: none;
|
| 38 |
-
--shadow-
|
| 39 |
-
--shadow-profile-card: 0 4px 10px rgba(0,0,0,0.1); /* Softer, more modern shadow */
|
| 40 |
|
| 41 |
--nav-height: 60px;
|
| 42 |
}
|
|
@@ -71,7 +70,6 @@
|
|
| 71 |
}
|
| 72 |
section.active { display: flex; flex-direction: column; }
|
| 73 |
|
| 74 |
-
/* Common Button Style (Red Accent) */
|
| 75 |
button, .button-like {
|
| 76 |
background: var(--accent-red);
|
| 77 |
color: var(--text-on-accent);
|
|
@@ -105,7 +103,6 @@
|
|
| 105 |
cursor: not-allowed;
|
| 106 |
}
|
| 107 |
|
| 108 |
-
/* --- Uploader & History Styles (Keep as is from previous version) --- */
|
| 109 |
.uploader { display: flex; flex-direction: column; gap: 16px; width: 100%; max-width: 480px; margin: auto; }
|
| 110 |
.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; }
|
| 111 |
.file-input-area:hover, .file-input-area.dragover { border-color: var(--accent-red); background-color: #fff5f5; }
|
|
@@ -137,205 +134,46 @@
|
|
| 137 |
.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); }
|
| 138 |
.history-item-url:hover { color: var(--accent-red); text-decoration: underline;}
|
| 139 |
.history-item button { padding: 7px 10px; font-size: 0.75rem; min-width: 80px; }
|
| 140 |
-
/* --- END Uploader & History Styles --- */
|
| 141 |
|
| 142 |
-
|
| 143 |
-
/* Profile UI - Revamped */
|
| 144 |
.profile-container {
|
| 145 |
display: flex;
|
| 146 |
flex-direction: column;
|
| 147 |
align-items: center;
|
| 148 |
-
gap: 24px;
|
| 149 |
-
padding: 16px 0;
|
| 150 |
-
width: 100%;
|
| 151 |
-
max-width: 450px; /* Control max width */
|
| 152 |
-
margin: auto; /* Center it */
|
| 153 |
-
}
|
| 154 |
-
|
| 155 |
-
.profile-header {
|
| 156 |
-
text-align: center;
|
| 157 |
-
margin-bottom: 16px; /* Space below header */
|
| 158 |
-
}
|
| 159 |
-
.profile-avatar-wrapper {
|
| 160 |
-
position: relative;
|
| 161 |
-
width: 120px; /* Avatar size */
|
| 162 |
-
height: 120px;
|
| 163 |
-
margin: 0 auto 20px auto; /* Centered, space below */
|
| 164 |
-
}
|
| 165 |
-
.profile-avatar {
|
| 166 |
-
width: 100%;
|
| 167 |
-
height: 100%;
|
| 168 |
-
border-radius: 50%;
|
| 169 |
-
object-fit: cover;
|
| 170 |
-
border: 3px solid var(--surface-primary); /* Inner border to lift from ring */
|
| 171 |
-
box-shadow: 0 2px 4px rgba(0,0,0,0.1); /* Subtle shadow on avatar */
|
| 172 |
-
position: relative;
|
| 173 |
-
z-index: 2;
|
| 174 |
-
}
|
| 175 |
-
.profile-avatar-ring {
|
| 176 |
-
position: absolute;
|
| 177 |
-
top: -5px; left: -5px; right: -5px; bottom: -5px; /* Make it slightly larger than avatar */
|
| 178 |
-
border-radius: 50%;
|
| 179 |
-
border: 2px dotted var(--accent-red);
|
| 180 |
-
animation: rotateRing 20s linear infinite;
|
| 181 |
-
z-index: 1;
|
| 182 |
-
}
|
| 183 |
-
@keyframes rotateRing {
|
| 184 |
-
from { transform: rotate(0deg); }
|
| 185 |
-
to { transform: rotate(360deg); }
|
| 186 |
-
}
|
| 187 |
-
|
| 188 |
-
.profile-name {
|
| 189 |
-
font-family: var(--font-mono); /* Pixel-style font */
|
| 190 |
-
font-size: 1.8rem; /* Prominent name */
|
| 191 |
-
color: var(--text-primary);
|
| 192 |
-
margin-bottom: 4px;
|
| 193 |
-
letter-spacing: 0.5px; /* Adjust for "pixel" feel */
|
| 194 |
-
}
|
| 195 |
-
.profile-email {
|
| 196 |
-
font-family: var(--font-main); /* Clean sans-serif */
|
| 197 |
-
font-size: 0.95rem;
|
| 198 |
-
color: var(--text-email-on-light); /* Lighter email text */
|
| 199 |
-
}
|
| 200 |
-
|
| 201 |
-
.profile-action-card {
|
| 202 |
-
background-color: var(--surface-primary);
|
| 203 |
-
border-radius: var(--radius-profile-card);
|
| 204 |
-
padding: 16px;
|
| 205 |
width: 100%;
|
| 206 |
-
|
| 207 |
-
|
| 208 |
-
}
|
| 209 |
-
.
|
| 210 |
-
|
| 211 |
-
|
| 212 |
-
|
| 213 |
-
|
| 214 |
-
|
| 215 |
-
|
| 216 |
-
|
| 217 |
-
|
| 218 |
-
|
| 219 |
-
|
| 220 |
-
|
| 221 |
-
|
| 222 |
-
|
| 223 |
-
letter-spacing: 0.5px;
|
| 224 |
-
transition: background-color 0.15s ease;
|
| 225 |
-
width: auto; /* Let it size by content or set a min-width */
|
| 226 |
-
min-width: 200px;
|
| 227 |
-
}
|
| 228 |
-
.portfolio-button:hover {
|
| 229 |
-
background-color: var(--accent-red-hover);
|
| 230 |
-
}
|
| 231 |
-
.portfolio-button .material-icons {
|
| 232 |
-
font-size: 20px; /* Icon size in button */
|
| 233 |
-
}
|
| 234 |
-
|
| 235 |
-
|
| 236 |
-
.profile-social-links {
|
| 237 |
-
display: grid;
|
| 238 |
-
grid-template-columns: repeat(auto-fit, minmax(100px, 1fr)); /* Responsive grid */
|
| 239 |
-
gap: 16px;
|
| 240 |
-
width: 100%;
|
| 241 |
-
padding: 0 8px; /* Slight horizontal padding for the grid */
|
| 242 |
-
}
|
| 243 |
-
.social-link-card {
|
| 244 |
-
background-color: var(--surface-primary);
|
| 245 |
-
border-radius: var(--radius-profile-card);
|
| 246 |
-
padding: 16px;
|
| 247 |
-
box-shadow: var(--shadow-profile-card);
|
| 248 |
-
display: flex;
|
| 249 |
-
flex-direction: column;
|
| 250 |
-
align-items: center;
|
| 251 |
-
justify-content: center;
|
| 252 |
-
text-decoration: none;
|
| 253 |
-
color: var(--text-primary);
|
| 254 |
-
gap: 8px;
|
| 255 |
-
transition: transform 0.2s ease, box-shadow 0.2s ease;
|
| 256 |
-
min-height: 100px; /* Ensure cards have some height */
|
| 257 |
-
}
|
| 258 |
-
.social-link-card:hover {
|
| 259 |
-
transform: translateY(-3px) scale(1.03);
|
| 260 |
-
box-shadow: 0 6px 12px rgba(0,0,0,0.12);
|
| 261 |
-
color: var(--accent-red); /* Change text/icon color on hover */
|
| 262 |
-
}
|
| 263 |
.social-link-card .social-icon {
|
| 264 |
-
|
| 265 |
-
|
| 266 |
-
|
| 267 |
-
|
| 268 |
-
align-items: center;
|
| 269 |
-
justify-content: center;
|
| 270 |
-
/* Basic Text Icon Styling - REPLACE WITH SVGS or ICON FONT */
|
| 271 |
-
font-family: var(--font-mono);
|
| 272 |
-
font-weight: bold;
|
| 273 |
-
border: 1px solid var(--border-light);
|
| 274 |
-
border-radius: 50%;
|
| 275 |
-
line-height: 30px; /* Center text icon */
|
| 276 |
-
transition: color 0.2s ease, border-color 0.2s ease;
|
| 277 |
-
}
|
| 278 |
-
.social-link-card:hover .social-icon {
|
| 279 |
-
color: var(--accent-red);
|
| 280 |
-
border-color: var(--accent-red);
|
| 281 |
}
|
| 282 |
-
.social-link-card span {
|
| 283 |
-
font-family: var(--font-main);
|
| 284 |
-
font-size: 0.8rem;
|
| 285 |
-
font-weight: 500;
|
| 286 |
-
text-align: center;
|
| 287 |
-
}
|
| 288 |
-
|
| 289 |
|
| 290 |
-
|
| 291 |
-
.
|
| 292 |
-
position: fixed;
|
| 293 |
-
bottom: 0;
|
| 294 |
-
left: 0;
|
| 295 |
-
right: 0;
|
| 296 |
-
width: 100%;
|
| 297 |
-
background: var(--surface-primary);
|
| 298 |
-
display: flex;
|
| 299 |
-
justify-content: space-around;
|
| 300 |
-
align-items: stretch;
|
| 301 |
-
border-top: 1px solid var(--border-light);
|
| 302 |
-
padding-bottom: env(safe-area-inset-bottom);
|
| 303 |
-
height: calc(var(--nav-height) + env(safe-area-inset-bottom));
|
| 304 |
-
box-shadow: var(--shadow-none);
|
| 305 |
-
}
|
| 306 |
-
.nav-item {
|
| 307 |
-
flex: 1;
|
| 308 |
-
display: flex;
|
| 309 |
-
flex-direction: column;
|
| 310 |
-
justify-content: center;
|
| 311 |
-
align-items: center;
|
| 312 |
-
font-size: 0.7rem;
|
| 313 |
-
color: var(--text-secondary);
|
| 314 |
-
cursor: pointer;
|
| 315 |
-
transition: color 0.15s ease;
|
| 316 |
-
padding-top: 5px;
|
| 317 |
-
padding-bottom: 3px;
|
| 318 |
-
position: relative;
|
| 319 |
-
}
|
| 320 |
.nav-item:hover { color: var(--text-primary); }
|
| 321 |
.nav-item.active { color: var(--accent-red); font-weight: 700; }
|
| 322 |
-
.nav-item.active::after {
|
| 323 |
-
|
| 324 |
-
position: absolute;
|
| 325 |
-
bottom: 5px;
|
| 326 |
-
left: 50%;
|
| 327 |
-
transform: translateX(-50%);
|
| 328 |
-
width: 5px;
|
| 329 |
-
height: 5px;
|
| 330 |
-
background-color: var(--accent-red);
|
| 331 |
-
border-radius: 50%;
|
| 332 |
-
}
|
| 333 |
-
.nav-item .material-icons {
|
| 334 |
-
font-size: 24px;
|
| 335 |
-
margin-bottom: 2px;
|
| 336 |
-
}
|
| 337 |
|
| 338 |
-
/* Animations */
|
| 339 |
@keyframes spin { from { transform: rotate(0deg); } to { transform: rotate(360deg); } }
|
| 340 |
@keyframes fadeIn { from { opacity: 0; transform: translateY(5px); } to { opacity: 1; transform: translateY(0); } }
|
| 341 |
</style>
|
|
@@ -343,7 +181,6 @@
|
|
| 343 |
<body>
|
| 344 |
<main>
|
| 345 |
<section id="uploadSection" class="active">
|
| 346 |
-
<!-- Uploader HTML (from previous version, unchanged) -->
|
| 347 |
<div class="uploader">
|
| 348 |
<label for="fileInput" class="file-input-area">
|
| 349 |
<i class="material-icons">upload_file</i>
|
|
@@ -363,7 +200,6 @@
|
|
| 363 |
</section>
|
| 364 |
|
| 365 |
<section id="historySection">
|
| 366 |
-
<!-- History HTML (from previous version, unchanged) -->
|
| 367 |
<div class="history-list" id="historyList"></div>
|
| 368 |
</section>
|
| 369 |
|
|
@@ -380,32 +216,30 @@
|
|
| 380 |
|
| 381 |
<div class="profile-action-card portfolio-cta-card">
|
| 382 |
<a href="https://www.adityadevarshi.online/" target="_blank" rel="noopener noreferrer" class="portfolio-button">
|
| 383 |
-
<i class="material-icons">visibility</i>
|
| 384 |
<span>View My Work</span>
|
| 385 |
</a>
|
| 386 |
</div>
|
| 387 |
|
| 388 |
<div class="profile-social-links">
|
| 389 |
<a href="https://github.com/devarshiadi/" target="_blank" rel="noopener noreferrer" class="social-link-card" aria-label="GitHub Profile">
|
| 390 |
-
<
|
| 391 |
<span>GitHub</span>
|
| 392 |
</a>
|
| 393 |
<a href="https://www.linkedin.com/in/aditya-devarshi/" target="_blank" rel="noopener noreferrer" class="social-link-card" aria-label="LinkedIn Profile">
|
| 394 |
-
<
|
| 395 |
<span>LinkedIn</span>
|
| 396 |
</a>
|
| 397 |
<a href="https://www.instagram.com/curseofwitcher/" target="_blank" rel="noopener noreferrer" class="social-link-card" aria-label="Instagram Profile">
|
| 398 |
-
<
|
| 399 |
<span>Instagram</span>
|
| 400 |
</a>
|
| 401 |
-
<!-- You can add a 4th item here for a 2x2 grid if desired -->
|
| 402 |
</div>
|
| 403 |
</div>
|
| 404 |
</section>
|
| 405 |
</main>
|
| 406 |
|
| 407 |
<nav class="bottom-nav">
|
| 408 |
-
<!-- Nav HTML (from previous version, unchanged) -->
|
| 409 |
<div class="nav-item active" data-target="uploadSection">
|
| 410 |
<i class="material-icons">home</i> Home
|
| 411 |
</div>
|
|
@@ -418,7 +252,8 @@
|
|
| 418 |
</nav>
|
| 419 |
|
| 420 |
<script>
|
| 421 |
-
|
|
|
|
| 422 |
const navItems = document.querySelectorAll('.nav-item');
|
| 423 |
const sections = document.querySelectorAll('section');
|
| 424 |
const uploadBtn = document.getElementById('uploadBtn');
|
|
@@ -435,55 +270,62 @@
|
|
| 435 |
let infoTimeout = null;
|
| 436 |
|
| 437 |
function displayInfoMessage(message, type = 'info', persistent = false) {
|
| 438 |
-
if (
|
| 439 |
-
|
| 440 |
-
|
| 441 |
-
|
| 442 |
-
|
| 443 |
-
|
| 444 |
-
|
| 445 |
-
|
|
|
|
| 446 |
|
| 447 |
-
|
| 448 |
-
|
| 449 |
-
|
| 450 |
-
|
| 451 |
-
|
| 452 |
-
|
| 453 |
-
|
|
|
|
| 454 |
}
|
| 455 |
}
|
| 456 |
|
| 457 |
function resetUploadUI(clearMessage = true) {
|
| 458 |
-
fileInput.value = '';
|
| 459 |
-
fileNameDisplay.textContent = 'No file selected';
|
| 460 |
-
uploadBtn.disabled = true;
|
| 461 |
-
progressContainer.style.display = 'none';
|
| 462 |
-
progressBar.style.width = '0%';
|
| 463 |
if (clearMessage) {
|
| 464 |
displayInfoMessage("");
|
| 465 |
}
|
| 466 |
-
loader.style.display = 'none';
|
| 467 |
-
shareLinkContainer
|
| 468 |
-
|
| 469 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 470 |
}
|
| 471 |
|
| 472 |
-
navItems.forEach(item => {
|
| 473 |
-
item.addEventListener('click', () => {
|
| 474 |
-
navItems.forEach(i => i.classList.remove('active'));
|
| 475 |
-
item.classList.add('active');
|
| 476 |
-
const targetId = item.dataset.target;
|
| 477 |
-
sections.forEach(sec => {
|
| 478 |
-
sec.classList.toggle('active', sec.id === targetId);
|
| 479 |
-
});
|
| 480 |
-
if (targetId === 'historySection') {
|
| 481 |
-
loadHistory();
|
| 482 |
-
}
|
| 483 |
-
});
|
| 484 |
-
});
|
| 485 |
|
| 486 |
-
if (fileInput
|
| 487 |
fileInput.addEventListener('change', () => {
|
| 488 |
if (fileInput.files.length > 0) {
|
| 489 |
fileNameDisplay.textContent = fileInput.files[0].name;
|
|
@@ -542,6 +384,7 @@
|
|
| 542 |
}
|
| 543 |
|
| 544 |
function loadHistory() {
|
|
|
|
| 545 |
const data = JSON.parse(localStorage.getItem(STORAGE_KEY) || '[]');
|
| 546 |
historyList.innerHTML = '';
|
| 547 |
if (data.length === 0) {
|
|
@@ -552,13 +395,16 @@
|
|
| 552 |
const el = document.createElement('div');
|
| 553 |
el.className = 'history-item';
|
| 554 |
const dateStr = entry.timestamp ? new Date(entry.timestamp).toLocaleDateString() : '';
|
|
|
|
|
|
|
|
|
|
| 555 |
el.innerHTML = `
|
| 556 |
<div class="history-item-info">
|
| 557 |
<div class="history-item-name">${entry.name}</div>
|
| 558 |
-
<a href="${
|
| 559 |
${dateStr ? `<span style="font-size:0.7em; color:var(--text-placeholder); display:block; margin-top:2px;">${dateStr}</span>` : ''}
|
| 560 |
</div>
|
| 561 |
-
<button data-url="${
|
| 562 |
`;
|
| 563 |
historyList.appendChild(el);
|
| 564 |
el.querySelector('button').addEventListener('click', function() {
|
|
@@ -567,7 +413,7 @@
|
|
| 567 |
});
|
| 568 |
}
|
| 569 |
|
| 570 |
-
if (uploadBtn
|
| 571 |
uploadBtn.addEventListener('click', () => {
|
| 572 |
const file = fileInput.files[0];
|
| 573 |
if (!file) {
|
|
@@ -601,29 +447,34 @@
|
|
| 601 |
if (xhr.status === 200) {
|
| 602 |
try {
|
| 603 |
const response = JSON.parse(xhr.responseText);
|
|
|
|
|
|
|
|
|
|
| 604 |
progressBar.style.width = '100%';
|
| 605 |
displayInfoMessage('Upload Complete!', 'success', true);
|
| 606 |
|
| 607 |
shareLinkContainer.innerHTML = `
|
| 608 |
<p>Share Link:</p>
|
| 609 |
<div class="share-link-display">
|
| 610 |
-
<input type="text" value="${
|
| 611 |
-
<button data-url="${
|
| 612 |
</div>
|
| 613 |
`;
|
| 614 |
shareLinkContainer.style.display = 'block';
|
| 615 |
shareLinkContainer.querySelector('button').addEventListener('click', function() {
|
| 616 |
const inputId = this.previousElementSibling.id;
|
| 617 |
const linkInput = document.getElementById(inputId);
|
| 618 |
-
|
| 619 |
-
|
| 620 |
-
|
|
|
|
|
|
|
| 621 |
});
|
| 622 |
|
| 623 |
const historyData = JSON.parse(localStorage.getItem(STORAGE_KEY) || '[]');
|
| 624 |
historyData.unshift({
|
| 625 |
name: response.filename || file.name,
|
| 626 |
-
url:
|
| 627 |
timestamp: Date.now()
|
| 628 |
});
|
| 629 |
localStorage.setItem(STORAGE_KEY, JSON.stringify(historyData.slice(0, 20)));
|
|
@@ -635,28 +486,28 @@
|
|
| 635 |
} catch (e) {
|
| 636 |
console.error("Error parsing server response:", e);
|
| 637 |
displayInfoMessage('Error processing response.', 'error', true);
|
| 638 |
-
progressBar.style.width = '0%';
|
| 639 |
}
|
| 640 |
} else {
|
| 641 |
displayInfoMessage(`Upload failed. Status: ${xhr.status}`, 'error', true);
|
| 642 |
-
progressBar.style.background = '#dc3545';
|
| 643 |
}
|
| 644 |
};
|
| 645 |
|
| 646 |
xhr.onerror = () => {
|
| 647 |
-
loader.style.display = 'none';
|
| 648 |
displayInfoMessage('Upload error. Check connection.', 'error', true);
|
| 649 |
-
progressBar.style.background = '#dc3545';
|
| 650 |
};
|
| 651 |
|
| 652 |
-
xhr.open('POST', '/upload/', true);
|
| 653 |
xhr.send(formData);
|
| 654 |
});
|
| 655 |
}
|
| 656 |
|
| 657 |
// Initial load
|
| 658 |
-
if (typeof resetUploadUI === 'function') resetUploadUI();
|
| 659 |
-
document.querySelector('.nav-item[data-target="uploadSection"]')?.click();
|
| 660 |
|
| 661 |
</script>
|
| 662 |
</body>
|
|
|
|
| 24 |
--text-primary: var(--primary-dark-ref);
|
| 25 |
--text-secondary: #666666;
|
| 26 |
--text-placeholder: #999999;
|
| 27 |
+
--text-email-on-light: rgba(27, 27, 29, 0.7);
|
| 28 |
|
| 29 |
/* Typography */
|
| 30 |
--font-main: 'Noto Sans', 'Poppins', sans-serif;
|
|
|
|
| 33 |
/* UI Elements */
|
| 34 |
--radius-sharp: 4px;
|
| 35 |
--radius-softer: 6px;
|
| 36 |
+
--radius-profile-card: 12px;
|
| 37 |
--shadow-none: none;
|
| 38 |
+
--shadow-profile-card: 0 4px 10px rgba(0,0,0,0.1);
|
|
|
|
| 39 |
|
| 40 |
--nav-height: 60px;
|
| 41 |
}
|
|
|
|
| 70 |
}
|
| 71 |
section.active { display: flex; flex-direction: column; }
|
| 72 |
|
|
|
|
| 73 |
button, .button-like {
|
| 74 |
background: var(--accent-red);
|
| 75 |
color: var(--text-on-accent);
|
|
|
|
| 103 |
cursor: not-allowed;
|
| 104 |
}
|
| 105 |
|
|
|
|
| 106 |
.uploader { display: flex; flex-direction: column; gap: 16px; width: 100%; max-width: 480px; margin: auto; }
|
| 107 |
.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; }
|
| 108 |
.file-input-area:hover, .file-input-area.dragover { border-color: var(--accent-red); background-color: #fff5f5; }
|
|
|
|
| 134 |
.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); }
|
| 135 |
.history-item-url:hover { color: var(--accent-red); text-decoration: underline;}
|
| 136 |
.history-item button { padding: 7px 10px; font-size: 0.75rem; min-width: 80px; }
|
|
|
|
| 137 |
|
|
|
|
|
|
|
| 138 |
.profile-container {
|
| 139 |
display: flex;
|
| 140 |
flex-direction: column;
|
| 141 |
align-items: center;
|
| 142 |
+
gap: 24px;
|
| 143 |
+
padding: 16px 0;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 144 |
width: 100%;
|
| 145 |
+
max-width: 450px;
|
| 146 |
+
margin: auto;
|
| 147 |
+
}
|
| 148 |
+
.profile-header { text-align: center; margin-bottom: 16px; }
|
| 149 |
+
.profile-avatar-wrapper { position: relative; width: 120px; height: 120px; margin: 0 auto 20px auto; }
|
| 150 |
+
.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; }
|
| 151 |
+
.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; }
|
| 152 |
+
@keyframes rotateRing { from { transform: rotate(0deg); } to { transform: rotate(360deg); } }
|
| 153 |
+
.profile-name { font-family: var(--font-mono); font-size: 1.8rem; color: var(--text-primary); margin-bottom: 4px; letter-spacing: 0.5px; }
|
| 154 |
+
.profile-email { font-family: var(--font-main); font-size: 0.95rem; color: var(--text-email-on-light); }
|
| 155 |
+
.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; }
|
| 156 |
+
.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; }
|
| 157 |
+
.portfolio-button:hover { background-color: var(--accent-red-hover); }
|
| 158 |
+
.portfolio-button .material-icons { font-size: 20px; }
|
| 159 |
+
.profile-social-links { display: grid; grid-template-columns: repeat(auto-fit, minmax(100px, 1fr)); gap: 16px; width: 100%; padding: 0 8px; }
|
| 160 |
+
.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; }
|
| 161 |
+
.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); }
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 162 |
.social-link-card .social-icon {
|
| 163 |
+
width: 28px; /* SVG size */
|
| 164 |
+
height: 28px;
|
| 165 |
+
fill: currentColor; /* Inherits color from .social-link-card for hover effect */
|
| 166 |
+
transition: fill 0.2s ease;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 167 |
}
|
| 168 |
+
.social-link-card span { font-family: var(--font-main); font-size: 0.8rem; font-weight: 500; text-align: center; }
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 169 |
|
| 170 |
+
.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); }
|
| 171 |
+
.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; }
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 172 |
.nav-item:hover { color: var(--text-primary); }
|
| 173 |
.nav-item.active { color: var(--accent-red); font-weight: 700; }
|
| 174 |
+
.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%; }
|
| 175 |
+
.nav-item .material-icons { font-size: 24px; margin-bottom: 2px; }
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 176 |
|
|
|
|
| 177 |
@keyframes spin { from { transform: rotate(0deg); } to { transform: rotate(360deg); } }
|
| 178 |
@keyframes fadeIn { from { opacity: 0; transform: translateY(5px); } to { opacity: 1; transform: translateY(0); } }
|
| 179 |
</style>
|
|
|
|
| 181 |
<body>
|
| 182 |
<main>
|
| 183 |
<section id="uploadSection" class="active">
|
|
|
|
| 184 |
<div class="uploader">
|
| 185 |
<label for="fileInput" class="file-input-area">
|
| 186 |
<i class="material-icons">upload_file</i>
|
|
|
|
| 200 |
</section>
|
| 201 |
|
| 202 |
<section id="historySection">
|
|
|
|
| 203 |
<div class="history-list" id="historyList"></div>
|
| 204 |
</section>
|
| 205 |
|
|
|
|
| 216 |
|
| 217 |
<div class="profile-action-card portfolio-cta-card">
|
| 218 |
<a href="https://www.adityadevarshi.online/" target="_blank" rel="noopener noreferrer" class="portfolio-button">
|
| 219 |
+
<i class="material-icons">visibility</i>
|
| 220 |
<span>View My Work</span>
|
| 221 |
</a>
|
| 222 |
</div>
|
| 223 |
|
| 224 |
<div class="profile-social-links">
|
| 225 |
<a href="https://github.com/devarshiadi/" target="_blank" rel="noopener noreferrer" class="social-link-card" aria-label="GitHub Profile">
|
| 226 |
+
<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>
|
| 227 |
<span>GitHub</span>
|
| 228 |
</a>
|
| 229 |
<a href="https://www.linkedin.com/in/aditya-devarshi/" target="_blank" rel="noopener noreferrer" class="social-link-card" aria-label="LinkedIn Profile">
|
| 230 |
+
<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>
|
| 231 |
<span>LinkedIn</span>
|
| 232 |
</a>
|
| 233 |
<a href="https://www.instagram.com/curseofwitcher/" target="_blank" rel="noopener noreferrer" class="social-link-card" aria-label="Instagram Profile">
|
| 234 |
+
<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>
|
| 235 |
<span>Instagram</span>
|
| 236 |
</a>
|
|
|
|
| 237 |
</div>
|
| 238 |
</div>
|
| 239 |
</section>
|
| 240 |
</main>
|
| 241 |
|
| 242 |
<nav class="bottom-nav">
|
|
|
|
| 243 |
<div class="nav-item active" data-target="uploadSection">
|
| 244 |
<i class="material-icons">home</i> Home
|
| 245 |
</div>
|
|
|
|
| 252 |
</nav>
|
| 253 |
|
| 254 |
<script>
|
| 255 |
+
const BASE_URL = 'https://triflix-uploadkro2.hf.space'; // Define Base URL
|
| 256 |
+
|
| 257 |
const navItems = document.querySelectorAll('.nav-item');
|
| 258 |
const sections = document.querySelectorAll('section');
|
| 259 |
const uploadBtn = document.getElementById('uploadBtn');
|
|
|
|
| 270 |
let infoTimeout = null;
|
| 271 |
|
| 272 |
function displayInfoMessage(message, type = 'info', persistent = false) {
|
| 273 |
+
if (uploadInfo) {
|
| 274 |
+
if (infoTimeout && !persistent) clearTimeout(infoTimeout);
|
| 275 |
+
uploadInfo.textContent = message;
|
| 276 |
+
uploadInfo.className = 'info';
|
| 277 |
+
if (type === 'error') {
|
| 278 |
+
uploadInfo.classList.add('error-message');
|
| 279 |
+
} else if (type === 'success') {
|
| 280 |
+
uploadInfo.classList.add('success-message');
|
| 281 |
+
}
|
| 282 |
|
| 283 |
+
if (message && !persistent && (type === 'error' || message === "Please select a file first.")) {
|
| 284 |
+
infoTimeout = setTimeout(() => {
|
| 285 |
+
if (uploadInfo.textContent === message) {
|
| 286 |
+
uploadInfo.textContent = "";
|
| 287 |
+
uploadInfo.className = 'info';
|
| 288 |
+
}
|
| 289 |
+
}, 3000);
|
| 290 |
+
}
|
| 291 |
}
|
| 292 |
}
|
| 293 |
|
| 294 |
function resetUploadUI(clearMessage = true) {
|
| 295 |
+
if (fileInput) fileInput.value = '';
|
| 296 |
+
if (fileNameDisplay) fileNameDisplay.textContent = 'No file selected';
|
| 297 |
+
if (uploadBtn) uploadBtn.disabled = true;
|
| 298 |
+
if (progressContainer) progressContainer.style.display = 'none';
|
| 299 |
+
if (progressBar) progressBar.style.width = '0%';
|
| 300 |
if (clearMessage) {
|
| 301 |
displayInfoMessage("");
|
| 302 |
}
|
| 303 |
+
if (loader) loader.style.display = 'none';
|
| 304 |
+
if (shareLinkContainer) {
|
| 305 |
+
shareLinkContainer.style.display = 'none';
|
| 306 |
+
shareLinkContainer.innerHTML = '';
|
| 307 |
+
}
|
| 308 |
+
if (progressBar) progressBar.style.background = 'var(--accent-red)';
|
| 309 |
+
}
|
| 310 |
+
|
| 311 |
+
if (navItems.length) {
|
| 312 |
+
navItems.forEach(item => {
|
| 313 |
+
item.addEventListener('click', () => {
|
| 314 |
+
navItems.forEach(i => i.classList.remove('active'));
|
| 315 |
+
item.classList.add('active');
|
| 316 |
+
const targetId = item.dataset.target;
|
| 317 |
+
sections.forEach(sec => {
|
| 318 |
+
sec.classList.toggle('active', sec.id === targetId);
|
| 319 |
+
});
|
| 320 |
+
if (targetId === 'historySection') {
|
| 321 |
+
loadHistory();
|
| 322 |
+
}
|
| 323 |
+
});
|
| 324 |
+
});
|
| 325 |
}
|
| 326 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 327 |
|
| 328 |
+
if (fileInput && fileInputArea && fileNameDisplay && uploadBtn && progressContainer && progressBar && uploadInfo && shareLinkContainer && loader) {
|
| 329 |
fileInput.addEventListener('change', () => {
|
| 330 |
if (fileInput.files.length > 0) {
|
| 331 |
fileNameDisplay.textContent = fileInput.files[0].name;
|
|
|
|
| 384 |
}
|
| 385 |
|
| 386 |
function loadHistory() {
|
| 387 |
+
if (!historyList) return;
|
| 388 |
const data = JSON.parse(localStorage.getItem(STORAGE_KEY) || '[]');
|
| 389 |
historyList.innerHTML = '';
|
| 390 |
if (data.length === 0) {
|
|
|
|
| 395 |
const el = document.createElement('div');
|
| 396 |
el.className = 'history-item';
|
| 397 |
const dateStr = entry.timestamp ? new Date(entry.timestamp).toLocaleDateString() : '';
|
| 398 |
+
// Ensure entry.url is a full URL (new entries will be, old ones might not if not migrated)
|
| 399 |
+
const fullUrl = entry.url.startsWith('http') ? entry.url : `${BASE_URL}${entry.url}`;
|
| 400 |
+
|
| 401 |
el.innerHTML = `
|
| 402 |
<div class="history-item-info">
|
| 403 |
<div class="history-item-name">${entry.name}</div>
|
| 404 |
+
<a href="${fullUrl}" target="_blank" rel="noopener noreferrer" class="history-item-url">${fullUrl.length > 50 ? fullUrl.substring(0,50)+'...' : fullUrl}</a>
|
| 405 |
${dateStr ? `<span style="font-size:0.7em; color:var(--text-placeholder); display:block; margin-top:2px;">${dateStr}</span>` : ''}
|
| 406 |
</div>
|
| 407 |
+
<button data-url="${fullUrl}">Copy</button>
|
| 408 |
`;
|
| 409 |
historyList.appendChild(el);
|
| 410 |
el.querySelector('button').addEventListener('click', function() {
|
|
|
|
| 413 |
});
|
| 414 |
}
|
| 415 |
|
| 416 |
+
if (uploadBtn && fileInput && loader && progressContainer && progressBar && uploadInfo && shareLinkContainer) {
|
| 417 |
uploadBtn.addEventListener('click', () => {
|
| 418 |
const file = fileInput.files[0];
|
| 419 |
if (!file) {
|
|
|
|
| 447 |
if (xhr.status === 200) {
|
| 448 |
try {
|
| 449 |
const response = JSON.parse(xhr.responseText);
|
| 450 |
+
// Prepend BASE_URL if download_url is relative
|
| 451 |
+
const fullDownloadUrl = response.download_url.startsWith('http') ? response.download_url : `${BASE_URL}${response.download_url}`;
|
| 452 |
+
|
| 453 |
progressBar.style.width = '100%';
|
| 454 |
displayInfoMessage('Upload Complete!', 'success', true);
|
| 455 |
|
| 456 |
shareLinkContainer.innerHTML = `
|
| 457 |
<p>Share Link:</p>
|
| 458 |
<div class="share-link-display">
|
| 459 |
+
<input type="text" value="${fullDownloadUrl}" id="shareableLinkInput-${Date.now()}" readonly>
|
| 460 |
+
<button data-url="${fullDownloadUrl}">Copy</button>
|
| 461 |
</div>
|
| 462 |
`;
|
| 463 |
shareLinkContainer.style.display = 'block';
|
| 464 |
shareLinkContainer.querySelector('button').addEventListener('click', function() {
|
| 465 |
const inputId = this.previousElementSibling.id;
|
| 466 |
const linkInput = document.getElementById(inputId);
|
| 467 |
+
if (linkInput) {
|
| 468 |
+
linkInput.select();
|
| 469 |
+
linkInput.setSelectionRange(0, 99999);
|
| 470 |
+
copyToClipboard(this.dataset.url, this);
|
| 471 |
+
}
|
| 472 |
});
|
| 473 |
|
| 474 |
const historyData = JSON.parse(localStorage.getItem(STORAGE_KEY) || '[]');
|
| 475 |
historyData.unshift({
|
| 476 |
name: response.filename || file.name,
|
| 477 |
+
url: fullDownloadUrl, // Store the full URL
|
| 478 |
timestamp: Date.now()
|
| 479 |
});
|
| 480 |
localStorage.setItem(STORAGE_KEY, JSON.stringify(historyData.slice(0, 20)));
|
|
|
|
| 486 |
} catch (e) {
|
| 487 |
console.error("Error parsing server response:", e);
|
| 488 |
displayInfoMessage('Error processing response.', 'error', true);
|
| 489 |
+
if(progressBar) progressBar.style.width = '0%';
|
| 490 |
}
|
| 491 |
} else {
|
| 492 |
displayInfoMessage(`Upload failed. Status: ${xhr.status}`, 'error', true);
|
| 493 |
+
if(progressBar) progressBar.style.background = '#dc3545';
|
| 494 |
}
|
| 495 |
};
|
| 496 |
|
| 497 |
xhr.onerror = () => {
|
| 498 |
+
if(loader) loader.style.display = 'none';
|
| 499 |
displayInfoMessage('Upload error. Check connection.', 'error', true);
|
| 500 |
+
if(progressBar) progressBar.style.background = '#dc3545';
|
| 501 |
};
|
| 502 |
|
| 503 |
+
xhr.open('POST', '/upload/', true); // DO NOT CHANGE BACKEND
|
| 504 |
xhr.send(formData);
|
| 505 |
});
|
| 506 |
}
|
| 507 |
|
| 508 |
// Initial load
|
| 509 |
+
if (typeof resetUploadUI === 'function') resetUploadUI();
|
| 510 |
+
document.querySelector('.nav-item[data-target="uploadSection"]')?.click();
|
| 511 |
|
| 512 |
</script>
|
| 513 |
</body>
|