diff --git a/static/app.js b/static/app.js index 1bb5c16..22e8737 100644 --- a/static/app.js +++ b/static/app.js @@ -10,6 +10,21 @@ marked.setOptions({ mangle: false, }); +// ---- Theme ---- +function initTheme() { + const saved = localStorage.getItem('rag-theme') || 'dark'; + document.documentElement.setAttribute('data-theme', saved); +} + +function toggleTheme() { + const current = document.documentElement.getAttribute('data-theme'); + const next = current === 'dark' ? 'light' : 'dark'; + document.documentElement.setAttribute('data-theme', next); + localStorage.setItem('rag-theme', next); +} + +initTheme(); + // DOM const sessionList = document.getElementById('session-list'); const messages = document.getElementById('messages'); @@ -21,6 +36,7 @@ const btnNewSession = document.getElementById('btn-new-session'); const btnClear = document.getElementById('btn-clear'); const btnDelete = document.getElementById('btn-delete'); const btnMenu = document.getElementById('btn-menu'); +const btnTheme = document.getElementById('btn-theme'); const chatTitle = document.getElementById('chat-title'); const statsEl = document.getElementById('stats'); @@ -232,6 +248,7 @@ btnNewSession.onclick = createSession; btnClear.onclick = clearMessages; btnDelete.onclick = deleteSession; btnSend.onclick = sendMessage; +btnTheme.onclick = toggleTheme; btnMenu.onclick = () => { document.getElementById('sidebar').classList.toggle('open'); diff --git a/static/style.css b/static/style.css index 6129103..52147cf 100644 --- a/static/style.css +++ b/static/style.css @@ -1,3 +1,4 @@ +/* ===== Dark theme (default) ===== */ :root { --bg: #0f0f0f; --bg-sidebar: #161616; @@ -18,6 +19,34 @@ --radius: 12px; --radius-sm: 8px; --sidebar-w: 280px; + --footer-bg: #111; + --footer-border: #222; + --code-bg: #0a0a0a; + --table-header: #ffffff08; +} + +/* ===== Light theme ===== */ +[data-theme="light"] { + --bg: #ffffff; + --bg-sidebar: #f7f7f8; + --bg-chat: #ffffff; + --bg-input: #f0f0f0; + --bg-user: #2563eb; + --bg-assistant: #f4f4f5; + --bg-hover: #ececec; + --bg-active: #2563eb15; + --border: #e0e0e0; + --text: #1a1a1a; + --text-dim: #666; + --text-muted: #999; + --accent: #2563eb; + --accent-hover: #1d4ed8; + --danger: #dc2626; + --danger-hover: #b91c1c; + --footer-bg: #f7f7f8; + --footer-border: #e0e0e0; + --code-bg: #f0f0f0; + --table-header: #00000008; } * { margin: 0; padding: 0; box-sizing: border-box; } @@ -35,7 +64,7 @@ body { height: 100vh; } -/* Sidebar */ +/* ===== Sidebar ===== */ .sidebar { width: var(--sidebar-w); background: var(--bg-sidebar); @@ -59,6 +88,11 @@ body { font-weight: 600; } +.sidebar-actions { + display: flex; + gap: 2px; +} + .session-list { flex: 1; overflow-y: auto; @@ -111,7 +145,7 @@ body { line-height: 1.6; } -/* Main */ +/* ===== Main ===== */ .main { flex: 1; display: flex; @@ -139,7 +173,7 @@ body { gap: 4px; } -/* Messages */ +/* ===== Messages ===== */ .messages { flex: 1; overflow-y: auto; @@ -213,7 +247,7 @@ body { .bubble li { margin: 2px 0; } .bubble code { - background: #ffffff10; + background: var(--code-bg); padding: 1px 5px; border-radius: 4px; font-size: 13px; @@ -221,7 +255,7 @@ body { } .bubble pre { - background: #0a0a0a; + background: var(--code-bg); border: 1px solid var(--border); border-radius: var(--radius-sm); padding: 10px 12px; @@ -243,9 +277,10 @@ body { } .bubble a { - color: #60a5fa; + color: #2563eb; text-decoration: none; } +[data-theme="dark"] .bubble a { color: #60a5fa; } .bubble a:hover { text-decoration: underline; } .bubble table { @@ -259,7 +294,7 @@ body { padding: 6px 10px; text-align: left; } -.bubble th { background: #ffffff08; font-weight: 600; } +.bubble th { background: var(--table-header); font-weight: 600; } .bubble strong { font-weight: 600; } .bubble em { font-style: italic; } @@ -328,9 +363,9 @@ body { 30% { transform: translateY(-4px); } } -/* Input */ +/* ===== Input ===== */ .input-area { - padding: 12px 16px 16px; + padding: 12px 16px 12px; border-top: 1px solid var(--border); background: var(--bg-sidebar); } @@ -390,7 +425,27 @@ body { margin-top: 6px; } -/* Buttons */ +/* ===== Footer ===== */ +.app-footer { + padding: 10px 16px; + text-align: center; + font-size: 12px; + color: var(--text-muted); + background: var(--footer-bg); + border-top: 1px solid var(--footer-border); + flex-shrink: 0; +} + +.app-footer a { + color: var(--accent); + text-decoration: none; +} + +.app-footer a:hover { + text-decoration: underline; +} + +/* ===== Buttons ===== */ .btn-icon { width: 32px; height: 32px; @@ -408,13 +463,19 @@ body { .btn-icon:hover { background: var(--bg-hover); color: var(--text); } .btn-icon.btn-danger:hover { background: var(--danger); color: white; } -/* Scrollbar */ +/* Theme toggle icons */ +[data-theme="dark"] .icon-sun { display: none; } +[data-theme="dark"] .icon-moon { display: block; } +[data-theme="light"] .icon-sun { display: block; } +[data-theme="light"] .icon-moon { display: none; } + +/* ===== Scrollbar ===== */ ::-webkit-scrollbar { width: 6px; } ::-webkit-scrollbar-track { background: transparent; } ::-webkit-scrollbar-thumb { background: var(--border); border-radius: 3px; } ::-webkit-scrollbar-thumb:hover { background: var(--text-muted); } -/* Responsive */ +/* ===== Responsive ===== */ @media (max-width: 768px) { .sidebar { position: fixed; diff --git a/templates/index.html b/templates/index.html index 1b2ae72..0374ec2 100644 --- a/templates/index.html +++ b/templates/index.html @@ -1,5 +1,5 @@ - +
@@ -13,9 +13,15 @@