feat: new interview page, MediaPipe Integration and Stun server discovery #20

Merged
subhajit.pal merged 12 commits from experimental/mediapipe-integration into main 2026-08-14 09:29:03 +00:00
Showing only changes of commit eba3ec96b3 - Show all commits

View File

@ -343,6 +343,70 @@ export function uploadTabScreenshotBlob(imageData, reason = 'tab_switch') {
.catch(err => console.log('System screenshot fetch error:', err)); .catch(err => console.log('System screenshot fetch error:', err));
} }
export function isEntireScreenShare(track) {
if (!track || !track.getSettings) return true;
const settings = track.getSettings();
const surface = settings.displaySurface;
if (surface) {
const allowedSurfaces = ['monitor', 'screen'];
if (!allowedSurfaces.includes(surface)) {
return false;
}
}
if (window.screen && window.screen.width && settings.width) {
if (settings.width < (window.screen.width * 0.65) && settings.height < (window.screen.height * 0.65)) {
return false;
}
}
return true;
}
export function isScreenSharingActive() {
return Boolean(screenShareStream && screenShareStream.active && screenShareStream.getVideoTracks().some(t => t.readyState === 'live'));
}
export function enforceBrowserFullscreen() {
if (!isScreenSharingActive() || document.fullscreenElement) return;
beginNativePrompt();
if (document.documentElement.requestFullscreen) {
document.documentElement.requestFullscreen()
.then(() => {
endNativePrompt();
armTrailingBuffer(1000);
})
.catch(() => {
endNativePrompt();
showFullscreenRequiredModal();
});
} else {
endNativePrompt();
showFullscreenRequiredModal();
}
}
export function showFullscreenRequiredModal() {
if (!isScreenSharingActive() || document.fullscreenElement) return;
if (typeof window.Swal !== 'undefined') {
window.Swal.fire({
icon: 'warning',
title: 'Fullscreen Mode Required',
text: 'Your assessment must be conducted in Fullscreen mode while screen sharing. Please click below to enter Fullscreen.',
confirmButtonText: 'Enter Fullscreen Mode',
confirmButtonColor: '#6366f1',
allowOutsideClick: false,
allowEscapeKey: false
}).then(() => {
if (isScreenSharingActive()) {
enforceBrowserFullscreen();
}
});
}
}
// --- Screen Share Enforcement & Live Overlay Detector (Category 6) --- // --- Screen Share Enforcement & Live Overlay Detector (Category 6) ---
export function startScreenShare() { export function startScreenShare() {
if (!navigator.mediaDevices || !navigator.mediaDevices.getDisplayMedia) { if (!navigator.mediaDevices || !navigator.mediaDevices.getDisplayMedia) {
@ -361,26 +425,27 @@ export function startScreenShare() {
.then(stream => { .then(stream => {
endNativePrompt(); endNativePrompt();
const track = stream.getVideoTracks()[0]; const track = stream.getVideoTracks()[0];
const settings = track.getSettings ? track.getSettings() : {};
// Enforce ENTIRE SCREEN share // Enforce ENTIRE SCREEN share across all browsers
if (settings.displaySurface && settings.displaySurface !== 'monitor') { if (!isEntireScreenShare(track)) {
track.stop();
screenShareStream = null;
logViolation('tab_switch', 'Candidate attempted non-entire screen share (tab or window)');
if (typeof window.Swal !== 'undefined') { if (typeof window.Swal !== 'undefined') {
window.Swal.fire({ window.Swal.fire({
icon: 'warning', icon: 'error',
title: 'Entire Screen Share Required', title: 'Entire Screen Share Required',
text: 'You selected a single tab or window. You MUST select "Entire Screen".', html: 'You selected a single tab or window.<br><br><b>You MUST select "Entire Screen" (or whole monitor)</b> to continue your interview.',
confirmButtonText: 'Reshare Entire Screen', confirmButtonText: 'Reshare Entire Screen Now',
confirmButtonColor: '#6366f1', confirmButtonColor: '#ef4444',
allowOutsideClick: false, allowOutsideClick: false,
allowEscapeKey: false allowEscapeKey: false
}).then(() => { }).then(() => {
track.stop();
setTimeout(startScreenShare, 300); setTimeout(startScreenShare, 300);
}); });
} else { } else {
alert('Entire Screen share is required. Please select Entire Screen.'); alert('Entire Screen share is required. Please select Entire Screen.');
track.stop();
setTimeout(startScreenShare, 300); setTimeout(startScreenShare, 300);
} }
return; return;
@ -391,21 +456,9 @@ export function startScreenShare() {
vidElem.srcObject = stream; vidElem.srcObject = stream;
vidElem.play().catch(() => {}); vidElem.play().catch(() => {});
const btn = document.getElementById('share-screen-btn'); updateScreenShareButton(true);
if (btn) {
btn.innerText = '🟢 Entire Screen Sharing Active';
btn.style.background = '#10b981';
}
beginNativePrompt(); enforceBrowserFullscreen();
if (document.documentElement.requestFullscreen) {
document.documentElement.requestFullscreen()
.catch(() => {})
.finally(() => {
endNativePrompt();
armTrailingBuffer(1000);
});
}
const interviewId = document.body.dataset.interviewId; const interviewId = document.body.dataset.interviewId;
if (typeof window.Peer !== 'undefined' && interviewId) { if (typeof window.Peer !== 'undefined' && interviewId) {
@ -417,10 +470,12 @@ export function startScreenShare() {
track.onended = () => { track.onended = () => {
screenShareStream = null; screenShareStream = null;
if (btn) { updateScreenShareButton(false);
btn.innerText = '🖥️ Share Screen with Interviewer';
btn.style.background = 'linear-gradient(135deg, #6366f1 0%, #0078d4 100%)'; if (document.fullscreenElement) {
try { document.exitFullscreen().catch(() => {}); } catch(e) {}
} }
logViolation('tab_switch', 'Candidate stopped screen sharing'); logViolation('tab_switch', 'Candidate stopped screen sharing');
}; };
@ -430,14 +485,50 @@ export function startScreenShare() {
endNativePrompt(); endNativePrompt();
console.log('Screen sharing cancelled/denied:', err); console.log('Screen sharing cancelled/denied:', err);
screenShareStream = null; screenShareStream = null;
const btn = document.getElementById('share-screen-btn'); updateScreenShareButton(false);
if (btn) {
btn.innerText = '🖥️ Share Screen with Interviewer';
btn.style.background = 'linear-gradient(135deg, #6366f1 0%, #0078d4 100%)';
}
}); });
} }
export function stopScreenShare() {
if (screenShareStream) {
try {
screenShareStream.getTracks().forEach(track => track.stop());
} catch(e) {}
screenShareStream = null;
}
updateScreenShareButton(false);
if (document.fullscreenElement) {
try { document.exitFullscreen().catch(() => {}); } catch(e) {}
}
logViolation('tab_switch', 'Candidate stopped screen sharing');
}
export function toggleScreenShare() {
if (screenShareStream && screenShareStream.active && screenShareStream.getVideoTracks().length > 0 && screenShareStream.getVideoTracks()[0].readyState === 'live') {
stopScreenShare();
} else {
startScreenShare();
}
}
export function updateScreenShareButton(active) {
const btn = document.getElementById('share-screen-btn');
if (!btn) return;
if (active) {
btn.innerHTML = '🛑 Stop Screen Sharing';
btn.style.background = '#ef4444';
btn.style.borderColor = '#dc2626';
btn.onclick = stopScreenShare;
} else {
btn.innerHTML = '🖥️ Share Screen with Interviewer';
btn.style.background = 'linear-gradient(135deg, #6366f1 0%, #0078d4 100%)';
btn.style.borderColor = '#6366f1';
btn.onclick = startScreenShare;
}
}
export function initLiveOverlayDetector() { export function initLiveOverlayDetector() {
if (overlayCheckInterval) clearInterval(overlayCheckInterval); if (overlayCheckInterval) clearInterval(overlayCheckInterval);
@ -624,12 +715,13 @@ export function initCandidateProctoringListeners() {
} }
}, 6000); }, 6000);
// 8. Fullscreen Exit Listener // 8. Fullscreen Exit Listener (Only enforced when screen sharing is active)
document.addEventListener('fullscreenchange', function() { document.addEventListener('fullscreenchange', function() {
if (isFocusSuppressed()) return; if (isFocusSuppressed() || !isScreenSharingActive()) return;
if (!document.fullscreenElement) { if (!document.fullscreenElement) {
logViolation('tab_switch', 'Candidate exited full-screen proctoring mode (possible overlay AI window interaction)'); logViolation('tab_switch', 'Candidate exited full-screen proctoring mode while screen sharing');
captureAndUploadTabScreenshot(); captureAndUploadTabScreenshot();
showFullscreenRequiredModal();
} }
}); });
} }
@ -1201,7 +1293,11 @@ if (typeof window !== 'undefined') {
window.endNativePrompt = endNativePrompt; window.endNativePrompt = endNativePrompt;
window.armTrailingBuffer = armTrailingBuffer; window.armTrailingBuffer = armTrailingBuffer;
window.isFocusSuppressed = isFocusSuppressed; window.isFocusSuppressed = isFocusSuppressed;
window.isScreenSharingActive = isScreenSharingActive;
window.startScreenShare = startScreenShare; window.startScreenShare = startScreenShare;
window.stopScreenShare = stopScreenShare;
window.toggleScreenShare = toggleScreenShare;
window.updateScreenShareButton = updateScreenShareButton;
window.initLiveOverlayDetector = initLiveOverlayDetector; window.initLiveOverlayDetector = initLiveOverlayDetector;
window.initQuestionRepeatDetection = initQuestionRepeatDetection; window.initQuestionRepeatDetection = initQuestionRepeatDetection;
window.initCandidateProctoringListeners = initCandidateProctoringListeners; window.initCandidateProctoringListeners = initCandidateProctoringListeners;