[mirotalk] #240 - enhance chat and participants UI/UX

This commit is contained in:
Miroslav Pejic
2026-03-31 07:05:42 +02:00
parent eab5c4f479
commit a4d3df6a51
12 changed files with 2512 additions and 514 deletions
+1 -1
View File
@@ -1,5 +1,5 @@
# ====================================================
# MiroTalk P2P v.1.7.70 - Environment Configuration
# MiroTalk P2P v.1.7.80 - Environment Configuration
# ====================================================
# App environment
+1 -1
View File
@@ -2,7 +2,7 @@
/**
* ==============================================
* MiroTalk P2P v.1.7.70 - Configuration File
* MiroTalk P2P v.1.7.80 - Configuration File
* ==============================================
*
* This file is the central configuration source.
+3 -2
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.70
* @version 1.7.80
*
*/
@@ -1900,7 +1900,7 @@ io.sockets.on('connect', async (socket) => {
if (!Validate.isValidData(config)) return;
// log.debug('Video player', config);
const { room_id, peer_id, peer_name, video_action, video_src } = config;
const { room_id, peer_id, peer_name, video_action, video_src, broadcast } = config;
// Check if valid video src url
if (video_action == 'open' && !isValidHttpURL(video_src)) {
@@ -1913,6 +1913,7 @@ io.sockets.on('connect', async (socket) => {
peer_name: peer_name,
video_action: video_action,
video_src: video_src,
broadcast: broadcast,
};
if (peer_id) {
+6 -6
View File
@@ -1,12 +1,12 @@
{
"name": "mirotalk",
"version": "1.7.70",
"version": "1.7.80",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "mirotalk",
"version": "1.7.70",
"version": "1.7.80",
"license": "AGPL-3.0",
"dependencies": {
"@mattermost/client": "11.5.0",
@@ -22,7 +22,7 @@
"dotenv": "^17.3.1",
"express": "^5.2.1",
"express-openid-connect": "^2.20.1",
"express-rate-limit": "^8.3.1",
"express-rate-limit": "^8.3.2",
"he": "^1.2.0",
"helmet": "^8.1.0",
"httpolyglot": "0.1.2",
@@ -3070,9 +3070,9 @@
}
},
"node_modules/express-rate-limit": {
"version": "8.3.1",
"resolved": "https://registry.npmjs.org/express-rate-limit/-/express-rate-limit-8.3.1.tgz",
"integrity": "sha512-D1dKN+cmyPWuvB+G2SREQDzPY1agpBIcTa9sJxOPMCNeH3gwzhqJRDWCXW3gg0y//+LQ/8j52JbMROWyrKdMdw==",
"version": "8.3.2",
"resolved": "https://registry.npmjs.org/express-rate-limit/-/express-rate-limit-8.3.2.tgz",
"integrity": "sha512-77VmFeJkO0/rvimEDuUC5H30oqUC4EyOhyGccfqoLebB0oiEYfM7nwPrsDsBL1gsTpwfzX8SFy2MT3TDyRq+bg==",
"license": "MIT",
"dependencies": {
"ip-address": "10.1.0"
+2 -2
View File
@@ -1,6 +1,6 @@
{
"name": "mirotalk",
"version": "1.7.70",
"version": "1.7.80",
"description": "A free WebRTC browser-based video call",
"main": "server.js",
"scripts": {
@@ -57,7 +57,7 @@
"dotenv": "^17.3.1",
"express": "^5.2.1",
"express-openid-connect": "^2.20.1",
"express-rate-limit": "^8.3.1",
"express-rate-limit": "^8.3.2",
"he": "^1.2.0",
"helmet": "^8.1.0",
"httpolyglot": "0.1.2",
+1228 -143
View File
File diff suppressed because it is too large Load Diff
+2 -1
View File
@@ -3802,8 +3802,9 @@ img.has-shadow {
border-radius: 18px;
}
.br-20,
.br-24 {
border-radius: 24px;
border-radius: 20px;
}
.w-300 {
+2 -2
View File
@@ -14,7 +14,7 @@
background: var(--wb-bg);
border: var(--border);
box-shadow: var(--box-shadow);
border-radius: 10px;
border-radius: 12px;
/* center */
top: 50%;
left: 50%;
@@ -63,7 +63,7 @@
display: flex;
justify-content: space-between;
padding: 5px;
border-radius: 10px;
border-radius: 12px;
background: rgba(0, 0, 0, 0.2);
color: #ffffff;
cursor: move;
+1 -1
View File
@@ -107,7 +107,7 @@ let brand = {
},
about: {
imageUrl: '../images/mirotalk-logo.gif',
title: 'WebRTC P2P v1.7.70',
title: 'WebRTC P2P v1.7.80',
html: `
<button
id="support-button"
+933 -228
View File
File diff suppressed because it is too large Load Diff
+2
View File
@@ -17,6 +17,8 @@ class LocalStorage {
share_on_join: true,
show_chat_on_msg: true,
speech_in_msg: false,
pin_chat_by_default: false,
pin_caption_by_default: true,
mic_noise_suppression: true, // Noise suppression using RNNoise
video_fps: 1, // default 30fps
screen_fps: 1, // default 30fps
+331 -127
View File
@@ -261,7 +261,12 @@ access to use this app.
<section id="msgerDraggable" class="msger-draggable fadein">
<section id="msger" class="msger">
<header id="msgerHeader" class="msger-header">
<div class="msger-header-title"></div>
<div class="msger-header-title">
<div class="msger-panel-heading msger-panel-heading-compact">
<span class="msger-sidebar-label">Chat</span>
<h3>Messages</h3>
</div>
</div>
<div class="msger-header-options">
<button id="msgerTogglePin" class="fas fa-map-pin"></button>
<button id="msgerTheme" class="fas fa-ghost"></button>
@@ -284,27 +289,53 @@ access to use this app.
>
<li>
<hr />
<div id="msgerShowChatOnMsgDiv" class="title ml-10">
<i class="fa-solid fa-eye"></i>
<label class="switch ml-10">
<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>
<label class="switch">
<input id="msgerShowChatOnMsg" type="checkbox" checked />
<span class="slider round"></span>
</label>
</div>
<div id="msgerSpeechMsgDiv" class="title">
<i class="fas fa-volume-high ml-10"></i>
<label class="switch ml-10">
<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>
<label class="switch">
<input id="msgerSpeechMsg" type="checkbox" />
<span class="slider round"></span>
</label>
</div>
<hr />
</li>
<li>
<button id="msgerSaveBtn"><i class="fas fa-save"></i> Save messages</button>
<li class="msger-menu-action-row">
<button id="msgerSaveBtn" class="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 All messages</span>
</button>
</li>
<li>
<button id="msgerClean"><i class="fas fa-trash"></i> Clean messages</button>
<li class="msger-menu-action-row">
<button
id="msgerClean"
class="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 All messages</span>
</button>
</li>
</ul>
</div>
@@ -312,125 +343,280 @@ access to use this app.
</div>
</header>
<main id="msgerChat" class="msger-chat">
<!-- EMPTY CHAT ILLUSTRATION -->
<div id="msgerEmptyNotice" class="empty-chat-notice">
<img alt="Empty chat illustration" class="empty-chat-img" src="../svg/emptyChat.svg" />
<h5 class="empty-chat-title">Start Conversation</h5>
<p class="empty-chat-desc">
There are no messages here yet. Start a conversation by sending a message.
</p>
</div>
<!-- Chat messages will be appended here -->
</main>
<!-- Start emoji picker -->
<section id="msgerEmojiPicker" class="hidden fadein chatEmojiPicker"></section>
<!-- End emoji picker -->
<div class="msger-inputarea">
<textarea
rows="1"
cols="1"
id="msgerInput"
class="msger-input"
placeholder="Write a message..."
></textarea>
</div>
<div class="msger-input-buttons">
<button id="msgerEmojiBtn" class="fas fa-smile"></button>
<button id="msgerMarkdownBtn" class="fab fa-markdown"></button>
<button id="msgerGPTBtn" class="fas fa-robot"></button>
<button id="msgerShareFileBtn" class="fas fa-paperclip"></button>
<button id="msgerVideoUrlBtn" class="fab fa-youtube"></button>
<button id="msgerCPBtn" class="fas fa-users"></button>
<button id="msgerCleanTextBtn" class="fas fa-trash"></button>
<button id="msgerPasteBtn" class="fas fa-paste"></button>
<button id="msgerSendBtn" class="fas fa-paper-plane"></button>
</div>
</section>
<!-- Start private msg -->
<section id="msgerCP" class="center fadein">
<section id="msgerCPSec" class="msger">
<header id="msgerCPHeader" class="msger-private-header">
<div class="msger-header-title"><i class="fas fa-users"></i> Participants</div>
<div class="msger-header-options">
<!-- More options -->
<div id="tabRoomParticipants" class="dropdown-custom">
<button
class="dropdown-toggle-custom"
type="button"
id="msgerCPDropDownMenuBtn"
data-bs-toggle="dropdown-custom"
aria-expanded="false"
>
<i class="fas fa-ellipsis-v"></i>
</button>
<ul
id="msgerCPDropDownContent"
class="dropdown-menu-custom"
aria-labelledby="msgerCPDropDownMenuBtn"
>
<li>
<button id="captionEveryoneBtn">
<i class="fas fa-play"></i> Start captions
</button>
</li>
<li>
<button id="captionEveryoneStopBtn">
<i class="fas fa-stop"></i> Stop captions
</button>
</li>
<li>
<button id="muteEveryoneBtn">
<i class="fas fa-microphone"></i> Mute everyone
</button>
</li>
<li>
<button id="hideEveryoneBtn"><i class="fas fa-video"></i> Hide everyone</button>
</li>
<li>
<button id="ejectEveryoneBtn">
<i class="fas fa-right-from-bracket"></i> Eject everyone
</button>
</li>
</ul>
<div class="msger-layout">
<aside class="msger-sidebar">
<div class="msger-sidebar-header">
<div class="msger-panel-heading">
<h3>Participants</h3>
</div>
<div class="msger-header-options msger-sidebar-header-options">
<div class="dropdown-custom">
<button
class="dropdown-toggle-custom"
type="button"
id="msgerSidebarDropDownMenuBtn"
data-bs-toggle="dropdown-custom"
aria-expanded="false"
>
<i class="fas fa-ellipsis-v"></i>
</button>
<ul
id="msgerSidebarDropDownContent"
class="dropdown-menu-custom msger-participant-dropdown-menu msger-room-actions-menu"
aria-labelledby="msgerSidebarDropDownMenuBtn"
>
<li>
<button
id="captionEveryoneBtnDesktop"
class="dropdown-item msger-participant-action"
>
<span class="msger-participant-action-icon"
><i class="fas fa-play"></i
></span>
<span class="msger-participant-action-label">Start captions</span>
</button>
</li>
<li>
<button
id="captionEveryoneStopBtnDesktop"
class="dropdown-item msger-participant-action"
>
<span class="msger-participant-action-icon"
><i class="fas fa-stop"></i
></span>
<span class="msger-participant-action-label">Stop captions</span>
</button>
</li>
<li class="msger-dropdown-divider" aria-hidden="true"></li>
<li>
<button
id="muteEveryoneBtnDesktop"
class="dropdown-item msger-participant-action msger-participant-action-danger"
>
<span class="msger-participant-action-icon"
><i class="fas fa-microphone"></i
></span>
<span class="msger-participant-action-label">Mute everyone</span>
</button>
</li>
<li>
<button
id="hideEveryoneBtnDesktop"
class="dropdown-item msger-participant-action msger-participant-action-danger"
>
<span class="msger-participant-action-icon"
><i class="fas fa-video"></i
></span>
<span class="msger-participant-action-label">Hide everyone</span>
</button>
</li>
<li>
<button
id="ejectEveryoneBtnDesktop"
class="dropdown-item msger-participant-action msger-participant-action-danger"
>
<span class="msger-participant-action-icon"
><i class="fas fa-right-from-bracket"></i
></span>
<span class="msger-participant-action-label">Eject everyone</span>
</button>
</li>
</ul>
</div>
<button id="msgerSidebarCloseBtn" class="fas fa-times"></button>
</div>
<button id="msgerCPCloseBtn" class="fas fa-times"></button>
</div>
</header>
<main id="msgerCPChat" class="msger-chat">
<div class="search-container">
<input
id="searchPeerBarName"
type="text"
placeholder=" 🔍 Search peer by name..."
maxlength="254"
name="search"
/>
</div>
<br />
<!-- EMPTY PARTICIPANTS ILLUSTRATION -->
<div id="msgerEmptyParticipantsNotice" class="empty-participants-notice">
<img
alt="Empty participants illustration"
class="empty-participants-img"
src="../svg/emptyParticipants.svg"
/>
<h5 class="empty-participants-title">Participants</h5>
<p class="empty-participants-desc">There are no participants here yet.</p>
</div>
<div id="msgerCPList"></div>
</main>
</section>
</section>
<!-- End private msg -->
<section class="msger-sidebar-section">
<span class="msger-sidebar-label">Room chat</span>
<div class="msger-chat-list">
<button id="msgerRoomChatItem" class="msger-chat-item active" type="button">
<span class="msger-chat-item-icon"><i class="fas fa-comments"></i></span>
<span class="msger-chat-item-copy">
<strong>Room chat</strong>
<small>Message everyone</small>
</span>
<span id="msgerRoomChatBadge" class="msger-chat-unread-badge hidden">0</span>
</button>
</div>
</section>
<section class="msger-sidebar-section">
<div class="msger-sidebar-row">
<span class="msger-sidebar-label">Participants</span>
</div>
<div class="search-container search-container-sidebar">
<label class="msger-search-field" for="searchPeerBarName">
<span class="msger-search-icon"><i class="fas fa-search"></i></span>
<span class="msger-search-copy">
<input
id="searchPeerBarName"
type="text"
placeholder="Filter participants"
maxlength="254"
name="search"
/>
</span>
</label>
</div>
<div id="msgerPrivateChatsEmpty" class="msger-sidebar-empty">
Participants will appear here when other people join the room.
</div>
<div id="msgerCPList" class="msger-chat-list msger-private-chat-list"></div>
</section>
</aside>
<section class="msger-main">
<div class="msger-conversation-banner">
<div class="msger-panel-heading msger-panel-heading-compact">
<span id="msgerConversationLabel" class="msger-sidebar-label">Current view</span>
<h3 id="msgerConversationTitle">All messages</h3>
</div>
<p id="msgerConversationMeta" class="msger-conversation-meta">
Public messages and direct replies appear here.
</p>
</div>
<main id="msgerChat" class="msger-chat">
<!-- EMPTY CHAT ILLUSTRATION -->
<div id="msgerEmptyNotice" class="empty-chat-notice">
<img alt="Empty chat illustration" class="empty-chat-img" src="../svg/emptyChat.svg" />
<h5 class="empty-chat-title">Start Conversation</h5>
<p class="empty-chat-desc">
There are no messages here yet. Start a conversation by sending a message.
</p>
</div>
<!-- Chat messages will be appended here -->
</main>
<div class="msger-composer">
<section id="msgerEmojiPicker" class="hidden fadein chatEmojiPicker"></section>
<div class="msger-inputarea">
<textarea
rows="1"
cols="1"
id="msgerInput"
class="msger-input"
placeholder="Write a message..."
></textarea>
</div>
<div class="msger-input-buttons">
<button id="msgerEmojiBtn" class="fas fa-smile"></button>
<button id="msgerMarkdownBtn" class="fab fa-markdown"></button>
<button id="msgerShareFileBtn" class="fas fa-paperclip"></button>
<button id="msgerVideoUrlBtn" class="fab fa-youtube"></button>
<button id="msgerCPBtn" class="fas fa-users"></button>
<button id="msgerCleanTextBtn" class="fas fa-trash"></button>
<button id="msgerPasteBtn" class="fas fa-paste"></button>
<button id="msgerSendBtn" class="fas fa-paper-plane"></button>
</div>
</div>
</section>
<section id="msgerCP" class="fadein" aria-hidden="true">
<header id="msgerCPHeader" class="msger-private-header">
<div class="msger-panel-heading msger-panel-heading-compact">
<span class="msger-sidebar-label">Participants</span>
<h3>People here</h3>
</div>
<div class="msger-header-options">
<!-- More options -->
<div id="tabRoomParticipants" class="dropdown-custom">
<button
class="dropdown-toggle-custom"
type="button"
id="msgerCPDropDownMenuBtn"
data-bs-toggle="dropdown-custom"
aria-expanded="false"
>
<i class="fas fa-ellipsis-v"></i>
</button>
<ul
id="msgerCPDropDownContent"
class="dropdown-menu-custom msger-participant-dropdown-menu msger-room-actions-menu"
aria-labelledby="msgerCPDropDownMenuBtn"
>
<li>
<button
id="captionEveryoneBtn"
class="dropdown-item msger-participant-action"
>
<span class="msger-participant-action-icon"
><i class="fas fa-play"></i
></span>
<span class="msger-participant-action-label">Start captions</span>
</button>
</li>
<li>
<button
id="captionEveryoneStopBtn"
class="dropdown-item msger-participant-action"
>
<span class="msger-participant-action-icon"
><i class="fas fa-stop"></i
></span>
<span class="msger-participant-action-label">Stop captions</span>
</button>
</li>
<li class="msger-dropdown-divider" aria-hidden="true"></li>
<li>
<button
id="muteEveryoneBtn"
class="dropdown-item msger-participant-action msger-participant-action-danger"
>
<span class="msger-participant-action-icon"
><i class="fas fa-microphone"></i
></span>
<span class="msger-participant-action-label">Mute everyone</span>
</button>
</li>
<li>
<button
id="hideEveryoneBtn"
class="dropdown-item msger-participant-action msger-participant-action-danger"
>
<span class="msger-participant-action-icon"
><i class="fas fa-video"></i
></span>
<span class="msger-participant-action-label">Hide everyone</span>
</button>
</li>
<li>
<button
id="ejectEveryoneBtn"
class="dropdown-item msger-participant-action msger-participant-action-danger"
>
<span class="msger-participant-action-icon"
><i class="fas fa-right-from-bracket"></i
></span>
<span class="msger-participant-action-label">Eject everyone</span>
</button>
</li>
</ul>
</div>
<button id="msgerCPCloseBtn" class="fas fa-times"></button>
</div>
</header>
<main id="msgerCPChat" class="msger-chat">
<!-- EMPTY PARTICIPANTS ILLUSTRATION -->
<div id="msgerEmptyParticipantsNotice" class="empty-participants-notice">
<img
alt="Empty participants illustration"
class="empty-participants-img"
src="../svg/emptyParticipants.svg"
/>
<h5 class="empty-participants-title">Participants</h5>
<p class="empty-participants-desc">There are no participants here yet.</p>
</div>
<div id="msgerParticipantsList" class="msger-participants-list"></div>
</main>
</section>
<!-- End private msg -->
</div>
</section>
</section>
<!-- End chat room -->
@@ -592,6 +778,24 @@ access to use this app.
</td>
<td><input id="switchKeepAwake" class="toggle" type="checkbox" /></td>
</tr>
<tr id="pinChatByDefaultRow">
<td class="w-80">
<div class="title">
<i class="fas fa-comments"></i>
<p>Pin chat</p>
</div>
</td>
<td><input id="switchPinChatByDefault" class="toggle" type="checkbox" /></td>
</tr>
<tr id="pinCaptionByDefaultRow">
<td class="w-80">
<div class="title">
<i class="fas fa-closed-captioning"></i>
<p>Pin transcription</p>
</div>
</td>
<td><input id="switchPinCaptionByDefault" class="toggle" type="checkbox" /></td>
</tr>
</table>
<br />
<table id="mySettingsTable">