[mirotalk] - #314 - add screenReaderAccessibility
This commit is contained in:
Generated
+4
-4
@@ -25,7 +25,7 @@
|
||||
"he": "^1.2.0",
|
||||
"helmet": "^8.1.0",
|
||||
"httpolyglot": "0.1.2",
|
||||
"js-yaml": "^4.1.0",
|
||||
"js-yaml": "^4.1.1",
|
||||
"jsdom": "^27.2.0",
|
||||
"jsonwebtoken": "^9.0.2",
|
||||
"nodemailer": "^7.0.10",
|
||||
@@ -3538,9 +3538,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/js-yaml": {
|
||||
"version": "4.1.0",
|
||||
"resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz",
|
||||
"integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==",
|
||||
"version": "4.1.1",
|
||||
"resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.1.tgz",
|
||||
"integrity": "sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"argparse": "^2.0.1"
|
||||
|
||||
+1
-1
@@ -59,7 +59,7 @@
|
||||
"httpolyglot": "0.1.2",
|
||||
"jsdom": "^27.2.0",
|
||||
"jsonwebtoken": "^9.0.2",
|
||||
"js-yaml": "^4.1.0",
|
||||
"js-yaml": "^4.1.1",
|
||||
"nodemailer": "^7.0.10",
|
||||
"openai": "^6.8.1",
|
||||
"qs": "^6.14.0",
|
||||
|
||||
+61
-5
@@ -767,8 +767,8 @@ function setButtonsToolTip() {
|
||||
setTippy(captionClean, 'Clean the messages', 'bottom');
|
||||
setTippy(captionSaveBtn, 'Save the messages', 'bottom');
|
||||
setTippy(speechRecognitionIcon, 'Status', 'bottom');
|
||||
setTippy(speechRecognitionStart, 'Start', 'top');
|
||||
setTippy(speechRecognitionStop, 'Stop', 'top');
|
||||
setTippy(speechRecognitionStart, 'Start caption', 'top');
|
||||
setTippy(speechRecognitionStop, 'Stop caption', 'top');
|
||||
// Settings
|
||||
setTippy(mySettingsCloseBtn, 'Close', 'bottom');
|
||||
setTippy(myPeerNameSetBtn, 'Change name', 'top');
|
||||
@@ -2246,6 +2246,9 @@ async function handleAddPeer(config) {
|
||||
|
||||
await wbUpdate();
|
||||
playSound('addPeer');
|
||||
|
||||
// Screen reader announcement for peer joined
|
||||
screenReaderAccessibility.announceMessage(`${peer_name} joined the room`);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -2750,6 +2753,10 @@ function handleRemovePeer(config) {
|
||||
|
||||
playSound('removePeer');
|
||||
|
||||
// Screen reader announcement for peer left
|
||||
const peer_name = allPeers && allPeers[peer_id] ? allPeers[peer_id]['peer_name'] : 'Participant';
|
||||
screenReaderAccessibility.announceMessage(`${peer_name} left the room`);
|
||||
|
||||
console.log('ALL PEERS', allPeers);
|
||||
}
|
||||
|
||||
@@ -5204,10 +5211,12 @@ function setShareRoomBtn() {
|
||||
shareRoomBtn.addEventListener('mouseenter', () => {
|
||||
if (isMobileDevice || !buttons.main.showShareQr) return;
|
||||
elemDisplay(qrRoomPopupContainer, true);
|
||||
screenReaderAccessibility.announceMessage('Room QR code displayed');
|
||||
});
|
||||
shareRoomBtn.addEventListener('mouseleave', () => {
|
||||
if (isMobileDevice || !buttons.main.showShareQr) return;
|
||||
elemDisplay(qrRoomPopupContainer, false);
|
||||
screenReaderAccessibility.announceMessage('Room QR code hidden');
|
||||
});
|
||||
}
|
||||
|
||||
@@ -7273,6 +7282,9 @@ function handleAudio(e, init, force = null) {
|
||||
}
|
||||
|
||||
setMyAudioStatus(myAudioStatus);
|
||||
|
||||
// Screen reader announcement
|
||||
screenReaderAccessibility.announceMessage(audioStatus ? 'Microphone on' : 'Microphone off');
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -7340,6 +7352,9 @@ async function handleVideo(e, init, force = null) {
|
||||
}
|
||||
|
||||
setMyVideoStatus(videoStatus);
|
||||
|
||||
// Screen reader announcement
|
||||
screenReaderAccessibility.announceMessage(videoStatus ? 'Camera on' : 'Camera off');
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -7502,6 +7517,11 @@ async function toggleScreenSharing(init = false) {
|
||||
}
|
||||
if (!useVideo) initVideoContainerShow(true);
|
||||
}
|
||||
|
||||
// Screen reader announcement for starting screen share
|
||||
if (!init) {
|
||||
screenReaderAccessibility.announceMessage('Screen sharing started');
|
||||
}
|
||||
} else {
|
||||
// STOP screen sharing
|
||||
const myScreenWrap = getId('myScreenWrap');
|
||||
@@ -7535,6 +7555,9 @@ async function toggleScreenSharing(init = false) {
|
||||
} catch (_) {}
|
||||
await emitPeerStatus('screen', false, {});
|
||||
await refreshMyStreamToPeers(undefined, true);
|
||||
|
||||
// Screen reader announcement for stopping screen share
|
||||
screenReaderAccessibility.announceMessage('Screen sharing stopped');
|
||||
}
|
||||
|
||||
// Update init preview when stopping during init
|
||||
@@ -7568,6 +7591,10 @@ async function toggleScreenSharing(init = false) {
|
||||
elemDisplay(myVideo, false);
|
||||
elemDisplay(myVideoAvatarImage, true, 'block');
|
||||
}
|
||||
|
||||
screenReaderAccessibility.announceMessage(
|
||||
isScreenStreaming ? 'Screen sharing started' : 'Screen sharing stopped'
|
||||
);
|
||||
} catch (err) {
|
||||
if (err && err.name === 'NotAllowedError') {
|
||||
console.error('Screen sharing permission was denied by the user.');
|
||||
@@ -7704,7 +7731,9 @@ function toggleFullScreen() {
|
||||
isDocumentOnFullScreen = false;
|
||||
}
|
||||
}
|
||||
setTippy(fullScreenBtn, isDocumentOnFullScreen ? 'Exit full screen' : 'View full screen', placement);
|
||||
const fullScreenLabel = isDocumentOnFullScreen ? 'Exit full screen' : 'View full screen';
|
||||
setTippy(fullScreenBtn, fullScreenLabel, placement);
|
||||
screenReaderAccessibility.announceMessage(fullScreenLabel);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -8226,6 +8255,7 @@ function handleMediaRecorderStart(event) {
|
||||
if (isMobileDevice) elemDisplay(swapCameraBtn, false);
|
||||
recStartTs = performance.now();
|
||||
playSound('recStart');
|
||||
screenReaderAccessibility.announceMessage('Recording started');
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -8262,6 +8292,7 @@ function handleMediaRecorderStop(event) {
|
||||
if (isMobileDevice) elemDisplay(swapCameraBtn, true, 'block');
|
||||
|
||||
playSound('recStop');
|
||||
screenReaderAccessibility.announceMessage('Recording stopped');
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -8453,6 +8484,7 @@ function showChatRoomDraggable() {
|
||||
}
|
||||
|
||||
setTippy(chatRoomBtn, 'Close the chat', bottomButtonsPlacement);
|
||||
screenReaderAccessibility.announceMessage('Chat opened');
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -8475,6 +8507,7 @@ function showCaptionDraggable() {
|
||||
}
|
||||
|
||||
setTippy(captionBtn, 'Close the caption', placement);
|
||||
screenReaderAccessibility.announceMessage('Caption opened');
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -8799,6 +8832,7 @@ function hideChatRoomAndEmojiPicker() {
|
||||
isChatRoomVisible = false;
|
||||
isChatEmojiVisible = false;
|
||||
setTippy(chatRoomBtn, 'Open the chat', bottomButtonsPlacement);
|
||||
screenReaderAccessibility.announceMessage('Chat closed');
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -8879,6 +8913,11 @@ function handleDataChannelChat(dataMessage) {
|
||||
setPeerChatAvatarImgName('left', msgFrom, msgFromAvatar);
|
||||
appendMessage(msgFrom, leftChatAvatar, 'left', msg, msgPrivate, msgId, msgFrom);
|
||||
speechInMessages ? speechMessage(true, msgFrom, msg) : playSound('chatMessage');
|
||||
|
||||
// Screen reader announcement for incoming chat message
|
||||
if (!speechInMessages) {
|
||||
screenReaderAccessibility.announceMessage(`New message from ${msgFrom}`);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -9674,12 +9713,14 @@ function hideShowMySettings() {
|
||||
setTippy(mySettingsBtn, 'Close the settings', bottomButtonsPlacement);
|
||||
isMySettingsVisible = true;
|
||||
videoMediaContainer.style.opacity = 0.3;
|
||||
screenReaderAccessibility.announceMessage('Settings opened');
|
||||
return;
|
||||
}
|
||||
elemDisplay(mySettings, false);
|
||||
setTippy(mySettingsBtn, 'Open the settings', bottomButtonsPlacement);
|
||||
isMySettingsVisible = false;
|
||||
videoMediaContainer.style.opacity = 1;
|
||||
screenReaderAccessibility.announceMessage('Settings closed');
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -9814,6 +9855,9 @@ function handleHideMe(isHideMeActive) {
|
||||
playSound('on');
|
||||
}
|
||||
resizeVideoMedia();
|
||||
screenReaderAccessibility.announceMessage(
|
||||
isHideMeActive ? 'You are hidden from the room' : 'You are visible in the room'
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -9847,9 +9891,11 @@ function setMyAudioStatus(status) {
|
||||
myAudioStatusIcon.className = audioClassName;
|
||||
// send my audio status to all peers in the room
|
||||
emitPeerStatus('audio', status);
|
||||
setTippy(myAudioStatusIcon, status ? 'My audio is on' : 'My audio is off', 'bottom');
|
||||
const audioStatusLabel = status ? 'My audio is on' : 'My audio is off';
|
||||
setTippy(myAudioStatusIcon, audioStatusLabel, 'bottom');
|
||||
setTippy(audioBtn, status ? 'Stop the audio' : 'Start the audio', bottomButtonsPlacement);
|
||||
status ? playSound('on') : playSound('off');
|
||||
screenReaderAccessibility.announceMessage(audioStatusLabel);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -9871,8 +9917,10 @@ function setMyVideoStatus(status) {
|
||||
// send my video status to all peers in the room
|
||||
emitPeerStatus('video', status);
|
||||
|
||||
const videoStatusLabel = status ? 'My video is on' : 'My video is off';
|
||||
|
||||
if (!isMobileDevice) {
|
||||
if (myVideoStatusIcon) setTippy(myVideoStatusIcon, status ? 'My video is on' : 'My video is off', 'bottom');
|
||||
if (myVideoStatusIcon) setTippy(myVideoStatusIcon, videoStatusLabel, 'bottom');
|
||||
setTippy(videoBtn, status ? 'Stop the video' : 'Start the video', bottomButtonsPlacement);
|
||||
}
|
||||
|
||||
@@ -9889,6 +9937,8 @@ function setMyVideoStatus(status) {
|
||||
]);
|
||||
playSound('off');
|
||||
}
|
||||
|
||||
screenReaderAccessibility.announceMessage(videoStatusLabel);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -10661,12 +10711,14 @@ function handleRoomStatus(config) {
|
||||
elemDisplay(lockRoomBtn, false);
|
||||
elemDisplay(unlockRoomBtn, true);
|
||||
isRoomLocked = true;
|
||||
screenReaderAccessibility.announceMessage(`${peer_name} locked the room`);
|
||||
break;
|
||||
case 'unlock':
|
||||
userLog('toast', `${icons.user} ${peer_name} \n has 🔓 UNLOCKED the room`, 'top-end');
|
||||
elemDisplay(unlockRoomBtn, false);
|
||||
elemDisplay(lockRoomBtn, true);
|
||||
isRoomLocked = false;
|
||||
screenReaderAccessibility.announceMessage(`${peer_name} unlocked the room`);
|
||||
break;
|
||||
case 'checkPassword':
|
||||
isRoomLocked = true;
|
||||
@@ -10779,6 +10831,7 @@ function toggleWhiteboard() {
|
||||
whiteboard.style.top = '50%';
|
||||
whiteboard.style.left = '50%';
|
||||
wbIsOpen = !wbIsOpen;
|
||||
screenReaderAccessibility.announceMessage(wbIsOpen ? 'Whiteboard opened' : 'Whiteboard closed');
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -11774,6 +11827,9 @@ function sendFileInformations(file, peer_id, broadcast = false) {
|
||||
setTimeout(() => {
|
||||
sendFileData(peer_id, broadcast);
|
||||
}, 1000);
|
||||
|
||||
// Screen reader announcement for file sharing
|
||||
screenReaderAccessibility.announceMessage(`Sending file ${fileToSend.name}`);
|
||||
} else {
|
||||
userLog('error', 'File dragged not valid or empty.');
|
||||
}
|
||||
|
||||
@@ -0,0 +1,74 @@
|
||||
'use strict';
|
||||
|
||||
class ScreenReaderAccessibility {
|
||||
constructor() {
|
||||
this.liveRegion = null;
|
||||
this.initialized = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize screen reader accessibility
|
||||
*/
|
||||
init() {
|
||||
if (this.initialized) return;
|
||||
|
||||
// Create ARIA live region for announcements
|
||||
this.createLiveRegion();
|
||||
|
||||
this.initialized = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create ARIA live region for screen reader announcements
|
||||
*/
|
||||
createLiveRegion() {
|
||||
if (!this.liveRegion) {
|
||||
this.liveRegion = document.createElement('div');
|
||||
this.liveRegion.setAttribute('role', 'status');
|
||||
this.liveRegion.setAttribute('aria-live', 'polite');
|
||||
this.liveRegion.setAttribute('aria-atomic', 'true');
|
||||
this.liveRegion.style.position = 'absolute';
|
||||
this.liveRegion.style.left = '-10000px';
|
||||
this.liveRegion.style.width = '1px';
|
||||
this.liveRegion.style.height = '1px';
|
||||
this.liveRegion.style.overflow = 'hidden';
|
||||
document.body.appendChild(this.liveRegion);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Announce a message to screen readers
|
||||
* @param {string} message Message to announce
|
||||
*/
|
||||
announce(message) {
|
||||
if (!this.liveRegion) return;
|
||||
this.liveRegion.textContent = '';
|
||||
setTimeout(() => {
|
||||
this.liveRegion.textContent = message;
|
||||
}, 100);
|
||||
}
|
||||
|
||||
/**
|
||||
* Announce a generic message
|
||||
* @param {string} message Message to announce
|
||||
* @param {string} priority 'polite' or 'assertive'
|
||||
* polite: Default priority for non-urgent messages.
|
||||
* assertive: Higher priority for urgent messages that require immediate attention.
|
||||
*/
|
||||
announceMessage(message, priority = 'polite') {
|
||||
this.liveRegion.setAttribute('aria-live', priority);
|
||||
this.announce(message);
|
||||
}
|
||||
}
|
||||
|
||||
// Create global instance
|
||||
const screenReaderAccessibility = new ScreenReaderAccessibility();
|
||||
|
||||
// Auto-initialize when DOM is ready
|
||||
if (document.readyState === 'loading') {
|
||||
document.addEventListener('DOMContentLoaded', () => {
|
||||
screenReaderAccessibility.init();
|
||||
});
|
||||
} else {
|
||||
screenReaderAccessibility.init();
|
||||
}
|
||||
@@ -1049,6 +1049,7 @@ access to use this app.
|
||||
<script defer src="../js/videoGrid.js"></script>
|
||||
<script defer src="../js/translate.js"></script>
|
||||
<script defer src="../js/fixWebmDuration.js"></script>
|
||||
<script defer src="../js/screenReader.js"></script>
|
||||
<script defer src="../js/client.js"></script>
|
||||
<script defer src="../js/speechRecognition.js"></script>
|
||||
<script defer src="../js/nodeProcessor.js"></script>
|
||||
|
||||
Reference in New Issue
Block a user