From ab38298c32e4fdc8b58790a1580d175faf52493d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=BD=A0=E7=9A=84GitHub=E7=94=A8=E6=88=B7=E5=90=8D?= <你的GitHub注册邮箱> Date: Sun, 30 Aug 2026 14:52:42 +0800 Subject: [PATCH 1/6] =?UTF-8?q?feat:=20liquid-glass=20UI=20redesign=20?= =?UTF-8?q?=E2=80=94=20rating=20filter=20in=20menu,=20unified=20glass=20pa?= =?UTF-8?q?nels,=20custom=20menu=20bg=20color?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Move star-rating filter (0–5) from searchbar into #color-filter dropdown panel. - Apply shared .glass liquid-glass style to color-filter, dialogs, preview panel, and selection bar. - Add Liquid Glass theme in Settings alongside Dark/Light. - Add custom menu background color picker, persisted via Tauri set_setting/get_setting. - Update zh-CN / en-US / messages locales. --- src/app.js | 56 ++++++++++++++---- src/index.html | 36 +++++++----- src/locales/en-US.json | 12 ++-- src/locales/messages.js | 26 +++++---- src/locales/zh-CN.json | 14 +++-- src/styles.css | 122 +++++++++++++++++++++++++++++++++++++--- 6 files changed, 213 insertions(+), 53 deletions(-) diff --git a/src/app.js b/src/app.js index 90f3282..a9ac7fb 100644 --- a/src/app.js +++ b/src/app.js @@ -216,22 +216,44 @@ function clearSharedFilters() { renderFilterDots(); updateFilterButton(); } -// --- Theme (dark / light) — persisted in localStorage, no backend needed --- +// --- Theme (dark / light / liquid-glass) — persisted in localStorage, no backend needed --- const THEME_KEY = "tiol-theme"; +const MENU_BG_KEY = "menu_bg_color"; + function applyTheme(theme) { document.documentElement.setAttribute("data-theme", theme); + if (theme === "liquid-glass") { + document.body.classList.add("theme-liquid-glass"); + } else { + document.body.classList.remove("theme-liquid-glass"); + } } function initTheme() { let saved = "dark"; try { saved = localStorage.getItem(THEME_KEY) || "dark"; } catch (e) {} applyTheme(saved); } + +async function applyMenuBgColor(color) { + if (!color) return; + document.documentElement.style.setProperty("--menu-bg-custom", color); + try { await invoke("set_setting", { key: MENU_BG_KEY, value: color }); } catch (e) {} +} + +async function initMenuBgColor() { + let saved = "#ffffff"; + try { saved = (await invoke("get_setting", { key: MENU_BG_KEY })) || "#ffffff"; } catch (e) {} + if (els.menuBgColor) els.menuBgColor.value = saved; + applyMenuBgColor(saved); +} + function renderThemeButtons() { const cur = document.documentElement.getAttribute("data-theme") || "dark"; els.themeOptions.querySelectorAll("[data-theme]").forEach((btn) => { btn.classList.toggle("btn--active", btn.dataset.theme === cur); }); } + els.themeOptions.addEventListener("click", (ev) => { const btn = ev.target.closest("[data-theme]"); if (!btn) return; @@ -270,6 +292,10 @@ els.toggleFxShadow.addEventListener("click", () => { try { localStorage.setItem(FX_SHADOW_KEY, next); } catch (e) {} applyFx(); }); + +els.menuBgColor?.addEventListener("input", (ev) => { + applyMenuBgColor(ev.target.value); +}); els.navPhotos.addEventListener("click", () => { switchView("photos"); onboardingOnPhotosClicked(); @@ -311,6 +337,7 @@ async function renderSettings() { }); renderThemeButtons(); applyFx(); + await initMenuBgColor(); renderHwDecode(); detectAndReportRenderer(); refreshModelStatus(); @@ -1176,12 +1203,16 @@ const COLOR_HEX = { // The unfiltered result of the current query — filters re-apply to it. let allPhotos = []; -// Star rating filter (C-17): only show photos with rating >= minRating. -// 0 = no filter. Driven by the #rating-filter dropdown in the search bar. -let minRating = 0; +// Star rating filter (C-17): show only photos whose rating is in the active +// set. Each checkbox toggles one rating (0–5). Unchecked ratings are hidden. +// Default: all ratings selected. +const activeRatings = new Set([0, 1, 2, 3, 4, 5]); const ratingFilterEl = document.getElementById("rating-filter"); ratingFilterEl.addEventListener("change", () => { - minRating = parseInt(ratingFilterEl.value, 10) || 0; + activeRatings.clear(); + ratingFilterEl.querySelectorAll('input[type="checkbox"]').forEach((cb) => { + if (cb.checked) activeRatings.add(parseInt(cb.value, 10)); + }); renderPhotos(applyFilters(allPhotos)); }); @@ -1191,7 +1222,7 @@ function hasActiveFilters() { activeLensFilters.size > 0 || focalMin != null || focalMax != null || - minRating > 0 + activeRatings.size < 6 ); } @@ -1217,8 +1248,8 @@ function applyFilters(photos) { if (focalMin != null && p.focal_length < focalMin) return false; if (focalMax != null && p.focal_length > focalMax) return false; } - // Star rating (C-17): photos without a rating (0) fail a >= filter. - if (minRating > 0 && (p.rating || 0) < minRating) return false; + // Star rating (C-17): photo must have a rating currently checked. + if (activeRatings.size < 6 && !activeRatings.has(p.rating || 0)) return false; return true; }); } @@ -1410,8 +1441,11 @@ document.getElementById("btn-color-filter-clear").addEventListener("click", () = focalMax = null; filterFocalMin.value = ""; filterFocalMax.value = ""; - minRating = 0; - ratingFilterEl.value = "0"; + activeRatings.clear(); + for (let i = 0; i <= 5; i++) activeRatings.add(i); + ratingFilterEl.querySelectorAll('input[type="checkbox"]').forEach((cb) => { + cb.checked = true; + }); renderFilterDots(); renderFilterLens(); updateFilterButton(); @@ -1735,7 +1769,7 @@ async function setPhotoRating(p, n) { const stars = p._card.querySelector(".card__stars"); if (stars) renderCardStars(stars, p.rating); } - if (minRating > 0) renderPhotos(applyFilters(allPhotos)); + if (activeRatings.size < 6) renderPhotos(applyFilters(allPhotos)); } catch (e) { alert(String(e)); } diff --git a/src/index.html b/src/index.html index 9c113c0..f8b2f65 100644 --- a/src/index.html +++ b/src/index.html @@ -29,17 +29,18 @@ - +
+ @@ -181,7 +182,7 @@