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

Update index.html

Browse files
Files changed (1) hide show
  1. index.html +250 -199
index.html CHANGED
@@ -4,8 +4,7 @@
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].1646424915/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;
@@ -262,6 +261,7 @@
262
  background-color: #4285f4;
263
  border-radius: 50%;
264
  transform: translate(-50%, -50%);
 
265
  }
266
 
267
  .hand-landmarks .landmark.index-tip {
@@ -270,6 +270,86 @@
270
  height: 12px;
271
  }
272
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
273
  /* For mobile responsiveness */
274
  @media (max-width: 768px) {
275
  .webcam-container {
@@ -279,7 +359,7 @@
279
  right: 10px;
280
  }
281
 
282
- .gesture-guide {
283
  max-width: 240px;
284
  font-size: 0.9em;
285
  padding: 10px;
@@ -334,8 +414,44 @@
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
  // ----------------------------
@@ -408,6 +524,69 @@
408
  }
409
  ];
410
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
411
  // ----------------------------
412
  // UI Management
413
  // ----------------------------
@@ -417,6 +596,7 @@
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 = [];
@@ -563,14 +743,29 @@
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 = [];
@@ -578,212 +773,68 @@
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/hands@0.4.1646424915/${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>
 
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
  <style>
9
  * {
10
  margin: 0;
 
261
  background-color: #4285f4;
262
  border-radius: 50%;
263
  transform: translate(-50%, -50%);
264
+ z-index: 100;
265
  }
266
 
267
  .hand-landmarks .landmark.index-tip {
 
270
  height: 12px;
271
  }
272
 
273
+ .gesture-path {
274
+ position: absolute;
275
+ width: 100%;
276
+ height: 100%;
277
+ }
278
+
279
+ .gesture-path .point {
280
+ position: absolute;
281
+ width: 4px;
282
+ height: 4px;
283
+ background-color: #ff0000;
284
+ border-radius: 50%;
285
+ transform: translate(-50%, -50%);
286
+ }
287
+
288
+ /* Debug Panel */
289
+ .debug-panel {
290
+ position: fixed;
291
+ top: 20px;
292
+ right: 20px;
293
+ background-color: rgba(0,0,0,0.7);
294
+ color: #0f0;
295
+ padding: 15px;
296
+ border-radius: 12px;
297
+ max-width: 300px;
298
+ z-index: 100;
299
+ font-family: monospace;
300
+ font-size: 12px;
301
+ line-height: 1.4;
302
+ }
303
+
304
+ .debug-panel h3 {
305
+ color: #4285f4;
306
+ margin-bottom: 10px;
307
+ }
308
+
309
+ .debug-item {
310
+ margin-bottom: 5px;
311
+ }
312
+
313
+ .debug-label {
314
+ display: inline-block;
315
+ width: 120px;
316
+ color: #aaa;
317
+ }
318
+
319
+ .debug-value {
320
+ color: #fff;
321
+ }
322
+
323
+ /* Status Indicator */
324
+ .status-indicator {
325
+ position: fixed;
326
+ bottom: 30px;
327
+ right: 30px;
328
+ width: 20px;
329
+ height: 20px;
330
+ border-radius: 50%;
331
+ z-index: 200;
332
+ }
333
+
334
+ .status-indicator.ready {
335
+ background-color: #34a853;
336
+ box-shadow: 0 0 10px #34a853;
337
+ }
338
+
339
+ .status-indicator.processing {
340
+ background-color: #fbbc05;
341
+ animation: blink 1s infinite;
342
+ }
343
+
344
+ .status-indicator.error {
345
+ background-color: #ea4335;
346
+ animation: blink 0.5s infinite;
347
+ }
348
+
349
+ @keyframes blink {
350
+ 50% { opacity: 0.5; }
351
+ }
352
+
353
  /* For mobile responsiveness */
354
  @media (max-width: 768px) {
355
  .webcam-container {
 
359
  right: 10px;
360
  }
361
 
362
+ .gesture-guide, .debug-panel {
363
  max-width: 240px;
364
  font-size: 0.9em;
365
  padding: 10px;
 
414
 
415
  <div class="gesture-visualization">
416
  <div class="hand-landmarks" id="handLandmarks"></div>
417
+ <div class="gesture-path" id="gesturePath"></div>
418
  </div>
419
+
420
  <div class="selection-highlight" id="selectionHighlight" style="display: none;"></div>
421
+
422
+ <div class="debug-panel">
423
+ <h3>Debug Information</h3>
424
+ <div class="debug-item">
425
+ <span class="debug-label">Status:</span>
426
+ <span class="debug-value" id="debugStatus">Initializing...</span>
427
+ </div>
428
+ <div class="debug-item">
429
+ <span class="debug-label">Selected Email:</span>
430
+ <span class="debug-value" id="debugSelectedEmail">None</span>
431
+ </div>
432
+ <div class="debug-item">
433
+ <span class="debug-label">Gesture Type:</span>
434
+ <span class="debug-value" id="debugGestureType">None</span>
435
+ </div>
436
+ <div class="debug-item">
437
+ <span class="debug-label">Gesture Buffer:</span>
438
+ <span class="debug-value" id="debugBufferCount">0 points</span>
439
+ </div>
440
+ <div class="debug-item">
441
+ <span class="debug-label">Circle Points:</span>
442
+ <span class="debug-value" id="debugCircleCount">0 points</span>
443
+ </div>
444
+ <div class="debug-item">
445
+ <span class="debug-label">Camera:</span>
446
+ <span class="debug-value" id="debugCameraStatus">Not initialized</span>
447
+ </div>
448
+ <div class="debug-item">
449
+ <span class="debug-label">Last Error:</span>
450
+ <span class="debug-value" id="debugLastError">None</span>
451
+ </div>
452
+ </div>
453
+
454
+ <div class="status-indicator" id="statusIndicator"></div>
455
 
456
  <script>
457
  // ----------------------------
 
524
  }
525
  ];
526
 
527
+ // ----------------------------
528
+ // Debug Utilities
529
+ // ----------------------------
530
+ class DebugManager {
531
+ constructor() {
532
+ this.statusEl = document.getElementById('debugStatus');
533
+ this.selectedEmailEl = document.getElementById('debugSelectedEmail');
534
+ this.gestureTypeEl = document.getElementById('debugGestureType');
535
+ this.bufferCountEl = document.getElementById('debugBufferCount');
536
+ this.circleCountEl = document.getElementById('debugCircleCount');
537
+ this.cameraStatusEl = document.getElementById('debugCameraStatus');
538
+ this.lastErrorEl = document.getElementById('debugLastError');
539
+ this.statusIndicator = document.getElementById('statusIndicator');
540
+ }
541
+
542
+ updateStatus(status) {
543
+ this.statusEl.textContent = status;
544
+ }
545
+
546
+ updateSelectedEmail(emailId) {
547
+ if (emailId) {
548
+ const email = emails.find(e => e.id === emailId);
549
+ this.selectedEmailEl.textContent = email ? email.subject.substring(0, 20) + '...' : 'Unknown';
550
+ } else {
551
+ this.selectedEmailEl.textContent = 'None';
552
+ }
553
+ }
554
+
555
+ updateGestureType(gestureType) {
556
+ this.gestureTypeEl.textContent = gestureType;
557
+ }
558
+
559
+ updateBufferCount(count) {
560
+ this.bufferCountEl.textContent = `${count} points`;
561
+ }
562
+
563
+ updateCircleCount(count) {
564
+ this.circleCountEl.textContent = `${count} points`;
565
+ }
566
+
567
+ updateCameraStatus(status) {
568
+ this.cameraStatusEl.textContent = status;
569
+ }
570
+
571
+ logError(error) {
572
+ console.error("Gesture Detection Error:", error);
573
+ this.lastErrorEl.textContent = error.message.substring(0, 50) + (error.message.length > 50 ? '...' : '');
574
+
575
+ // Update status indicator to red
576
+ this.statusIndicator.className = 'status-indicator error';
577
+ }
578
+
579
+ setReady() {
580
+ this.updateStatus('Ready');
581
+ this.statusIndicator.className = 'status-indicator ready';
582
+ }
583
+
584
+ setProcessing() {
585
+ this.updateStatus('Processing...');
586
+ this.statusIndicator.className = 'status-indicator processing';
587
+ }
588
+ }
589
+
590
  // ----------------------------
591
  // UI Management
592
  // ----------------------------
 
596
  this.actionFeedback = document.getElementById('actionFeedback');
597
  this.selectionHighlight = document.getElementById('selectionHighlight');
598
  this.handLandmarks = document.getElementById('handLandmarks');
599
+ this.gesturePath = document.getElementById('gesturePath');
600
 
601
  this.selectedEmail = null;
602
  this.emailElements = [];
 
743
  this.handLandmarks.appendChild(landmarkEl);
744
  });
745
  }
746
+
747
+ updateGesturePath(points) {
748
+ this.gesturePath.innerHTML = '';
749
+
750
+ if (!points || points.length === 0) return;
751
+
752
+ points.forEach(point => {
753
+ const pointEl = document.createElement('div');
754
+ pointEl.className = 'point';
755
+ pointEl.style.left = `${point.x * 100}%`;
756
+ pointEl.style.top = `${point.y * 100}%`;
757
+ this.gesturePath.appendChild(pointEl);
758
+ });
759
+ }
760
  }
761
 
762
  // ----------------------------
763
  // Gesture Detection
764
  // ----------------------------
765
  class GestureDetector {
766
+ constructor(uiManager, debugManager) {
767
  this.uiManager = uiManager;
768
+ this.debugManager = debugManager;
769
  this.selectedEmailId = null;
770
  this.gestureBuffer = [];
771
  this.circlePoints = [];
 
773
  this.swipeThreshold = 50;
774
  this.gestureCooldown = 1500; // 1.5 seconds
775
  this.lastGestureTime = 0;
776
+ this.isProcessing = false;
777
 
778
+ this.debugManager.updateStatus('Setting up MediaPipe...');
779
  this.setupMediaPipe();
780
  }
781
 
782
  setupMediaPipe() {
783
+ try {
784
+ const hands = new Hands({locateFile: (file) => {
785
+ return `https://cdn.jsdelivr.net/npm/@mediapipe/[email protected]/${file}`;
786
+ }});
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
787
 
788
+ hands.setOptions({
789
+ maxNumHands: 1,
790
+ modelComplexity: 1,
791
+ minDetectionConfidence: 0.7,
792
+ minTrackingConfidence: 0.7
793
+ });
794
 
795
+ hands.onResults(results => {
796
+ this.processResults(results);
797
+ });
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
798
 
799
+ const videoElement = document.getElementById('webcam');
 
 
 
800
 
801
+ const camera = new Camera(videoElement, {
802
+ onFrame: async () => {
803
+ try {
804
+ this.debugManager.setProcessing();
805
+ await hands.send({image: videoElement});
806
+ } catch (error) {
807
+ this.debugManager.logError(error);
808
+ }
809
+ },
810
+ width: 320,
811
+ height: 240
812
+ });
813
+
814
+ camera.start()
815
+ .then(() => {
816
+ this.debugManager.updateCameraStatus('Active');
817
+ this.debugManager.setReady();
818
+ console.log("Camera initialized successfully");
819
+ })
820
+ .catch(error => {
821
+ this.debugManager.updateCameraStatus('Error');
822
+ this.debugManager.logError(error);
823
+ alert("Failed to start camera: " + error.message);
824
+ });
825
 
826
+ } catch (error) {
827
+ this.debugManager.logError(error);
828
+ console.error("MediaPipe setup error:", error);
829
+ alert("Failed to initialize gesture detection: " + error.message);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
830
  }
831
  }
832
 
833
+ processResults(results) {
834
+ try {
835
+ // Update hand landmarks visualization
836
+ if (results.multiHandLandmarks && results.multiHandLandmarks.length > 0) {
837
+ this.uiManager.updateHandLandmarks(results.multiHandLandmarks[0]);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
838
 
839
+ // Update gesture path for debugging
840
+ i