feat: ADD darkmode #16

This commit is contained in:
Caffeine Fueled 2026-03-07 09:48:42 +01:00
parent 3bfe886a96
commit 0fff347858
Signed by: cf7
GPG key ID: CA295D643074C68C

107
app.py
View file

@ -134,46 +134,69 @@ HTML = """<!doctype html>
<meta charset="utf-8"/> <meta charset="utf-8"/>
<title>aukpad</title> <title>aukpad</title>
<style> <style>
:root { --line-h: 1.4; } :root {
--line-h: 1.4;
--bg: #fbfbfb;
--text: #111;
--border: #ddd;
--btn-bg: #fff;
--gutter-bg: #f8fafc;
--gutter-border: #eee;
--gutter-color: #9ca3af;
--panel-bg: #fff;
--overlay-bg: rgba(0,0,0,.55);
}
[data-theme="dark"] {
--bg: #17181E;
--text: #F0F0F0;
--border: #3a3b45;
--btn-bg: #2a2b33;
--gutter-bg: #1e1f28;
--gutter-border: #2e2f3a;
--gutter-color: #6b7280;
--panel-bg: #2a2b33;
--overlay-bg: rgba(0,0,0,.75);
}
* { box-sizing: border-box; } * { box-sizing: border-box; }
html, body { height: 100%; margin: 0; padding: 0; background-color: #fbfbfb; } html, body { height: 100%; margin: 0; padding: 0; background-color: var(--bg); color: var(--text); }
body { font-family: ui-sans-serif, system-ui, -apple-system, "Segoe UI", Roboto, "Helvetica Neue", Arial, "Noto Sans", "Apple Color Emoji","Segoe UI Emoji"; body { font-family: ui-sans-serif, system-ui, -apple-system, "Segoe UI", Roboto, "Helvetica Neue", Arial, "Noto Sans", "Apple Color Emoji","Segoe UI Emoji";
max-width: 1000px; margin: 0 auto; padding: 1rem; display: flex; flex-direction: column; height: 100vh; box-sizing: border-box; } max-width: 1000px; margin: 0 auto; padding: 1rem; display: flex; flex-direction: column; height: 100vh; box-sizing: border-box; }
header { display:flex; justify-content:space-between; align-items:center; margin-bottom: .5rem; flex-shrink: 0; } header { display:flex; justify-content:space-between; align-items:center; margin-bottom: .5rem; flex-shrink: 0; }
a,button { padding:.35rem .6rem; text-decoration:none; border:1px solid #ddd; border-radius:4px; background:#fff; cursor:pointer; } a,button { padding:.35rem .6rem; text-decoration:none; border:1px solid var(--border); border-radius:4px; background:var(--btn-bg); color:var(--text); cursor:pointer; }
#newpad { background:#000; color:#fff; border:1px solid #000; font-weight:bold; } #newpad { background:#000; color:#fff; border:1px solid #000; font-weight:bold; }
#status { font-size:.9rem; opacity:.7; margin-left:.5rem; } #status { font-size:.9rem; opacity:.7; margin-left:.5rem; }
#status::before { content: "●"; margin-right: .3rem; color: #ef4444; } #status::before { content: "●"; margin-right: .3rem; color: #ef4444; }
#status.connected::before { color: #22c55e; } #status.connected::before { color: #22c55e; }
#peers { font-size:.95rem; font-weight:bold; margin-left:.5rem; margin-right:.3rem; color:#22c55e; display:none; } #peers { font-size:.95rem; font-weight:bold; margin-left:.5rem; margin-right:.3rem; color:#22c55e; display:none; }
#wrap { display:grid; grid-template-columns: max-content 1fr; border:1px solid #ddd; border-radius:4px; overflow:hidden; #wrap { display:grid; grid-template-columns: max-content 1fr; border:1px solid var(--border); border-radius:4px; overflow:hidden;
flex: 1; } flex: 1; }
#gutter, #t { font: 14px/var(--line-h) ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace; } #gutter, #t { font: 14px/var(--line-h) ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace; }
#gutter { padding:.5rem .75rem; text-align:right; color:#9ca3af; background:#f8fafc; border-right:1px solid #eee; #gutter { padding:.5rem .75rem; text-align:right; color:var(--gutter-color); background:var(--gutter-bg); border-right:1px solid var(--gutter-border);
user-select:none; min-width: 3ch; white-space: pre; height: 100%; overflow: hidden; } user-select:none; min-width: 3ch; white-space: pre; height: 100%; overflow: hidden; }
#t { padding:.5rem .75rem; width:100%; height: 100%; resize: none; border:0; outline:0; #t { padding:.5rem .75rem; width:100%; height: 100%; resize: none; border:0; outline:0;
overflow:auto; white-space: pre; } overflow:auto; white-space: pre; background:var(--bg); color:var(--text); }
#newpad { margin-left:.5rem; } #newpad { margin-left:.5rem; }
#info { margin-left:.5rem; color: black; font-size: 0.8rem; } #info { margin-left:.5rem; font-size: 0.8rem; }
pre { margin: 0; } pre { margin: 0; }
/* Password protection */ /* Password protection */
#lock-btn.locked { background:#fef3c7; border-color:#f59e0b; } #lock-btn.locked { background:#dbeafe; border-color:#3b82f6; }
#pw-panel { display:none; position:absolute; top:2.5rem; right:0; background:#fff; border:1px solid #ddd; [data-theme="dark"] #lock-btn.locked { background:#1e3a5f; border-color:#3b82f6; }
#pw-panel { display:none; position:absolute; top:2.5rem; right:0; background:var(--panel-bg); border:1px solid var(--border);
border-radius:6px; padding:.75rem; box-shadow:0 4px 12px rgba(0,0,0,.1); z-index:10; min-width:230px; } border-radius:6px; padding:.75rem; box-shadow:0 4px 12px rgba(0,0,0,.1); z-index:10; min-width:230px; }
#pw-panel.open { display:block; } #pw-panel.open { display:block; }
#pw-panel label { font-size:.8rem; font-weight:bold; display:block; margin-bottom:.4rem; } #pw-panel label { font-size:.8rem; font-weight:bold; display:block; margin-bottom:.4rem; }
#pw-panel input { width:100%; padding:.35rem .5rem; border:1px solid #ddd; border-radius:4px; #pw-panel input { width:100%; padding:.35rem .5rem; border:1px solid var(--border); border-radius:4px;
margin-bottom:.5rem; font-size:.9rem; font-family:inherit; } margin-bottom:.5rem; font-size:.9rem; font-family:inherit; background:var(--btn-bg); color:var(--text); }
.pw-btns { display:flex; gap:.4rem; } .pw-btns { display:flex; gap:.4rem; }
.pw-btns button { flex:1; font-size:.8rem; padding:.3rem .4rem; } .pw-btns button { flex:1; font-size:.8rem; padding:.3rem .4rem; }
#pw-msg { font-size:.8rem; margin-top:.4rem; color:#6b7280; min-height:1.2em; } #pw-msg { font-size:.8rem; margin-top:.4rem; color:#6b7280; min-height:1.2em; }
#pw-overlay { display:none; position:fixed; inset:0; background:rgba(0,0,0,.55); z-index:100; #pw-overlay { display:none; position:fixed; inset:0; background:var(--overlay-bg); z-index:100;
align-items:center; justify-content:center; } align-items:center; justify-content:center; }
#pw-overlay.open { display:flex; } #pw-overlay.open { display:flex; }
#pw-box { background:#fff; border-radius:8px; padding:1.5rem; width:300px; } #pw-box { background:var(--panel-bg); border-radius:8px; padding:1.5rem; width:300px; }
#pw-box h2 { margin:0 0 .75rem; font-size:1rem; } #pw-box h2 { margin:0 0 .75rem; font-size:1rem; }
#auth-input { width:100%; padding:.45rem .6rem; border:1px solid #ddd; border-radius:4px; #auth-input { width:100%; padding:.45rem .6rem; border:1px solid var(--border); border-radius:4px;
font-size:1rem; margin-bottom:.5rem; font-family:inherit; display:block; } font-size:1rem; margin-bottom:.5rem; font-family:inherit; display:block; background:var(--btn-bg); color:var(--text); }
#auth-error { color:#ef4444; font-size:.85rem; margin-bottom:.5rem; display:none; } #auth-error { color:#ef4444; font-size:.85rem; margin-bottom:.5rem; display:none; }
#auth-submit { width:100%; padding:.45rem; background:#000; color:#fff; border:none; #auth-submit { width:100%; padding:.45rem; background:#000; color:#fff; border:none;
border-radius:4px; font-size:.95rem; cursor:pointer; } border-radius:4px; font-size:.95rem; cursor:pointer; }
@ -187,6 +210,7 @@ HTML = """<!doctype html>
<button id="lock-btn" onclick="togglePwPanel()" title="No password click to set one">Lock</button> <button id="lock-btn" onclick="togglePwPanel()" title="No password click to set one">Lock</button>
<a id="newpad" href="/" target="_blank">New pad</a> <a id="newpad" href="/" target="_blank">New pad</a>
<a id="info" href="/system/info">Info</a> <a id="info" href="/system/info">Info</a>
<button id="theme-btn" onclick="toggleTheme()" title="Toggle dark/light mode">Dark</button>
<div id="pw-panel"> <div id="pw-panel">
<label>Password protection</label> <label>Password protection</label>
<input id="pw-input" type="password" placeholder="New password…" onkeydown="if(event.key==='Enter')setPassword()"/> <input id="pw-input" type="password" placeholder="New password…" onkeydown="if(event.key==='Enter')setPassword()"/>
@ -219,6 +243,28 @@ const $ = s => document.querySelector(s);
const proto = location.protocol === "https:" ? "wss" : "ws"; const proto = location.protocol === "https:" ? "wss" : "ws";
const rand = () => Math.random().toString(36).slice(2, 6); // 4 chars const rand = () => Math.random().toString(36).slice(2, 6); // 4 chars
// Theme
function applyTheme(dark) {
document.documentElement.setAttribute('data-theme', dark ? 'dark' : 'light');
$('#theme-btn').textContent = dark ? 'Light' : 'Dark';
}
function toggleTheme() {
const isDark = document.documentElement.getAttribute('data-theme') === 'dark';
localStorage.setItem('theme', isDark ? 'light' : 'dark');
applyTheme(!isDark);
}
(function() {
const saved = localStorage.getItem('theme');
if (saved) { applyTheme(saved === 'dark'); }
else {
const prefersDark = window.matchMedia('(prefers-color-scheme: dark)').matches;
applyTheme(prefersDark);
}
window.matchMedia('(prefers-color-scheme: dark)').addEventListener('change', e => {
if (!localStorage.getItem('theme')) applyTheme(e.matches);
});
})();
// Derive docId from path; redirect root to random // Derive docId from path; redirect root to random
let docId = decodeURIComponent(location.pathname.replace(/(^\\/|\\/$)/g, "")); let docId = decodeURIComponent(location.pathname.replace(/(^\\/|\\/$)/g, ""));
if (!docId) { location.replace("/" + rand() + "/"); } if (!docId) { location.replace("/" + rand() + "/"); }
@ -434,16 +480,39 @@ def get_system_info():
<meta charset="utf-8"/> <meta charset="utf-8"/>
<title>aukpad - System Info</title> <title>aukpad - System Info</title>
<style> <style>
:root {{
--bg: #fbfbfb;
--text: #111;
--heading: #333;
--section-bg: #f8fafc;
--value-bg: #e5e7eb;
}}
[data-theme="dark"] {{
--bg: #17181E;
--text: #F0F0F0;
--heading: #e0e0e0;
--section-bg: #1e1f28;
--value-bg: #2a2b33;
}}
body {{ font-family: ui-sans-serif, system-ui, -apple-system, "Segoe UI", Roboto, "Helvetica Neue", Arial; body {{ font-family: ui-sans-serif, system-ui, -apple-system, "Segoe UI", Roboto, "Helvetica Neue", Arial;
max-width: 800px; margin: 2rem auto; padding: 1rem; line-height: 1.6; }} max-width: 800px; margin: 2rem auto; padding: 1rem; line-height: 1.6;
h1, h2 {{ color: #333; }} background-color: var(--bg); color: var(--text); }}
.info-section {{ background: #f8fafc; padding: 1rem; border-radius: 8px; margin: 1rem 0; }} h1, h2 {{ color: var(--heading); }}
.info-section {{ background: var(--section-bg); padding: 1rem; border-radius: 8px; margin: 1rem 0; }}
.config-item {{ margin: 0.5rem 0; }} .config-item {{ margin: 0.5rem 0; }}
.value {{ font-family: monospace; background: #e5e7eb; padding: 0.2rem 0.4rem; border-radius: 4px; }} .value {{ font-family: monospace; background: var(--value-bg); padding: 0.2rem 0.4rem; border-radius: 4px; }}
.back-link {{ display: inline-block; margin-top: 1rem; padding: 0.5rem 1rem; .back-link {{ display: inline-block; margin-top: 1rem; padding: 0.5rem 1rem;
background: #000; color: #fff; text-decoration: none; border-radius: 8px; }} background: #000; color: #fff; text-decoration: none; border-radius: 8px; }}
.back-link:hover {{ background: #333; }} .back-link:hover {{ background: #333; }}
</style> </style>
<script>
(function() {{
const saved = localStorage.getItem('theme');
const prefersDark = window.matchMedia('(prefers-color-scheme: dark)').matches;
const dark = saved ? saved === 'dark' : prefersDark;
document.documentElement.setAttribute('data-theme', dark ? 'dark' : 'light');
}})();
</script>
</head> </head>
<body> <body>
<h1>System Information</h1> <h1>System Information</h1>