[mirotalk] - feat: add Send to all switch in transcription & improve dropdown switch alignment

This commit is contained in:
Miroslav Pejic
2026-04-02 23:51:11 +02:00
parent e2b178397a
commit 24899b9efa
11 changed files with 234 additions and 77 deletions
+1 -1
View File
@@ -1,5 +1,5 @@
# ====================================================
# MiroTalk P2P v.1.7.88 - Environment Configuration
# MiroTalk P2P v.1.7.89 - Environment Configuration
# ====================================================
# App environment
+1 -1
View File
@@ -2,7 +2,7 @@
/**
* ==============================================
* MiroTalk P2P v.1.7.88 - Configuration File
* MiroTalk P2P v.1.7.89 - Configuration File
* ==============================================
*
* This file is the central configuration source.
+1 -1
View File
@@ -45,7 +45,7 @@ dependencies: {
* @license For commercial use or closed source, contact us at license.mirotalk@gmail.com or purchase directly from CodeCanyon
* @license CodeCanyon: https://codecanyon.net/item/mirotalk-p2p-webrtc-realtime-video-conferences/38376661
* @author Miroslav Pejic - miroslav.pejic.85@gmail.com
* @version 1.7.88
* @version 1.7.89
*
*/
+2 -2
View File
@@ -1,12 +1,12 @@
{
"name": "mirotalk",
"version": "1.7.88",
"version": "1.7.89",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "mirotalk",
"version": "1.7.88",
"version": "1.7.89",
"license": "AGPL-3.0",
"dependencies": {
"@mattermost/client": "11.5.0",
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "mirotalk",
"version": "1.7.88",
"version": "1.7.89",
"description": "A free WebRTC browser-based video call",
"main": "server.js",
"scripts": {
+73 -41
View File
@@ -644,7 +644,7 @@ body {
background: var(--msger-bg);
border: var(--border);
resize: none;
overflow: hidden;
overflow: visible;
border-radius: 20px;
box-shadow: 0 8px 32px rgba(0, 0, 0, 0.4);
backdrop-filter: blur(10px);
@@ -675,6 +675,8 @@ body {
/* Draggable Header */
.caption-header {
position: relative;
z-index: 10;
display: flex;
align-items: center;
justify-content: space-between;
@@ -695,23 +697,25 @@ body {
}
.caption-header-title button,
.caption-header-options button {
.caption-header-options > button,
.caption-header-options > .dropdown-custom > .dropdown-toggle-custom {
display: inline-flex;
align-items: center;
justify-content: center;
padding: 8px 12px;
border: none;
border: none !important;
font-size: 1rem;
line-height: 1;
background: rgba(255, 255, 255, 0.1);
background: rgba(255, 255, 255, 0.1) !important;
color: #fff;
border-radius: 8px;
transition: all 0.25s ease;
}
.caption-header-title button:hover,
.caption-header-options button:hover {
background: rgba(255, 255, 255, 0.2);
.caption-header-options > button:hover,
.caption-header-options > .dropdown-custom > .dropdown-toggle-custom:hover {
background: rgba(255, 255, 255, 0.2) !important;
transform: scale(1.05);
}
@@ -3830,7 +3834,8 @@ hr {
}
}
#msgerDropDownContent .msger-menu-action {
#msgerDropDownContent .msger-menu-action,
#captionDropDownContent .msger-menu-action {
position: relative;
display: flex;
align-items: center;
@@ -3844,15 +3849,18 @@ hr {
line-height: 1.35;
}
#msgerDropDownContent .msger-menu-action-row {
#msgerDropDownContent .msger-menu-action-row,
#captionDropDownContent .msger-menu-action-row {
padding: 0;
}
#msgerDropDownContent .msger-menu-action:hover {
#msgerDropDownContent .msger-menu-action:hover,
#captionDropDownContent .msger-menu-action:hover {
background: rgba(255, 255, 255, 0.08);
}
#msgerDropDownContent .msger-menu-action-icon {
#msgerDropDownContent .msger-menu-action-icon,
#captionDropDownContent .msger-menu-action-icon {
position: absolute;
left: 12px;
top: 50%;
@@ -3867,7 +3875,8 @@ hr {
transform: translateY(-50%);
}
#msgerDropDownContent .msger-menu-action i {
#msgerDropDownContent .msger-menu-action i,
#captionDropDownContent .msger-menu-action i {
display: inline-flex;
align-items: center;
justify-content: center;
@@ -3878,7 +3887,8 @@ hr {
color: rgba(232, 247, 249, 0.92);
}
#msgerDropDownContent .msger-menu-action-label {
#msgerDropDownContent .msger-menu-action-label,
#captionDropDownContent .msger-menu-action-label {
display: inline-flex;
align-items: center;
width: 100%;
@@ -3888,64 +3898,86 @@ hr {
line-height: 1.25;
}
#msgerDropDownContent .msger-menu-action-danger {
#msgerDropDownContent .msger-menu-action-danger,
#captionDropDownContent .msger-menu-action-danger {
color: #ffd3cf;
}
#msgerDropDownContent .msger-menu-action-danger .msger-menu-action-icon {
#msgerDropDownContent .msger-menu-action-danger .msger-menu-action-icon,
#captionDropDownContent .msger-menu-action-danger .msger-menu-action-icon {
background: rgba(255, 95, 87, 0.12);
}
#msgerDropDownContent .msger-menu-action-danger i {
#msgerDropDownContent .msger-menu-action-danger i,
#captionDropDownContent .msger-menu-action-danger i {
color: #ff8c84;
}
#msgerDropDownContent .msger-menu-action-danger:hover {
#msgerDropDownContent .msger-menu-action-danger:hover,
#captionDropDownContent .msger-menu-action-danger:hover {
background: rgba(255, 95, 87, 0.1);
}
#msgerDropDownContent .msger-menu-toggle-row {
display: grid;
grid-template-columns: 18px minmax(0, 1fr) auto;
/*--------------------------------------------------------------
# Transcription/Chat switch rows (dropdown menu)
--------------------------------------------------------------*/
.transcription-switch-row {
display: flex;
align-items: center;
gap: 12px;
justify-content: space-between;
padding: 10px 12px;
color: #ffffff;
gap: 10px;
}
#msgerDropDownContent .msger-menu-toggle-icon {
display: inline-flex;
align-items: center;
justify-content: center;
width: 18px;
.transcription-switch-row i {
font-size: 1rem;
min-width: 18px;
text-align: center;
color: rgba(232, 247, 249, 0.92);
}
#msgerDropDownContent .msger-menu-toggle-copy {
display: flex;
min-width: 0;
flex-direction: column;
gap: 2px;
}
#msgerDropDownContent .msger-menu-toggle-copy strong {
display: block;
.transcription-switch-label {
flex: 1;
font-size: 0.9rem;
font-weight: 600;
line-height: 1.2;
color: #ffffff;
}
#msgerDropDownContent .msger-menu-toggle-copy small {
display: block;
font-size: 0.76rem;
line-height: 1.3;
color: rgba(214, 234, 238, 0.7);
#captionDropDownContent {
padding: 14px 10px 10px;
}
#msgerDropDownContent .msger-menu-toggle-row .switch {
margin-left: 8px;
#captionDropDownContent > li {
padding: 0;
}
#captionDropDownContent hr {
margin: 6px 6px;
border: 0;
border-top: 1px solid rgba(255, 255, 255, 0.08);
}
#captionDropDownContent .msger-menu-action-row + .msger-menu-action-row {
margin-top: 4px;
}
.caption-header-options .dropdown-custom {
position: relative;
z-index: 10;
}
.caption-header-options .dropdown-custom > .dropdown-menu-custom {
position: absolute;
top: calc(100% + 10px);
right: 0;
left: auto;
z-index: 9999;
width: min(240px, calc(100vw - 32px));
min-width: min(240px, calc(100vw - 32px));
max-width: min(240px, calc(100vw - 32px));
}
/* Styles for table cell with title (td) */
+1 -1
View File
@@ -107,7 +107,7 @@ let brand = {
},
about: {
imageUrl: '../images/mirotalk-logo.gif',
title: 'WebRTC P2P v1.7.88',
title: 'WebRTC P2P v1.7.89',
html: `
<button
id="support-button"
+81 -10
View File
@@ -15,7 +15,7 @@
* @license For commercial use or closed source, contact us at license.mirotalk@gmail.com or purchase directly from CodeCanyon
* @license CodeCanyon: https://codecanyon.net/item/mirotalk-p2p-webrtc-realtime-video-conferences/38376661
* @author Miroslav Pejic - miroslav.pejic.85@gmail.com
* @version 1.7.88
* @version 1.7.89
*
*/
@@ -330,6 +330,10 @@ const captionMinBtn = getId('captionMinBtn');
const captionClean = getId('captionClean');
const captionSaveBtn = getId('captionSaveBtn');
const captionClose = getId('captionClose');
const captionDropDownMenuBtn = getId('captionDropDownMenuBtn');
const captionDropDownContent = getId('captionDropDownContent');
const transcriptShowOnMsgEl = getId('transcriptShowOnMsg');
const transcriptSendToAllEl = getId('transcriptSendToAll');
const captionChat = getId('captionChat');
const captionEmptyNotice = getId('captionEmptyNotice');
const captionFooter = getId('captionFooter');
@@ -646,6 +650,8 @@ let leftChatAvatar;
let rightChatAvatar;
let chatMessagesId = 0;
let showChatOnMessage = true;
let transcriptShowOnMsg = true;
let transcriptSendToAll = true;
let isChatPinned = false;
let isCaptionPinned = false;
let isChatRoomVisible = false;
@@ -830,8 +836,7 @@ function setButtonsToolTip() {
setTippy(captionMinBtn, 'Minimize', 'bottom');
setTippy(captionTogglePin, 'Toggle caption pin', 'bottom');
setTippy(captionTheme, 'Ghost theme', 'bottom');
setTippy(captionClean, 'Clean the messages', 'bottom');
setTippy(captionSaveBtn, 'Save the messages', 'bottom');
setTippy(transcriptSendToAllEl, 'When enabled, your transcription will be sent to all participants', 'bottom');
setTippy(speechRecognitionIcon, 'Status', 'bottom');
setTippy(speechRecognitionStart, 'Start caption', 'top');
setTippy(speechRecognitionStop, 'Stop caption', 'top');
@@ -6321,6 +6326,37 @@ function setCaptionRoomBtn() {
userLog('info', 'No captions to save');
});
// dropdown caption menu
// Prevent drag handler on captionHeader from intercepting dropdown interactions
captionDropDownContent.addEventListener('mousedown', (e) => e.stopPropagation());
captionDropDownMenuBtn.addEventListener('mousedown', (e) => e.stopPropagation());
captionDropDownMenuBtn.addEventListener('click', () => {
toggleCaptionDropDownMenu();
});
// transcript show on message
transcriptShowOnMsgEl.addEventListener('change', (e) => {
playSound('switch');
transcriptShowOnMsg = e.currentTarget.checked;
transcriptShowOnMsg
? msgPopup('info', 'Caption will be shown, when you receive a new transcript', 'top-end', 3000)
: msgPopup('info', 'Caption will not be shown, when you receive a new transcript', 'top-end', 3000);
lsSettings.transcript_show_on_msg = transcriptShowOnMsg;
lS.setSettings(lsSettings);
});
// transcript send to all
transcriptSendToAllEl.addEventListener('change', (e) => {
playSound('switch');
transcriptSendToAll = e.currentTarget.checked;
transcriptSendToAll
? msgPopup('info', 'Transcription will be sent to all participants', 'top-end', 3000)
: msgPopup('info', 'Transcription will not be sent to participants', 'top-end', 3000);
lsSettings.transcript_send_to_all = transcriptSendToAll;
lS.setSettings(lsSettings);
});
// close caption box - show left button and status menu if hide
captionClose.addEventListener('click', (e) => {
captionMinimize();
@@ -7490,10 +7526,14 @@ function setKeyboardShortcuts(enabled) {
*/
function loadSettingsFromLocalStorage() {
showChatOnMessage = lsSettings.show_chat_on_msg;
transcriptShowOnMsg = lsSettings.transcript_show_on_msg !== undefined ? lsSettings.transcript_show_on_msg : true;
transcriptSendToAll = lsSettings.transcript_send_to_all !== undefined ? lsSettings.transcript_send_to_all : true;
speechInMessages = lsSettings.speech_in_msg;
pinChatByDefault = lsSettings.pin_chat_by_default;
msgerShowChatOnMsg.checked = showChatOnMessage;
msgerSpeechMsg.checked = speechInMessages;
transcriptShowOnMsgEl.checked = transcriptShowOnMsg;
transcriptSendToAllEl.checked = transcriptSendToAll;
screenFpsSelect.selectedIndex = lsSettings.screen_fps;
videoFpsSelect.selectedIndex = lsSettings.video_fps;
screenFpsSelectedIndex = screenFpsSelect.selectedIndex;
@@ -9589,12 +9629,20 @@ function toggleChatDropDownMenu() {
: (msgerDropDownContent.style.display = 'block');
}
function toggleCaptionDropDownMenu() {
captionDropDownContent.style.display === 'block'
? (captionDropDownContent.style.display = 'none')
: (captionDropDownContent.style.display = 'block');
}
function closeMsgerDropdownMenus() {
[msgerDropDownContent, msgerCPDropDownContent, msgerSidebarDropDownContent].forEach((menuEl) => {
if (menuEl) {
elemDisplay(menuEl, false);
[msgerDropDownContent, msgerCPDropDownContent, msgerSidebarDropDownContent, captionDropDownContent].forEach(
(menuEl) => {
if (menuEl) {
elemDisplay(menuEl, false);
}
}
});
);
}
function isEventInsideElements(target, ...elements) {
@@ -9610,7 +9658,9 @@ function handleMsgerDropdownOutsidePress(event) {
msgerCPDropDownMenuBtn,
msgerCPDropDownContent,
msgerSidebarDropDownMenuBtn,
msgerSidebarDropDownContent
msgerSidebarDropDownContent,
captionDropDownMenuBtn,
captionDropDownContent
)
) {
return;
@@ -10136,7 +10186,7 @@ function handleSpeechTranscript(config) {
? genGravatar(peer_name)
: genAvatarSvg(peer_name, 32);
if (!isCaptionBoxVisible) showCaptionDraggable();
if (!isCaptionBoxVisible && transcriptShowOnMsg) showCaptionDraggable();
const msgHTML = `
<div class="msg left-msg">
@@ -14750,7 +14800,7 @@ function showAbout() {
Swal.fire({
background: swBg,
position: 'center',
title: brand.about?.title && brand.about.title.trim() !== '' ? brand.about.title : 'WebRTC P2P v1.7.88',
title: brand.about?.title && brand.about.title.trim() !== '' ? brand.about.title : 'WebRTC P2P v1.7.89',
imageUrl: brand.about?.imageUrl && brand.about.imageUrl.trim() !== '' ? brand.about.imageUrl : images.about,
customClass: { image: 'img-about' },
html: `
@@ -15781,6 +15831,27 @@ function handleDropdownHover() {
whiteboardDropdownMenu.addEventListener('mouseenter', () => clearTimeout(wbTimeoutId));
whiteboardDropdownMenu.addEventListener('mouseleave', hideWhiteboardDropdown);
}
// Handle Caption dropdown menu hover
if (captionDropDownMenuBtn && captionDropDownContent) {
let captionTimeoutId;
const showCaptionDropdown = () => {
clearTimeout(captionTimeoutId);
elemDisplay(captionDropDownContent, true, 'block');
};
const hideCaptionDropdown = () => {
captionTimeoutId = setTimeout(() => {
elemDisplay(captionDropDownContent, false);
}, 200);
};
captionDropDownMenuBtn.addEventListener('mouseenter', showCaptionDropdown);
captionDropDownMenuBtn.addEventListener('mouseleave', hideCaptionDropdown);
captionDropDownContent.addEventListener('mouseenter', () => clearTimeout(captionTimeoutId));
captionDropDownContent.addEventListener('mouseleave', hideCaptionDropdown);
}
}
/**
+2
View File
@@ -16,6 +16,8 @@ class LocalStorage {
this.P2P_SETTINGS = {
share_on_join: true,
show_chat_on_msg: true,
transcript_show_on_msg: true,
transcript_send_to_all: true,
speech_in_msg: false,
pin_chat_by_default: false,
mic_noise_suppression: true, // Noise suppression using RNNoise
+1 -1
View File
@@ -118,7 +118,7 @@ if (speechRecognition) {
};
// save also my speech to text
handleSpeechTranscript(config);
sendToDataChannel(config);
if (transcriptSendToAll) sendToDataChannel(config);
}
};
+70 -18
View File
@@ -314,27 +314,19 @@ access to use this app.
aria-labelledby="msgerDropDownMenuBtn"
>
<li>
<div id="msgerShowChatOnMsgDiv" class="msger-menu-toggle-row">
<span class="msger-menu-toggle-icon">
<i class="fa-solid fa-eye"></i>
</span>
<span class="msger-menu-toggle-copy">
<strong>Open chat on new message</strong>
<small>Bring the chat panel forward when a new message arrives</small>
</span>
<div id="msgerShowChatOnMsgDiv" class="transcription-switch-row">
<i class="fa-solid fa-eye"></i>
<span class="transcription-switch-label">Show on msg</span>
<label class="switch">
<input id="msgerShowChatOnMsg" type="checkbox" checked />
<span class="slider round"></span>
</label>
</div>
<div id="msgerSpeechMsgDiv" class="msger-menu-toggle-row">
<span class="msger-menu-toggle-icon">
<i class="fas fa-volume-high"></i>
</span>
<span class="msger-menu-toggle-copy">
<strong>Speak incoming messages</strong>
<small>Read new chat messages aloud</small>
</span>
</li>
<li id="msgerSpeechMsgDiv">
<div class="transcription-switch-row">
<i class="fas fa-volume-high"></i>
<span class="transcription-switch-label">Speech msg</span>
<label class="switch">
<input id="msgerSpeechMsg" type="checkbox" />
<span class="slider round"></span>
@@ -549,8 +541,68 @@ access to use this app.
<button id="captionMaxBtn" class="fas fa-expand"></button>
<button id="captionMinBtn" class="fas fa-compress"></button>
<button id="captionTheme" class="fas fa-ghost"></button>
<button id="captionSaveBtn" class="fas fa-save"></button>
<button id="captionClean" class="fas fa-trash"></button>
<div class="dropdown-custom">
<button
class="dropdown-toggle-custom"
type="button"
id="captionDropDownMenuBtn"
data-bs-toggle="dropdown-custom"
aria-expanded="false"
>
<i class="fas fa-ellipsis-v"></i>
</button>
<ul
id="captionDropDownContent"
class="dropdown-menu-custom app-dropdown-menu"
aria-labelledby="captionDropDownMenuBtn"
>
<li>
<div class="transcription-switch-row">
<i class="fa-solid fa-eye"></i>
<span class="transcription-switch-label">Show on msg</span>
<label class="switch">
<input id="transcriptShowOnMsg" type="checkbox" checked />
<span class="slider round"></span>
</label>
</div>
</li>
<li>
<div class="transcription-switch-row">
<i class="fa-solid fa-share-nodes"></i>
<span class="transcription-switch-label">Send to all</span>
<label class="switch">
<input id="transcriptSendToAll" type="checkbox" checked />
<span class="slider round"></span>
</label>
</div>
<hr />
</li>
<li class="msger-menu-action-row">
<button
id="captionSaveBtn"
class="app-dropdown-action msger-menu-action"
type="button"
>
<span class="msger-menu-action-icon">
<i class="fas fa-save"></i>
</span>
<span class="msger-menu-action-label">Save messages</span>
</button>
</li>
<li class="msger-menu-action-row">
<button
id="captionClean"
class="app-dropdown-action msger-menu-action msger-menu-action-danger"
type="button"
>
<span class="msger-menu-action-icon">
<i class="fas fa-trash"></i>
</span>
<span class="msger-menu-action-label">Clean messages</span>
</button>
</li>
</ul>
</div>
<button id="captionClose" class="fas fa-times"></button>
</div>
</header>