4dc8506c9b
Follow-up to the player-modal / maintenance-page fix: the same root cause --
colours hard-coded for the light theme -- affected the whole app, in two
places.
1) Inline style attributes (inline outranks the theme rules, so the light
value survives dark mode and the inherited text colour turns
light-on-light). base.html now carries a single, property-scoped
dark-mode override layer that maps every light literal found in the
templates to the theme variables: light backgrounds/whites -> dark
surfaces, semantic pastels -> dark equivalents, dark text -> light text,
light borders -> dark borders, plus .back-link. Scoped to
body.dark-mode, so the light theme is untouched. This covers all
templates, including ones not edited here and any added later.
2) Page-level <style> blocks with no dark support:
* admin/https_config.html hard-coded a light-only palette (blue-tint
panels, dark labels) -- added a body.dark-mode block for it.
* dashboard.html / help.html / manage_player.html coloured text links
with --primary-color (#7c3aed in dark = too dim). Introduced a
dedicated --link-color (#667eea light / #a78bfa dark) and used it for
text links, leaving --primary-color for button/background use.
Verified with an automated luminance scan of all 18 pages in both themes:
dark mode has 0 light backgrounds and 0 dark-on-dark text (the only
remaining light surfaces are the intentional white buttons on the admin
gradient cards), and light mode is unchanged.
634 lines
25 KiB
HTML
634 lines
25 KiB
HTML
<!DOCTYPE html>
|
|
<html lang="en">
|
|
<head>
|
|
<meta charset="UTF-8">
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
<title>{% block title %}DigiServer v2{% endblock %}</title>
|
|
<style>
|
|
/* Ensure emoji font support */
|
|
@supports (font-family: "Apple Color Emoji") {
|
|
body {
|
|
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Noto Color Emoji";
|
|
}
|
|
}
|
|
|
|
:root {
|
|
/* Light Mode Colors */
|
|
--primary-gradient: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
|
--primary-color: #667eea;
|
|
--primary-dark: #5568d3;
|
|
--secondary-color: #764ba2;
|
|
/* Colour for text links. Kept separate from --primary-color so the
|
|
primary colour can stay dark enough for button backgrounds while
|
|
links remain legible on the matching background. */
|
|
--link-color: #667eea;
|
|
--bg-color: #f5f7fa;
|
|
--card-bg: #ffffff;
|
|
--text-color: #2d3748;
|
|
--text-secondary: #718096;
|
|
--border-color: #e2e8f0;
|
|
--header-bg: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
|
--shadow: 0 4px 6px rgba(0, 0, 0, 0.07);
|
|
--shadow-lg: 0 10px 25px rgba(0, 0, 0, 0.1);
|
|
}
|
|
|
|
body.dark-mode {
|
|
/* Dark Mode Colors */
|
|
--primary-gradient: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
|
--primary-color: #7c3aed;
|
|
--primary-dark: #6d28d9;
|
|
--secondary-color: #8b5cf6;
|
|
/* Dark background needs a lighter link colour (#7c3aed is too dim
|
|
on a dark surface). */
|
|
--link-color: #a78bfa;
|
|
--bg-color: #1a202c;
|
|
--card-bg: #2d3748;
|
|
--text-color: #e2e8f0;
|
|
--text-secondary: #a0aec0;
|
|
--border-color: #4a5568;
|
|
--header-bg: linear-gradient(135deg, #7c3aed 0%, #8b5cf6 100%);
|
|
--shadow: 0 4px 6px rgba(0, 0, 0, 0.3);
|
|
--shadow-lg: 0 10px 25px rgba(0, 0, 0, 0.4);
|
|
}
|
|
|
|
* { margin: 0; padding: 0; box-sizing: border-box; }
|
|
|
|
body {
|
|
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, sans-serif;
|
|
line-height: 1.6;
|
|
color: var(--text-color);
|
|
background: var(--bg-color);
|
|
transition: background 0.3s, color 0.3s;
|
|
}
|
|
.container {
|
|
max-width: 1200px;
|
|
margin: 0 auto;
|
|
padding: 20px;
|
|
}
|
|
header {
|
|
background: var(--header-bg);
|
|
color: white;
|
|
padding: 1rem 0;
|
|
margin-bottom: 2rem;
|
|
box-shadow: var(--shadow);
|
|
}
|
|
header .container {
|
|
display: flex;
|
|
justify-content: space-between;
|
|
align-items: center;
|
|
flex-wrap: wrap;
|
|
gap: 1rem;
|
|
}
|
|
header h1 {
|
|
font-size: 1.5rem;
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 0.5rem;
|
|
}
|
|
nav {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 0.5rem;
|
|
flex-wrap: wrap;
|
|
}
|
|
nav a {
|
|
color: white;
|
|
text-decoration: none;
|
|
padding: 0.5rem 1rem;
|
|
border-radius: 6px;
|
|
transition: all 0.3s;
|
|
font-weight: 500;
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 0.5rem;
|
|
}
|
|
nav a:hover {
|
|
background: rgba(255,255,255,0.2);
|
|
transform: translateY(-2px);
|
|
}
|
|
nav a img {
|
|
width: 18px;
|
|
height: 18px;
|
|
filter: brightness(0) invert(1);
|
|
}
|
|
|
|
/* Mobile Responsive */
|
|
@media (max-width: 768px) {
|
|
header .container {
|
|
flex-direction: column;
|
|
align-items: stretch;
|
|
padding: 1rem;
|
|
}
|
|
|
|
header h1 {
|
|
font-size: 1.25rem;
|
|
justify-content: center;
|
|
text-align: center;
|
|
}
|
|
|
|
nav {
|
|
flex-direction: column;
|
|
gap: 0.5rem;
|
|
width: 100%;
|
|
}
|
|
|
|
nav a {
|
|
justify-content: center;
|
|
width: 100%;
|
|
padding: 0.75rem 1rem;
|
|
}
|
|
|
|
.dark-mode-toggle {
|
|
margin-left: 0;
|
|
width: 100%;
|
|
padding: 0.75rem;
|
|
}
|
|
|
|
.container {
|
|
padding: 15px;
|
|
}
|
|
|
|
.card {
|
|
padding: 1rem;
|
|
margin-bottom: 1rem;
|
|
}
|
|
|
|
.card-header {
|
|
padding: 15px;
|
|
margin: -1rem -1rem 1rem -1rem;
|
|
}
|
|
}
|
|
|
|
@media (max-width: 480px) {
|
|
header h1 {
|
|
font-size: 1.1rem;
|
|
}
|
|
|
|
nav a {
|
|
font-size: 0.9rem;
|
|
padding: 0.6rem 0.8rem;
|
|
}
|
|
|
|
nav a img {
|
|
width: 16px;
|
|
height: 16px;
|
|
}
|
|
}
|
|
|
|
.dark-mode-toggle {
|
|
background: rgba(255,255,255,0.2);
|
|
border: 2px solid rgba(255,255,255,0.3);
|
|
color: white;
|
|
cursor: pointer;
|
|
padding: 0.5rem;
|
|
border-radius: 6px;
|
|
transition: all 0.3s;
|
|
margin-left: 0.5rem;
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
}
|
|
.dark-mode-toggle:hover {
|
|
background: rgba(255,255,255,0.3);
|
|
transform: scale(1.1);
|
|
}
|
|
.dark-mode-toggle img {
|
|
width: 20px;
|
|
height: 20px;
|
|
filter: brightness(0) invert(1);
|
|
}
|
|
|
|
/* Emoji fallback for systems without emoji fonts */
|
|
.emoji-fallback::before {
|
|
content: attr(data-emoji);
|
|
font-family: 'Apple Color Emoji', 'Segoe UI Emoji', 'Noto Color Emoji', sans-serif;
|
|
}
|
|
.alert {
|
|
padding: 1rem;
|
|
margin-bottom: 1rem;
|
|
border-radius: 8px;
|
|
border-left: 4px solid;
|
|
transition: all 0.3s;
|
|
}
|
|
.alert-success {
|
|
background: #d4edda;
|
|
border-color: #28a745;
|
|
color: #155724;
|
|
}
|
|
body.dark-mode .alert-success {
|
|
background: rgba(40, 167, 69, 0.2);
|
|
border-color: #28a745;
|
|
color: #7ce3a3;
|
|
}
|
|
.alert-danger {
|
|
background: #f8d7da;
|
|
border-color: #dc3545;
|
|
color: #721c24;
|
|
}
|
|
body.dark-mode .alert-danger {
|
|
background: rgba(220, 53, 69, 0.2);
|
|
border-color: #dc3545;
|
|
color: #f88f9a;
|
|
}
|
|
.alert-warning {
|
|
background: #fff3cd;
|
|
border-color: #ffc107;
|
|
color: #856404;
|
|
}
|
|
body.dark-mode .alert-warning {
|
|
background: rgba(255, 193, 7, 0.2);
|
|
border-color: #ffc107;
|
|
color: #ffd454;
|
|
}
|
|
.alert-info {
|
|
background: #d1ecf1;
|
|
border-color: #17a2b8;
|
|
color: #0c5460;
|
|
}
|
|
body.dark-mode .alert-info {
|
|
background: rgba(23, 162, 184, 0.2);
|
|
border-color: #17a2b8;
|
|
color: #7dd3e0;
|
|
}
|
|
.card {
|
|
background: var(--card-bg);
|
|
border-radius: 12px;
|
|
padding: 1.5rem;
|
|
margin-bottom: 1.5rem;
|
|
box-shadow: var(--shadow);
|
|
transition: all 0.3s;
|
|
border: 1px solid var(--border-color);
|
|
}
|
|
.card:hover {
|
|
box-shadow: var(--shadow-lg);
|
|
transform: translateY(-2px);
|
|
}
|
|
.card h2 {
|
|
margin-bottom: 1rem;
|
|
color: var(--text-color);
|
|
font-weight: 600;
|
|
}
|
|
.card h3 {
|
|
color: var(--text-color);
|
|
}
|
|
|
|
/* Card with gradient header */
|
|
.card-header {
|
|
background: var(--primary-gradient);
|
|
color: white;
|
|
padding: 20px;
|
|
border-radius: 12px 12px 0 0;
|
|
margin: -1.5rem -1.5rem 1.5rem -1.5rem;
|
|
}
|
|
.card-header h2 {
|
|
margin: 0;
|
|
color: white;
|
|
}
|
|
|
|
.btn {
|
|
display: inline-block;
|
|
padding: 0.5rem 1rem;
|
|
background: var(--primary-color);
|
|
color: white;
|
|
text-decoration: none;
|
|
border-radius: 6px;
|
|
border: none;
|
|
cursor: pointer;
|
|
transition: all 0.3s;
|
|
font-weight: 500;
|
|
}
|
|
.btn:hover {
|
|
background: var(--primary-dark);
|
|
transform: translateY(-2px);
|
|
box-shadow: 0 4px 12px rgba(102, 126, 234, 0.4);
|
|
}
|
|
.btn-primary {
|
|
background: var(--primary-gradient);
|
|
}
|
|
.btn-primary:hover {
|
|
transform: translateY(-2px);
|
|
box-shadow: 0 6px 20px rgba(102, 126, 234, 0.4);
|
|
}
|
|
.btn-danger { background: #e74c3c; }
|
|
.btn-danger:hover {
|
|
background: #c0392b;
|
|
box-shadow: 0 4px 12px rgba(231, 76, 60, 0.4);
|
|
}
|
|
.btn-success { background: #27ae60; }
|
|
.btn-success:hover {
|
|
background: #229954;
|
|
box-shadow: 0 4px 12px rgba(39, 174, 96, 0.4);
|
|
}
|
|
.btn-info {
|
|
background: #3498db;
|
|
}
|
|
.btn-info:hover {
|
|
background: #2980b9;
|
|
box-shadow: 0 4px 12px rgba(52, 152, 219, 0.4);
|
|
}
|
|
.btn-sm {
|
|
padding: 0.375rem 0.75rem;
|
|
font-size: 0.875rem;
|
|
}
|
|
|
|
/* Form Inputs */
|
|
input, textarea, select {
|
|
background: var(--card-bg);
|
|
color: var(--text-color);
|
|
border: 1px solid var(--border-color);
|
|
transition: all 0.3s;
|
|
}
|
|
input:focus, textarea:focus, select:focus {
|
|
border-color: var(--primary-color);
|
|
outline: none;
|
|
box-shadow: 0 0 0 3px rgba(102, 126, 234, 0.1);
|
|
}
|
|
body.dark-mode input:focus,
|
|
body.dark-mode textarea:focus,
|
|
body.dark-mode select:focus {
|
|
box-shadow: 0 0 0 3px rgba(124, 58, 237, 0.3);
|
|
}
|
|
|
|
/* Tables */
|
|
table {
|
|
background: var(--card-bg);
|
|
color: var(--text-color);
|
|
}
|
|
th {
|
|
background: var(--bg-color);
|
|
color: var(--text-color);
|
|
}
|
|
tr {
|
|
border-bottom: 1px solid var(--border-color);
|
|
}
|
|
|
|
/* Code blocks */
|
|
code, pre {
|
|
background: var(--bg-color);
|
|
color: var(--text-color);
|
|
border: 1px solid var(--border-color);
|
|
}
|
|
|
|
footer {
|
|
margin-top: 2rem;
|
|
padding: 1rem 0;
|
|
text-align: center;
|
|
color: var(--text-secondary);
|
|
font-size: 0.9rem;
|
|
}
|
|
|
|
/* ================================================================
|
|
Dark-mode fixes for INLINE colours
|
|
----------------------------------------------------------------
|
|
A recurring pattern across the templates is to set colours with
|
|
inline style="..." attributes (a few JS blocks build nodes the
|
|
same way). An inline declaration outranks every normal rule --
|
|
including the body.dark-mode rules above -- so those literals keep
|
|
their LIGHT value in dark mode. The text, which inherits the (now
|
|
light) --text-color, then ends up light-on-light: unreadable.
|
|
|
|
Inline styles can only be beaten with !important, so the light
|
|
literals are mapped to the theme variables here, centrally, with
|
|
the property scoped so a colour used for two things (e.g. #1a202c
|
|
is both a dark surface and dark text) is only changed where it is
|
|
wrong. This fixes every template at once -- including any future
|
|
page that repeats the pattern -- instead of patching each file.
|
|
|
|
Everything is scoped to body.dark-mode, so the LIGHT theme is
|
|
completely untouched.
|
|
|
|
Ordering matters: selectors have equal specificity, so later rules
|
|
win. The bare "#fff" rule is therefore placed BEFORE the "#fff3cd"
|
|
/"#fff8e1" ones, because "#fff" is a string prefix of them.
|
|
================================================================ */
|
|
|
|
/* --- White / near-white surfaces -> dark card --------------------- */
|
|
body.dark-mode [style*="background: white"], body.dark-mode [style*="background-color: white"],
|
|
body.dark-mode [style*="background: #fff"], body.dark-mode [style*="background-color: #fff"],
|
|
body.dark-mode [style*="background: #ffffff"], body.dark-mode [style*="background-color: #ffffff"],
|
|
body.dark-mode [style*="background: #fefefe"], body.dark-mode [style*="background-color: #fefefe"],
|
|
body.dark-mode [style*="background: #f5f7fa"], body.dark-mode [style*="background-color: #f5f7fa"],
|
|
body.dark-mode [style*="background: #e9ecef"], body.dark-mode [style*="background-color: #e9ecef"],
|
|
body.dark-mode [style*="background: #e0e0e0"], body.dark-mode [style*="background-color: #e0e0e0"],
|
|
body.dark-mode [style*="background: #f0f2ff"], body.dark-mode [style*="background-color: #f0f2ff"],
|
|
body.dark-mode [style*="background: #e8ebff"], body.dark-mode [style*="background-color: #e8ebff"],
|
|
body.dark-mode [style*="background: #e7e9ff"], body.dark-mode [style*="background-color: #e7e9ff"],
|
|
body.dark-mode [style*="background: #ede9fe"], body.dark-mode [style*="background-color: #ede9fe"] {
|
|
background: var(--card-bg) !important;
|
|
}
|
|
|
|
/* --- Light grey panels -> dark page background -------------------- */
|
|
body.dark-mode [style*="background: #f8f9fa"], body.dark-mode [style*="background-color: #f8f9fa"],
|
|
body.dark-mode [style*="background: #f0f0f0"], body.dark-mode [style*="background-color: #f0f0f0"],
|
|
body.dark-mode [style*="background: #f5f5f5"], body.dark-mode [style*="background-color: #f5f5f5"],
|
|
body.dark-mode [style*="background: #f4f4f4"], body.dark-mode [style*="background-color: #f4f4f4"],
|
|
body.dark-mode [style*="background: #f8fafc"], body.dark-mode [style*="background-color: #f8fafc"] {
|
|
background: var(--bg-color) !important;
|
|
}
|
|
|
|
/* --- Semantic pastel backgrounds -> dark equivalents -------------- */
|
|
/* info / blue */
|
|
body.dark-mode [style*="background: #d1ecf1"], body.dark-mode [style*="background-color: #d1ecf1"],
|
|
body.dark-mode [style*="background: #cce5ff"], body.dark-mode [style*="background-color: #cce5ff"],
|
|
body.dark-mode [style*="background: #e7f3ff"], body.dark-mode [style*="background-color: #e7f3ff"],
|
|
body.dark-mode [style*="background: #f0f7ff"], body.dark-mode [style*="background-color: #f0f7ff"] {
|
|
background: #1e3a5f !important;
|
|
}
|
|
/* success / green */
|
|
body.dark-mode [style*="background: #d4edda"], body.dark-mode [style*="background-color: #d4edda"],
|
|
body.dark-mode [style*="background: #d1f0e0"], body.dark-mode [style*="background-color: #d1f0e0"],
|
|
body.dark-mode [style*="background: #c6f6d5"], body.dark-mode [style*="background-color: #c6f6d5"] {
|
|
background: #1a4d2e !important;
|
|
}
|
|
/* warning / amber (must stay AFTER the "#fff" rule above) */
|
|
body.dark-mode [style*="background: #fff3cd"], body.dark-mode [style*="background-color: #fff3cd"],
|
|
body.dark-mode [style*="background: #fff8e1"], body.dark-mode [style*="background-color: #fff8e1"],
|
|
body.dark-mode [style*="background: #ffeaa7"], body.dark-mode [style*="background-color: #ffeaa7"],
|
|
body.dark-mode [style*="background: #fef3c7"], body.dark-mode [style*="background-color: #fef3c7"],
|
|
body.dark-mode [style*="background: #feebc8"], body.dark-mode [style*="background-color: #feebc8"] {
|
|
background: #4a3800 !important;
|
|
}
|
|
/* danger / red */
|
|
body.dark-mode [style*="background: #f8d7da"], body.dark-mode [style*="background-color: #f8d7da"],
|
|
body.dark-mode [style*="background: #fed7d7"], body.dark-mode [style*="background-color: #fed7d7"] {
|
|
background: #4a1a1a !important;
|
|
}
|
|
/* mid-grey chips / knobs */
|
|
body.dark-mode [style*="background: #ccc"], body.dark-mode [style*="background-color: #ccc"],
|
|
body.dark-mode [style*="background: #6c757d"], body.dark-mode [style*="background-color: #6c757d"] {
|
|
background: #4a5568 !important;
|
|
}
|
|
|
|
/* --- Dark text -> light text -------------------------------------- */
|
|
body.dark-mode [style*="color: black"] {
|
|
color: var(--text-color) !important;
|
|
}
|
|
body.dark-mode [style*="color: #6c757d"], body.dark-mode [style*="color:#6c757d"],
|
|
body.dark-mode [style*="color: #666"], body.dark-mode [style*="color: #888"],
|
|
body.dark-mode [style*="color: #999"], body.dark-mode [style*="color: #aaa"],
|
|
body.dark-mode [style*="color: #495057"], body.dark-mode [style*="color: #718096"],
|
|
body.dark-mode [style*="color: #7f8c8d"], body.dark-mode [style*="color: #5a6a78"],
|
|
body.dark-mode [style*="color: #4a5568"] {
|
|
color: var(--text-secondary) !important;
|
|
}
|
|
body.dark-mode [style*="color: #555"], body.dark-mode [style*="color: #475569"],
|
|
body.dark-mode [style*="color: #64748b"] {
|
|
color: #cbd5e1 !important;
|
|
}
|
|
body.dark-mode [style*="color: #333"], body.dark-mode [style*="color: #2c3e50"],
|
|
body.dark-mode [style*="color: #000"], body.dark-mode [style*="color: #1a202c"],
|
|
body.dark-mode [style*="color: #2d3748"] {
|
|
color: var(--text-color) !important;
|
|
}
|
|
/* links / info blue */
|
|
body.dark-mode [style*="color: #0066cc"], body.dark-mode [style*="color: #007bff"],
|
|
body.dark-mode [style*="color: #0056b3"], body.dark-mode [style*="color: #0c5460"],
|
|
body.dark-mode [style*="color: #004085"], body.dark-mode [style*="color: #3498db"] {
|
|
color: #90cdf4 !important;
|
|
}
|
|
/* success text */
|
|
body.dark-mode [style*="color: #155724"], body.dark-mode [style*="color: #156b2e"],
|
|
body.dark-mode [style*="color: #28a745"], body.dark-mode [style*="color: #27ae60"],
|
|
body.dark-mode [style*="color: #2f855a"] {
|
|
color: #86efac !important;
|
|
}
|
|
/* warning text */
|
|
body.dark-mode [style*="color: #856404"], body.dark-mode [style*="color: #92400e"],
|
|
body.dark-mode [style*="color: #c05621"], body.dark-mode [style*="color: #e67e22"] {
|
|
color: #fbbf24 !important;
|
|
}
|
|
/* danger text */
|
|
body.dark-mode [style*="color: #721c24"], body.dark-mode [style*="color: #dc3545"],
|
|
body.dark-mode [style*="color: #e74c3c"], body.dark-mode [style*="color: #e53e3e"],
|
|
body.dark-mode [style*="color:#e53e3e"], body.dark-mode [style*="color: #c53030"],
|
|
body.dark-mode [style*="color: #b91c1c"] {
|
|
color: #fca5a5 !important;
|
|
}
|
|
/* purple accents */
|
|
body.dark-mode [style*="color: #5b21b6"] {
|
|
color: #a78bfa !important;
|
|
}
|
|
|
|
/* --- Light borders -> dark borders ------------------------------- */
|
|
body.dark-mode [style*="border-top-color: white"] {
|
|
border-top-color: var(--border-color) !important;
|
|
}
|
|
body.dark-mode [style*="#e2e8f0"], body.dark-mode [style*="#dee2e6"],
|
|
body.dark-mode [style*="#ddd"], body.dark-mode [style*="#ccc"],
|
|
body.dark-mode [style*="#ced4da"], body.dark-mode [style*="#e0e0e0"],
|
|
body.dark-mode [style*="#eee"], body.dark-mode [style*="#e9ecef"],
|
|
body.dark-mode [style*="#f0f0f0"], body.dark-mode [style*="#f0f2ff"],
|
|
body.dark-mode [style*="#888"], body.dark-mode [style*="#999"] {
|
|
border-color: #4a5568 !important;
|
|
}
|
|
body.dark-mode [style*="#c3e6cb"], body.dark-mode [style*="#d1f0e0"] {
|
|
border-color: #2f855a !important;
|
|
}
|
|
body.dark-mode [style*="#f5c6cb"] {
|
|
border-color: #c53030 !important;
|
|
}
|
|
body.dark-mode [style*="#bee5eb"], body.dark-mode [style*="#cce5ff"] {
|
|
border-color: #2c5282 !important;
|
|
}
|
|
|
|
/* --- "Back" links (class used across the admin pages) ------------- */
|
|
body.dark-mode .back-link {
|
|
color: #90cdf4 !important;
|
|
}
|
|
</style>
|
|
{% block extra_css %}{% endblock %}
|
|
</head>
|
|
<body>
|
|
<header>
|
|
<div class="container">
|
|
<h1>
|
|
<img src="{{ url_for('static', filename='uploads/header_logo.png') }}?v={{ logo_version }}" alt="DigiServer" style="height: 32px; width: auto; margin-right: 8px;" onerror="this.style.display='none';" onload="this.style.display='inline';">
|
|
DigiServer
|
|
</h1>
|
|
<nav>
|
|
{% if current_user.is_authenticated %}
|
|
<a href="{{ url_for('main.dashboard') }}">
|
|
<img src="{{ url_for('static', filename='icons/home.svg') }}" alt="">
|
|
Dashboard
|
|
</a>
|
|
<a href="{{ url_for('players.list') }}">
|
|
<img src="{{ url_for('static', filename='icons/monitor.svg') }}" alt="">
|
|
Players
|
|
</a>
|
|
<a href="{{ url_for('content.content_list') }}">
|
|
<img src="{{ url_for('static', filename='icons/playlist.svg') }}" alt="">
|
|
Playlists
|
|
</a>
|
|
<a href="{{ url_for('main.help_page') }}">
|
|
<img src="{{ url_for('static', filename='icons/help.svg') }}" alt="" onerror="this.style.display='none';">
|
|
Ajutor
|
|
</a>
|
|
<a href="{{ url_for('admin.admin_panel') }}">Admin</a>
|
|
<a href="{{ url_for('auth.logout') }}">Logout ({{ current_user.username }})</a>
|
|
<button class="dark-mode-toggle" onclick="toggleDarkMode()" title="Toggle Dark Mode">
|
|
<img id="theme-icon" src="{{ url_for('static', filename='icons/moon.svg') }}" alt="Toggle theme">
|
|
</button>
|
|
{% else %}
|
|
<a href="{{ url_for('auth.login') }}">Login</a>
|
|
<a href="{{ url_for('auth.register') }}">Register</a>
|
|
{% endif %}
|
|
</nav>
|
|
</div>
|
|
</header>
|
|
|
|
<div class="container">
|
|
{% with messages = get_flashed_messages(with_categories=true) %}
|
|
{% if messages %}
|
|
{% for category, message in messages %}
|
|
<div class="alert alert-{{ category }}">
|
|
{{ message }}
|
|
</div>
|
|
{% endfor %}
|
|
{% endif %}
|
|
{% endwith %}
|
|
|
|
{% block content %}{% endblock %}
|
|
</div>
|
|
|
|
<footer>
|
|
<div class="container">
|
|
DigiServer v2.0.0-alpha | Blueprint Architecture | {{ server_version if server_version else 'Development' }}
|
|
</div>
|
|
</footer>
|
|
|
|
<script>
|
|
// Dark Mode Toggle
|
|
function toggleDarkMode() {
|
|
const body = document.body;
|
|
const themeIcon = document.getElementById('theme-icon');
|
|
|
|
body.classList.toggle('dark-mode');
|
|
|
|
// Update icon
|
|
if (body.classList.contains('dark-mode')) {
|
|
themeIcon.src = "{{ url_for('static', filename='icons/sun.svg') }}";
|
|
localStorage.setItem('darkMode', 'enabled');
|
|
} else {
|
|
themeIcon.src = "{{ url_for('static', filename='icons/moon.svg') }}";
|
|
localStorage.setItem('darkMode', 'disabled');
|
|
}
|
|
}
|
|
|
|
// Load saved theme preference
|
|
document.addEventListener('DOMContentLoaded', function() {
|
|
const darkMode = localStorage.getItem('darkMode');
|
|
const themeIcon = document.getElementById('theme-icon');
|
|
|
|
if (darkMode === 'enabled') {
|
|
document.body.classList.add('dark-mode');
|
|
if (themeIcon) {
|
|
themeIcon.src = "{{ url_for('static', filename='icons/sun.svg') }}";
|
|
}
|
|
}
|
|
});
|
|
</script>
|
|
|
|
{% block extra_js %}{% endblock %}
|
|
</body>
|
|
</html>
|