parthmax commited on
Commit
7d73e9e
Β·
verified Β·
1 Parent(s): 4ab541b

Update templates/index.html

Browse files
Files changed (1) hide show
  1. templates/index.html +577 -130
templates/index.html CHANGED
@@ -1,184 +1,631 @@
1
  <!DOCTYPE html>
2
-
3
  <html lang="en">
4
  <head>
5
- <meta charset="UTF-8">
6
- <title>QuickDrop</title>
7
-
 
 
8
  <style>
9
- body {
10
- font-family: 'Segoe UI', sans-serif;
11
- background: linear-gradient(135deg, #020617, #0f172a);
12
- color: white;
13
- text-align: center;
14
- padding: 40px;
 
 
 
 
15
  }
16
 
17
- h1 { font-size: 34px; }
18
 
19
- .drop-area {
20
- border: 2px dashed #3b82f6;
21
- padding: 50px;
22
- border-radius: 18px;
23
- margin-top: 20px;
24
- cursor: pointer;
 
 
 
 
 
 
25
  }
26
 
27
- .drop-area.hover {
28
- background: #1e293b;
 
 
 
29
  }
30
 
31
- input { display: none; }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
32
 
33
- .progress {
34
- margin-top: 20px;
35
- background: #1e293b;
36
- border-radius: 10px;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
37
  }
 
38
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
39
  .progress-bar {
40
- height: 10px;
41
- width: 0%;
42
- background: #22c55e;
 
 
43
  }
44
 
45
- .preview { margin-top: 20px; }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
46
 
47
- button {
48
- margin-top: 10px;
49
- padding: 10px 20px;
50
- background: #3b82f6;
51
- border: none;
52
- border-radius: 8px;
53
- color: white;
54
- cursor: pointer;
 
 
 
 
 
 
 
 
 
 
 
 
 
55
  }
 
56
 
57
- .copy-btn { background: #22c55e; }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
58
 
59
- a { color: #38bdf8; }
60
- </style>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
61
 
62
- </head>
 
 
 
 
63
 
64
- <body>
 
 
 
 
 
 
 
 
65
 
66
- <h1>⚑ QuickDrop</h1>
67
- <p>Upload & Share Files Instantly</p>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
68
 
69
- <div class="drop-area" id="dropArea">
70
- Click or Drag file here
 
 
 
 
 
 
 
 
 
 
71
  </div>
72
 
73
- <input type="file" id="fileInput">
74
-
75
- <div class="progress">
 
 
 
 
76
  <div class="progress-bar" id="progressBar"></div>
 
77
  </div>
78
 
79
- <div id="progressText"></div>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
80
 
81
- <div class="preview" id="preview"></div>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
82
 
83
- <h3>πŸ”— Share Link</h3>
84
- <p id="link">No upload yet</p>
85
 
86
  <script>
87
-
88
- let file;
89
-
90
- const dropArea = document.getElementById("dropArea");
91
- const fileInput = document.getElementById("fileInput");
92
-
93
- // Click
94
- dropArea.onclick = () => fileInput.click();
95
-
96
- // Drag
97
- dropArea.addEventListener("dragover", e => {
98
- e.preventDefault();
99
- dropArea.classList.add("hover");
 
 
 
 
 
 
 
 
 
 
 
 
 
 
100
  });
101
 
102
- dropArea.addEventListener("dragleave", () => {
103
- dropArea.classList.remove("hover");
 
104
  });
105
 
106
- dropArea.addEventListener("drop", e => {
107
- e.preventDefault();
108
- dropArea.classList.remove("hover");
109
- file = e.dataTransfer.files[0];
110
- preview(file);
111
  });
112
 
113
- fileInput.addEventListener("change", e => {
114
- file = e.target.files[0];
115
- preview(file);
116
- });
117
-
118
- // Preview
119
- function preview(f) {
120
- const preview = document.getElementById("preview");
121
- preview.innerHTML = "";
122
-
123
- if (!f) return;
124
-
125
- const size = (f.size / 1024).toFixed(1);
126
-
127
- preview.innerHTML = `<p>${f.name} (${size} KB)</p>`;
128
-
129
- if (f.type.startsWith("image")) {
130
- preview.innerHTML += `<img src="${URL.createObjectURL(f)}" width="200">`;
 
 
 
 
 
 
 
 
 
 
 
 
 
131
  }
 
132
 
133
- const btn = document.createElement("button");
134
- btn.innerText = "Upload";
135
- btn.onclick = upload;
136
- preview.appendChild(btn);
137
  }
138
 
139
- // Upload
140
- function upload() {
141
- if (!file) return alert("Select file");
142
-
143
- const xhr = new XMLHttpRequest();
144
- const bar = document.getElementById("progressBar");
145
- const text = document.getElementById("progressText");
146
-
147
- xhr.upload.onprogress = e => {
148
- let percent = Math.round((e.loaded / e.total) * 100);
149
- bar.style.width = percent + "%";
150
- text.innerText = percent + "% uploaded";
151
- };
152
-
153
- xhr.onload = () => {
154
- if (xhr.status === 200) {
155
- const data = JSON.parse(xhr.responseText);
156
-
157
- const url = data.link;
158
 
159
- document.getElementById("link").innerHTML = `
160
- <a href="${url}" target="_blank">${url}</a><br>
161
- <button class="copy-btn" onclick="copy('${url}')">Copy</button>
162
- `;
163
- } else {
164
- document.getElementById("link").innerText = "Upload failed ❌";
165
- }
166
- };
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
167
 
168
- xhr.open("POST", "/upload");
 
 
 
 
 
 
 
 
 
 
169
 
170
- const formData = new FormData();
171
- formData.append("file", file);
 
172
 
173
- xhr.send(formData);
 
 
 
 
 
 
 
 
 
 
 
 
174
  }
175
 
176
- // Copy
177
- function copy(url) {
178
- navigator.clipboard.writeText(url);
 
 
179
  }
180
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
181
  </script>
182
-
183
  </body>
184
- </html>
 
1
  <!DOCTYPE html>
 
2
  <html lang="en">
3
  <head>
4
+ <meta charset="UTF-8"/>
5
+ <meta name="viewport" content="width=device-width,initial-scale=1"/>
6
+ <title>DropVault Β· Instant File Share</title>
7
+ <link rel="preconnect" href="https://fonts.googleapis.com">
8
+ <link href="https://fonts.googleapis.com/css2?family=Syne:wght@400;600;700;800&family=Instrument+Sans:ital,wght@0,400;0,500;1,400&display=swap" rel="stylesheet">
9
  <style>
10
+ :root {
11
+ --bg: #f5f2eb;
12
+ --ink: #1a1814;
13
+ --muted: #8a8478;
14
+ --accent: #d4481a;
15
+ --accent2: #2a5caa;
16
+ --card: #ffffff;
17
+ --border: #ddd9d0;
18
+ --success: #2a7a4b;
19
+ --warn: #c07a1a;
20
  }
21
 
22
+ *, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
23
 
24
+ body {
25
+ font-family: 'Instrument Sans', sans-serif;
26
+ background: var(--bg);
27
+ color: var(--ink);
28
+ min-height: 100vh;
29
+ display: flex;
30
+ flex-direction: column;
31
+ align-items: center;
32
+ padding: 3rem 1.5rem 5rem;
33
+ background-image:
34
+ radial-gradient(circle at 10% 20%, rgba(212,72,26,0.06) 0%, transparent 50%),
35
+ radial-gradient(circle at 90% 80%, rgba(42,92,170,0.06) 0%, transparent 50%);
36
  }
37
 
38
+ /* ── Header ── */
39
+ header {
40
+ text-align: center;
41
+ margin-bottom: 3.5rem;
42
+ animation: fadeDown 0.6s ease both;
43
  }
44
 
45
+ .logo {
46
+ font-family: 'Syne', sans-serif;
47
+ font-weight: 800;
48
+ font-size: clamp(2.4rem, 6vw, 3.8rem);
49
+ letter-spacing: -0.04em;
50
+ color: var(--ink);
51
+ line-height: 1;
52
+ }
53
+ .logo span { color: var(--accent); }
54
+
55
+ .tagline {
56
+ margin-top: 0.5rem;
57
+ font-size: 0.85rem;
58
+ color: var(--muted);
59
+ letter-spacing: 0.18em;
60
+ text-transform: uppercase;
61
+ font-weight: 500;
62
+ }
63
 
64
+ /* ── Drop zone ── */
65
+ .drop-zone {
66
+ width: min(580px, 100%);
67
+ background: var(--card);
68
+ border: 2px dashed var(--border);
69
+ border-radius: 1.4rem;
70
+ padding: 3.5rem 2rem;
71
+ display: flex;
72
+ flex-direction: column;
73
+ align-items: center;
74
+ gap: 1.1rem;
75
+ cursor: pointer;
76
+ transition: border-color 0.2s, background 0.2s, transform 0.15s;
77
+ animation: fadeUp 0.6s 0.1s ease both;
78
+ position: relative;
79
+ overflow: hidden;
80
+ }
81
+ .drop-zone::before {
82
+ content: '';
83
+ position: absolute;
84
+ inset: 0;
85
+ background: radial-gradient(circle at 50% 0%, rgba(212,72,26,0.04), transparent 60%);
86
+ pointer-events: none;
87
+ }
88
+ .drop-zone:hover, .drop-zone.drag-over {
89
+ border-color: var(--accent);
90
+ background: #fff8f6;
91
+ transform: translateY(-2px);
92
+ }
93
+ .drop-zone.drag-over { border-style: solid; }
94
+
95
+ .drop-icon {
96
+ width: 4.5rem;
97
+ height: 4.5rem;
98
+ background: linear-gradient(135deg, #fff0ec, #fde4db);
99
+ border-radius: 1.2rem;
100
+ display: flex;
101
+ align-items: center;
102
+ justify-content: center;
103
+ font-size: 2rem;
104
+ transition: transform 0.2s;
105
+ border: 1.5px solid rgba(212,72,26,0.15);
106
  }
107
+ .drop-zone:hover .drop-icon { transform: scale(1.08) rotate(-4deg); }
108
 
109
+ .drop-text {
110
+ font-family: 'Syne', sans-serif;
111
+ font-size: 1.1rem;
112
+ font-weight: 600;
113
+ color: var(--ink);
114
+ }
115
+ .drop-sub {
116
+ font-size: 0.8rem;
117
+ color: var(--muted);
118
+ text-align: center;
119
+ line-height: 1.6;
120
+ }
121
+
122
+ .browse-btn {
123
+ background: var(--accent);
124
+ color: white;
125
+ border: none;
126
+ padding: 0.65rem 1.8rem;
127
+ border-radius: 2rem;
128
+ font-family: 'Syne', sans-serif;
129
+ font-weight: 600;
130
+ font-size: 0.88rem;
131
+ cursor: pointer;
132
+ letter-spacing: 0.02em;
133
+ transition: background 0.15s, transform 0.1s;
134
+ }
135
+ .browse-btn:hover { background: #bc3e14; transform: scale(1.03); }
136
+ .browse-btn:active { transform: scale(0.98); }
137
+
138
+ #fileInput { display: none; }
139
+
140
+ /* ── Progress ── */
141
+ .progress-wrap {
142
+ width: min(580px, 100%);
143
+ display: none;
144
+ flex-direction: column;
145
+ gap: 0.6rem;
146
+ animation: fadeUp 0.4s ease both;
147
+ }
148
+ .progress-wrap.show { display: flex; }
149
+ .progress-label {
150
+ font-size: 0.82rem;
151
+ color: var(--muted);
152
+ display: flex;
153
+ justify-content: space-between;
154
+ }
155
+ .progress-bar-bg {
156
+ height: 6px;
157
+ background: var(--border);
158
+ border-radius: 99px;
159
+ overflow: hidden;
160
+ }
161
  .progress-bar {
162
+ height: 100%;
163
+ background: linear-gradient(90deg, var(--accent), #e8703a);
164
+ border-radius: 99px;
165
+ width: 0%;
166
+ transition: width 0.3s ease;
167
  }
168
 
169
+ /* ── Result card ── */
170
+ .result-card {
171
+ width: min(580px, 100%);
172
+ background: var(--card);
173
+ border: 1.5px solid var(--border);
174
+ border-radius: 1.4rem;
175
+ padding: 1.8rem;
176
+ display: none;
177
+ flex-direction: column;
178
+ gap: 1.2rem;
179
+ animation: fadeUp 0.5s ease both;
180
+ position: relative;
181
+ overflow: hidden;
182
+ }
183
+ .result-card::before {
184
+ content: '';
185
+ position: absolute;
186
+ top: 0; left: 0; right: 0;
187
+ height: 3px;
188
+ background: linear-gradient(90deg, var(--accent), var(--accent2));
189
+ }
190
+ .result-card.show { display: flex; }
191
 
192
+ .result-header {
193
+ display: flex;
194
+ align-items: center;
195
+ gap: 0.8rem;
196
+ }
197
+ .result-icon {
198
+ width: 2.8rem;
199
+ height: 2.8rem;
200
+ background: #eef6f1;
201
+ border-radius: 0.8rem;
202
+ display: flex;
203
+ align-items: center;
204
+ justify-content: center;
205
+ font-size: 1.3rem;
206
+ flex-shrink: 0;
207
+ }
208
+ .result-name {
209
+ font-family: 'Syne', sans-serif;
210
+ font-weight: 700;
211
+ font-size: 1rem;
212
+ word-break: break-all;
213
  }
214
+ .result-meta { font-size: 0.78rem; color: var(--muted); margin-top: 0.1rem; }
215
 
216
+ .link-row {
217
+ display: flex;
218
+ gap: 0.6rem;
219
+ align-items: center;
220
+ }
221
+ .link-box {
222
+ flex: 1;
223
+ background: #f7f5f0;
224
+ border: 1.5px solid var(--border);
225
+ border-radius: 0.7rem;
226
+ padding: 0.65rem 0.9rem;
227
+ font-size: 0.82rem;
228
+ color: var(--accent2);
229
+ word-break: break-all;
230
+ font-family: 'Instrument Sans', monospace;
231
+ }
232
+ .copy-btn {
233
+ background: var(--ink);
234
+ color: white;
235
+ border: none;
236
+ padding: 0.65rem 1.2rem;
237
+ border-radius: 0.7rem;
238
+ font-family: 'Syne', sans-serif;
239
+ font-weight: 600;
240
+ font-size: 0.8rem;
241
+ cursor: pointer;
242
+ white-space: nowrap;
243
+ transition: background 0.15s, transform 0.1s;
244
+ flex-shrink: 0;
245
+ }
246
+ .copy-btn:hover { background: #333; }
247
+ .copy-btn.copied { background: var(--success); }
248
+
249
+ /* ── Timer ── */
250
+ .timer-row {
251
+ display: flex;
252
+ align-items: center;
253
+ gap: 0.7rem;
254
+ }
255
+ .timer-label {
256
+ font-size: 0.78rem;
257
+ color: var(--muted);
258
+ white-space: nowrap;
259
+ }
260
+ .timer-bar-bg {
261
+ flex: 1;
262
+ height: 5px;
263
+ background: var(--border);
264
+ border-radius: 99px;
265
+ overflow: hidden;
266
+ }
267
+ .timer-bar {
268
+ height: 100%;
269
+ background: linear-gradient(90deg, var(--success), #6fcf97);
270
+ border-radius: 99px;
271
+ width: 100%;
272
+ transition: width 1s linear, background 1s;
273
+ }
274
+ .timer-count {
275
+ font-family: 'Syne', sans-serif;
276
+ font-weight: 700;
277
+ font-size: 0.85rem;
278
+ color: var(--ink);
279
+ min-width: 2.5rem;
280
+ text-align: right;
281
+ }
282
 
283
+ .download-btn {
284
+ display: flex;
285
+ align-items: center;
286
+ justify-content: center;
287
+ gap: 0.5rem;
288
+ background: transparent;
289
+ color: var(--accent2);
290
+ border: 1.5px solid var(--accent2);
291
+ padding: 0.65rem 1.4rem;
292
+ border-radius: 0.7rem;
293
+ font-family: 'Syne', sans-serif;
294
+ font-weight: 600;
295
+ font-size: 0.85rem;
296
+ cursor: pointer;
297
+ text-decoration: none;
298
+ transition: background 0.15s, color 0.15s;
299
+ }
300
+ .download-btn:hover { background: var(--accent2); color: white; }
301
+
302
+ .upload-another {
303
+ background: none;
304
+ border: none;
305
+ color: var(--muted);
306
+ font-size: 0.8rem;
307
+ cursor: pointer;
308
+ text-align: center;
309
+ font-family: 'Instrument Sans', sans-serif;
310
+ text-decoration: underline;
311
+ transition: color 0.15s;
312
+ }
313
+ .upload-another:hover { color: var(--ink); }
314
+
315
+ /* ── How it works ── */
316
+ .steps {
317
+ width: min(580px, 100%);
318
+ margin-top: 2.5rem;
319
+ display: grid;
320
+ grid-template-columns: repeat(3, 1fr);
321
+ gap: 1rem;
322
+ animation: fadeUp 0.6s 0.25s ease both;
323
+ }
324
+ .step {
325
+ background: var(--card);
326
+ border: 1.5px solid var(--border);
327
+ border-radius: 1rem;
328
+ padding: 1.2rem 1rem;
329
+ text-align: center;
330
+ display: flex;
331
+ flex-direction: column;
332
+ align-items: center;
333
+ gap: 0.5rem;
334
+ }
335
+ .step-num {
336
+ font-family: 'Syne', sans-serif;
337
+ font-weight: 800;
338
+ font-size: 1.5rem;
339
+ color: var(--border);
340
+ line-height: 1;
341
+ }
342
+ .step-title {
343
+ font-family: 'Syne', sans-serif;
344
+ font-weight: 600;
345
+ font-size: 0.85rem;
346
+ }
347
+ .step-desc { font-size: 0.75rem; color: var(--muted); line-height: 1.5; }
348
 
349
+ @media (max-width: 480px) {
350
+ .steps { grid-template-columns: 1fr; }
351
+ .link-row { flex-direction: column; }
352
+ .copy-btn { width: 100%; }
353
+ }
354
 
355
+ /* ── Animations ── */
356
+ @keyframes fadeDown {
357
+ from { opacity: 0; transform: translateY(-18px); }
358
+ to { opacity: 1; transform: translateY(0); }
359
+ }
360
+ @keyframes fadeUp {
361
+ from { opacity: 0; transform: translateY(18px); }
362
+ to { opacity: 1; transform: translateY(0); }
363
+ }
364
 
365
+ /* ── Toast ── */
366
+ .toast {
367
+ position: fixed;
368
+ bottom: 2rem;
369
+ left: 50%;
370
+ transform: translateX(-50%) translateY(4rem);
371
+ background: var(--ink);
372
+ color: white;
373
+ padding: 0.7rem 1.5rem;
374
+ border-radius: 2rem;
375
+ font-size: 0.85rem;
376
+ font-family: 'Syne', sans-serif;
377
+ font-weight: 600;
378
+ transition: transform 0.35s cubic-bezier(0.34,1.56,0.64,1);
379
+ pointer-events: none;
380
+ z-index: 100;
381
+ white-space: nowrap;
382
+ }
383
+ .toast.show { transform: translateX(-50%) translateY(0); }
384
+ </style>
385
+ </head>
386
+ <body>
387
 
388
+ <header>
389
+ <div class="logo">Drop<span>Vault</span></div>
390
+ <div class="tagline">Instant Β· Secure Β· 5-Minute Links</div>
391
+ </header>
392
+
393
+ <!-- Drop Zone -->
394
+ <div class="drop-zone" id="dropZone">
395
+ <div class="drop-icon">πŸ“¦</div>
396
+ <div class="drop-text">Drop your file here</div>
397
+ <div class="drop-sub">Any file type supported<br>Link auto-expires in 5 minutes</div>
398
+ <button class="browse-btn" onclick="document.getElementById('fileInput').click()">Browse Files</button>
399
+ <input type="file" id="fileInput"/>
400
  </div>
401
 
402
+ <!-- Progress -->
403
+ <div class="progress-wrap" id="progressWrap" style="margin-top:1rem">
404
+ <div class="progress-label">
405
+ <span id="progressFile">Uploading…</span>
406
+ <span id="progressPct">0%</span>
407
+ </div>
408
+ <div class="progress-bar-bg">
409
  <div class="progress-bar" id="progressBar"></div>
410
+ </div>
411
  </div>
412
 
413
+ <!-- Result -->
414
+ <div class="result-card" id="resultCard" style="margin-top:1rem">
415
+ <div class="result-header">
416
+ <div class="result-icon">βœ…</div>
417
+ <div>
418
+ <div class="result-name" id="resultName">filename.txt</div>
419
+ <div class="result-meta">Ready to share Β· expires in <span id="metaTimer">5:00</span></div>
420
+ </div>
421
+ </div>
422
+
423
+ <div class="link-row">
424
+ <div class="link-box" id="linkBox">–</div>
425
+ <button class="copy-btn" id="copyBtn" onclick="copyLink()">Copy</button>
426
+ </div>
427
+
428
+ <div class="timer-row">
429
+ <span class="timer-label">Expires in</span>
430
+ <div class="timer-bar-bg">
431
+ <div class="timer-bar" id="timerBar"></div>
432
+ </div>
433
+ <span class="timer-count" id="timerCount">5:00</span>
434
+ </div>
435
+
436
+ <a class="download-btn" id="downloadBtn" href="#" target="_blank">⬇ Download File</a>
437
+
438
+ <button class="upload-another" onclick="resetUI()">Upload another file</button>
439
+ </div>
440
 
441
+ <!-- Steps -->
442
+ <div class="steps" id="steps">
443
+ <div class="step">
444
+ <div class="step-num">01</div>
445
+ <div class="step-title">Upload</div>
446
+ <div class="step-desc">Drop or browse any file β€” no account needed</div>
447
+ </div>
448
+ <div class="step">
449
+ <div class="step-num">02</div>
450
+ <div class="step-title">Share</div>
451
+ <div class="step-desc">Copy the link and send it to anyone instantly</div>
452
+ </div>
453
+ <div class="step">
454
+ <div class="step-num">03</div>
455
+ <div class="step-title">Auto-Delete</div>
456
+ <div class="step-desc">File vanishes after 5 minutes β€” zero clutter</div>
457
+ </div>
458
+ </div>
459
 
460
+ <!-- Toast -->
461
+ <div class="toast" id="toast">Link copied!</div>
462
 
463
  <script>
464
+ const dropZone = document.getElementById('dropZone');
465
+ const fileInput = document.getElementById('fileInput');
466
+ const progressWrap = document.getElementById('progressWrap');
467
+ const progressBar = document.getElementById('progressBar');
468
+ const progressPct = document.getElementById('progressPct');
469
+ const progressFile = document.getElementById('progressFile');
470
+ const resultCard = document.getElementById('resultCard');
471
+ const resultName = document.getElementById('resultName');
472
+ const linkBox = document.getElementById('linkBox');
473
+ const copyBtn = document.getElementById('copyBtn');
474
+ const downloadBtn= document.getElementById('downloadBtn');
475
+ const timerBar = document.getElementById('timerBar');
476
+ const timerCount = document.getElementById('timerCount');
477
+ const metaTimer = document.getElementById('metaTimer');
478
+ const steps = document.getElementById('steps');
479
+ const toast = document.getElementById('toast');
480
+
481
+ let timerInterval = null;
482
+
483
+ // ── Drag & Drop ──────────────────────────────────────────────────────────────
484
+ dropZone.addEventListener('dragover', e => { e.preventDefault(); dropZone.classList.add('drag-over'); });
485
+ dropZone.addEventListener('dragleave', () => dropZone.classList.remove('drag-over'));
486
+ dropZone.addEventListener('drop', e => {
487
+ e.preventDefault();
488
+ dropZone.classList.remove('drag-over');
489
+ const file = e.dataTransfer.files[0];
490
+ if (file) uploadFile(file);
491
  });
492
 
493
+ dropZone.addEventListener('click', e => {
494
+ if (e.target.classList.contains('browse-btn')) return;
495
+ fileInput.click();
496
  });
497
 
498
+ fileInput.addEventListener('change', () => {
499
+ if (fileInput.files[0]) uploadFile(fileInput.files[0]);
 
 
 
500
  });
501
 
502
+ // ── Upload ───────────────────────────────────────────────────────────────────
503
+ function uploadFile(file) {
504
+ dropZone.style.display = 'none';
505
+ steps.style.display = 'none';
506
+ progressWrap.classList.add('show');
507
+ progressFile.textContent = file.name;
508
+ progressBar.style.width = '0%';
509
+ progressPct.textContent = '0%';
510
+
511
+ const form = new FormData();
512
+ form.append('file', file);
513
+
514
+ const xhr = new XMLHttpRequest();
515
+ xhr.open('POST', '/upload');
516
+
517
+ xhr.upload.onprogress = e => {
518
+ if (e.lengthComputable) {
519
+ const pct = Math.round(e.loaded / e.total * 100);
520
+ progressBar.style.width = pct + '%';
521
+ progressPct.textContent = pct + '%';
522
+ }
523
+ };
524
+
525
+ xhr.onload = () => {
526
+ progressWrap.classList.remove('show');
527
+ if (xhr.status === 200) {
528
+ const data = JSON.parse(xhr.responseText);
529
+ showResult(data, file.name);
530
+ } else {
531
+ alert('Upload failed. Please try again.');
532
+ resetUI();
533
  }
534
+ };
535
 
536
+ xhr.onerror = () => { alert('Network error.'); resetUI(); };
537
+ xhr.send(form);
 
 
538
  }
539
 
540
+ // ── Show result ───────────────────────────────────────────────────────────────
541
+ function showResult(data, originalName) {
542
+ const fullLink = window.location.origin + data.link;
543
+ resultName.textContent = originalName;
544
+ linkBox.textContent = fullLink;
545
+ downloadBtn.href = data.link;
546
+ resultCard.classList.add('show');
547
+ startTimer(300);
548
+ }
 
 
 
 
 
 
 
 
 
 
549
 
550
+ // ── Countdown timer ───────────────────────────────────────────────────────────
551
+ function startTimer(seconds) {
552
+ if (timerInterval) clearInterval(timerInterval);
553
+ let remaining = seconds;
554
+
555
+ function tick() {
556
+ const m = Math.floor(remaining / 60);
557
+ const s = remaining % 60;
558
+ const label = `${m}:${s.toString().padStart(2, '0')}`;
559
+ timerCount.textContent = label;
560
+ metaTimer.textContent = label;
561
+
562
+ const pct = (remaining / 300) * 100;
563
+ timerBar.style.width = pct + '%';
564
+
565
+ // Color shift: green β†’ orange β†’ red
566
+ if (pct > 50) {
567
+ timerBar.style.background = 'linear-gradient(90deg,#2a7a4b,#6fcf97)';
568
+ } else if (pct > 20) {
569
+ timerBar.style.background = 'linear-gradient(90deg,#c07a1a,#f4a742)';
570
+ } else {
571
+ timerBar.style.background = 'linear-gradient(90deg,#d4481a,#f07050)';
572
+ }
573
 
574
+ if (remaining <= 0) {
575
+ clearInterval(timerInterval);
576
+ timerCount.textContent = 'Expired';
577
+ linkBox.textContent = 'Link has expired';
578
+ downloadBtn.style.opacity = '0.4';
579
+ downloadBtn.style.pointerEvents = 'none';
580
+ copyBtn.style.opacity = '0.4';
581
+ copyBtn.style.pointerEvents = 'none';
582
+ }
583
+ remaining--;
584
+ }
585
 
586
+ tick();
587
+ timerInterval = setInterval(tick, 1000);
588
+ }
589
 
590
+ // ── Copy link ─────────────────────────────────────────────────────────────────
591
+ function copyLink() {
592
+ const link = linkBox.textContent;
593
+ if (!link || link === '–' || link.includes('expired')) return;
594
+ navigator.clipboard.writeText(link).then(() => {
595
+ copyBtn.textContent = 'Copied!';
596
+ copyBtn.classList.add('copied');
597
+ showToast('Link copied to clipboard!');
598
+ setTimeout(() => {
599
+ copyBtn.textContent = 'Copy';
600
+ copyBtn.classList.remove('copied');
601
+ }, 2000);
602
+ });
603
  }
604
 
605
+ // ── Toast ─────────────────────────────────────────────────────────────────────
606
+ function showToast(msg) {
607
+ toast.textContent = msg;
608
+ toast.classList.add('show');
609
+ setTimeout(() => toast.classList.remove('show'), 2500);
610
  }
611
 
612
+ // ── Reset ─────────────────────────────────────────────────────────────────────
613
+ function resetUI() {
614
+ if (timerInterval) clearInterval(timerInterval);
615
+ resultCard.classList.remove('show');
616
+ progressWrap.classList.remove('show');
617
+ dropZone.style.display = '';
618
+ steps.style.display = '';
619
+ fileInput.value = '';
620
+ progressBar.style.width = '0%';
621
+ copyBtn.textContent = 'Copy';
622
+ copyBtn.classList.remove('copied');
623
+ downloadBtn.style.opacity = '';
624
+ downloadBtn.style.pointerEvents = '';
625
+ copyBtn.style.opacity = '';
626
+ copyBtn.style.pointerEvents = '';
627
+ timerBar.style.width = '100%';
628
+ }
629
  </script>
 
630
  </body>
631
+ </html>