Yingtao-Zheng commited on
Commit
a18d8e0
·
1 Parent(s): 930ad55

Fix: bring back pop up for eye gaze

Browse files
Files changed (1) hide show
  1. src/components/FocusPageLocal.jsx +98 -0
src/components/FocusPageLocal.jsx CHANGED
@@ -109,6 +109,10 @@ function FocusPageLocal({ videoManager, sessionResult, setSessionResult, isActiv
109
  const [calibration, setCalibration] = useState(null);
110
  const [l2csBoost, setL2csBoost] = useState(false);
111
  const [l2csBoostAvailable, setL2csBoostAvailable] = useState(false);
 
 
 
 
112
 
113
  const localVideoRef = useRef(null);
114
  const displayCanvasRef = useRef(null);
@@ -337,9 +341,21 @@ function FocusPageLocal({ videoManager, sessionResult, setSessionResult, isActiv
337
 
338
  const handleEyeGazeToggle = async () => {
339
  const next = !l2csBoost;
 
 
 
 
340
  await applyEyeGazeChange(next, false);
341
  };
342
 
 
 
 
 
 
 
 
 
343
  const handleStart = async () => {
344
  try {
345
  setIsStarting(true);
@@ -682,9 +698,91 @@ function FocusPageLocal({ videoManager, sessionResult, setSessionResult, isActiv
682
  return null;
683
  };
684
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
685
  return (
686
  <main id="page-b" className="page" style={pageStyle}>
687
  {renderIntroCard()}
 
688
 
689
  <section id="display-area" className="focus-display-shell">
690
  <video
 
109
  const [calibration, setCalibration] = useState(null);
110
  const [l2csBoost, setL2csBoost] = useState(false);
111
  const [l2csBoostAvailable, setL2csBoostAvailable] = useState(false);
112
+ const [showEyeGazeModal, setShowEyeGazeModal] = useState(false);
113
+ const [eyeGazeDontShow, setEyeGazeDontShow] = useState(
114
+ () => localStorage.getItem('focusguard_eyegaze_noshowalert') === 'true'
115
+ );
116
 
117
  const localVideoRef = useRef(null);
118
  const displayCanvasRef = useRef(null);
 
341
 
342
  const handleEyeGazeToggle = async () => {
343
  const next = !l2csBoost;
344
+ if (next && !eyeGazeDontShow) {
345
+ setShowEyeGazeModal(true);
346
+ return;
347
+ }
348
  await applyEyeGazeChange(next, false);
349
  };
350
 
351
+ const handleEyeGazeModalAction = async (withCalibration) => {
352
+ if (eyeGazeDontShow) {
353
+ localStorage.setItem('focusguard_eyegaze_noshowalert', 'true');
354
+ }
355
+ setShowEyeGazeModal(false);
356
+ await applyEyeGazeChange(true, withCalibration);
357
+ };
358
+
359
  const handleStart = async () => {
360
  try {
361
  setIsStarting(true);
 
698
  return null;
699
  };
700
 
701
+ const renderEyeGazeModal = () => {
702
+ if (!showEyeGazeModal) return null;
703
+ return (
704
+ <div className="focus-flow-overlay" style={{ zIndex: 2000 }}>
705
+ <div className="focus-flow-card">
706
+ <div className="focus-flow-header">
707
+ <div>
708
+ <div className="focus-flow-eyebrow">Eye Gaze Tracking</div>
709
+ <h2>Before you enable</h2>
710
+ </div>
711
+ <div className="focus-flow-icon">
712
+ <svg width="96" height="96" viewBox="0 0 96 96" aria-hidden="true">
713
+ <ellipse cx="48" cy="48" rx="38" ry="24" fill="none" stroke="#007BFF" strokeWidth="5" />
714
+ <circle cx="48" cy="48" r="13" fill="none" stroke="#007BFF" strokeWidth="5" />
715
+ <circle cx="48" cy="48" r="5" fill="#007BFF" />
716
+ </svg>
717
+ </div>
718
+ </div>
719
+
720
+ <p className="focus-flow-lead">
721
+ Eye gaze tracking runs an additional deep neural network (L2CS-Net) alongside your current model.
722
+ Please read the notes below before proceeding.
723
+ </p>
724
+
725
+ <div className="focus-flow-grid">
726
+ <article className="focus-flow-panel focus-flow-panel-warn">
727
+ <h3>Performance impact</h3>
728
+ <p>Enabling eye gaze tracking increases CPU usage and may reduce frame rate. If the system feels sluggish, consider disabling it.</p>
729
+ </article>
730
+ <article className="focus-flow-panel">
731
+ <h3>Calibration (recommended)</h3>
732
+ <p>For best accuracy, calibrate by looking at 9 screen positions one at a time, followed by 1 validation point. The whole process takes about 30 seconds.</p>
733
+ </article>
734
+ </div>
735
+
736
+ <div className="focus-flow-steps">
737
+ <div className="focus-flow-step">
738
+ <div className="focus-flow-step-number">1</div>
739
+ <div className="focus-flow-step-copy">
740
+ <h3>Click "Start Calibration"</h3>
741
+ <p>A dot will appear on screen. Look directly at it and keep your gaze steady. It will cycle through 9 positions then show a final validation dot.</p>
742
+ </div>
743
+ </div>
744
+ <div className="focus-flow-step">
745
+ <div className="focus-flow-step-number">2</div>
746
+ <div className="focus-flow-step-copy">
747
+ <h3>Or skip for now</h3>
748
+ <p>Click "Skip" to enable eye gaze tracking without calibrating. You can recalibrate at any time using the "Recalibrate" button during a session.</p>
749
+ </div>
750
+ </div>
751
+ </div>
752
+
753
+ <label className="eye-gaze-modal-checkbox">
754
+ <input
755
+ type="checkbox"
756
+ checked={eyeGazeDontShow}
757
+ onChange={(e) => setEyeGazeDontShow(e.target.checked)}
758
+ />
759
+ Don't show this again
760
+ </label>
761
+
762
+ <div className="focus-flow-footer">
763
+ <button
764
+ type="button"
765
+ className="focus-flow-secondary"
766
+ onClick={() => handleEyeGazeModalAction(false)}
767
+ >
768
+ Skip
769
+ </button>
770
+ <button
771
+ className="focus-flow-button"
772
+ onClick={() => handleEyeGazeModalAction(true)}
773
+ >
774
+ Start Calibration
775
+ </button>
776
+ </div>
777
+ </div>
778
+ </div>
779
+ );
780
+ };
781
+
782
  return (
783
  <main id="page-b" className="page" style={pageStyle}>
784
  {renderIntroCard()}
785
+ {renderEyeGazeModal()}
786
 
787
  <section id="display-area" className="focus-display-shell">
788
  <video