[mirotalk] - #314 - add screenReaderAccessibility

This commit is contained in:
Miroslav Pejic
2025-11-12 23:29:59 +01:00
parent cdaa95dca6
commit 94041b21d8
5 changed files with 141 additions and 10 deletions
+4 -4
View File
@@ -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
View File
@@ -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
View File
@@ -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.');
}
+74
View File
@@ -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();
}
+1
View File
@@ -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>