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:
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
2
frontend/src/lib/types.d.ts
vendored
2
frontend/src/lib/types.d.ts
vendored
@@ -10,6 +10,8 @@ interface EMLy_GUI_Settings {
|
||||
enableAttachedDebuggerProtection?: boolean;
|
||||
useDarkEmailViewer?: boolean;
|
||||
enableUpdateChecker?: boolean;
|
||||
musicInspirationEnabled?: boolean;
|
||||
theme?: "light" | "dark";
|
||||
}
|
||||
|
||||
type SupportedLanguages = "en" | "it";
|
||||
|
||||
45
frontend/src/lib/utils/theme.ts
Normal file
45
frontend/src/lib/utils/theme.ts
Normal 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;
|
||||
}
|
||||
Reference in New Issue
Block a user