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

@@ -6,7 +6,7 @@
import { Label } from "$lib/components/ui/label";
import { Separator } from "$lib/components/ui/separator";
import { Switch } from "$lib/components/ui/switch";
import { ChevronLeft, Flame, Download, Upload, RefreshCw, CheckCircle2, AlertCircle } from "@lucide/svelte";
import { ChevronLeft, Flame, Download, Upload, RefreshCw, CheckCircle2, AlertCircle, Sun, Moon } from "@lucide/svelte";
import type { EMLy_GUI_Settings } from "$lib/types";
import { toast } from "svelte-sonner";
import { It, Us } from "svelte-flags";
@@ -40,7 +40,8 @@
previewFileSupportedTypes: ["jpg", "jpeg", "png"],
enableAttachedDebuggerProtection: true,
useDarkEmailViewer: true,
enableUpdateChecker: true,
enableUpdateChecker: false,
theme: "dark",
};
async function setLanguage(
@@ -73,8 +74,10 @@
s.enableAttachedDebuggerProtection ?? defaults.enableAttachedDebuggerProtection ?? true,
useDarkEmailViewer:
s.useDarkEmailViewer ?? defaults.useDarkEmailViewer ?? true,
enableUpdateChecker:
s.enableUpdateChecker ?? defaults.enableUpdateChecker ?? true,
enableUpdateChecker: runningInDevMode
? false
: (s.enableUpdateChecker ?? defaults.enableUpdateChecker ?? true),
theme: s.theme || defaults.theme || "light",
};
}
@@ -86,6 +89,7 @@
!!a.enableAttachedDebuggerProtection === !!b.enableAttachedDebuggerProtection &&
!!a.useDarkEmailViewer === !!b.useDarkEmailViewer &&
!!a.enableUpdateChecker === !!b.enableUpdateChecker &&
(a.theme ?? "light") === (b.theme ?? "light") &&
JSON.stringify(a.previewFileSupportedTypes?.sort()) ===
JSON.stringify(b.previewFileSupportedTypes?.sort())
);
@@ -278,25 +282,25 @@
updateStatus = status;
if (status.updateAvailable) {
toast.success(`Update available: ${status.availableVersion}`);
toast.success(m.settings_toast_update_available({ version: status.availableVersion }));
} else if (!status.errorMessage) {
toast.info("You're on the latest version");
toast.info(m.settings_toast_latest_version());
} else {
toast.error(status.errorMessage);
}
} catch (err) {
console.error("Failed to check for updates:", err);
toast.error("Failed to check for updates");
toast.error(m.settings_toast_check_failed());
}
}
async function downloadUpdate() {
try {
await DownloadUpdate();
toast.success("Update downloaded successfully");
toast.success(m.settings_toast_download_success());
} catch (err) {
console.error("Failed to download update:", err);
toast.error("Failed to download update");
toast.error(m.settings_toast_download_failed());
}
}
@@ -306,7 +310,7 @@
// App will quit, so no toast needed
} catch (err) {
console.error("Failed to install update:", err);
toast.error("Failed to launch installer");
toast.error(m.settings_toast_install_failed());
}
}
@@ -324,7 +328,7 @@
});
</script>
<div class="min-h-[calc(100vh-1rem)] from-background to-muted/30">
<div class="min-h-[calc(100vh-1rem)] bg-gradient-to-b from-background to-muted/30">
<div
class="mx-auto flex max-w-3xl flex-col gap-4 px-4 py-6 sm:px-6 sm:py-10 opacity-80"
>
@@ -393,6 +397,52 @@
</Card.Content>
</Card.Root>
<Card.Root>
<Card.Header class="space-y-1">
<Card.Title>{m.settings_appearance_title()}</Card.Title>
<Card.Description>{m.settings_appearance_description()}</Card.Description>
</Card.Header>
<Card.Content>
<RadioGroup.Root
bind:value={form.theme}
class="flex flex-col gap-3"
>
<div class="flex items-center space-x-2">
<RadioGroup.Item
value="light"
id="theme-light"
class="cursor-pointer hover:cursor-pointer"
/>
<Label
for="theme-light"
class="flex items-center gap-2 cursor-pointer hover:cursor-pointer"
>
<Sun class="size-4" />
{m.settings_theme_light()}
</Label>
</div>
<div class="flex items-center space-x-2">
<RadioGroup.Item
value="dark"
id="theme-dark"
class="cursor-pointer hover:cursor-pointer"
/>
<Label
for="theme-dark"
class="flex items-center gap-2 cursor-pointer hover:cursor-pointer"
>
<Moon class="size-4" />
{m.settings_theme_dark()}
</Label>
</div>
</RadioGroup.Root>
<div class="text-xs text-muted-foreground mt-4">
<strong>Info:</strong>
{m.settings_theme_hint()}
</div>
</Card.Content>
</Card.Root>
<Card.Root>
<Card.Header class="space-y-1">
<Card.Title>{m.settings_export_import_title()}</Card.Title>
@@ -592,14 +642,14 @@
{#if form.enableUpdateChecker}
<Card.Root>
<Card.Header class="space-y-1">
<Card.Title>Updates</Card.Title>
<Card.Description>Check for and install application updates from your network share</Card.Description>
<Card.Title>{m.settings_updates_title()}</Card.Title>
<Card.Description>{m.settings_updates_description()}</Card.Description>
</Card.Header>
<Card.Content class="space-y-4">
<!-- Current Version -->
<div class="flex items-center justify-between gap-4 rounded-lg border bg-card p-4">
<div>
<div class="font-medium">Current Version</div>
<div class="font-medium">{m.settings_updates_current_version()}</div>
<div class="text-sm text-muted-foreground">
{updateStatus.currentVersion} ({config?.GUIReleaseChannel || "stable"})
</div>
@@ -607,17 +657,17 @@
{#if updateStatus.updateAvailable}
<div class="flex items-center gap-2 text-sm font-medium text-green-600 dark:text-green-400">
<AlertCircle class="size-4" />
Update Available
{m.settings_updates_available()}
</div>
{:else if updateStatus.errorMessage && updateStatus.lastCheckTime}
<div class="flex items-center gap-2 text-sm text-destructive">
<AlertCircle class="size-4" />
Check failed
{m.settings_updates_check_failed()}
</div>
{:else if updateStatus.lastCheckTime}
<div class="flex items-center gap-2 text-sm text-muted-foreground">
<CheckCircle2 class="size-4" />
No updates found
{m.settings_updates_no_updates()}
</div>
{/if}
</div>
@@ -627,12 +677,12 @@
<!-- Check for Updates -->
<div class="flex items-center justify-between gap-4 rounded-lg border bg-card p-4">
<div>
<div class="font-medium">Check for Updates</div>
<div class="font-medium">{m.settings_updates_check_label()}</div>
<div class="text-sm text-muted-foreground">
{#if updateStatus.lastCheckTime}
Last checked: {updateStatus.lastCheckTime}
{m.settings_updates_last_checked({ time: updateStatus.lastCheckTime })}
{:else}
Click to check for available updates
{m.settings_updates_click_check()}
{/if}
</div>
</div>
@@ -643,7 +693,7 @@
disabled={updateStatus.checking || updateStatus.downloading}
>
<RefreshCw class="size-4 mr-2 {updateStatus.checking ? 'animate-spin' : ''}" />
{updateStatus.checking ? "Checking..." : "Check Now"}
{updateStatus.checking ? m.settings_updates_checking() : m.settings_updates_check_now()}
</Button>
</div>
@@ -652,12 +702,12 @@
<Separator />
<div class="flex items-center justify-between gap-4 rounded-lg border border-blue-500/30 bg-blue-500/10 p-4">
<div>
<div class="font-medium">Version {updateStatus.availableVersion} Available</div>
<div class="font-medium">{m.settings_updates_version_available({ version: updateStatus.availableVersion })}</div>
<div class="text-sm text-muted-foreground">
{#if updateStatus.downloading}
Downloading... {updateStatus.downloadProgress}%
{m.settings_updates_downloading({ progress: updateStatus.downloadProgress })}
{:else}
Click to download the update
{m.settings_updates_click_download()}
{/if}
</div>
{#if updateStatus.releaseNotes}
@@ -673,7 +723,7 @@
disabled={updateStatus.downloading}
>
<Download class="size-4 mr-2" />
{updateStatus.downloading ? `${updateStatus.downloadProgress}%` : "Download"}
{updateStatus.downloading ? `${updateStatus.downloadProgress}%` : m.settings_updates_download_button()}
</Button>
</div>
{/if}
@@ -683,9 +733,9 @@
<Separator />
<div class="flex items-center justify-between gap-4 rounded-lg border border-green-500/30 bg-green-500/10 p-4">
<div>
<div class="font-medium">Update Ready to Install</div>
<div class="font-medium">{m.settings_updates_ready_title()}</div>
<div class="text-sm text-muted-foreground">
Version {updateStatus.availableVersion} has been downloaded and verified
{m.settings_updates_ready_ref({ version: updateStatus.availableVersion })}
</div>
</div>
<Button
@@ -694,7 +744,7 @@
onclick={installUpdate}
>
<CheckCircle2 class="size-4 mr-2" />
Install Now
{m.settings_updates_install_button()}
</Button>
</div>
{/if}
@@ -713,11 +763,11 @@
<!-- Info about update path -->
<div class="text-xs text-muted-foreground">
<strong>Info:</strong> Updates are checked from your configured network share path.
<strong>Info:</strong> {m.settings_updates_info_message()}
{#if (config as any)?.UpdatePath}
Current path: <code class="text-xs bg-muted px-1 py-0.5 rounded">{(config as any).UpdatePath}</code>
{m.settings_updates_current_path()} <code class="text-xs bg-muted px-1 py-0.5 rounded">{(config as any).UpdatePath}</code>
{:else}
<span class="text-amber-600 dark:text-amber-400">No update path configured</span>
<span class="text-amber-600 dark:text-amber-400">{m.settings_updates_no_path()}</span>
{/if}
</div>
</Card.Content>
@@ -843,18 +893,19 @@
class="flex items-center justify-between gap-4 rounded-lg border bg-card p-4 border-destructive/30"
>
<div class="space-y-1">
<Label class="text-sm">Enable Update Checker</Label>
<Label class="text-sm">{m.settings_danger_update_checker_label()}</Label>
<div class="text-sm text-muted-foreground">
Check for application updates from network share
{m.settings_danger_update_checker_hint()}
</div>
</div>
<Switch
bind:checked={form.enableUpdateChecker}
class="cursor-pointer hover:cursor-pointer"
disabled={runningInDevMode}
/>
</div>
<div class="text-xs text-muted-foreground">
<strong>Info:</strong> When enabled, the app will check for updates from your configured network share. Disable this if you manage updates manually or don't have network access.
{m.settings_danger_update_checker_info()}
</div>
<Separator />
@@ -871,6 +922,7 @@
</Card.Root>
{/if}
{#if !runningInDevMode}
<AlertDialog.Root bind:open={dangerWarningOpen}>
<AlertDialog.Content>
<AlertDialog.Header>
@@ -878,7 +930,11 @@
>{m.settings_danger_alert_title()}</AlertDialog.Title
>
<AlertDialog.Description>
{m.settings_danger_alert_description()}
{m.settings_danger_alert_description_part1()}
<br />
{m.settings_danger_alert_description_part2()}
<br />
{m.settings_danger_alert_description_part3()}
</AlertDialog.Description>
</AlertDialog.Header>
<AlertDialog.Footer>
@@ -888,5 +944,6 @@
</AlertDialog.Footer>
</AlertDialog.Content>
</AlertDialog.Root>
{/if}
</div>
</div>