Features and Localization Updates for 1.5.2

Enhances user experience with increased contrast option for titlebar buttons.

Adds localization option for the PDF preview page, improving accessibility for international users.

Includes localization option and upgrade message for the InnoSetup installer, ensuring a smoother and more informative installation process.
This commit is contained in:
Flavio Fois
2026-02-10 09:21:33 +01:00
parent 4b6f2d727c
commit 4c99c14be7
12 changed files with 184 additions and 46 deletions

View File

@@ -17,6 +17,7 @@ const defaults: EMLy_GUI_Settings = {
musicInspirationEnabled: false,
reduceMotion: false,
theme: "dark",
increaseWindowButtonsContrast: false,
};
class SettingsStore {

View File

@@ -13,6 +13,7 @@ interface EMLy_GUI_Settings {
musicInspirationEnabled?: boolean;
reduceMotion?: boolean;
theme?: "light" | "dark";
increaseWindowButtonsContrast?: boolean;
}
type SupportedLanguages = "en" | "it";

View File

@@ -304,7 +304,7 @@
</div>
</div>
<div class="controls">
<div class="controls" class:high-contrast={settingsStore.settings.increaseWindowButtonsContrast}>
<button class="btn" onclick={minimize}>─</button>
<button class="btn" onclick={toggleMaximize}>
@@ -694,6 +694,10 @@
opacity: 0.5;
}
.controls.high-contrast {
opacity: 1;
}
.btn {
width: 46px;
height: 100%;

View File

@@ -43,6 +43,7 @@
enableUpdateChecker: false,
reduceMotion: false,
theme: "dark",
increaseWindowButtonsContrast: false,
};
async function setLanguage(
@@ -80,6 +81,7 @@
: (s.enableUpdateChecker ?? defaults.enableUpdateChecker ?? true),
reduceMotion: s.reduceMotion ?? defaults.reduceMotion ?? false,
theme: s.theme || defaults.theme || "light",
increaseWindowButtonsContrast: s.increaseWindowButtonsContrast ?? defaults.increaseWindowButtonsContrast ?? false,
};
}
@@ -93,6 +95,7 @@
!!a.enableUpdateChecker === !!b.enableUpdateChecker &&
!!a.reduceMotion === !!b.reduceMotion &&
(a.theme ?? "light") === (b.theme ?? "light") &&
!!a.increaseWindowButtonsContrast === !!b.increaseWindowButtonsContrast &&
JSON.stringify(a.previewFileSupportedTypes?.sort()) ===
JSON.stringify(b.previewFileSupportedTypes?.sort())
);
@@ -467,6 +470,45 @@
<p class="text-xs text-muted-foreground mt-2">
{m.settings_reduce_motion_info()}
</p>
<Separator />
<div
class="flex items-center justify-between gap-4 rounded-lg border bg-card p-4"
>
<div>
<div class="font-medium">
{m.settings_window_buttons_contrast_label()}
</div>
<div class="text-sm text-muted-foreground">
{m.settings_window_buttons_contrast_hint()}
</div>
</div>
<Switch
bind:checked={form.increaseWindowButtonsContrast}
class="cursor-pointer hover:cursor-pointer"
/>
</div>
<div
class="flex items-center justify-between gap-4 rounded-lg border bg-card p-4"
>
<div>
<div class="font-medium">
{m.settings_email_dark_viewer_label()}
</div>
<div class="text-sm text-muted-foreground">
{m.settings_email_dark_viewer_hint()}
</div>
</div>
<Switch
bind:checked={form.useDarkEmailViewer}
class="cursor-pointer hover:cursor-pointer"
/>
</div>
<p class="text-xs text-muted-foreground mt-2">
{m.settings_email_dark_viewer_info()}
</p>
</div>
</Card.Content>
</Card.Root>
@@ -495,7 +537,7 @@
{m.settings_export_button()}
</Button>
</div>
<Separator />
<div
class="flex items-center justify-between gap-4 rounded-lg border bg-card p-4"
>
@@ -640,29 +682,6 @@
{m.settings_preview_pdf_builtin_info()}
</p>
</div>
<Separator />
<div class="space-y-3">
<div
class="flex items-center justify-between gap-4 rounded-lg border bg-card p-4"
>
<div>
<div class="font-medium">
{m.settings_email_dark_viewer_label()}
</div>
<div class="text-sm text-muted-foreground">
{m.settings_email_dark_viewer_hint()}
</div>
</div>
<Switch
bind:checked={form.useDarkEmailViewer}
class="cursor-pointer hover:cursor-pointer"
/>
</div>
<p class="text-xs text-muted-foreground mt-2">
{m.settings_email_dark_viewer_info()}
</p>
</div>
</Card.Content>
</Card.Root>

View File

@@ -7,6 +7,7 @@
Quit,
} from "$lib/wailsjs/runtime/runtime";
import type { LayoutProps } from "./$types";
import { settingsStore } from "$lib/stores/settings.svelte.js";
let { data, children }: LayoutProps = $props();
@@ -57,7 +58,7 @@
>
<div class="title">EMLy PDF Viewer</div>
<div class="controls">
<div class="controls" class:high-contrast={settingsStore.settings.increaseWindowButtonsContrast}>
<button class="btn" onclick={minimize}>─</button>
<button class="btn" onclick={toggleMaximize}>
{#if isMaximized}
@@ -120,6 +121,10 @@
opacity: 0.5;
}
.controls.high-contrast {
opacity: 1;
}
.btn {
width: 46px;
height: 100%;

View File

@@ -10,6 +10,7 @@
} from "@lucide/svelte";
import { sidebarOpen } from "$lib/stores/app";
import { toast } from "svelte-sonner";
import * as m from "$lib/paraglide/messages.js";
import * as pdfjsLib from "pdfjs-dist";
import pdfWorker from "pdfjs-dist/build/pdf.worker.min.mjs?url";
@@ -63,9 +64,8 @@
await loadPDF();
} else {
toast.error("No PDF data provided");
error =
"No PDF data provided. Please open this window from the main EMLy application.";
toast.error(m.pdf_error_no_data());
error = m.pdf_error_no_data_desc();
loading = false;
}
} catch (e) {
@@ -81,8 +81,7 @@
const timeout = setTimeout(() => {
if (loading) {
loading = false;
error =
"Timeout loading PDF. The worker might have failed to initialize.";
error = m.pdf_error_timeout();
toast.error(error);
}
}, 10000);
@@ -96,7 +95,7 @@
loading = false;
} catch (e) {
console.error(e);
error = "Error parsing PDF: " + e;
error = m.pdf_error_parsing() + e;
loading = false;
} finally {
clearTimeout(timeout);
@@ -135,7 +134,7 @@
} catch (e: any) {
if (e.name !== "RenderingCancelledException") {
console.error(e);
toast.error("Error rendering page: " + e.message);
toast.error(m.pdf_error_rendering() + e.message);
}
}
}
@@ -189,7 +188,7 @@
{#if loading}
<div class="loading-overlay">
<div class="spinner"></div>
<div>Loading PDF...</div>
<div>{m.pdf_loading()}</div>
</div>
{/if}
@@ -200,24 +199,24 @@
{/if}
<div class="toolbar">
<h1 class="title" title={filename}>{filename || "Image Viewer"}</h1>
<h1 class="title" title={filename}>{filename || m.pdf_viewer_title()}</h1>
<div class="controls">
<button class="btn" onclick={() => zoom(0.1)} title="Zoom In">
<button class="btn" onclick={() => zoom(0.1)} title={m.pdf_zoom_in()}>
<ZoomIn size="16" />
</button>
<button class="btn" onclick={() => zoom(-0.1)} title="Zoom Out">
<button class="btn" onclick={() => zoom(-0.1)} title={m.pdf_zoom_out()}>
<ZoomOut size="16" />
</button>
<div class="separator"></div>
<button class="btn" onclick={() => rotate(-90)} title="Rotate Left">
<button class="btn" onclick={() => rotate(-90)} title={m.pdf_rotate_left()}>
<RotateCcw size="16" />
</button>
<button class="btn" onclick={() => rotate(90)} title="Rotate Right">
<button class="btn" onclick={() => rotate(90)} title={m.pdf_rotate_right()}>
<RotateCw size="16" />
</button>
<div class="separator"></div>
<button class="btn" onclick={fitToWidth} title="Reset">
<button class="btn" onclick={fitToWidth} title={m.pdf_fit_width()}>
<AlignHorizontalSpaceAround size="16" />
</button>
</div>