Skip to content

Commit 94dadf0

Browse files
v2.10.1: drop save-as-default, replace the three new themes with real palettes
- removed 'save as default' and /api/theme: pingping is a read-only system and should not write preferences server-side. Theme choice stays in the browser, as before. - the three themes I improvised were poor. Replaced with palettes that exist because someone spent real effort on them: nord — Nord, desaturated blue-greys, one frost accent solarized — Solarized Light, warm paper tuned for long reading amber — DEC VT220 amber phosphor, single hue in three intensities Six total: dark, daylight, arctic, nord, solarized, amber. Verified: all six apply correctly, save button gone, /api/theme returns 404, no JS errors.
1 parent cec82ed commit 94dadf0

2 files changed

Lines changed: 27 additions & 72 deletions

File tree

static/index.html

Lines changed: 27 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -30,32 +30,34 @@
3030
--smoke:rgba(11,114,133,.18); --lossbar:rgba(209,67,63,.5); --glow:0;
3131
--smoke-hot:#064a57; --smoke-mid:#1596ad; --smoke-cool:#b3722f;
3232
}
33-
/* Amber: the old amber-phosphor CRT — warm orange trace on near-black */
34-
html[data-theme="amber"] {
35-
--bg:#0c0a06; --panel:#16120b; --screen:#080602; --line:#2a2115;
36-
--phos:#ffb340; --amber:#ffd28a; --alert:#ff6b4a; --ink:#e8d5b0; --dim:#8a7550;
37-
--grid-axis:#3a2e1c; --grid-split:#1e1810;
38-
--smoke:rgba(255,179,64,.16); --lossbar:rgba(255,107,74,.5); --glow:7;
39-
--smoke-hot:#fff0d0; --smoke-mid:#ffb340; --smoke-cool:#8a6020;
40-
--shadow:0 1px 2px rgba(0,0,0,.6); --shadow-lg:0 10px 30px rgba(0,0,0,.5);
33+
/* Nord — the arctic palette that ships in half the terminals on earth.
34+
Desaturated blue-greys with a single frost accent; nothing shouts. */
35+
html[data-theme="nord"] {
36+
--bg:#2e3440; --panel:#3b4252; --screen:#292e39; --line:#434c5e;
37+
--phos:#88c0d0; --amber:#ebcb8b; --alert:#bf616a; --ink:#e5e9f0; --dim:#7b88a1;
38+
--grid-axis:#4c566a; --grid-split:#373e4c;
39+
--smoke:rgba(136,192,208,.18); --lossbar:rgba(191,97,106,.55); --glow:5;
40+
--smoke-hot:#eceff4; --smoke-mid:#88c0d0; --smoke-cool:#5e81ac;
41+
--shadow:0 1px 2px rgba(0,0,0,.35); --shadow-lg:0 10px 30px rgba(0,0,0,.30);
4142
}
42-
/* Mono: high-contrast black on white — for printing and bright rooms */
43-
html[data-theme="mono"] {
44-
--bg:#ffffff; --panel:#fbfbfb; --screen:#ffffff; --line:#d8d8d8;
45-
--phos:#111111; --amber:#666666; --alert:#c00000; --ink:#111111; --dim:#777777;
46-
--grid-axis:#c8c8c8; --grid-split:#ededed;
47-
--smoke:rgba(17,17,17,.22); --lossbar:rgba(192,0,0,.45); --glow:0;
48-
--smoke-hot:#000000; --smoke-mid:#444444; --smoke-cool:#999999;
49-
--shadow:0 1px 2px rgba(0,0,0,.08); --shadow-lg:0 8px 24px rgba(0,0,0,.10);
43+
/* Solarized Light — Ethan Schoonover's palette, tuned for long reading sessions.
44+
Warm paper base, muted cyan trace, red reserved strictly for loss. */
45+
html[data-theme="solarized"] {
46+
--bg:#fdf6e3; --panel:#fffbf0; --screen:#fffdf6; --line:#e5ddc8;
47+
--phos:#2aa198; --amber:#b58900; --alert:#dc322f; --ink:#586e75; --dim:#93a1a1;
48+
--grid-axis:#d6cfba; --grid-split:#efe8d5;
49+
--smoke:rgba(42,161,152,.22); --lossbar:rgba(220,50,47,.45); --glow:0;
50+
--smoke-hot:#0f5f5a; --smoke-mid:#2aa198; --smoke-cool:#93a1a1;
51+
--shadow:0 1px 3px rgba(101,123,131,.12); --shadow-lg:0 10px 28px rgba(101,123,131,.16);
5052
}
51-
/* Midnight: deep blue, low blue-light glare for night shifts */
52-
html[data-theme="midnight"] {
53-
--bg:#0a0e1a; --panel:#111726; --screen:#070a14; --line:#1e2740;
54-
--phos:#6db3ff; --amber:#ffc857; --alert:#ff5c7a; --ink:#c3d2e8; --dim:#5a6b85;
55-
--grid-axis:#243050; --grid-split:#131a2c;
56-
--smoke:rgba(109,179,255,.16); --lossbar:rgba(255,92,122,.5); --glow:6;
57-
--smoke-hot:#e8f4ff; --smoke-mid:#6db3ff; --smoke-cool:#3a5580;
58-
--shadow:0 1px 2px rgba(0,0,0,.5); --shadow-lg:0 10px 30px rgba(0,0,0,.45);
53+
/* Amber CRT — a DEC VT220 in a dim room. One hue, three intensities, that is it. */
54+
html[data-theme="amber"] {
55+
--bg:#100c04; --panel:#1a1409; --screen:#0a0702; --line:#3a2c14;
56+
--phos:#ffb000; --amber:#ffcc55; --alert:#ff5f2e; --ink:#f0c674; --dim:#8a6d3a;
57+
--grid-axis:#463a1c; --grid-split:#221a0c;
58+
--smoke:rgba(255,176,0,.16); --lossbar:rgba(255,95,46,.5); --glow:8;
59+
--smoke-hot:#fff3d0; --smoke-mid:#ffb000; --smoke-cool:#7a5410;
60+
--shadow:0 1px 2px rgba(0,0,0,.6); --shadow-lg:0 10px 30px rgba(0,0,0,.55);
5961
}
6062
* { box-sizing:border-box; margin:0; }
6163
body { background:var(--bg); color:var(--ink); font-family:var(--mono); padding:18px 22px; }
@@ -112,7 +114,6 @@
112114
<div class="mark">pingping ●</div>
113115
<div class="tag">One binary. One graph. Zero guesswork.</div>
114116
<button class="pill" id="themeBtn" title="switch theme" style="margin-left:auto"><span id="themeName">dark</span></button>
115-
<button class="pill" id="themeSave" title="save this theme as the default for everyone on this install">save as default</button>
116117
</header>
117118

118119
<div class="row">
@@ -176,17 +177,9 @@
176177
<script>
177178
const $ = id => document.getElementById(id);
178179
const cssv = n => getComputedStyle(document.documentElement).getPropertyValue(n).trim();
179-
const THEMES = ['dark', 'daylight', 'arctic', 'amber', 'mono', 'midnight'];
180+
const THEMES = ['dark', 'daylight', 'arctic', 'nord', 'solarized', 'amber'];
180181
let theme = localStorage.getItem('pp_theme') || 'dark';
181182
if (!THEMES.includes(theme)) theme = 'dark';
182-
// 本地没手动选过 -> 跟随服务端保存的默认主题
183-
if (!localStorage.getItem('pp_theme')) {
184-
fetch('/api/theme').then(r => r.json()).then(d => {
185-
if (d.theme && THEMES.includes(d.theme) && d.theme !== theme) {
186-
theme = d.theme; applyTheme(); render();
187-
}
188-
}).catch(() => {});
189-
}
190183
function applyTheme() {
191184
if (theme === 'dark') delete document.documentElement.dataset.theme;
192185
else document.documentElement.dataset.theme = theme;
@@ -418,15 +411,6 @@
418411
applyTheme();
419412
render();
420413
};
421-
$('themeSave').onclick = async () => {
422-
const btn = $('themeSave'), old = btn.textContent;
423-
try {
424-
await fetch('/api/theme', { method: 'POST', body: theme });
425-
btn.textContent = 'saved ✓';
426-
} catch { btn.textContent = 'failed'; }
427-
setTimeout(() => { btn.textContent = old; }, 1500);
428-
};
429-
430414
window.addEventListener('resize', () => chart && chart.resize());
431415
</script>
432416
</body>

web.go

Lines changed: 0 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -6,13 +6,9 @@ import (
66
"embed"
77
"encoding/hex"
88
"encoding/json"
9-
"io"
109
"log"
1110
"net/http"
12-
"os"
13-
"path/filepath"
1411
"strconv"
15-
"strings"
1612
"sync"
1713
"time"
1814
)
@@ -119,31 +115,6 @@ func serveWeb(cfg *Config, store *Store, users map[string]string) error {
119115
writeJSON(w, map[string]bool{"ok": true})
120116
})
121117

122-
// Default theme, stored next to the data so it survives restarts and follows the
123-
// install rather than one browser. GET returns it; POST saves it.
124-
themeFile := filepath.Join(cfg.DataDir, "theme")
125-
mux.HandleFunc("/api/theme", func(w http.ResponseWriter, r *http.Request) {
126-
if r.Method == http.MethodPost {
127-
body, _ := io.ReadAll(io.LimitReader(r.Body, 32))
128-
name := strings.TrimSpace(string(body))
129-
for _, c := range name {
130-
if !(c >= 'a' && c <= 'z') { // whitelist: theme ids are lowercase letters
131-
http.Error(w, `{"error":"bad theme"}`, http.StatusBadRequest)
132-
return
133-
}
134-
}
135-
if name == "" {
136-
http.Error(w, `{"error":"bad theme"}`, http.StatusBadRequest)
137-
return
138-
}
139-
os.WriteFile(themeFile, []byte(name), 0o644)
140-
writeJSON(w, map[string]string{"theme": name})
141-
return
142-
}
143-
b, _ := os.ReadFile(themeFile)
144-
writeJSON(w, map[string]string{"theme": strings.TrimSpace(string(b))})
145-
})
146-
147118
mux.HandleFunc("/api/version", func(w http.ResponseWriter, r *http.Request) {
148119
writeJSON(w, map[string]string{"version": version})
149120
})

0 commit comments

Comments
 (0)