Sa-m commited on
Commit
5b6c2a2
·
verified ·
1 Parent(s): f3d233d

Update assets/js/script.js

Browse files
Files changed (1) hide show
  1. assets/js/script.js +24 -506
assets/js/script.js CHANGED
@@ -1,76 +1,22 @@
1
- // ----------------------------
2
  // Sample Email Data
3
- // ----------------------------
4
  const emails = [
5
  {
6
  id: 1,
7
  sender: "Interview Kicks",
8
  subject: "Can I Become an ML Engineer? Why Not, We Ask!",
9
  snippet: "It's time to banish that doubt forever. Attend our masterclass to unlock your potential.",
10
- timestamp: "1:31 PM",
11
- category: "primary"
12
  },
13
  {
14
  id: 2,
15
  sender: "Career Brew",
16
  subject: "7th Sep Jobs, 87 Hottest Jobs and Early Career Jobs - Do Not Miss",
17
  snippet: "Top companies are hiring right now — don't miss your shot at these exclusive opportunities!",
18
- timestamp: "12:35 PM",
19
- category: "promotions"
20
  },
21
- {
22
- id: 3,
23
- sender: "Professor Smith",
24
- subject: "Research Collaboration Proposal",
25
- snippet: "I've reviewed your proposal draft. Let's schedule a call next week to discuss next steps.",
26
- timestamp: "10:22 AM",
27
- category: "primary"
28
- },
29
- {
30
- id: 4,
31
- sender: "Friend",
32
- subject: "Weekend Plans",
33
- snippet: "Are we still on for the hiking trip this weekend? Let me know so I can book the gear.",
34
- timestamp: "Yesterday",
35
- category: "social"
36
- },
37
- {
38
- id: 5,
39
- sender: "Bank Support",
40
- subject: "Monthly Statement",
41
- snippet: "Your statement is ready. Minimum due: $120. Avoid penalties by paying before the 15th.",
42
- timestamp: "Sep 5",
43
- category: "updates"
44
- },
45
- {
46
- id: 6,
47
- sender: "Netflix",
48
- subject: "New Releases This Week!",
49
- snippet: "Check out the hottest new shows and movies added to your watchlist this week.",
50
- timestamp: "Sep 4",
51
- category: "promotions"
52
- },
53
- {
54
- id: 7,
55
- sender: "Amazon",
56
- subject: "Your Order #12345 Has Shipped",
57
- snippet: "Your package is on the way! Track your delivery using the link below.",
58
- timestamp: "Sep 3",
59
- category: "updates"
60
- },
61
- {
62
- id: 8,
63
- sender: "LinkedIn",
64
- subject: "You have 5 new notifications",
65
- snippet: "See who viewed your profile and new job recommendations.",
66
- timestamp: "Sep 2",
67
- category: "social"
68
- }
69
  ];
70
 
71
- // ----------------------------
72
- // Debug Utilities
73
- // ----------------------------
74
  class DebugManager {
75
  constructor() {
76
  this.statusEl = document.getElementById('debugStatus');
@@ -81,93 +27,15 @@ class DebugManager {
81
  this.cameraStatusEl = document.getElementById('debugCameraStatus');
82
  this.lastErrorEl = document.getElementById('debugLastError');
83
  this.statusIndicator = document.getElementById('statusIndicator');
84
-
85
- // Get debug toggle button
86
  this.toggleButton = document.getElementById('debugToggle');
87
  this.debugPanel = document.getElementById('debugPanel');
88
-
89
- // Add click event listener
90
- this.toggleButton.addEventListener('click', () => {
91
- this.toggleDebugPanel();
92
- });
93
- }
94
-
95
- updateStatus(status) {
96
- this.statusEl.textContent = status;
97
  }
98
 
99
- updateSelectedEmail(emailId) {
100
- if (emailId) {
101
- const email = emails.find(e => e.id === emailId);
102
- this.selectedEmailEl.textContent = email ? email.subject.substring(0, 20) + '...' : 'Unknown';
103
- } else {
104
- this.selectedEmailEl.textContent = 'None';
105
- }
106
- }
107
-
108
- updateGestureType(gestureType) {
109
- this.gestureTypeEl.textContent = gestureType;
110
- }
111
-
112
- updateBufferCount(count) {
113
- this.bufferCountEl.textContent = `${count} points`;
114
- }
115
-
116
- updateCircleCount(count) {
117
- this.circleCountEl.textContent = `${count} points`;
118
- }
119
-
120
- updateCameraStatus(status) {
121
- this.cameraStatusEl.textContent = status;
122
- }
123
-
124
- logError(error) {
125
- console.error("Gesture Detection Error:", error);
126
- this.lastErrorEl.textContent = error.message.substring(0, 50) + (error.message.length > 50 ? '...' : '');
127
-
128
- // Update status indicator to red
129
- this.statusIndicator.className = 'status-indicator error';
130
- }
131
-
132
- setReady() {
133
- this.updateStatus('Ready');
134
- this.statusIndicator.className = 'status-indicator ready';
135
- }
136
-
137
- setProcessing() {
138
- this.updateStatus('Processing...');
139
- this.statusIndicator.className = 'status-indicator processing';
140
- }
141
-
142
- toggleDebugPanel() {
143
- if (this.debugPanel.classList.contains('visible')) {
144
- this.debugPanel.classList.remove('visible');
145
- this.toggleButton.innerHTML = '⚙️';
146
- } else {
147
- this.debugPanel.classList.add('visible');
148
- this.toggleButton.innerHTML = '⚙️';
149
- }
150
- }
151
  }
152
 
153
- // ----------------------------
154
- // UI Management
155
- // ----------------------------
156
  class UIManager {
157
- constructor() {
158
- this.emailList = document.getElementById('emailList');
159
- this.actionFeedback = document.getElementById('actionFeedback');
160
- this.selectionHighlight = document.getElementById('selectionHighlight');
161
- this.handLandmarks = document.getElementById('handLandmarks');
162
- this.gesturePath = document.getElementById('gesturePath');
163
-
164
- this.selectedEmail = null;
165
- this.emailElements = [];
166
-
167
- this.renderEmails();
168
- this.setupEventListeners();
169
- }
170
-
171
  renderEmails() {
172
  this.emailList.innerHTML = '';
173
  this.emailElements = [];
@@ -179,8 +47,8 @@ class UIManager {
179
 
180
  emailElement.innerHTML = `
181
  <div class="email-header">
182
- <span class="email-sender">${email.sender}</span>
183
- <span class="email-time">${email.timestamp}</span>
184
  </div>
185
  <div class="email-subject">${email.subject}</div>
186
  <div class="email-snippet">${email.snippet}</div>
@@ -194,137 +62,12 @@ class UIManager {
194
  });
195
  });
196
 
197
- // Update email positions
198
  this.updateEmailPositions();
199
  }
200
 
201
- updateEmailPositions() {
202
- this.emailElements.forEach(item => {
203
- const rect = item.element.getBoundingClientRect();
204
- item.rect = {
205
- left: rect.left,
206
- top: rect.top,
207
- right: rect.right,
208
- bottom: rect.bottom,
209
- width: rect.width,
210
- height: rect.height
211
- };
212
- });
213
- }
214
-
215
- selectEmail(emailId) {
216
- // Remove previous selection
217
- if (this.selectedEmail) {
218
- const prevElement = this.emailElements.find(e => e.id === this.selectedEmail);
219
- if (prevElement) {
220
- prevElement.element.classList.remove('selected');
221
- }
222
- }
223
-
224
- // Set new selection
225
- this.selectedEmail = emailId;
226
- const newElement = this.emailElements.find(e => e.id === emailId);
227
-
228
- if (newElement) {
229
- newElement.element.classList.add('selected');
230
- this.showSelectionHighlight(newElement.rect);
231
- }
232
-
233
- return newElement ? newElement.rect : null;
234
- }
235
-
236
- showSelectionHighlight(rect) {
237
- this.selectionHighlight.style.display = 'block';
238
- this.selectionHighlight.style.left = `${rect.left}px`;
239
- this.selectionHighlight.style.top = `${rect.top}px`;
240
- this.selectionHighlight.style.width = `${rect.width}px`;
241
- this.selectionHighlight.style.height = `${rect.height}px`;
242
- }
243
-
244
- hideSelectionHighlight() {
245
- this.selectionHighlight.style.display = 'none';
246
- }
247
-
248
- showActionFeedback(message, type) {
249
- this.actionFeedback.textContent = message;
250
- this.actionFeedback.className = 'action-feedback';
251
-
252
- if (type === 'delete') {
253
- this.actionFeedback.classList.add('delete');
254
- } else if (type === 'archive') {
255
- this.actionFeedback.classList.add('archive');
256
- } else {
257
- this.actionFeedback.classList.add('summary');
258
- }
259
-
260
- this.actionFeedback.classList.add('show');
261
-
262
- setTimeout(() => {
263
- this.actionFeedback.classList.remove('show');
264
- }, 2000);
265
- }
266
-
267
- clearSelection() {
268
- if (this.selectedEmail) {
269
- const element = this.emailElements.find(e => e.id === this.selectedEmail);
270
- if (element) {
271
- element.element.classList.remove('selected');
272
- }
273
- this.selectedEmail = null;
274
- this.hideSelectionHighlight();
275
- }
276
- }
277
-
278
- setupEventListeners() {
279
- window.addEventListener('resize', () => {
280
- this.updateEmailPositions();
281
- if (this.selectedEmail) {
282
- const element = this.emailElements.find(e => e.id === this.selectedEmail);
283
- if (element) {
284
- this.showSelectionHighlight(element.rect);
285
- }
286
- }
287
- });
288
- }
289
-
290
- // For gesture visualization
291
- updateHandLandmarks(landmarks) {
292
- this.handLandmarks.innerHTML = '';
293
-
294
- if (!landmarks || landmarks.length === 0) return;
295
-
296
- landmarks.forEach((landmark, i) => {
297
- const landmarkEl = document.createElement('div');
298
- landmarkEl.className = 'landmark';
299
- if (i === 8) { // Index finger tip
300
- landmarkEl.classList.add('index-tip');
301
- }
302
-
303
- landmarkEl.style.left = `${landmark.x * 100}%`;
304
- landmarkEl.style.top = `${landmark.y * 100}%`;
305
-
306
- this.handLandmarks.appendChild(landmarkEl);
307
- });
308
- }
309
-
310
- updateGesturePath(points) {
311
- this.gesturePath.innerHTML = '';
312
-
313
- if (!points || points.length === 0) return;
314
-
315
- points.forEach(point => {
316
- const pointEl = document.createElement('div');
317
- pointEl.className = 'point';
318
- pointEl.style.left = `${point.x * 100}%`;
319
- pointEl.style.top = `${point.y * 100}%`;
320
- this.gesturePath.appendChild(pointEl);
321
- });
322
- }
323
  }
324
 
325
- // ----------------------------
326
- // Gesture Detection
327
- // ----------------------------
328
  class GestureDetector {
329
  constructor(uiManager, debugManager) {
330
  this.uiManager = uiManager;
@@ -332,119 +75,25 @@ class GestureDetector {
332
  this.selectedEmailId = null;
333
  this.gestureBuffer = [];
334
  this.circlePoints = [];
335
- this.circleThreshold = 10;
336
- this.swipeThreshold = 50;
337
- this.gestureCooldown = 1500; // 1.5 seconds
338
  this.lastGestureTime = 0;
339
- this.isProcessing = false;
340
- this.camera = null;
341
-
342
- this.debugManager.updateStatus('Setting up MediaPipe...');
343
  this.setupMediaPipe();
344
  }
345
 
346
- setupMediaPipe() {
347
- try {
348
- const hands = new Hands({locateFile: (file) => {
349
- return `https://cdn.jsdelivr.net/npm/@mediapipe/[email protected]/${file}`;
350
- }});
351
-
352
- hands.setOptions({
353
- maxNumHands: 1,
354
- modelComplexity: 1,
355
- minDetectionConfidence: 0.7,
356
- minTrackingConfidence: 0.7
357
- });
358
-
359
- hands.onResults(results => {
360
- this.processResults(results);
361
- });
362
-
363
- const videoElement = document.getElementById('webcam');
364
-
365
- // Check if Camera is available
366
- if (typeof Camera === 'undefined') {
367
- this.debugManager.logError(new Error("Camera utils not loaded. Make sure camera_utils.js is included."));
368
- return;
369
- }
370
-
371
- this.camera = new Camera(videoElement, {
372
- onFrame: async () => {
373
- try {
374
- this.debugManager.setProcessing();
375
- await hands.send({image: videoElement});
376
- } catch (error) {
377
- this.debugManager.logError(error);
378
- }
379
- },
380
- width: 320,
381
- height: 240
382
- });
383
-
384
- this.startCamera();
385
- } catch (error) {
386
- this.debugManager.logError(error);
387
- console.error("MediaPipe setup error:", error);
388
- }
389
- }
390
-
391
- async startCamera() {
392
- try {
393
- this.debugManager.updateCameraStatus('Starting...');
394
- await this.camera.start();
395
- this.debugManager.updateCameraStatus('Active');
396
- this.debugManager.setReady();
397
- console.log("Camera initialized successfully");
398
- } catch (error) {
399
- this.debugManager.updateCameraStatus('Error');
400
- this.debugManager.logError(error);
401
-
402
- // Try to get more specific error information
403
- if (error.name === 'NotAllowedError') {
404
- alert("Camera access denied. Please allow camera access in your browser settings.");
405
- } else if (error.name === 'NotFoundError') {
406
- alert("No camera found. Please connect a camera device.");
407
- } else {
408
- alert("Failed to start camera: " + error.message);
409
- }
410
- }
411
- }
412
-
413
- processResults(results) {
414
- try {
415
- // Update hand landmarks visualization
416
- if (results.multiHandLandmarks && results.multiHandLandmarks.length > 0) {
417
- this.uiManager.updateHandLandmarks(results.multiHandLandmarks[0]);
418
-
419
- // Update gesture path for debugging
420
- if (this.gestureBuffer.length > 0) {
421
- this.uiManager.updateGesturePath(this.gestureBuffer);
422
- }
423
-
424
- this.detectGesture(results.multiHandLandmarks[0]);
425
- } else {
426
- this.uiManager.clearSelection();
427
- this.gestureBuffer = [];
428
- this.circlePoints = [];
429
- this.debugManager.updateGestureType('None');
430
- this.debugManager.updateBufferCount(0);
431
- this.debugManager.updateCircleCount(0);
432
- }
433
- } catch (error) {
434
- this.debugManager.logError(error);
435
- }
436
- }
437
-
438
  detectGesture(landmarks) {
439
  try {
440
- // Convert to pixel coordinates for easier calculation
441
  const indexTip = landmarks[8];
442
  const middleTip = landmarks[12];
443
  const wrist = landmarks[0];
444
 
445
- // Pointing detection (index finger higher than middle)
446
- if (indexTip.y < middleTip.y) {
447
- this.checkEmailSelection(indexTip.x, indexTip.y);
 
 
 
448
  } else {
449
  this.uiManager.clearSelection();
450
  this.gestureBuffer = [];
@@ -454,19 +103,14 @@ class GestureDetector {
454
  this.debugManager.updateCircleCount(0);
455
  }
456
 
457
- // Only process gestures if an email is selected
458
- if (this.selectedEmailId === null) {
459
- return;
460
- }
461
 
462
- // Get palm center for gesture detection
463
  const palmCenterX = (wrist.x + landmarks[9].x) / 2;
464
  const palmCenterY = (wrist.y + landmarks[9].y) / 2;
465
 
466
- // Add to gesture buffer
467
  this.gestureBuffer.push({x: palmCenterX, y: palmCenterY});
468
 
469
- // Check for swipe
470
  if (this.gestureBuffer.length > 2) {
471
  const prev = this.gestureBuffer[this.gestureBuffer.length - 2];
472
  const current = this.gestureBuffer[this.gestureBuffer.length - 1];
@@ -474,7 +118,6 @@ class GestureDetector {
474
  const dx = (current.x - prev.x) * window.innerWidth;
475
  const dy = (current.y - prev.y) * window.innerHeight;
476
 
477
- // Check if it's a horizontal swipe
478
  if (Math.abs(dx) > this.swipeThreshold && Math.abs(dx) > Math.abs(dy) * 2) {
479
  if (Date.now() - this.lastGestureTime > this.gestureCooldown) {
480
  this.lastGestureTime = Date.now();
@@ -493,7 +136,6 @@ class GestureDetector {
493
  }
494
  }
495
 
496
- // Check for circle
497
  this.circlePoints.push({x: palmCenterX, y: palmCenterY});
498
  this.debugManager.updateCircleCount(this.circlePoints.length);
499
 
@@ -501,10 +143,9 @@ class GestureDetector {
501
  const startPoint = this.circlePoints[0];
502
  const endPoint = this.circlePoints[this.circlePoints.length - 1];
503
 
504
- const distance = Math.sqrt(
505
- Math.pow(startPoint.x - endPoint.x, 2) +
506
- Math.pow(startPoint.y - endPoint.y, 2)
507
- ) * window.innerWidth;
508
 
509
  if (distance < 40 && Date.now() - this.lastGestureTime > this.gestureCooldown) {
510
  this.lastGestureTime = Date.now();
@@ -521,128 +162,5 @@ class GestureDetector {
521
  }
522
  }
523
 
524
- checkEmailSelection(x, y) {
525
- try {
526
- // Convert normalized coordinates to screen coordinates
527
- const screenX = x * window.innerWidth;
528
- const screenY = y * window.innerHeight;
529
-
530
- // Check which email is under the finger
531
- for (let i = this.uiManager.emailElements.length - 1; i >= 0; i--) {
532
- const email = this.uiManager.emailElements[i];
533
- if (email.rect &&
534
- screenX >= email.rect.left &&
535
- screenX <= email.rect.right &&
536
- screenY >= email.rect.top &&
537
- screenY <= email.rect.bottom) {
538
-
539
- // Only select if it's a different email
540
- if (this.selectedEmailId !== email.id) {
541
- this.selectedEmailId = email.id;
542
- this.uiManager.selectEmail(email.id);
543
- this.debugManager.updateSelectedEmail(email.id);
544
- }
545
- return;
546
- }
547
- }
548
-
549
- // If no email is selected, clear selection
550
- if (!this.uiManager.emailElements.some(email =>
551
- email.rect &&
552
- screenX >= email.rect.left &&
553
- screenX <= email.rect.right &&
554
- screenY >= email.rect.top &&
555
- screenY <= email.rect.bottom)) {
556
- this.uiManager.clearSelection();
557
- this.selectedEmailId = null;
558
- this.debugManager.updateSelectedEmail(null);
559
- }
560
- } catch (error) {
561
- this.debugManager.logError(error);
562
- }
563
- }
564
-
565
- handleGesture(gesture) {
566
- try {
567
- if (!this.selectedEmailId) return;
568
-
569
- const email = emails.find(e => e.id === this.selectedEmailId);
570
- if (!email) return;
571
-
572
- switch (gesture) {
573
- case 'swipe_left':
574
- this.uiManager.showActionFeedback(`🗑️ Deleted: ${email.subject}`, 'delete');
575
- // Remove from UI
576
- const index = emails.findIndex(e => e.id === this.selectedEmailId);
577
- if (index !== -1) emails.splice(index, 1);
578
- this.uiManager.renderEmails();
579
- this.selectedEmailId = null;
580
- this.debugManager.updateSelectedEmail(null);
581
- break;
582
-
583
- case 'swipe_right':
584
- this.uiManager.showActionFeedback(`✅ Archived: ${email.subject}`, 'archive');
585
- // In a real app, we'd move to archive
586
- const archiveIndex = emails.findIndex(e => e.id === this.selectedEmailId);
587
- if (archiveIndex !== -1) emails.splice(archiveIndex, 1);
588
- this.uiManager.renderEmails();
589
- this.selectedEmailId = null;
590
- this.debugManager.updateSelectedEmail(null);
591
- break;
592
-
593
- case 'circle':
594
- const summary = `This email discusses ${email.subject.toLowerCase()}.`;
595
- this.uiManager.showActionFeedback(`📝 Summary: ${summary}`, 'summary');
596
- break;
597
- }
598
- } catch (error) {
599
- this.debugManager.logError(error);
600
- }
601
- }
602
- }
603
-
604
- // ----------------------------
605
- // Initialize App
606
- // ----------------------------
607
- document.addEventListener('DOMContentLoaded', () => {
608
- // Initialize debug manager first
609
- const debugManager = new DebugManager();
610
- debugManager.updateStatus('Initializing UI...');
611
-
612
- // Initialize UI
613
- const uiManager = new UIManager();
614
- debugManager.setReady();
615
-
616
- // Request camera access
617
- const videoElement = document.getElementById('webcam');
618
-
619
- navigator.mediaDevices.getUserMedia({ video: true })
620
- .then(stream => {
621
- videoElement.srcObject = stream;
622
- debugManager.updateCameraStatus('Initializing...');
623
-
624
- // Initialize gesture detection after a short delay to ensure UI is ready
625
- setTimeout(() => {
626
- debugManager.updateStatus('Setting up gesture detection...');
627
- try {
628
- new GestureDetector(uiManager, debugManager);
629
- } catch (error) {
630
- debugManager.logError(error);
631
- }
632
- }, 1500);
633
- })
634
- .catch(err => {
635
- debugManager.updateCameraStatus('Denied');
636
- debugManager.logError(err);
637
- console.error("Error accessing camera:", err);
638
-
639
- // More specific error handling
640
- if (err.name === 'NotAllowedError') {
641
- alert("Camera access denied. Please allow camera access in your browser settings.");
642
- } else if (err.name === 'NotFoundError') {
643
- alert("No camera found. Please connect a camera device.");
644
- } else {
645
- alert("Camera error: " + err.message);
646
- }
647
- });
648
- });
 
 
1
  // Sample Email Data
 
2
  const emails = [
3
  {
4
  id: 1,
5
  sender: "Interview Kicks",
6
  subject: "Can I Become an ML Engineer? Why Not, We Ask!",
7
  snippet: "It's time to banish that doubt forever. Attend our masterclass to unlock your potential.",
8
+ timestamp: "1:31 PM"
 
9
  },
10
  {
11
  id: 2,
12
  sender: "Career Brew",
13
  subject: "7th Sep Jobs, 87 Hottest Jobs and Early Career Jobs - Do Not Miss",
14
  snippet: "Top companies are hiring right now — don't miss your shot at these exclusive opportunities!",
15
+ timestamp: "12:35 PM"
 
16
  },
17
+ // ... other emails ...
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
18
  ];
19
 
 
 
 
20
  class DebugManager {
21
  constructor() {
22
  this.statusEl = document.getElementById('debugStatus');
 
27
  this.cameraStatusEl = document.getElementById('debugCameraStatus');
28
  this.lastErrorEl = document.getElementById('debugLastError');
29
  this.statusIndicator = document.getElementById('statusIndicator');
 
 
30
  this.toggleButton = document.getElementById('debugToggle');
31
  this.debugPanel = document.getElementById('debugPanel');
32
+ this.toggleButton.addEventListener('click', () => this.toggleDebugPanel());
 
 
 
 
 
 
 
 
33
  }
34
 
35
+ // ... rest of DebugManager methods ...
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
36
  }
37
 
 
 
 
38
  class UIManager {
 
 
 
 
 
 
 
 
 
 
 
 
 
 
39
  renderEmails() {
40
  this.emailList.innerHTML = '';
41
  this.emailElements = [];
 
47
 
48
  emailElement.innerHTML = `
49
  <div class="email-header">
50
+ <div class="email-sender">${email.sender}</div>
51
+ <div class="email-time">${email.timestamp}</div>
52
  </div>
53
  <div class="email-subject">${email.subject}</div>
54
  <div class="email-snippet">${email.snippet}</div>
 
62
  });
63
  });
64
 
 
65
  this.updateEmailPositions();
66
  }
67
 
68
+ // ... rest of UIManager methods ...
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
69
  }
70
 
 
 
 
71
  class GestureDetector {
72
  constructor(uiManager, debugManager) {
73
  this.uiManager = uiManager;
 
75
  this.selectedEmailId = null;
76
  this.gestureBuffer = [];
77
  this.circlePoints = [];
78
+ this.circleThreshold = 8; // Reduced from 10
79
+ this.swipeThreshold = 30; // Reduced from 50
80
+ this.gestureCooldown = 1500;
81
  this.lastGestureTime = 0;
 
 
 
 
82
  this.setupMediaPipe();
83
  }
84
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
85
  detectGesture(landmarks) {
86
  try {
 
87
  const indexTip = landmarks[8];
88
  const middleTip = landmarks[12];
89
  const wrist = landmarks[0];
90
 
91
+ // Fix Y-axis inversion
92
+ const screenX = indexTip.x * window.innerWidth;
93
+ const screenY = (1 - indexTip.y) * window.innerHeight; // Fixed
94
+
95
+ if (screenY < middleTip.y * window.innerHeight) { // Pointing detection
96
+ this.checkEmailSelection(screenX, screenY);
97
  } else {
98
  this.uiManager.clearSelection();
99
  this.gestureBuffer = [];
 
103
  this.debugManager.updateCircleCount(0);
104
  }
105
 
106
+ if (this.selectedEmailId === null) return;
 
 
 
107
 
108
+ // Improved palm center calculation
109
  const palmCenterX = (wrist.x + landmarks[9].x) / 2;
110
  const palmCenterY = (wrist.y + landmarks[9].y) / 2;
111
 
 
112
  this.gestureBuffer.push({x: palmCenterX, y: palmCenterY});
113
 
 
114
  if (this.gestureBuffer.length > 2) {
115
  const prev = this.gestureBuffer[this.gestureBuffer.length - 2];
116
  const current = this.gestureBuffer[this.gestureBuffer.length - 1];
 
118
  const dx = (current.x - prev.x) * window.innerWidth;
119
  const dy = (current.y - prev.y) * window.innerHeight;
120
 
 
121
  if (Math.abs(dx) > this.swipeThreshold && Math.abs(dx) > Math.abs(dy) * 2) {
122
  if (Date.now() - this.lastGestureTime > this.gestureCooldown) {
123
  this.lastGestureTime = Date.now();
 
136
  }
137
  }
138
 
 
139
  this.circlePoints.push({x: palmCenterX, y: palmCenterY});
140
  this.debugManager.updateCircleCount(this.circlePoints.length);
141
 
 
143
  const startPoint = this.circlePoints[0];
144
  const endPoint = this.circlePoints[this.circlePoints.length - 1];
145
 
146
+ const dx = endPoint.x - startPoint.x;
147
+ const dy = endPoint.y - startPoint.y;
148
+ const distance = Math.sqrt(dx*dx + dy*dy) * window.innerWidth;
 
149
 
150
  if (distance < 40 && Date.now() - this.lastGestureTime > this.gestureCooldown) {
151
  this.lastGestureTime = Date.now();
 
162
  }
163
  }
164
 
165
+ // ... rest of GestureDetector methods ...
166
+ }