Spaces:
Sleeping
Sleeping
Commit ·
a18d8e0
1
Parent(s): 930ad55
Fix: bring back pop up for eye gaze
Browse files
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
|