1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
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();
})();
|