feat: ADD darkmode #16
This commit is contained in:
parent
3bfe886a96
commit
6f07a180c9
1 changed files with 88 additions and 19 deletions
107
app.py
107
app.py
|
|
@ -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>
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue