Sa-m commited on
Commit
e45d9f8
·
verified ·
1 Parent(s): 87c9452

Update index.html

Browse files
Files changed (1) hide show
  1. index.html +789 -19
index.html CHANGED
@@ -1,19 +1,789 @@
1
- <!doctype html>
2
- <html>
3
- <head>
4
- <meta charset="utf-8" />
5
- <meta name="viewport" content="width=device-width" />
6
- <title>My static Space</title>
7
- <link rel="stylesheet" href="style.css" />
8
- </head>
9
- <body>
10
- <div class="card">
11
- <h1>Welcome to your static Space!</h1>
12
- <p>You can modify this app directly by editing <i>index.html</i> in the Files and versions tab.</p>
13
- <p>
14
- Also don't forget to check the
15
- <a href="https://huggingface.co/docs/hub/spaces" target="_blank">Spaces documentation</a>.
16
- </p>
17
- </div>
18
- </body>
19
- </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.0">
6
+ <title>GestureMail Pro</title>
7
+ <script src="https://cdn.jsdelivr.net/npm/@mediapipe/[email protected]/hands.js"></script>
8
+ <script src="https://cdn.jsdelivr.net/npm/@tensorflow/[email protected]/dist/tf.min.js"></script>
9
+ <style>
10
+ * {
11
+ margin: 0;
12
+ padding: 0;
13
+ box-sizing: border-box;
14
+ font-family: 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif;
15
+ }
16
+
17
+ body {
18
+ background-color: #f5f5f5;
19
+ color: #333;
20
+ overflow-x: hidden;
21
+ }
22
+
23
+ .app-container {
24
+ display: flex;
25
+ flex-direction: column;
26
+ height: 100vh;
27
+ max-width: 1600px;
28
+ margin: 0 auto;
29
+ background-color: white;
30
+ box-shadow: 0 0 20px rgba(0,0,0,0.1);
31
+ }
32
+
33
+ /* Header Styles */
34
+ .header {
35
+ display: flex;
36
+ justify-content: space-between;
37
+ align-items: center;
38
+ padding: 15px 25px;
39
+ background-color: #fff;
40
+ border-bottom: 1px solid #e0e0e0;
41
+ height: 60px;
42
+ }
43
+
44
+ .logo {
45
+ display: flex;
46
+ align-items: center;
47
+ gap: 10px;
48
+ font-size: 20px;
49
+ font-weight: bold;
50
+ color: #ea4335;
51
+ }
52
+
53
+ .logo i {
54
+ font-size: 24px;
55
+ }
56
+
57
+ .search-bar {
58
+ display: flex;
59
+ align-items: center;
60
+ background-color: #f1f3f4;
61
+ border-radius: 24px;
62
+ padding: 8px 16px;
63
+ width: 300px;
64
+ }
65
+
66
+ .search-bar input {
67
+ border: none;
68
+ background: transparent;
69
+ width: 100%;
70
+ padding: 5px;
71
+ font-size: 14px;
72
+ outline: none;
73
+ }
74
+
75
+ /* Main Content Layout */
76
+ .content {
77
+ display: flex;
78
+ flex: 1;
79
+ overflow: hidden;
80
+ }
81
+
82
+ /* Email List Styles */
83
+ .email-list {
84
+ flex: 1;
85
+ overflow-y: auto;
86
+ border-right: 1px solid #e0e0e0;
87
+ }
88
+
89
+ .email-item {
90
+ padding: 15px 20px;
91
+ border-bottom: 1px solid #f1f3f4;
92
+ cursor: pointer;
93
+ transition: background-color 0.2s;
94
+ }
95
+
96
+ .email-item:hover {
97
+ background-color: #f8f9fa;
98
+ }
99
+
100
+ .email-item.selected {
101
+ background-color: #e8f0fe;
102
+ border-left: 4px solid #4285f4;
103
+ }
104
+
105
+ .email-header {
106
+ display: flex;
107
+ justify-content: space-between;
108
+ margin-bottom: 5px;
109
+ }
110
+
111
+ .email-sender {
112
+ font-weight: bold;
113
+ color: #202124;
114
+ }
115
+
116
+ .email-time {
117
+ color: #5f6368;
118
+ font-size: 13px;
119
+ }
120
+
121
+ .email-subject {
122
+ font-weight: 500;
123
+ color: #202124;
124
+ margin-bottom: 5px;
125
+ }
126
+
127
+ .email-snippet {
128
+ color: #5f6368;
129
+ font-size: 14px;
130
+ white-space: nowrap;
131
+ overflow: hidden;
132
+ text-overflow: ellipsis;
133
+ }
134
+
135
+ /* Webcam Overlay Styles */
136
+ .webcam-container {
137
+ position: fixed;
138
+ bottom: 20px;
139
+ right: 20px;
140
+ width: 320px;
141
+ height: 240px;
142
+ border-radius: 12px;
143
+ overflow: hidden;
144
+ box-shadow: 0 4px 12px rgba(0,0,0,0.2);
145
+ z-index: 100;
146
+ background-color: #000;
147
+ }
148
+
149
+ #webcam {
150
+ width: 100%;
151
+ height: 100%;
152
+ object-fit: cover;
153
+ }
154
+
155
+ .gesture-guide {
156
+ position: fixed;
157
+ bottom: 20px;
158
+ left: 20px;
159
+ background-color: rgba(0,0,0,0.7);
160
+ color: white;
161
+ padding: 15px;
162
+ border-radius: 12px;
163
+ max-width: 300px;
164
+ z-index: 100;
165
+ backdrop-filter: blur(5px);
166
+ }
167
+
168
+ .gesture-guide h3 {
169
+ margin-bottom: 10px;
170
+ color: #4285f4;
171
+ }
172
+
173
+ .gesture-item {
174
+ display: flex;
175
+ align-items: center;
176
+ margin-bottom: 8px;
177
+ }
178
+
179
+ .gesture-icon {
180
+ width: 30px;
181
+ height: 30px;
182
+ background-color: #4285f4;
183
+ border-radius: 50%;
184
+ display: flex;
185
+ align-items: center;
186
+ justify-content: center;
187
+ margin-right: 10px;
188
+ color: white;
189
+ font-weight: bold;
190
+ }
191
+
192
+ /* Action Feedback */
193
+ .action-feedback {
194
+ position: fixed;
195
+ top: 20px;
196
+ left: 50%;
197
+ transform: translateX(-50%);
198
+ padding: 15px 25px;
199
+ border-radius: 8px;
200
+ color: white;
201
+ font-weight: bold;
202
+ z-index: 200;
203
+ opacity: 0;
204
+ transition: opacity 0.3s;
205
+ min-width: 300px;
206
+ text-align: center;
207
+ }
208
+
209
+ .action-feedback.show {
210
+ opacity: 1;
211
+ }
212
+
213
+ .action-feedback.delete {
214
+ background-color: #ea4335;
215
+ }
216
+
217
+ .action-feedback.archive {
218
+ background-color: #34a853;
219
+ }
220
+
221
+ .action-feedback.summary {
222
+ background-color: #4285f4;
223
+ }
224
+
225
+ /* Selected Email Highlight */
226
+ .selection-highlight {
227
+ position: absolute;
228
+ border: 3px solid #4285f4;
229
+ border-radius: 8px;
230
+ pointer-events: none;
231
+ z-index: 50;
232
+ animation: pulse 1.5s infinite;
233
+ }
234
+
235
+ @keyframes pulse {
236
+ 0% { box-shadow: 0 0 0 0 rgba(66, 133, 244, 0.4); }
237
+ 70% { box-shadow: 0 0 0 10px rgba(66, 133, 244, 0); }
238
+ 100% { box-shadow: 0 0 0 0 rgba(66, 133, 244, 0); }
239
+ }
240
+
241
+ /* Gesture Visualization */
242
+ .gesture-visualization {
243
+ position: fixed;
244
+ top: 0;
245
+ left: 0;
246
+ width: 100%;
247
+ height: 100%;
248
+ pointer-events: none;
249
+ z-index: 40;
250
+ }
251
+
252
+ .hand-landmarks {
253
+ position: absolute;
254
+ width: 100%;
255
+ height: 100%;
256
+ }
257
+
258
+ .hand-landmarks .landmark {
259
+ position: absolute;
260
+ width: 8px;
261
+ height: 8px;
262
+ background-color: #4285f4;
263
+ border-radius: 50%;
264
+ transform: translate(-50%, -50%);
265
+ }
266
+
267
+ .hand-landmarks .landmark.index-tip {
268
+ background-color: #34a853;
269
+ width: 12px;
270
+ height: 12px;
271
+ }
272
+
273
+ /* For mobile responsiveness */
274
+ @media (max-width: 768px) {
275
+ .webcam-container {
276
+ width: 240px;
277
+ height: 180px;
278
+ bottom: 10px;
279
+ right: 10px;
280
+ }
281
+
282
+ .gesture-guide {
283
+ max-width: 240px;
284
+ font-size: 0.9em;
285
+ padding: 10px;
286
+ }
287
+ }
288
+ </style>
289
+ </head>
290
+ <body>
291
+ <div class="app-container">
292
+ <div class="header">
293
+ <div class="logo">
294
+ <i>📧</i>
295
+ <span>GestureMail Pro</span>
296
+ </div>
297
+ <div class="search-bar">
298
+ <input type="text" placeholder="Search emails...">
299
+ </div>
300
+ </div>
301
+
302
+ <div class="content">
303
+ <div class="email-list" id="emailList">
304
+ <!-- Emails will be populated here -->
305
+ </div>
306
+ </div>
307
+ </div>
308
+
309
+ <div class="webcam-container">
310
+ <video id="webcam" autoplay playsinline></video>
311
+ </div>
312
+
313
+ <div class="gesture-guide">
314
+ <h3>🖐️ Gesture Guide</h3>
315
+ <div class="gesture-item">
316
+ <div class="gesture-icon">1</div>
317
+ <div>Point to select an email</div>
318
+ </div>
319
+ <div class="gesture-item">
320
+ <div class="gesture-icon">←</div>
321
+ <div>Swipe left to delete</div>
322
+ </div>
323
+ <div class="gesture-item">
324
+ <div class="gesture-icon">→</div>
325
+ <div>Swipe right to archive</div>
326
+ </div>
327
+ <div class="gesture-item">
328
+ <div class="gesture-icon">◯</div>
329
+ <div>Draw circle for summary</div>
330
+ </div>
331
+ </div>
332
+
333
+ <div class="action-feedback" id="actionFeedback"></div>
334
+
335
+ <div class="gesture-visualization">
336
+ <div class="hand-landmarks" id="handLandmarks"></div>
337
+ </div>
338
+ <div class="selection-highlight" id="selectionHighlight" style="display: none;"></div>
339
+
340
+ <script>
341
+ // ----------------------------
342
+ // Sample Email Data
343
+ // ----------------------------
344
+ const emails = [
345
+ {
346
+ id: 1,
347
+ sender: "Interview Kicks",
348
+ subject: "Can I Become an ML Engineer? Why Not, We Ask!",
349
+ snippet: "It's time to banish that doubt forever. Attend our masterclass to unlock your potential.",
350
+ timestamp: "1:31 PM",
351
+ category: "primary"
352
+ },
353
+ {
354
+ id: 2,
355
+ sender: "Career Brew",
356
+ subject: "7th Sep Jobs, 87 Hottest Jobs and Early Career Jobs - Do Not Miss",
357
+ snippet: "Top companies are hiring right now — don't miss your shot at these exclusive opportunities!",
358
+ timestamp: "12:35 PM",
359
+ category: "promotions"
360
+ },
361
+ {
362
+ id: 3,
363
+ sender: "Professor Smith",
364
+ subject: "Research Collaboration Proposal",
365
+ snippet: "I've reviewed your proposal draft. Let's schedule a call next week to discuss next steps.",
366
+ timestamp: "10:22 AM",
367
+ category: "primary"
368
+ },
369
+ {
370
+ id: 4,
371
+ sender: "Friend",
372
+ subject: "Weekend Plans",
373
+ snippet: "Are we still on for the hiking trip this weekend? Let me know so I can book the gear.",
374
+ timestamp: "Yesterday",
375
+ category: "social"
376
+ },
377
+ {
378
+ id: 5,
379
+ sender: "Bank Support",
380
+ subject: "Monthly Statement",
381
+ snippet: "Your statement is ready. Minimum due: $120. Avoid penalties by paying before the 15th.",
382
+ timestamp: "Sep 5",
383
+ category: "updates"
384
+ },
385
+ {
386
+ id: 6,
387
+ sender: "Netflix",
388
+ subject: "New Releases This Week!",
389
+ snippet: "Check out the hottest new shows and movies added to your watchlist this week.",
390
+ timestamp: "Sep 4",
391
+ category: "promotions"
392
+ },
393
+ {
394
+ id: 7,
395
+ sender: "Amazon",
396
+ subject: "Your Order #12345 Has Shipped",
397
+ snippet: "Your package is on the way! Track your delivery using the link below.",
398
+ timestamp: "Sep 3",
399
+ category: "updates"
400
+ },
401
+ {
402
+ id: 8,
403
+ sender: "LinkedIn",
404
+ subject: "You have 5 new notifications",
405
+ snippet: "See who viewed your profile and new job recommendations.",
406
+ timestamp: "Sep 2",
407
+ category: "social"
408
+ }
409
+ ];
410
+
411
+ // ----------------------------
412
+ // UI Management
413
+ // ----------------------------
414
+ class UIManager {
415
+ constructor() {
416
+ this.emailList = document.getElementById('emailList');
417
+ this.actionFeedback = document.getElementById('actionFeedback');
418
+ this.selectionHighlight = document.getElementById('selectionHighlight');
419
+ this.handLandmarks = document.getElementById('handLandmarks');
420
+
421
+ this.selectedEmail = null;
422
+ this.emailElements = [];
423
+
424
+ this.renderEmails();
425
+ this.setupEventListeners();
426
+ }
427
+
428
+ renderEmails() {
429
+ this.emailList.innerHTML = '';
430
+ this.emailElements = [];
431
+
432
+ emails.forEach(email => {
433
+ const emailElement = document.createElement('div');
434
+ emailElement.className = 'email-item';
435
+ emailElement.dataset.id = email.id;
436
+
437
+ emailElement.innerHTML = `
438
+ <div class="email-header">
439
+ <span class="email-sender">${email.sender}</span>
440
+ <span class="email-time">${email.timestamp}</span>
441
+ </div>
442
+ <div class="email-subject">${email.subject}</div>
443
+ <div class="email-snippet">${email.snippet}</div>
444
+ `;
445
+
446
+ this.emailList.appendChild(emailElement);
447
+ this.emailElements.push({
448
+ id: email.id,
449
+ element: emailElement,
450
+ rect: null
451
+ });
452
+ });
453
+
454
+ // Update email positions
455
+ this.updateEmailPositions();
456
+ }
457
+
458
+ updateEmailPositions() {
459
+ this.emailElements.forEach(item => {
460
+ const rect = item.element.getBoundingClientRect();
461
+ item.rect = {
462
+ left: rect.left,
463
+ top: rect.top,
464
+ right: rect.right,
465
+ bottom: rect.bottom,
466
+ width: rect.width,
467
+ height: rect.height
468
+ };
469
+ });
470
+ }
471
+
472
+ selectEmail(emailId) {
473
+ // Remove previous selection
474
+ if (this.selectedEmail) {
475
+ const prevElement = this.emailElements.find(e => e.id === this.selectedEmail);
476
+ if (prevElement) {
477
+ prevElement.element.classList.remove('selected');
478
+ }
479
+ }
480
+
481
+ // Set new selection
482
+ this.selectedEmail = emailId;
483
+ const newElement = this.emailElements.find(e => e.id === emailId);
484
+
485
+ if (newElement) {
486
+ newElement.element.classList.add('selected');
487
+ this.showSelectionHighlight(newElement.rect);
488
+ }
489
+
490
+ return newElement ? newElement.rect : null;
491
+ }
492
+
493
+ showSelectionHighlight(rect) {
494
+ this.selectionHighlight.style.display = 'block';
495
+ this.selectionHighlight.style.left = `${rect.left}px`;
496
+ this.selectionHighlight.style.top = `${rect.top}px`;
497
+ this.selectionHighlight.style.width = `${rect.width}px`;
498
+ this.selectionHighlight.style.height = `${rect.height}px`;
499
+ }
500
+
501
+ hideSelectionHighlight() {
502
+ this.selectionHighlight.style.display = 'none';
503
+ }
504
+
505
+ showActionFeedback(message, type) {
506
+ this.actionFeedback.textContent = message;
507
+ this.actionFeedback.className = 'action-feedback';
508
+
509
+ if (type === 'delete') {
510
+ this.actionFeedback.classList.add('delete');
511
+ } else if (type === 'archive') {
512
+ this.actionFeedback.classList.add('archive');
513
+ } else {
514
+ this.actionFeedback.classList.add('summary');
515
+ }
516
+
517
+ this.actionFeedback.classList.add('show');
518
+
519
+ setTimeout(() => {
520
+ this.actionFeedback.classList.remove('show');
521
+ }, 2000);
522
+ }
523
+
524
+ clearSelection() {
525
+ if (this.selectedEmail) {
526
+ const element = this.emailElements.find(e => e.id === this.selectedEmail);
527
+ if (element) {
528
+ element.element.classList.remove('selected');
529
+ }
530
+ this.selectedEmail = null;
531
+ this.hideSelectionHighlight();
532
+ }
533
+ }
534
+
535
+ setupEventListeners() {
536
+ window.addEventListener('resize', () => {
537
+ this.updateEmailPositions();
538
+ if (this.selectedEmail) {
539
+ const element = this.emailElements.find(e => e.id === this.selectedEmail);
540
+ if (element) {
541
+ this.showSelectionHighlight(element.rect);
542
+ }
543
+ }
544
+ });
545
+ }
546
+
547
+ // For gesture visualization
548
+ updateHandLandmarks(landmarks) {
549
+ this.handLandmarks.innerHTML = '';
550
+
551
+ if (!landmarks || landmarks.length === 0) return;
552
+
553
+ landmarks.forEach((landmark, i) => {
554
+ const landmarkEl = document.createElement('div');
555
+ landmarkEl.className = 'landmark';
556
+ if (i === 8) { // Index finger tip
557
+ landmarkEl.classList.add('index-tip');
558
+ }
559
+
560
+ landmarkEl.style.left = `${landmark.x * 100}%`;
561
+ landmarkEl.style.top = `${landmark.y * 100}%`;
562
+
563
+ this.handLandmarks.appendChild(landmarkEl);
564
+ });
565
+ }
566
+ }
567
+
568
+ // ----------------------------
569
+ // Gesture Detection
570
+ // ----------------------------
571
+ class GestureDetector {
572
+ constructor(uiManager) {
573
+ this.uiManager = uiManager;
574
+ this.selectedEmailId = null;
575
+ this.gestureBuffer = [];
576
+ this.circlePoints = [];
577
+ this.circleThreshold = 10;
578
+ this.swipeThreshold = 50;
579
+ this.gestureCooldown = 1500; // 1.5 seconds
580
+ this.lastGestureTime = 0;
581
+
582
+ this.setupMediaPipe();
583
+ }
584
+
585
+ setupMediaPipe() {
586
+ const hands = new Hands({locateFile: (file) => {
587
+ return `https://cdn.jsdelivr.net/npm/@mediapipe/[email protected]/${file}`;
588
+ }});
589
+
590
+ hands.setOptions({
591
+ maxNumHands: 1,
592
+ modelComplexity: 1,
593
+ minDetectionConfidence: 0.7,
594
+ minTrackingConfidence: 0.7
595
+ });
596
+
597
+ hands.onResults(results => {
598
+ this.processResults(results);
599
+ });
600
+
601
+ const videoElement = document.getElementById('webcam');
602
+
603
+ const camera = new Camera(videoElement, {
604
+ onFrame: async () => {
605
+ await hands.send({image: videoElement});
606
+ },
607
+ width: 320,
608
+ height: 240
609
+ });
610
+ camera.start();
611
+ }
612
+
613
+ processResults(results) {
614
+ // Update hand landmarks visualization
615
+ if (results.multiHandLandmarks && results.multiHandLandmarks.length > 0) {
616
+ this.uiManager.updateHandLandmarks(results.multiHandLandmarks[0]);
617
+ this.detectGesture(results.multiHandLandmarks[0]);
618
+ } else {
619
+ this.uiManager.clearSelection();
620
+ this.gestureBuffer = [];
621
+ this.circlePoints = [];
622
+ }
623
+ }
624
+
625
+ detectGesture(landmarks) {
626
+ // Convert to pixel coordinates for easier calculation
627
+ const indexTip = landmarks[8];
628
+ const middleTip = landmarks[12];
629
+ const wrist = landmarks[0];
630
+
631
+ // Pointing detection (index finger higher than middle)
632
+ if (indexTip.y < middleTip.y) {
633
+ this.checkEmailSelection(indexTip.x, indexTip.y);
634
+ } else {
635
+ this.uiManager.clearSelection();
636
+ this.gestureBuffer = [];
637
+ this.circlePoints = [];
638
+ }
639
+
640
+ // Only process gestures if an email is selected
641
+ if (this.selectedEmailId === null) {
642
+ return;
643
+ }
644
+
645
+ // Get palm center for gesture detection
646
+ const palmCenterX = (wrist.x + landmarks[9].x) / 2;
647
+ const palmCenterY = (wrist.y + landmarks[9].y) / 2;
648
+
649
+ // Add to gesture buffer
650
+ this.gestureBuffer.push({x: palmCenterX, y: palmCenterY});
651
+
652
+ // Check for swipe
653
+ if (this.gestureBuffer.length > 2) {
654
+ const prev = this.gestureBuffer[this.gestureBuffer.length - 2];
655
+ const current = this.gestureBuffer[this.gestureBuffer.length - 1];
656
+
657
+ const dx = (current.x - prev.x) * window.innerWidth;
658
+ const dy = (current.y - prev.y) * window.innerHeight;
659
+
660
+ // Check if it's a horizontal swipe
661
+ if (Math.abs(dx) > this.swipeThreshold && Math.abs(dx) > Math.abs(dy) * 2) {
662
+ if (Date.now() - this.lastGestureTime > this.gestureCooldown) {
663
+ this.lastGestureTime = Date.now();
664
+
665
+ if (dx > 0) {
666
+ this.handleGesture('swipe_right');
667
+ } else {
668
+ this.handleGesture('swipe_left');
669
+ }
670
+
671
+ this.gestureBuffer = [];
672
+ }
673
+ }
674
+ }
675
+
676
+ // Check for circle
677
+ this.circlePoints.push({x: palmCenterX, y: palmCenterY});
678
+ if (this.circlePoints.length > this.circleThreshold) {
679
+ const startPoint = this.circlePoints[0];
680
+ const endPoint = this.circlePoints[this.circlePoints.length - 1];
681
+
682
+ const distance = Math.sqrt(
683
+ Math.pow(startPoint.x - endPoint.x, 2) +
684
+ Math.pow(startPoint.y - endPoint.y, 2)
685
+ ) * window.innerWidth;
686
+
687
+ if (distance < 40 && Date.now() - this.lastGestureTime > this.gestureCooldown) {
688
+ this.lastGestureTime = Date.now();
689
+ this.handleGesture('circle');
690
+ this.circlePoints = [];
691
+ }
692
+ }
693
+ }
694
+
695
+ checkEmailSelection(x, y) {
696
+ // Convert normalized coordinates to screen coordinates
697
+ const screenX = x * window.innerWidth;
698
+ const screenY = y * window.innerHeight;
699
+
700
+ // Check which email is under the finger
701
+ for (let i = this.uiManager.emailElements.length - 1; i >= 0; i--) {
702
+ const email = this.uiManager.emailElements[i];
703
+ if (email.rect &&
704
+ screenX >= email.rect.left &&
705
+ screenX <= email.rect.right &&
706
+ screenY >= email.rect.top &&
707
+ screenY <= email.rect.bottom) {
708
+
709
+ // Only select if it's a different email
710
+ if (this.selectedEmailId !== email.id) {
711
+ this.selectedEmailId = email.id;
712
+ this.uiManager.selectEmail(email.id);
713
+ }
714
+ return;
715
+ }
716
+ }
717
+
718
+ // If no email is selected, clear selection
719
+ if (!this.uiManager.emailElements.some(email =>
720
+ email.rect &&
721
+ screenX >= email.rect.left &&
722
+ screenX <= email.rect.right &&
723
+ screenY >= email.rect.top &&
724
+ screenY <= email.rect.bottom)) {
725
+ this.uiManager.clearSelection();
726
+ this.selectedEmailId = null;
727
+ }
728
+ }
729
+
730
+ handleGesture(gesture) {
731
+ if (!this.selectedEmailId) return;
732
+
733
+ const email = emails.find(e => e.id === this.selectedEmailId);
734
+ if (!email) return;
735
+
736
+ switch (gesture) {
737
+ case 'swipe_left':
738
+ this.uiManager.showActionFeedback(`🗑️ Deleted: ${email.subject}`, 'delete');
739
+ // Remove from UI
740
+ const index = emails.findIndex(e => e.id === this.selectedEmailId);
741
+ if (index !== -1) emails.splice(index, 1);
742
+ this.uiManager.renderEmails();
743
+ this.selectedEmailId = null;
744
+ break;
745
+
746
+ case 'swipe_right':
747
+ this.uiManager.showActionFeedback(`✅ Archived: ${email.subject}`, 'archive');
748
+ // In a real app, we'd move to archive
749
+ const archiveIndex = emails.findIndex(e => e.id === this.selectedEmailId);
750
+ if (archiveIndex !== -1) emails.splice(archiveIndex, 1);
751
+ this.uiManager.renderEmails();
752
+ this.selectedEmailId = null;
753
+ break;
754
+
755
+ case 'circle':
756
+ const summary = `This email discusses ${email.subject.toLowerCase()}.`;
757
+ this.uiManager.showActionFeedback(`📝 Summary: ${summary}`, 'summary');
758
+ break;
759
+ }
760
+ }
761
+ }
762
+
763
+ // ----------------------------
764
+ // Initialize App
765
+ // ----------------------------
766
+ document.addEventListener('DOMContentLoaded', () => {
767
+ // Request camera access
768
+ const videoElement = document.getElementById('webcam');
769
+
770
+ navigator.mediaDevices.getUserMedia({ video: true })
771
+ .then(stream => {
772
+ videoElement.srcObject = stream;
773
+ })
774
+ .catch(err => {
775
+ console.error("Error accessing camera:", err);
776
+ alert("Camera access is required for this app to function properly.");
777
+ });
778
+
779
+ // Initialize UI
780
+ const uiManager = new UIManager();
781
+
782
+ // Initialize gesture detection
783
+ setTimeout(() => {
784
+ new GestureDetector(uiManager);
785
+ }, 1000); // Give time for UI to render
786
+ });
787
+ </script>
788
+ </body>
789
+ </html>