feat: Added light mode plus various bug fixes

- Added click handler for Easter egg that enables music inspiration feature.
- Updated credits page to include new icons and handle click events.
- Enhanced inspiration page to fetch and display Spotify track embed HTML.
- Refactored inspiration loading logic to include track data.
- Introduced theme selection in settings with light and dark modes.
- Updated settings page to reflect new theme options and improve toast messages.
- Refined layout styles across various pages for consistent theming.
- Bumped application version to 1.5.0 in installer script.
This commit is contained in:
Flavio Fois
2026-02-09 21:38:17 +01:00
parent 5b62790248
commit 51679b61eb
22 changed files with 575 additions and 282 deletions

View File

@@ -403,8 +403,8 @@
}
.panel {
background: rgba(255, 255, 255, 0.04);
border: 1px solid rgba(255, 255, 255, 0.1);
background: var(--card);
border: 1px solid var(--border);
border-radius: 14px;
overflow: hidden;
}
@@ -423,20 +423,20 @@
height: 34px;
padding: 0 12px;
border-radius: 10px;
border: 1px solid rgba(255, 255, 255, 0.12);
background: rgba(255, 255, 255, 0.06);
color: inherit;
border: 1px solid var(--border);
background: var(--muted);
color: var(--muted-foreground);
cursor: pointer;
user-select: none;
font-size: 11px;
font-weight: 700;
text-transform: uppercase;
letter-spacing: 0.05em;
color: rgba(255, 255, 255, 0.5);
}
.btn:hover {
background: rgba(255, 255, 255, 0.09);
background: var(--accent);
color: var(--accent-foreground);
}
.events {
@@ -454,16 +454,16 @@
}
.email-header-content {
background: rgba(255, 255, 255, 0.05);
background: var(--card);
padding: 16px;
border-bottom: 1px solid rgba(255, 255, 255, 0.08);
border-bottom: 1px solid var(--border);
}
.email-subject {
font-size: 18px;
font-weight: 600;
line-height: 1.25;
color: inherit;
color: var(--foreground);
min-width: 0;
overflow-wrap: break-word;
}
@@ -496,21 +496,21 @@
.email-meta-grid .label {
text-align: right;
color: rgba(255, 255, 255, 0.5);
color: var(--muted-foreground);
margin-right: 8px;
font-weight: 500;
}
.email-meta-grid .value {
color: rgba(255, 255, 255, 0.9);
color: var(--foreground);
word-break: break-all;
font-weight: 500;
}
.email-attachments {
padding: 10px 16px;
border-bottom: 1px solid rgba(255, 255, 255, 0.08);
background: rgba(255, 255, 255, 0.03);
border-bottom: 1px solid var(--border);
background: var(--muted);
display: flex;
align-items: center;
gap: 12px;
@@ -522,7 +522,7 @@
font-weight: 700;
text-transform: uppercase;
letter-spacing: 0.05em;
color: rgba(255, 255, 255, 0.5);
color: var(--muted-foreground);
flex-shrink: 0;
}
@@ -538,9 +538,9 @@
height: 28px;
padding: 0 10px;
border-radius: 6px;
border: 1px solid rgba(255, 255, 255, 0.15);
border: 1px solid var(--border);
background: transparent;
color: rgba(255, 255, 255, 0.8);
color: var(--foreground);
font-size: 12px;
cursor: pointer;
text-decoration: none;
@@ -548,8 +548,8 @@
}
.att-btn:hover {
background: rgba(255, 255, 255, 0.05);
color: #fff;
background: var(--accent);
color: var(--accent-foreground);
}
.att-btn.image {
@@ -628,10 +628,10 @@
justify-content: center;
height: 36px;
padding: 0 16px;
background: rgba(255, 255, 255, 0.1);
border: 1px solid rgba(255, 255, 255, 0.15);
background: var(--muted);
border: 1px solid var(--border);
border-radius: 8px;
color: white;
color: var(--foreground);
font-size: 13px;
font-weight: 500;
cursor: pointer;
@@ -639,8 +639,8 @@
}
.browse-btn:hover {
background: rgba(255, 255, 255, 0.15);
border-color: rgba(255, 255, 255, 0.25);
background: var(--accent);
border-color: var(--accent-foreground);
}
.browse-btn:disabled,
@@ -660,12 +660,12 @@
}
::-webkit-scrollbar-thumb {
background: rgba(255, 255, 255, 0.1);
background: var(--border);
border-radius: 6px;
}
::-webkit-scrollbar-thumb:hover {
background: rgba(255, 255, 255, 0.2);
background: var(--muted-foreground);
}
::-webkit-scrollbar-corner {
@@ -674,7 +674,7 @@
.att-empty {
font-size: 11px;
color: rgba(255, 255, 255, 0.4);
color: var(--muted-foreground);
font-style: italic;
}

View File

@@ -56,8 +56,8 @@
<Sidebar.Root style="opacity: 0.8;">
<Sidebar.Header>
<div
class="sidebar-title items-center justify-center p-3 border-b border-white/10"
style="padding: 12px; border-bottom: 1px solid rgba(255, 255, 255, 0.08); display: flex; justify-content: center;"
class="sidebar-title items-center justify-center p-3 border-b border-border flex"
style="padding: 12px; display: flex; justify-content: center;"
>
<img src="/appicon.png" alt="Logo" width="64" height="64" />
<span

View File

@@ -5,7 +5,7 @@
let { onSave, onReset } = $props();
</script>
<div class="flex items-center gap-4 rounded-lg border bg-background px-4 py-3 shadow-lg w-full max-w-md">
<div class="flex items-center gap-4 rounded-lg border bg-card px-4 py-3 shadow-lg w-full max-w-md">
<span class="text-sm text-muted-foreground flex-1">
{m.settings_unsaved_toast_message()}
</span>
@@ -19,9 +19,3 @@
</Button>
</div>
</div>
<style>
.bg-background {
background-color: oklch(0.205 0 0);
}
</style>

View File

@@ -1,6 +1,8 @@
import { browser } from "$app/environment";
import type { EMLy_GUI_Settings } from "$lib/types";
import { getFromLocalStorage, saveToLocalStorage } from "$lib/utils/localStorageHelper";
import { applyTheme, getStoredTheme } from "$lib/utils/theme";
import { setLocale } from "$lib/paraglide/runtime";
const STORAGE_KEY = "emly_gui_settings";
@@ -11,7 +13,9 @@ const defaults: EMLy_GUI_Settings = {
previewFileSupportedTypes: ["jpg", "jpeg", "png"],
enableAttachedDebuggerProtection: true,
useDarkEmailViewer: true,
enableUpdateChecker: true,
enableUpdateChecker: false,
musicInspirationEnabled: false,
theme: "dark",
};
class SettingsStore {
@@ -33,6 +37,37 @@ class SettingsStore {
console.error("Failed to load settings", e);
}
}
// Migration: Check for legacy musicInspirationEnabled key
const legacyMusic = getFromLocalStorage("musicInspirationEnabled");
if (legacyMusic !== null) {
this.settings.musicInspirationEnabled = legacyMusic === "true";
localStorage.removeItem("musicInspirationEnabled");
this.save(); // Save immediately to persist the migration
}
// Sync theme from localStorage key used in app.html
const storedTheme = getStoredTheme();
if (!this.settings.theme) {
this.settings.theme = storedTheme;
} else if (this.settings.theme !== storedTheme) {
// If there's a mismatch, prioritize the theme from emly_theme key
this.settings.theme = storedTheme;
}
// Apply the theme
applyTheme(this.settings.theme);
// Apply the language
if (this.settings.selectedLanguage) {
setLocale(this.settings.selectedLanguage);
}
// Save defaults/merged settings to storage if they didn't exist or were updated during load
if (!stored) {
this.save();
}
this.hasHydrated = true;
}
@@ -43,11 +78,20 @@ class SettingsStore {
update(newSettings: Partial<EMLy_GUI_Settings>) {
this.settings = { ...this.settings, ...newSettings };
// Apply theme if it changed
if (newSettings.theme && this.settings.theme) {
applyTheme(this.settings.theme);
}
this.save();
}
reset() {
this.settings = { ...defaults };
if (this.settings.theme) {
applyTheme(this.settings.theme);
}
this.save();
}
}

View File

@@ -10,6 +10,8 @@ interface EMLy_GUI_Settings {
enableAttachedDebuggerProtection?: boolean;
useDarkEmailViewer?: boolean;
enableUpdateChecker?: boolean;
musicInspirationEnabled?: boolean;
theme?: "light" | "dark";
}
type SupportedLanguages = "en" | "it";

View File

@@ -0,0 +1,45 @@
import { browser } from "$app/environment";
const THEME_KEY = "emly_theme";
export type Theme = "light" | "dark";
/**
* Applies the theme to the document element and saves it to localStorage
*/
export function applyTheme(theme: Theme) {
if (!browser) return;
const isDark = theme === "dark";
document.documentElement.classList.toggle("dark", isDark);
try {
localStorage.setItem(THEME_KEY, theme);
} catch (e) {
console.error("Failed to save theme to localStorage:", e);
}
}
/**
* Gets the current theme from localStorage or returns the default
*/
export function getStoredTheme(): Theme {
if (!browser) return "light";
try {
const stored = localStorage.getItem(THEME_KEY);
return stored === "light" || stored === "dark" ? stored : "light";
} catch {
return "light";
}
}
/**
* Toggles between light and dark theme
*/
export function toggleTheme(): Theme {
const current = getStoredTheme();
const newTheme: Theme = current === "dark" ? "light" : "dark";
applyTheme(newTheme);
return newTheme;
}