From 7a395a24b48a7bebe09b7fa3b47a976b4e3eeb84 Mon Sep 17 00:00:00 2001 From: kodi Date: Mon, 9 Mar 2026 15:40:46 +0100 Subject: [PATCH] feat (ui): light/dark mode --- app/static/app.js | 33 +++++++ app/static/styles.css | 188 +++++++++++++++++++++++++++---------- app/templates/debug.html | 10 ++ app/templates/index.html | 15 ++- data/session_state.sqlite3 | Bin 221184 -> 221184 bytes 5 files changed, 194 insertions(+), 52 deletions(-) diff --git a/app/static/app.js b/app/static/app.js index 9265b34..0552c4d 100644 --- a/app/static/app.js +++ b/app/static/app.js @@ -1,5 +1,6 @@ (function () { const STORAGE_KEY = "rename_mvp_session_id"; + const THEME_STORAGE_KEY = "rename_mvp_theme"; const state = { sessionId: initSessionId(), @@ -29,6 +30,7 @@ const el = { sessionMeta: document.getElementById("sessionMeta"), outputBox: document.getElementById("outputBox"), + themeToggleBtn: document.getElementById("themeToggleBtn"), searchInput: document.getElementById("searchInput"), searchDropdownBtn: document.getElementById("searchDropdownBtn"), searchCombobox: document.getElementById("searchCombobox"), @@ -92,6 +94,33 @@ return created; } + function getStoredTheme() { + const stored = localStorage.getItem(THEME_STORAGE_KEY); + if (stored === "light" || stored === "dark") return stored; + return "dark"; + } + + function applyTheme(theme) { + const normalized = theme === "light" ? "light" : "dark"; + document.documentElement.setAttribute("data-theme", normalized); + localStorage.setItem(THEME_STORAGE_KEY, normalized); + if (el.themeToggleBtn) { + const isDark = normalized === "dark"; + // Icon represents switch action: dark -> light (sun), light -> dark (moon). + el.themeToggleBtn.textContent = isDark ? "☀️" : "🌙"; + el.themeToggleBtn.setAttribute( + "aria-label", + isDark ? "Switch to light theme" : "Switch to dark theme" + ); + el.themeToggleBtn.title = isDark ? "Switch to light theme" : "Switch to dark theme"; + } + } + + function toggleTheme() { + const current = document.documentElement.getAttribute("data-theme") === "light" ? "light" : "dark"; + applyTheme(current === "dark" ? "light" : "dark"); + } + function q(path) { return path.includes("?") ? `${path}&session_id=${encodeURIComponent(state.sessionId)}` @@ -834,6 +863,9 @@ } function bindEvents() { + if (el.themeToggleBtn) { + el.themeToggleBtn.addEventListener("click", toggleTheme); + } el.searchBtn.addEventListener("click", () => withHandler(doSearch, el.searchBtn)); el.searchInput.addEventListener("focus", openRememberedDropdown); el.searchInput.addEventListener("click", openRememberedDropdown); @@ -903,6 +935,7 @@ } async function init() { + applyTheme(getStoredTheme()); el.sessionMeta.textContent = `session_id: ${state.sessionId}`; el.modalRecursiveInput.checked = true; renderSelectedSeriesDetails(); diff --git a/app/static/styles.css b/app/static/styles.css index 4afc7c9..011f04d 100644 --- a/app/static/styles.css +++ b/app/static/styles.css @@ -2,12 +2,74 @@ box-sizing: border-box; } +:root { + --bg-page: #111827; + --text-primary: #e5e7eb; + --text-muted: #94a3b8; + --topbar-bg: #020617; + --topbar-text: #e2e8f0; + --surface: #1f2937; + --surface-elevated: #0f172a; + --surface-subtle: #1e293b; + --border: #334155; + --border-soft: #475569; + --divider: #334155; + --button-primary-bg: #2563eb; + --button-primary-border: #2563eb; + --button-primary-text: #ffffff; + --button-secondary-bg: #334155; + --button-secondary-border: #475569; + --button-secondary-text: #e2e8f0; + --button-secondary-hover-bg: #3b4a61; + --badge-bg: #0b3a6e; + --badge-border: #1d4f85; + --badge-text: #dbeafe; + --list-selected-bg: #1e3a8a; + --season-header-bg: #1e293b; + --season-header-border: #334155; + --danger-text: #f87171; + --overlay-bg: rgba(2, 6, 23, 0.7); + --shadow-lg: 0 8px 22px rgba(2, 6, 23, 0.45); + --series-image-bg: #0b1220; +} + +[data-theme="light"] { + --bg-page: #f2f4f8; + --text-primary: #1a1f2b; + --text-muted: #64748b; + --topbar-bg: #0f172a; + --topbar-text: #e2e8f0; + --surface: #ffffff; + --surface-elevated: #ffffff; + --surface-subtle: #f8fafc; + --border: #d7dee9; + --border-soft: #c3cedf; + --divider: #e4eaf2; + --button-primary-bg: #0f172a; + --button-primary-border: #0f172a; + --button-primary-text: #ffffff; + --button-secondary-bg: #e2e8f0; + --button-secondary-border: #c3cedf; + --button-secondary-text: #1a1f2b; + --button-secondary-hover-bg: #eef2f7; + --badge-bg: #dbeafe; + --badge-border: #bfdbfe; + --badge-text: #0f172a; + --list-selected-bg: #e0f2fe; + --season-header-bg: #eef2ff; + --season-header-border: #dbe4fb; + --danger-text: #b91c1c; + --overlay-bg: rgba(2, 6, 23, 0.55); + --shadow-lg: 0 8px 22px rgba(15, 23, 42, 0.12); + --series-image-bg: #f8fafc; +} + body { margin: 0; padding: 0; font-family: "Segoe UI", Tahoma, sans-serif; - background: #f2f4f8; - color: #1a1f2b; + background: var(--bg-page); + color: var(--text-primary); height: 100vh; overflow: hidden; display: flex; @@ -19,8 +81,8 @@ body { justify-content: space-between; align-items: center; padding: 12px 16px; - background: #0f172a; - color: #e2e8f0; + background: var(--topbar-bg); + color: var(--topbar-text); } .topbar h1 { @@ -32,6 +94,26 @@ body { font-size: 12px; } +.topbar-right { + display: flex; + align-items: center; + gap: 10px; +} + +.theme-toggle-btn { + border: 1px solid var(--button-secondary-border); + background: var(--button-secondary-bg); + color: var(--button-secondary-text); + border-radius: 999px; + width: 34px; + height: 34px; + padding: 0; + display: inline-flex; + align-items: center; + justify-content: center; + line-height: 1; +} + .grid { display: grid; grid-template-columns: repeat(4, minmax(280px, 1fr)); @@ -44,8 +126,8 @@ body { } .panel { - background: #ffffff; - border: 1px solid #d7dee9; + background: var(--surface); + border: 1px solid var(--border); border-radius: 8px; padding: 10px; min-height: 420px; @@ -130,10 +212,10 @@ body { right: 0; top: calc(100% - 6px); z-index: 20; - background: #ffffff; - border: 1px solid #d7dee9; + background: var(--surface-elevated); + border: 1px solid var(--border); border-radius: 6px; - box-shadow: 0 8px 22px rgba(15, 23, 42, 0.12); + box-shadow: var(--shadow-lg); padding: 6px; } @@ -157,7 +239,7 @@ body { #rememberedDropdownList .remembered-remove-btn { border: none; background: transparent; - color: #64748b; + color: var(--text-muted); width: 22px; height: 22px; line-height: 1; @@ -168,8 +250,8 @@ body { } #rememberedDropdownList .remembered-remove-btn:hover { - background: #eef2f7; - color: #0f172a; + background: var(--button-secondary-hover-bg); + color: var(--text-primary); } .stack { @@ -186,25 +268,27 @@ body { input[type="text"], select { - border: 1px solid #c3cedf; + border: 1px solid var(--border-soft); border-radius: 6px; padding: 6px 8px; min-width: 160px; + background: var(--surface-subtle); + color: var(--text-primary); } button { - border: 1px solid #0f172a; - background: #0f172a; - color: #ffffff; + border: 1px solid var(--button-primary-border); + background: var(--button-primary-bg); + color: var(--button-primary-text); border-radius: 6px; padding: 6px 10px; cursor: pointer; } button.secondary { - background: #e2e8f0; - color: #1a1f2b; - border-color: #c3cedf; + background: var(--button-secondary-bg); + color: var(--button-secondary-text); + border-color: var(--button-secondary-border); } .list { @@ -213,8 +297,9 @@ button.secondary { padding: 0; max-height: 260px; overflow: auto; - border: 1px solid #e4eaf2; + border: 1px solid var(--divider); border-radius: 6px; + background: var(--surface-elevated); } .linked-list-wrap { @@ -256,22 +341,22 @@ button.secondary { display: inline-block; font-size: 11px; font-weight: 700; - color: #0f172a; - background: #dbeafe; - border: 1px solid #bfdbfe; + color: var(--badge-text); + background: var(--badge-bg); + border: 1px solid var(--badge-border); border-radius: 999px; padding: 1px 6px; margin-right: 6px; } .list li.selected { - background: #e0f2fe; + background: var(--list-selected-bg); } .list li.season-header { - background: #eef2ff; - border-bottom: 1px solid #dbe4fb; - color: #1e293b; + background: var(--season-header-bg); + border-bottom: 1px solid var(--season-header-border); + color: var(--text-primary); font-weight: 700; justify-content: flex-start; padding: 8px; @@ -280,8 +365,8 @@ button.secondary { .panel-footer { position: sticky; bottom: 0; - background: #ffffff; - border-top: 1px solid #e4eaf2; + background: var(--surface); + border-top: 1px solid var(--divider); padding-top: 8px; margin-top: 10px; display: flex; @@ -297,14 +382,14 @@ button.secondary { } .mismatch { - color: #b91c1c; + color: var(--danger-text); font-weight: 700; } .modal { position: fixed; inset: 0; - background: rgba(2, 6, 23, 0.55); + background: var(--overlay-bg); display: flex; align-items: center; justify-content: center; @@ -318,8 +403,8 @@ button.secondary { .modal-card { width: min(1400px, 90vw); height: 80vh; - background: #ffffff; - border: 1px solid #d7dee9; + background: var(--surface); + border: 1px solid var(--border); border-radius: 10px; padding: 12px; display: flex; @@ -377,7 +462,7 @@ button.secondary { margin-top: 10px; margin-bottom: 0; justify-content: flex-end; - border-top: 1px solid #e4eaf2; + border-top: 1px solid var(--divider); padding-top: 10px; } @@ -388,7 +473,7 @@ button.secondary { .settings-section h4 { margin: 0 0 8px; font-size: 14px; - color: #1e293b; + color: var(--text-primary); } .settings-field { @@ -400,7 +485,7 @@ button.secondary { .settings-field label { font-size: 12px; - color: #64748b; + color: var(--text-muted); } .settings-field select { @@ -422,7 +507,7 @@ button.secondary { align-items: center; gap: 8px; font-size: 13px; - color: #334155; + color: var(--text-primary); } .settings-actions { @@ -434,16 +519,16 @@ button.secondary { #panelSelectedEpisodes .panel-footer button:last-child, #panelSelectedFiles .panel-footer button:first-child, #panelSelectedFiles .panel-footer button:last-child { - border-color: #0b3a6e; - background: #0b3a6e; - color: #ffffff; + border-color: var(--button-primary-border); + background: var(--button-primary-bg); + color: var(--button-primary-text); } .list li { display: flex; justify-content: space-between; gap: 8px; - border-bottom: 1px solid #edf1f7; + border-bottom: 1px solid var(--divider); padding: 6px 8px; font-size: 13px; } @@ -474,7 +559,7 @@ button.secondary { } .muted { - color: #475569; + color: var(--text-muted); font-size: 12px; margin-bottom: 8px; } @@ -503,7 +588,7 @@ button.secondary { } .series-details { - border-top: 1px solid #e4eaf2; + border-top: 1px solid var(--divider); padding-top: 10px; } @@ -522,32 +607,32 @@ button.secondary { display: block; margin: 0; border-radius: 6px; - border: 1px solid #d7dee9; - background: #f8fafc; + border: 1px solid var(--border); + background: var(--series-image-bg); } .series-meta { font-size: 12px; - color: #334155; + color: var(--text-primary); display: grid; gap: 4px; margin-bottom: 8px; } .series-meta span:first-child { - color: #64748b; + color: var(--text-muted); } .series-overview { margin: 0 0 8px; font-size: 12px; line-height: 1.35; - color: #1e293b; + color: var(--text-primary); } .series-link { font-size: 12px; - color: #64748b; + color: var(--text-muted); text-decoration: none; } @@ -557,13 +642,14 @@ button.secondary { #outputBox { margin: 0; - background: #0b1220; - color: #dbeafe; + background: var(--surface-subtle); + color: var(--text-primary); border-radius: 6px; padding: 10px; max-height: 320px; overflow: auto; font-size: 12px; + border: 1px solid var(--border); } .debug-page { diff --git a/app/templates/debug.html b/app/templates/debug.html index ac885e6..f67ad57 100644 --- a/app/templates/debug.html +++ b/app/templates/debug.html @@ -4,6 +4,16 @@ Rename MVP Debug + diff --git a/app/templates/index.html b/app/templates/index.html index fc9c48d..2af9544 100644 --- a/app/templates/index.html +++ b/app/templates/index.html @@ -4,12 +4,25 @@ Rename MVP +

Rename MVP

-
+
+
+ +
diff --git a/data/session_state.sqlite3 b/data/session_state.sqlite3 index 63e3fae2f139fa57020117d35bd0d36646c4e380..eef230f3325966d01fc138af73b949584599a3bf 100644 GIT binary patch delta 2357 zcmb7FUrZZy9KTqV9qE8TaU8ACF0<`j@A~&ar2Gk!jBFDzb2L!sb#$~q`e#d3 z(p}MH*%l{W%x#HIFmpZ`W=hidf+S{We3tncUM-lS5~y27(iyOsS{+r8|k+Li@0d71wG;eyQz7h7NB;J;G_Ilk`E2@(~-nf zh))En5d`7zVG*Ol=zI8ZuI9);n6I(QO@LIy%DCMu?Q(f(*5&4`jK|9cpbIbU8j`cb zmW{gHqUUW4<#18N-Uy@L;vxh!;o?udT5}Op1L`h=B>vgkkbBhoYCY<}uZ!5oDEbY4 zJr^Bm#ytjDL{X9&vH*Q}8Q*;F$MFsf!Yzx~^eDQHFXy7uXK_~}>MoP9P~J5!>e7ul zGQHV@T9hdfTj^6NpD4dqrl8PQlnTE^6jt?_y;>c#!)2veEA1U%I3MfvaSofuLA#ya z6Ey9kX@vr9H&-L>Q$*ocQr|udm(S_6aUR;s(AyaPwr2nU$ZZurURBvEaB%uKmyPu} z++ODn2R!%s>MDU+)OitG>r<)!QvasTLrueKd3YUr!7OEmXD;h$%PBYh)1y74QpEn9 zCCC=Ck<^nKGIwTeNu8sPECZ9O3J}2310OiBbQd1xu7a_xv+%FYpR+vOICD%}bq%TR zCqBbBa1++A3hB(GL(6Cwb%!!lbq#VJuW||F0TtpVX3Ztr`Mxm{GWsZnVZHJoUZac& zJ|5u{Qp-JsQG9TtHN!B_n2aQ&Jd88?`Cuv;38$izpHIev>0s0d-N8tlmtl;%)8S-u zG&ssfW%FA!)6FoH6(;RZhsHfgGeUF-vc+fZJ8WBv4zHLlcaX01#~>hMd&|IrX{sxj zeqo;UKAt-O`$8LiLX=xI?dGt~z42PqG;U>Xs zsA+o_x&1ucTL?FLVJEw$5>#+91fP@UWP+LsQ(Y&|QQh(?u(h2nJ{SiCoqNKU~r)ahV+g6fPU)6x$UfH_{;N4*p0$0mwO zN z!q@hb2Nq3U>zCbD`EaGady0n6!`IX}BMbFL{$(C!gv}x|r@$U0$~V8=N4en-76o&w6|uV{@`B z&A7HdP=xHLmDc`N=%lTOAfRELK8CfiF3v@J^5I{>)%(S!CmDt9j%F`VURYLS!QA|* KJW8Qk?f(O-1%`J3 delta 495 zcmY+6J!lhA0D$l1k$dm{-m8S8&=w-ZkU%lHT$|sa8if)N0~RbgNJE-SBc>RLgT=x8 zR1h3$eAKvDN+&0yJmIzy5L>5O2Z!1lTmw=Z#G#PZ>!$1XeSD3+*63@`$3dktaTQeF z7xGaMWWA^kvg}hd;P2`HMnT=GT8j^?(t=gHTfbkj>bGP;5KO$zkWmJY__>h~W(z68 zCLNXcQa2Ga@h(G`%iyi+ZewO2YV0Vd@*lYiqtL(~87dUv8??)-1wZa=rr5Iz;a@9L z$d$htsukgPOnJ37=gI`zeyFjD6^)aXKeI3>&M?%!=6-*A`OMCa1cR!Vq5U($74CIT zeK1SH4E<`mWJBZO6X(X5B9;U>FRfz-2T)!t(GW4Um>N^EZZs=O&80a}NaA*o`O~`H zng=auL_`CD47HXzD)*#$G;qsr$Q~?#@$i_8wgggC%gSek%2%W(xI+bP5p9Vt-D(xd z&3jGV9ZKzs-R0L{)iz#%gOG2Hd>{wpga&AiF4Gs(rjDD}Nq21zBz-X>W2PfAU~Y@ncV9L-}W9H?}=((Am6h J!}D->=`VbSnqdF{