koyelog commited on
Commit
2c6864e
Β·
verified Β·
1 Parent(s): 459d441

Create index.html

Browse files
Files changed (1) hide show
  1. index.html +658 -0
index.html ADDED
@@ -0,0 +1,658 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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.0">
6
+ <title>🎭 AI Emotion Detector | koyelog</title>
7
+
8
+ <!-- Fonts -->
9
+ <link href="https://fonts.googleapis.com/css2?family=Poppins:wght@300;400;500;600;700&display=swap" rel="stylesheet">
10
+
11
+ <!-- Font Awesome for icons -->
12
+ <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
13
+
14
+ <style>
15
+ * {
16
+ margin: 0;
17
+ padding: 0;
18
+ box-sizing: border-box;
19
+ }
20
+
21
+ body {
22
+ font-family: 'Poppins', sans-serif;
23
+ background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
24
+ min-height: 100vh;
25
+ padding: 20px;
26
+ color: #333;
27
+ }
28
+
29
+ .container {
30
+ max-width: 1200px;
31
+ margin: 0 auto;
32
+ }
33
+
34
+ /* Header */
35
+ .header {
36
+ text-align: center;
37
+ color: white;
38
+ padding: 40px 20px;
39
+ margin-bottom: 40px;
40
+ animation: fadeInDown 0.8s ease;
41
+ }
42
+
43
+ .header h1 {
44
+ font-size: 3.5rem;
45
+ font-weight: 700;
46
+ margin-bottom: 15px;
47
+ text-shadow: 2px 2px 4px rgba(0,0,0,0.2);
48
+ }
49
+
50
+ .header p {
51
+ font-size: 1.2rem;
52
+ opacity: 0.95;
53
+ }
54
+
55
+ .badge {
56
+ display: inline-block;
57
+ background: rgba(255,255,255,0.2);
58
+ padding: 8px 20px;
59
+ border-radius: 20px;
60
+ margin: 10px 5px;
61
+ font-size: 0.9rem;
62
+ backdrop-filter: blur(10px);
63
+ }
64
+
65
+ /* Main Card */
66
+ .main-card {
67
+ background: white;
68
+ border-radius: 25px;
69
+ box-shadow: 0 20px 60px rgba(0,0,0,0.2);
70
+ overflow: hidden;
71
+ animation: fadeInUp 0.8s ease;
72
+ }
73
+
74
+ /* Tabs */
75
+ .tabs {
76
+ display: flex;
77
+ background: #f8f9fa;
78
+ border-bottom: 2px solid #e9ecef;
79
+ }
80
+
81
+ .tab-button {
82
+ flex: 1;
83
+ padding: 20px;
84
+ border: none;
85
+ background: transparent;
86
+ font-size: 1.1rem;
87
+ font-weight: 600;
88
+ cursor: pointer;
89
+ transition: all 0.3s ease;
90
+ color: #666;
91
+ }
92
+
93
+ .tab-button:hover {
94
+ background: rgba(102, 126, 234, 0.1);
95
+ }
96
+
97
+ .tab-button.active {
98
+ background: white;
99
+ color: #667eea;
100
+ border-bottom: 3px solid #667eea;
101
+ }
102
+
103
+ .tab-button i {
104
+ margin-right: 10px;
105
+ }
106
+
107
+ /* Tab Content */
108
+ .tab-content {
109
+ display: none;
110
+ padding: 40px;
111
+ animation: fadeIn 0.5s ease;
112
+ }
113
+
114
+ .tab-content.active {
115
+ display: block;
116
+ }
117
+
118
+ /* Upload Section */
119
+ .upload-section {
120
+ display: grid;
121
+ grid-template-columns: 1fr 1fr;
122
+ gap: 30px;
123
+ margin-bottom: 30px;
124
+ }
125
+
126
+ .upload-area {
127
+ border: 3px dashed #667eea;
128
+ border-radius: 20px;
129
+ padding: 40px;
130
+ text-align: center;
131
+ cursor: pointer;
132
+ transition: all 0.3s ease;
133
+ background: #f8f9fa;
134
+ }
135
+
136
+ .upload-area:hover {
137
+ background: #e9ecef;
138
+ border-color: #764ba2;
139
+ transform: translateY(-5px);
140
+ }
141
+
142
+ .upload-area i {
143
+ font-size: 4rem;
144
+ color: #667eea;
145
+ margin-bottom: 20px;
146
+ }
147
+
148
+ .upload-area input[type="file"] {
149
+ display: none;
150
+ }
151
+
152
+ /* Webcam */
153
+ #webcam-video, #preview-image {
154
+ width: 100%;
155
+ border-radius: 15px;
156
+ box-shadow: 0 4px 16px rgba(0,0,0,0.1);
157
+ }
158
+
159
+ .button {
160
+ display: inline-block;
161
+ padding: 15px 40px;
162
+ margin: 10px 5px;
163
+ border: none;
164
+ border-radius: 50px;
165
+ font-size: 1.1rem;
166
+ font-weight: 600;
167
+ cursor: pointer;
168
+ transition: all 0.3s ease;
169
+ box-shadow: 0 4px 15px rgba(0,0,0,0.2);
170
+ }
171
+
172
+ .button-primary {
173
+ background: linear-gradient(135deg, #667eea, #764ba2);
174
+ color: white;
175
+ }
176
+
177
+ .button-primary:hover {
178
+ transform: translateY(-3px);
179
+ box-shadow: 0 6px 20px rgba(102, 126, 234, 0.4);
180
+ }
181
+
182
+ .button-secondary {
183
+ background: #6c757d;
184
+ color: white;
185
+ }
186
+
187
+ .button-secondary:hover {
188
+ background: #5a6268;
189
+ }
190
+
191
+ /* Result Section */
192
+ .result-section {
193
+ background: linear-gradient(135deg, #f8f9fa, #e9ecef);
194
+ border-radius: 20px;
195
+ padding: 30px;
196
+ margin-top: 30px;
197
+ display: none;
198
+ animation: fadeIn 0.5s ease;
199
+ }
200
+
201
+ .result-section.show {
202
+ display: block;
203
+ }
204
+
205
+ .emotion-result {
206
+ text-align: center;
207
+ padding: 40px;
208
+ background: white;
209
+ border-radius: 20px;
210
+ margin-bottom: 30px;
211
+ box-shadow: 0 4px 16px rgba(0,0,0,0.1);
212
+ }
213
+
214
+ .emotion-emoji {
215
+ font-size: 120px;
216
+ animation: bounce 1s ease infinite;
217
+ }
218
+
219
+ .emotion-name {
220
+ font-size: 3rem;
221
+ font-weight: 700;
222
+ margin: 20px 0;
223
+ }
224
+
225
+ .confidence-bar {
226
+ width: 100%;
227
+ height: 40px;
228
+ background: #e9ecef;
229
+ border-radius: 20px;
230
+ overflow: hidden;
231
+ margin: 20px 0;
232
+ box-shadow: inset 0 2px 4px rgba(0,0,0,0.1);
233
+ }
234
+
235
+ .confidence-fill {
236
+ height: 100%;
237
+ background: linear-gradient(90deg, #667eea, #764ba2);
238
+ display: flex;
239
+ align-items: center;
240
+ justify-content: center;
241
+ color: white;
242
+ font-weight: 600;
243
+ transition: width 1s ease;
244
+ }
245
+
246
+ /* All Emotions List */
247
+ .emotions-list {
248
+ background: white;
249
+ padding: 25px;
250
+ border-radius: 15px;
251
+ box-shadow: 0 4px 16px rgba(0,0,0,0.1);
252
+ }
253
+
254
+ .emotion-item {
255
+ display: flex;
256
+ align-items: center;
257
+ justify-content: space-between;
258
+ padding: 15px;
259
+ margin: 10px 0;
260
+ background: #f8f9fa;
261
+ border-radius: 10px;
262
+ transition: all 0.3s ease;
263
+ }
264
+
265
+ .emotion-item:hover {
266
+ background: #e9ecef;
267
+ transform: translateX(5px);
268
+ }
269
+
270
+ .emotion-label {
271
+ display: flex;
272
+ align-items: center;
273
+ gap: 15px;
274
+ font-weight: 500;
275
+ }
276
+
277
+ .emotion-label span {
278
+ font-size: 2rem;
279
+ }
280
+
281
+ .emotion-bar {
282
+ width: 200px;
283
+ height: 10px;
284
+ background: #e9ecef;
285
+ border-radius: 5px;
286
+ overflow: hidden;
287
+ }
288
+
289
+ .emotion-bar-fill {
290
+ height: 100%;
291
+ transition: width 0.5s ease;
292
+ }
293
+
294
+ /* Animations */
295
+ @keyframes fadeInDown {
296
+ from {
297
+ opacity: 0;
298
+ transform: translateY(-30px);
299
+ }
300
+ to {
301
+ opacity: 1;
302
+ transform: translateY(0);
303
+ }
304
+ }
305
+
306
+ @keyframes fadeInUp {
307
+ from {
308
+ opacity: 0;
309
+ transform: translateY(30px);
310
+ }
311
+ to {
312
+ opacity: 1;
313
+ transform: translateY(0);
314
+ }
315
+ }
316
+
317
+ @keyframes fadeIn {
318
+ from { opacity: 0; }
319
+ to { opacity: 1; }
320
+ }
321
+
322
+ @keyframes bounce {
323
+ 0%, 100% { transform: translateY(0); }
324
+ 50% { transform: translateY(-20px); }
325
+ }
326
+
327
+ /* Loading Spinner */
328
+ .loading {
329
+ display: none;
330
+ text-align: center;
331
+ padding: 40px;
332
+ }
333
+
334
+ .loading.show {
335
+ display: block;
336
+ }
337
+
338
+ .spinner {
339
+ border: 5px solid #f3f3f3;
340
+ border-top: 5px solid #667eea;
341
+ border-radius: 50%;
342
+ width: 60px;
343
+ height: 60px;
344
+ animation: spin 1s linear infinite;
345
+ margin: 0 auto 20px;
346
+ }
347
+
348
+ @keyframes spin {
349
+ 0% { transform: rotate(0deg); }
350
+ 100% { transform: rotate(360deg); }
351
+ }
352
+
353
+ /* Footer */
354
+ .footer {
355
+ text-align: center;
356
+ color: white;
357
+ padding: 40px 20px;
358
+ margin-top: 40px;
359
+ background: rgba(0,0,0,0.2);
360
+ border-radius: 20px;
361
+ backdrop-filter: blur(10px);
362
+ }
363
+
364
+ .footer a {
365
+ color: white;
366
+ text-decoration: underline;
367
+ }
368
+
369
+ /* Responsive */
370
+ @media (max-width: 768px) {
371
+ .header h1 {
372
+ font-size: 2.5rem;
373
+ }
374
+
375
+ .upload-section {
376
+ grid-template-columns: 1fr;
377
+ }
378
+
379
+ .tabs {
380
+ flex-direction: column;
381
+ }
382
+
383
+ .emotion-name {
384
+ font-size: 2rem;
385
+ }
386
+
387
+ .emotion-emoji {
388
+ font-size: 80px;
389
+ }
390
+ }
391
+ </style>
392
+ </head>
393
+ <body>
394
+ <div class="container">
395
+ <!-- Header -->
396
+ <div class="header">
397
+ <h1>🎭 AI Emotion Detector</h1>
398
+ <p>Powered by Vision Transformer | 98.80% Accuracy</p>
399
+ <div>
400
+ <span class="badge">Model: koyelog/face</span>
401
+ <span class="badge">7 Emotions</span>
402
+ <span class="badge">Real-time Detection</span>
403
+ </div>
404
+ </div>
405
+
406
+ <!-- Main Card -->
407
+ <div class="main-card">
408
+ <!-- Tabs -->
409
+ <div class="tabs">
410
+ <button class="tab-button active" onclick="switchTab('webcam')">
411
+ <i class="fas fa-video"></i> Live Webcam
412
+ </button>
413
+ <button class="tab-button" onclick="switchTab('upload')">
414
+ <i class="fas fa-upload"></i> Upload Image
415
+ </button>
416
+ </div>
417
+
418
+ <!-- Webcam Tab -->
419
+ <div id="webcam-tab" class="tab-content active">
420
+ <h2 style="text-align: center; margin-bottom: 30px;">
421
+ <i class="fas fa-camera"></i> Capture Your Emotion
422
+ </h2>
423
+
424
+ <div class="upload-section">
425
+ <div>
426
+ <video id="webcam-video" autoplay playsinline></video>
427
+ <canvas id="webcam-canvas" style="display: none;"></canvas>
428
+ <div style="text-align: center; margin-top: 20px;">
429
+ <button class="button button-primary" onclick="startWebcam()">
430
+ <i class="fas fa-play"></i> Start Webcam
431
+ </button>
432
+ <button class="button button-secondary" onclick="captureAndDetect()">
433
+ <i class="fas fa-camera"></i> Capture & Detect
434
+ </button>
435
+ </div>
436
+ </div>
437
+ <div id="webcam-result"></div>
438
+ </div>
439
+ </div>
440
+
441
+ <!-- Upload Tab -->
442
+ <div id="upload-tab" class="tab-content">
443
+ <h2 style="text-align: center; margin-bottom: 30px;">
444
+ <i class="fas fa-cloud-upload-alt"></i> Upload Face Image
445
+ </h2>
446
+
447
+ <div class="upload-section">
448
+ <div>
449
+ <div class="upload-area" onclick="document.getElementById('file-input').click()">
450
+ <i class="fas fa-image"></i>
451
+ <h3>Click to Upload</h3>
452
+ <p>or drag & drop your image here</p>
453
+ <p style="color: #999; margin-top: 10px;">Supports: JPG, PNG, JPEG</p>
454
+ <input type="file" id="file-input" accept="image/*" onchange="handleFileUpload(event)">
455
+ </div>
456
+ <img id="preview-image" style="display: none; margin-top: 20px;">
457
+ </div>
458
+ <div id="upload-result"></div>
459
+ </div>
460
+ </div>
461
+
462
+ <!-- Loading -->
463
+ <div id="loading" class="loading">
464
+ <div class="spinner"></div>
465
+ <p>Analyzing emotion...</p>
466
+ </div>
467
+ </div>
468
+
469
+ <!-- Footer -->
470
+ <div class="footer">
471
+ <h3>πŸ“Š Model Information</h3>
472
+ <p><strong>Model:</strong> koyelog/face | <strong>Architecture:</strong> Vision Transformer (ViT)</p>
473
+ <p><strong>Parameters:</strong> 85.8M | <strong>Training Accuracy:</strong> 99.29% | <strong>Validation Accuracy:</strong> 98.80%</p>
474
+ <p><strong>Emotions:</strong> 😠 Angry | 🀒 Disgust | 😨 Fear | 😊 Happy | 😒 Sad | 😲 Surprise | 😐 Neutral</p>
475
+ <p style="margin-top: 20px;">
476
+ Created by <strong>Koyeliya Ghosh</strong> |
477
+ <a href="https://huggingface.co/koyelog/face" target="_blank">View Model on HuggingFace</a>
478
+ </p>
479
+ </div>
480
+ </div>
481
+
482
+ <script>
483
+ // Configuration
484
+ const API_URL = 'YOUR_HUGGINGFACE_SPACE_URL/api/predict'; // Update this!
485
+
486
+ const EMOTIONS = {
487
+ 0: { name: 'Angry', emoji: '😠', color: '#ff4444' },
488
+ 1: { name: 'Disgust', emoji: '🀒', color: '#44ff44' },
489
+ 2: { name: 'Fear', emoji: '😨', color: '#9944ff' },
490
+ 3: { name: 'Happy', emoji: '😊', color: '#ffdd44' },
491
+ 4: { name: 'Sad', emoji: '😒', color: '#4444ff' },
492
+ 5: { name: 'Surprise', emoji: '😲', color: '#ff44ff' },
493
+ 6: { name: 'Neutral', emoji: '😐', color: '#888888' }
494
+ };
495
+
496
+ let webcamStream = null;
497
+
498
+ // Tab switching
499
+ function switchTab(tab) {
500
+ document.querySelectorAll('.tab-button').forEach(btn => btn.classList.remove('active'));
501
+ document.querySelectorAll('.tab-content').forEach(content => content.classList.remove('active'));
502
+
503
+ if (tab === 'webcam') {
504
+ document.querySelector('.tab-button:first-child').classList.add('active');
505
+ document.getElementById('webcam-tab').classList.add('active');
506
+ } else {
507
+ document.querySelector('.tab-button:last-child').classList.add('active');
508
+ document.getElementById('upload-tab').classList.add('active');
509
+ }
510
+ }
511
+
512
+ // Start webcam
513
+ async function startWebcam() {
514
+ try {
515
+ webcamStream = await navigator.mediaDevices.getUserMedia({
516
+ video: { facingMode: 'user' }
517
+ });
518
+ document.getElementById('webcam-video').srcObject = webcamStream;
519
+ } catch (error) {
520
+ alert('Error accessing webcam: ' + error.message);
521
+ }
522
+ }
523
+
524
+ // Capture and detect from webcam
525
+ function captureAndDetect() {
526
+ const video = document.getElementById('webcam-video');
527
+ const canvas = document.getElementById('webcam-canvas');
528
+
529
+ canvas.width = video.videoWidth;
530
+ canvas.height = video.videoHeight;
531
+ canvas.getContext('2d').drawImage(video, 0, 0);
532
+
533
+ canvas.toBlob(blob => {
534
+ detectEmotion(blob, 'webcam-result');
535
+ }, 'image/jpeg');
536
+ }
537
+
538
+ // Handle file upload
539
+ function handleFileUpload(event) {
540
+ const file = event.target.files[0];
541
+ if (!file) return;
542
+
543
+ const reader = new FileReader();
544
+ reader.onload = (e) => {
545
+ const preview = document.getElementById('preview-image');
546
+ preview.src = e.target.result;
547
+ preview.style.display = 'block';
548
+
549
+ detectEmotion(file, 'upload-result');
550
+ };
551
+ reader.readAsDataURL(file);
552
+ }
553
+
554
+ // Detect emotion (mock function - replace with API call)
555
+ async function detectEmotion(imageBlob, resultId) {
556
+ document.getElementById('loading').classList.add('show');
557
+
558
+ // Simulate API call (replace with actual fetch to your backend)
559
+ setTimeout(() => {
560
+ const mockResult = {
561
+ predicted_id: 3, // Happy
562
+ confidence: 0.87,
563
+ probabilities: [0.02, 0.01, 0.03, 0.87, 0.04, 0.02, 0.01]
564
+ };
565
+
566
+ displayResult(mockResult, resultId);
567
+ document.getElementById('loading').classList.remove('show');
568
+ }, 2000);
569
+
570
+ /*
571
+ // Uncomment this for real API call:
572
+ const formData = new FormData();
573
+ formData.append('file', imageBlob);
574
+
575
+ try {
576
+ const response = await fetch(API_URL, {
577
+ method: 'POST',
578
+ body: formData
579
+ });
580
+ const result = await response.json();
581
+ displayResult(result, resultId);
582
+ } catch (error) {
583
+ alert('Error: ' + error.message);
584
+ } finally {
585
+ document.getElementById('loading').classList.remove('show');
586
+ }
587
+ */
588
+ }
589
+
590
+ // Display result
591
+ function displayResult(result, containerId) {
592
+ const emotion = EMOTIONS[result.predicted_id];
593
+ const confidence = (result.confidence * 100).toFixed(2);
594
+
595
+ let html = `
596
+ <div class="emotion-result" style="background: linear-gradient(135deg, ${emotion.color}15, ${emotion.color}30);">
597
+ <div class="emotion-emoji">${emotion.emoji}</div>
598
+ <div class="emotion-name" style="color: ${emotion.color};">${emotion.name}</div>
599
+ <p style="font-size: 1.5rem; color: #666;">Confidence: ${confidence}%</p>
600
+ <div class="confidence-bar">
601
+ <div class="confidence-fill" style="width: ${confidence}%; background: ${emotion.color};">
602
+ ${confidence}%
603
+ </div>
604
+ </div>
605
+ </div>
606
+
607
+ <div class="emotions-list">
608
+ <h3 style="margin-bottom: 20px;">πŸ“Š All Emotions</h3>
609
+ `;
610
+
611
+ result.probabilities.forEach((prob, idx) => {
612
+ const emo = EMOTIONS[idx];
613
+ const percentage = (prob * 100).toFixed(1);
614
+ html += `
615
+ <div class="emotion-item">
616
+ <div class="emotion-label">
617
+ <span>${emo.emoji}</span>
618
+ <strong>${emo.name}</strong>
619
+ </div>
620
+ <div style="display: flex; align-items: center; gap: 15px;">
621
+ <div class="emotion-bar">
622
+ <div class="emotion-bar-fill" style="width: ${percentage}%; background: ${emo.color};"></div>
623
+ </div>
624
+ <span style="font-weight: 600; min-width: 50px;">${percentage}%</span>
625
+ </div>
626
+ </div>
627
+ `;
628
+ });
629
+
630
+ html += '</div>';
631
+ document.getElementById(containerId).innerHTML = html;
632
+ }
633
+
634
+ // Drag and drop
635
+ const uploadArea = document.querySelector('.upload-area');
636
+
637
+ uploadArea.addEventListener('dragover', (e) => {
638
+ e.preventDefault();
639
+ uploadArea.style.background = '#e9ecef';
640
+ });
641
+
642
+ uploadArea.addEventListener('dragleave', () => {
643
+ uploadArea.style.background = '#f8f9fa';
644
+ });
645
+
646
+ uploadArea.addEventListener('drop', (e) => {
647
+ e.preventDefault();
648
+ uploadArea.style.background = '#f8f9fa';
649
+
650
+ const file = e.dataTransfer.files[0];
651
+ if (file && file.type.startsWith('image/')) {
652
+ const event = { target: { files: [file] } };
653
+ handleFileUpload(event);
654
+ }
655
+ });
656
+ </script>
657
+ </body>
658
+ </html>