|
68 | 68 | --cp-dim: #5a6a8a; |
69 | 69 | } |
70 | 70 |
|
| 71 | + /* ============================================================ |
| 72 | + "Kids" theme - a bright, friendly, rounded look for a |
| 73 | + children's player. It piggybacks on the light Bootstrap |
| 74 | + theme (set via JS) so all the light-mode structural fixes |
| 75 | + apply, then overrides the palette + neon flourishes below. |
| 76 | + Placed AFTER the light palette so it wins on source order |
| 77 | + (equal specificity). |
| 78 | + ============================================================ */ |
| 79 | + body[data-theme="kids"] { |
| 80 | + --cp-cyan: #3aa0ff; |
| 81 | + /* primary accent / links - cheerful blue */ |
| 82 | + --cp-magenta: #ff6b9d; |
| 83 | + /* bubblegum pink (hover / secondary accent) */ |
| 84 | + --cp-yellow: #ffb703; |
| 85 | + /* sunny */ |
| 86 | + --cp-green: #38c172; |
| 87 | + /* grass */ |
| 88 | + --cp-bg: #eaf4ff; |
| 89 | + /* soft sky */ |
| 90 | + --cp-panel: #ffffff; |
| 91 | + --cp-surface: #ffffff; |
| 92 | + --cp-surface-2: #fff4d6; |
| 93 | + /* warm sunny cards */ |
| 94 | + --cp-border: #ffd8a8; |
| 95 | + --cp-border-glow: rgba(58, 160, 255, .25); |
| 96 | + --cp-text: #2b2350; |
| 97 | + /* friendly deep purple-navy */ |
| 98 | + --cp-dim: #7c83a8; |
| 99 | + } |
| 100 | + |
71 | 101 | body { |
72 | 102 | /* remap Bootstrap theme variables onto the cyberpunk palette */ |
73 | 103 | --bs-body-bg: var(--cp-bg); |
|
1624 | 1654 | padding-right: 0; |
1625 | 1655 | padding-left: 5px; |
1626 | 1656 | } |
| 1657 | + |
| 1658 | + /* ============================================================ |
| 1659 | + "Kids" theme - playful overrides on top of the palette. |
| 1660 | + Strips the cyberpunk neon/CRT flourishes (scanlines, glows, |
| 1661 | + angled clip-paths, flicker) and rounds everything off into a |
| 1662 | + soft, friendly toy-like UI. |
| 1663 | + ============================================================ */ |
| 1664 | + body[data-theme="kids"] { |
| 1665 | + font-family: "Comic Sans MS", "Chalkboard SE", "Comic Neue", "Baloo 2", "Quicksand", "Segoe UI Rounded", system-ui, sans-serif; |
| 1666 | + background-image: |
| 1667 | + radial-gradient(900px 500px at 50% -10%, rgba(255, 183, 3, .18), transparent 70%), |
| 1668 | + radial-gradient(700px 500px at 100% 100%, rgba(255, 107, 157, .15), transparent 70%); |
| 1669 | + background-size: auto, auto; |
| 1670 | + } |
| 1671 | + |
| 1672 | + /* no CRT scanline overlay */ |
| 1673 | + body[data-theme="kids"]::after { |
| 1674 | + display: none; |
| 1675 | + } |
| 1676 | + |
| 1677 | + /* kill neon glows, flicker/blink loops and angled corners everywhere */ |
| 1678 | + body[data-theme="kids"] *, |
| 1679 | + body[data-theme="kids"] *::before, |
| 1680 | + body[data-theme="kids"] *::after { |
| 1681 | + text-shadow: none !important; |
| 1682 | + clip-path: none !important; |
| 1683 | + animation: none !important; |
| 1684 | + letter-spacing: normal !important; |
| 1685 | + text-transform: none !important; |
| 1686 | + } |
| 1687 | + |
| 1688 | + /* round everything off for a soft, toy-like feel */ |
| 1689 | + body[data-theme="kids"] .navbar, |
| 1690 | + body[data-theme="kids"] fieldset, |
| 1691 | + body[data-theme="kids"] .card, |
| 1692 | + body[data-theme="kids"] .btn, |
| 1693 | + body[data-theme="kids"] .form-control, |
| 1694 | + body[data-theme="kids"] .form-select, |
| 1695 | + body[data-theme="kids"] .modal-content, |
| 1696 | + body[data-theme="kids"] .toast, |
| 1697 | + body[data-theme="kids"] .dropdown-menu, |
| 1698 | + body[data-theme="kids"] .list-group-item, |
| 1699 | + body[data-theme="kids"] .nav-link { |
| 1700 | + border-radius: 18px !important; |
| 1701 | + } |
| 1702 | + |
| 1703 | + body[data-theme="kids"] .btn { |
| 1704 | + font-weight: 700; |
| 1705 | + box-shadow: 0 4px 0 rgba(0, 0, 0, .08); |
| 1706 | + } |
| 1707 | + |
| 1708 | + /* bright, friendly navbar instead of the dark neon bar */ |
| 1709 | + body[data-theme="kids"] .navbar.bg-primary { |
| 1710 | + background: linear-gradient(180deg, #fff7e6, #ffe3ef) !important; |
| 1711 | + border-bottom: 3px solid var(--cp-magenta); |
| 1712 | + box-shadow: 0 4px 16px rgba(255, 107, 157, .25); |
| 1713 | + } |
| 1714 | + |
| 1715 | + body[data-theme="kids"] #navbar-brand-co { |
| 1716 | + color: var(--cp-magenta) !important; |
| 1717 | + } |
| 1718 | + |
| 1719 | + body[data-theme="kids"] #navbar-heading { |
| 1720 | + color: var(--cp-cyan) !important; |
| 1721 | + } |
| 1722 | + |
| 1723 | + body[data-theme="kids"] #menuToggle, |
| 1724 | + body[data-theme="kids"] .navbar .navbar-text { |
| 1725 | + color: var(--cp-text) !important; |
| 1726 | + } |
| 1727 | + |
| 1728 | + body[data-theme="kids"] .nav-logo { |
| 1729 | + filter: none; |
| 1730 | + } |
| 1731 | + |
| 1732 | + body[data-theme="kids"] fieldset { |
| 1733 | + box-shadow: 0 6px 18px rgba(0, 0, 0, .06); |
| 1734 | + } |
1627 | 1735 | </style> |
1628 | 1736 | </head> |
1629 | 1737 |
|
1630 | 1738 | <body data-bs-theme="dark"> |
| 1739 | + <!-- Apply the stored theme before paint to avoid a flash of the wrong colours. --> |
| 1740 | + <script> |
| 1741 | + (function () { |
| 1742 | + var theme = localStorage.getItem('uiTheme') || 'cyberpunk'; |
| 1743 | + document.body.setAttribute('data-theme', theme); |
| 1744 | + var mode = theme === 'kids' ? 'light' : (localStorage.getItem('lightSwitch') || 'dark'); |
| 1745 | + document.body.setAttribute('data-bs-theme', mode); |
| 1746 | + })(); |
| 1747 | + </script> |
1631 | 1748 | <div class="toast-container position-fixed top-0 end-0 p-3 mt-5" id="toaster"> |
1632 | 1749 | <div class="toast align-items-center border-0" id="toast-template" role="alert" aria-live="assertive" |
1633 | 1750 | aria-atomic="true"> |
|
1690 | 1807 | </select> |
1691 | 1808 | </div> |
1692 | 1809 | </li> |
| 1810 | + <li> |
| 1811 | + <div class="dropdown-item d-flex"> |
| 1812 | + <i class="fas fa-palette menu-icon" |
| 1813 | + style="margin-left: 0; margin-top: 10px; margin-right: 5px;"></i> |
| 1814 | + <select class="form-control form-select" id="themeSel" name="themeSel"> |
| 1815 | + <option value="cyberpunk" data-i18n="themeCyberpunk"></option> |
| 1816 | + <option value="kids" data-i18n="themeKids"></option> |
| 1817 | + </select> |
| 1818 | + </div> |
| 1819 | + </li> |
1693 | 1820 | </ul> |
1694 | 1821 | </li> |
1695 | 1822 | </ul> |
@@ -7246,27 +7373,64 @@ <h5 class="modal-title" data-i18n="tools.nvs.erase.title"></h5> |
7246 | 7373 | }); |
7247 | 7374 |
|
7248 | 7375 | const lightSwitch = $('#lightSwitch'); |
7249 | | - /* Changes the color mode to a new one and saves the value to local storage. */ |
7250 | | - function newColorMode(mode) { |
7251 | | - console.log('new mode', mode); |
| 7376 | + const themeSel = $('#themeSel'); |
7252 | 7377 |
|
| 7378 | + /* Applies a Bootstrap color mode (dark/light) to body + file explorer. */ |
| 7379 | + function applyColorMode(mode) { |
7253 | 7380 | $('body').attr("data-bs-theme", mode); |
7254 | 7381 | // File explorer knows 'default' or 'default-dark' as themes |
7255 | 7382 | $('#explorerTree').jstree('set_theme', mode == 'dark' ? 'default-dark' : 'default'); |
| 7383 | + } |
7256 | 7384 |
|
| 7385 | + /* Changes the color mode to a new one and saves the value to local storage. */ |
| 7386 | + function newColorMode(mode) { |
| 7387 | + console.log('new mode', mode); |
| 7388 | + applyColorMode(mode); |
7257 | 7389 | localStorage.setItem('lightSwitch', mode); |
7258 | 7390 | } |
7259 | 7391 |
|
| 7392 | + /* The "kids" theme is intrinsically bright, so it pins the light |
| 7393 | + * Bootstrap mode and the dark-mode toggle has no effect there. */ |
| 7394 | + function activeTheme() { |
| 7395 | + return localStorage.getItem('uiTheme') || 'cyberpunk'; |
| 7396 | + } |
| 7397 | + |
| 7398 | + /* Applies a named UI theme (palette) and reconciles the dark/light |
| 7399 | + * toggle, which only makes sense for the cyberpunk theme. */ |
| 7400 | + function applyTheme(theme) { |
| 7401 | + $('body').attr('data-theme', theme); |
| 7402 | + localStorage.setItem('uiTheme', theme); |
| 7403 | + themeSel.val(theme); |
| 7404 | + |
| 7405 | + const darkToggle = lightSwitch.closest('li'); |
| 7406 | + if (theme == 'kids') { |
| 7407 | + applyColorMode('light'); |
| 7408 | + darkToggle.addClass('d-none'); |
| 7409 | + } else { |
| 7410 | + darkToggle.removeClass('d-none'); |
| 7411 | + applyColorMode((localStorage.getItem('lightSwitch') || 'dark')); |
| 7412 | + } |
| 7413 | + } |
| 7414 | + |
7260 | 7415 | /* Triggers a newColorMode(..) call with either 'dark' or 'light' as the new color mode. */ |
7261 | 7416 | function onSwitchDarkMode() { |
| 7417 | + /* The dark/light switch is inert while the kids theme is active. */ |
| 7418 | + if (activeTheme() == 'kids') { |
| 7419 | + return; |
| 7420 | + } |
7262 | 7421 | newColorMode(lightSwitch.prop('checked') ? 'dark' : 'light'); |
7263 | 7422 | } |
7264 | | - /* Fetches the settings for lightSwitch from storage, defaulting to dark. */ |
| 7423 | + /* Fetches the settings for lightSwitch + theme from storage, defaulting |
| 7424 | + * to the dark cyberpunk look. */ |
7265 | 7425 | function darkmodeSwitchSetup() { |
7266 | 7426 | /* The cyberpunk theme is dark by default, regardless of the system theme. */ |
7267 | 7427 | lightSwitch.prop('checked', (localStorage.getItem('lightSwitch') || 'dark') == 'dark'); |
| 7428 | + applyTheme(activeTheme()); |
7268 | 7429 | onSwitchDarkMode(); |
7269 | 7430 | lightSwitch.change(onSwitchDarkMode); |
| 7431 | + themeSel.change(function() { |
| 7432 | + applyTheme(themeSel.val()); |
| 7433 | + }); |
7270 | 7434 | } |
7271 | 7435 |
|
7272 | 7436 | function addOption(selectelement, value) { |
|
0 commit comments