/* ALFRED_CAMERA_POPUP_V1 * Utilise le pilote caméra MJPEG existant (alfred-camera.js), sans ouvrir * un deuxième flux concurrent : ouverture = clic sur cameraToggle ; * fermeture = arrêt immédiat du flux, libérant la webcam pour le MCP visage. */ (() => { 'use strict'; const trigger = document.getElementById('cameraPopupButton'); const overlay = document.getElementById('cameraPopup'); const closeButton = document.getElementById('cameraPopupClose'); const toggle = document.getElementById('cameraToggle'); if (!trigger || !overlay || !closeButton || !toggle) return; let previousFocus = null; function active() { return toggle.getAttribute('aria-pressed') === 'true'; } function opened() { return !overlay.hidden; } function setExpanded(value) { trigger.setAttribute('aria-expanded', String(value)); } function openPopup() { if (opened()) return; previousFocus = document.activeElement; overlay.hidden = false; document.body.classList.add('alfred-camera-dialog-open'); setExpanded(true); closeButton.focus(); // Réutilise la logique précédente pour accéder à /api/camera/stream. if (!active()) toggle.click(); } function closePopup() { if (!opened()) return; // L'ancienne implémentation du bouton ferme le flux MJPEG et retire src. if (active()) toggle.click(); // Garde-fou contre un éventuel gestionnaire caméra incomplet. const feed = document.getElementById('cameraFeed'); if (feed && feed.hasAttribute('src')) feed.removeAttribute('src'); overlay.hidden = true; document.body.classList.remove('alfred-camera-dialog-open'); setExpanded(false); if (previousFocus && document.contains(previousFocus) && typeof previousFocus.focus === 'function') { previousFocus.focus(); } else { trigger.focus(); } previousFocus = null; } trigger.addEventListener('click', () => opened() ? closePopup() : openPopup()); closeButton.addEventListener('click', closePopup); overlay.addEventListener('pointerdown', evt => { if (evt.target === overlay) closePopup(); }); document.addEventListener('keydown', evt => { if (!opened()) return; if (evt.key === 'Escape') { evt.preventDefault(); closePopup(); } else if (evt.key === 'Tab') { // La page principale ne doit pas recevoir le focus derrière la modale. const nodes = [...overlay.querySelectorAll('button:not([disabled]),a[href],input:not([disabled])')] .filter(node => node.getClientRects().length); if (!nodes.length) return; const first = nodes[0], last = nodes[nodes.length - 1]; if (evt.shiftKey && document.activeElement === first) { evt.preventDefault(); last.focus(); } else if (!evt.shiftKey && document.activeElement === last) { evt.preventDefault(); first.focus(); } } }); // Le flux est également fermé par alfred-camera.js en cas de page masquée. window.addEventListener('pagehide', () => { if (opened()) closePopup(); }); setExpanded(false); })();