aboutsummaryrefslogtreecommitdiff
path: root/src/zenserver/frontend/html/theme.js
diff options
context:
space:
mode:
Diffstat (limited to 'src/zenserver/frontend/html/theme.js')
-rw-r--r--src/zenserver/frontend/html/theme.js116
1 files changed, 116 insertions, 0 deletions
diff --git a/src/zenserver/frontend/html/theme.js b/src/zenserver/frontend/html/theme.js
new file mode 100644
index 000000000..52ca116ab
--- /dev/null
+++ b/src/zenserver/frontend/html/theme.js
@@ -0,0 +1,116 @@
+// Copyright Epic Games, Inc. All Rights Reserved.
+
+// Theme toggle: cycles system → light → dark → system.
+// Persists choice in localStorage. Applies data-theme attribute on <html>.
+
+(function() {
+ var KEY = 'zen-theme';
+
+ function getStored() {
+ try { return localStorage.getItem(KEY); } catch (e) { return null; }
+ }
+
+ function setStored(value) {
+ try {
+ if (value) localStorage.setItem(KEY, value);
+ else localStorage.removeItem(KEY);
+ } catch (e) {}
+ }
+
+ function apply(theme) {
+ if (theme)
+ document.documentElement.setAttribute('data-theme', theme);
+ else
+ document.documentElement.removeAttribute('data-theme');
+ }
+
+ function getEffective(stored) {
+ if (stored) return stored;
+ return window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light';
+ }
+
+ // Apply stored preference immediately (before paint)
+ var stored = getStored();
+ apply(stored);
+
+ // Create toggle button once DOM is ready
+ function createToggle() {
+ var btn = document.createElement('button');
+ btn.id = 'zen_theme_toggle';
+ btn.title = 'Toggle theme';
+
+ function updateIcon() {
+ var effective = getEffective(getStored());
+ // Show sun in dark mode (click to go light), moon in light mode (click to go dark)
+ btn.textContent = effective === 'dark' ? '\u2600' : '\u263E';
+
+ var isManual = getStored() != null;
+ btn.title = isManual
+ ? 'Theme: ' + effective + ' (click to change, double-click for system)'
+ : 'Theme: system (click to change)';
+ }
+
+ btn.addEventListener('click', function() {
+ var current = getStored();
+ var effective = getEffective(current);
+ // Toggle to the opposite
+ var next = effective === 'dark' ? 'light' : 'dark';
+ setStored(next);
+ apply(next);
+ updateIcon();
+ });
+
+ btn.addEventListener('dblclick', function(e) {
+ e.preventDefault();
+ // Reset to system preference
+ setStored(null);
+ apply(null);
+ updateIcon();
+ });
+
+ // Update icon when system preference changes
+ window.matchMedia('(prefers-color-scheme: dark)').addEventListener('change', function() {
+ if (!getStored()) updateIcon();
+ });
+
+ updateIcon();
+ document.body.appendChild(btn);
+
+ // WebSocket pause/play toggle
+ var WS_KEY = 'zen-ws-paused';
+ var wsBtn = document.createElement('button');
+ wsBtn.id = 'zen_ws_toggle';
+
+ var initialPaused = false;
+ try { initialPaused = localStorage.getItem(WS_KEY) === 'true'; } catch (e) {}
+
+ function updateWsIcon(paused) {
+ wsBtn.dataset.paused = paused ? 'true' : 'false';
+ wsBtn.textContent = paused ? '\u25B6' : '\u23F8';
+ wsBtn.title = paused ? 'Resume live updates' : 'Pause live updates';
+ }
+
+ updateWsIcon(initialPaused);
+
+ // Fire initial event so pages pick up persisted state
+ document.addEventListener('DOMContentLoaded', function() {
+ if (initialPaused) {
+ document.dispatchEvent(new CustomEvent('zen-ws-toggle', { detail: { paused: true } }));
+ }
+ });
+
+ wsBtn.addEventListener('click', function() {
+ var paused = wsBtn.dataset.paused !== 'true';
+ try { localStorage.setItem(WS_KEY, paused ? 'true' : 'false'); } catch (e) {}
+ updateWsIcon(paused);
+ document.dispatchEvent(new CustomEvent('zen-ws-toggle', { detail: { paused: paused } }));
+ });
+
+ document.body.appendChild(wsBtn);
+ }
+
+ if (document.readyState === 'loading')
+ document.addEventListener('DOMContentLoaded', createToggle);
+ else
+ createToggle();
+})();