[mirotalk] - feat: replace theme select dropdown with clickable icon cards in settings

This commit is contained in:
Miroslav Pejic
2026-04-03 16:46:23 +02:00
parent 3fff3e5266
commit c374c62b2f
9 changed files with 205 additions and 18 deletions
+1 -1
View File
@@ -1,5 +1,5 @@
# ====================================================
# MiroTalk P2P v.1.7.90 - Environment Configuration
# MiroTalk P2P v.1.7.91 - Environment Configuration
# ====================================================
# App environment
+1 -1
View File
@@ -2,7 +2,7 @@
/**
* ==============================================
* MiroTalk P2P v.1.7.90 - Configuration File
* MiroTalk P2P v.1.7.91 - 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.90
* @version 1.7.91
*
*/
+2 -2
View File
@@ -1,12 +1,12 @@
{
"name": "mirotalk",
"version": "1.7.90",
"version": "1.7.91",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "mirotalk",
"version": "1.7.90",
"version": "1.7.91",
"license": "AGPL-3.0",
"dependencies": {
"@mattermost/client": "11.5.0",
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "mirotalk",
"version": "1.7.90",
"version": "1.7.91",
"description": "A free WebRTC browser-based video call",
"main": "server.js",
"scripts": {
+128
View File
@@ -2677,6 +2677,134 @@ button {
color: white;
}
/*--------------------------------------------------------------
# Settings - theme cards
--------------------------------------------------------------*/
.theme-cards-grid {
display: grid;
grid-template-columns: repeat(4, 1fr);
gap: 8px;
margin-bottom: 10px;
}
.theme-card {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
gap: 6px;
padding: 10px 4px;
border: 2px solid rgba(255, 255, 255, 0.1);
border-radius: 10px;
background: rgba(255, 255, 255, 0.05);
cursor: pointer;
transition: all 0.25s ease;
user-select: none;
}
.theme-card i {
font-size: 1.2rem;
color: rgba(255, 255, 255, 0.7);
transition: color 0.25s ease;
}
.theme-card span {
font-size: 0.7rem;
color: rgba(255, 255, 255, 0.6);
text-transform: capitalize;
}
.theme-card:hover {
background: rgba(255, 255, 255, 0.1);
border-color: rgba(255, 255, 255, 0.3);
transform: translateY(-2px);
}
.theme-card.active {
border-color: var(--dd-color, #66beff);
background: rgba(255, 255, 255, 0.1);
box-shadow: 0 0 12px rgba(102, 190, 255, 0.2);
}
.theme-card.active i {
color: var(--dd-color, #66beff);
}
.theme-card.active span {
color: #ffffff;
}
.theme-card.disabled {
opacity: 0.4;
pointer-events: none;
}
/* Theme card specific icon colors */
.theme-card[data-theme='dark'] i {
color: #9a9abb;
}
.theme-card[data-theme='grey'] i {
color: #b0b0c0;
}
.theme-card[data-theme='green'] i {
color: #6fcf97;
}
.theme-card[data-theme='blue'] i {
color: #6ba3d6;
}
.theme-card[data-theme='red'] i {
color: #e07070;
}
.theme-card[data-theme='purple'] i {
color: #b07cc8;
}
.theme-card[data-theme='orange'] i {
color: #e8a560;
}
.theme-card[data-theme='yellow'] i {
color: #d4b85c;
}
.theme-card.active[data-theme='dark'] {
border-color: #9a9abb;
box-shadow: 0 0 12px rgba(154, 154, 187, 0.15);
}
.theme-card.active[data-theme='grey'] {
border-color: #b0b0c0;
box-shadow: 0 0 12px rgba(176, 176, 192, 0.15);
}
.theme-card.active[data-theme='green'] {
border-color: #6fcf97;
box-shadow: 0 0 12px rgba(111, 207, 151, 0.2);
}
.theme-card.active[data-theme='blue'] {
border-color: #6ba3d6;
box-shadow: 0 0 12px rgba(107, 163, 214, 0.2);
}
.theme-card.active[data-theme='red'] {
border-color: #e07070;
box-shadow: 0 0 12px rgba(224, 112, 112, 0.2);
}
.theme-card.active[data-theme='purple'] {
border-color: #b07cc8;
box-shadow: 0 0 12px rgba(176, 124, 200, 0.2);
}
.theme-card.active[data-theme='orange'] {
border-color: #e8a560;
box-shadow: 0 0 12px rgba(232, 165, 96, 0.2);
}
.theme-card.active[data-theme='yellow'] {
border-color: #d4b85c;
box-shadow: 0 0 12px rgba(212, 184, 92, 0.2);
}
@media screen and (max-width: 500px) {
.theme-cards-grid {
grid-template-columns: repeat(3, 1fr);
}
}
#themeColorPicker {
z-index: 6;
padding: 10px;
+1 -1
View File
@@ -107,7 +107,7 @@ let brand = {
},
about: {
imageUrl: '../images/mirotalk-logo.gif',
title: 'WebRTC P2P v1.7.90',
title: 'WebRTC P2P v1.7.91',
html: `
<button
id="support-button"
+27 -2
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.90
* @version 1.7.91
*
*/
@@ -3328,6 +3328,21 @@ function setTheme() {
}
wbIsBgTransparent = false;
//setButtonsBarPosition(mainButtonsBarPosition);
updateThemeCardsActive();
}
function updateThemeCardsActive() {
const cards = document.querySelectorAll('.theme-card');
cards.forEach((card) => {
card.classList.toggle('active', parseInt(card.dataset.index) === themeSelect.selectedIndex);
});
}
function updateThemeCardsDisabled() {
const cards = document.querySelectorAll('.theme-card');
cards.forEach((card) => {
card.classList.toggle('disabled', themeSelect.disabled);
});
}
/**
@@ -7043,6 +7058,7 @@ function setMySettingsBtn() {
keepCustomTheme.onchange = (e) => {
themeCustom.keep = e.currentTarget.checked;
themeSelect.disabled = themeCustom.keep;
updateThemeCardsDisabled();
lsSettings.theme_custom = themeCustom.keep;
lsSettings.theme_color = themeCustom.color;
lS.setSettings(lsSettings);
@@ -7304,6 +7320,14 @@ function setupMySettings() {
lS.setSettings(lsSettings);
setTheme();
});
document.querySelectorAll('.theme-card').forEach((card) => {
card.onclick = () => {
if (card.classList.contains('disabled')) return;
const index = parseInt(card.dataset.index);
themeSelect.selectedIndex = index;
themeSelect.dispatchEvent(new Event('change'));
};
});
// video object fit
videoObjFitSelect.addEventListener('change', (e) => {
lsSettings.video_obj_fit = videoObjFitSelect.selectedIndex;
@@ -7552,6 +7576,7 @@ function loadSettingsFromLocalStorage() {
switchShortcuts.checked = isShortcutsEnabled;
keepCustomTheme.checked = themeCustom.keep;
themeSelect.disabled = themeCustom.keep;
updateThemeCardsDisabled();
themeCustom.input.value = themeCustom.color;
switchNoiseSuppression.checked = lsSettings.mic_noise_suppression;
@@ -14800,7 +14825,7 @@ function showAbout() {
Swal.fire({
background: swBg,
position: 'center',
title: brand.about?.title && brand.about.title.trim() !== '' ? brand.about.title : 'WebRTC P2P v1.7.90',
title: brand.about?.title && brand.about.title.trim() !== '' ? brand.about.title : 'WebRTC P2P v1.7.91',
imageUrl: brand.about?.imageUrl && brand.about.imageUrl.trim() !== '' ? brand.about.imageUrl : images.about,
customClass: { image: 'img-about' },
html: `
+43 -9
View File
@@ -1159,16 +1159,50 @@ access to use this app.
<p>Theme</p>
</div>
<br />
<select id="mirotalkTheme">
<option value="dark">⚫️ Dark</option>
<option value="grey">⚪️ Grey</option>
<option value="green">🟢 Green</option>
<option value="blue">🔵 Blue</option>
<option value="red">🔴 Red</option>
<option value="purple">🟣 Purple</option>
<option value="orange">🟠 Orange</option>
<option value="yellow">🟡 Yellow</option>
<select id="mirotalkTheme" style="display: none">
<option value="dark">Dark</option>
<option value="grey">Grey</option>
<option value="green">Green</option>
<option value="blue">Blue</option>
<option value="red">Red</option>
<option value="purple">Purple</option>
<option value="orange">Orange</option>
<option value="yellow">Yellow</option>
</select>
<div id="themeCardsGrid" class="theme-cards-grid">
<div class="theme-card" data-theme="dark" data-index="0">
<i class="fa-solid fa-moon"></i>
<span>Dark</span>
</div>
<div class="theme-card" data-theme="grey" data-index="1">
<i class="fa-solid fa-cloud"></i>
<span>Grey</span>
</div>
<div class="theme-card" data-theme="green" data-index="2">
<i class="fa-solid fa-leaf"></i>
<span>Green</span>
</div>
<div class="theme-card" data-theme="blue" data-index="3">
<i class="fa-solid fa-droplet"></i>
<span>Blue</span>
</div>
<div class="theme-card" data-theme="red" data-index="4">
<i class="fa-solid fa-fire"></i>
<span>Red</span>
</div>
<div class="theme-card" data-theme="purple" data-index="5">
<i class="fa-solid fa-gem"></i>
<span>Purple</span>
</div>
<div class="theme-card" data-theme="orange" data-index="6">
<i class="fa-solid fa-sun"></i>
<span>Orange</span>
</div>
<div class="theme-card" data-theme="yellow" data-index="7">
<i class="fa-solid fa-star"></i>
<span>Yellow</span>
</div>
</div>
<br />
<div class="title">
<i class="fas fa-fill-drip"></i>