From f1d603cc455d0933e6d0cce340223f0381b593fa Mon Sep 17 00:00:00 2001 From: Flavio Fois Date: Thu, 5 Feb 2026 23:42:29 +0100 Subject: [PATCH] feat: add credits page with team acknowledgments and technology stack details --- app_system.go | 13 + config.ini | 6 +- frontend/messages/en.json | 39 ++- frontend/messages/it.json | 39 ++- frontend/src/lib/components/SidebarApp.svelte | 9 +- frontend/src/routes/(app)/+layout.svelte | 14 +- .../src/routes/(app)/credits/+page.svelte | 265 ++++++++++++++++++ frontend/src/routes/(app)/credits/+page.ts | 19 ++ installer/installer.iss | 2 +- 9 files changed, 398 insertions(+), 8 deletions(-) create mode 100644 frontend/src/routes/(app)/credits/+page.svelte create mode 100644 frontend/src/routes/(app)/credits/+page.ts diff --git a/app_system.go b/app_system.go index fe24693..e0c39d6 100644 --- a/app_system.go +++ b/app_system.go @@ -144,3 +144,16 @@ func (a *App) OpenFolderInExplorer(folderPath string) error { cmd := exec.Command("explorer", folderPath) return cmd.Start() } + +// OpenURLInBrowser opens the specified URL in the system's default web browser. +// Uses the Windows "start" command to launch the default browser. +// +// Parameters: +// - url: The URL to open (must be a valid http/https URL) +// +// Returns: +// - error: Error if launching the browser fails +func (a *App) OpenURLInBrowser(url string) error { + cmd := exec.Command("cmd", "/c", "start", "", url) + return cmd.Start() +} diff --git a/config.ini b/config.ini index e03a372..17124b2 100644 --- a/config.ini +++ b/config.ini @@ -1,6 +1,6 @@ [EMLy] -SDK_DECODER_SEMVER="1.3.1" +SDK_DECODER_SEMVER="1.3.2" SDK_DECODER_RELEASE_CHANNEL="beta" -GUI_SEMVER="1.3.1" -GUI_RELEASE_CHANNEL="beta" +GUI_SEMVER="1.4.0" +GUI_RELEASE_CHANNEL="stable" LANGUAGE="it" \ No newline at end of file diff --git a/frontend/messages/en.json b/frontend/messages/en.json index 1ba5c04..346513e 100644 --- a/frontend/messages/en.json +++ b/frontend/messages/en.json @@ -123,5 +123,42 @@ "settings_import_invalid": "Invalid settings file.", "settings_email_dark_viewer_label": "Dark theme for email content", "settings_email_dark_viewer_hint": "Display email body with a dark background matching the app theme.", - "settings_email_dark_viewer_info": "Info: When disabled, emails will display with their original light background. Some emails may be designed for light backgrounds and look better with this disabled." + "settings_email_dark_viewer_info": "Info: When disabled, emails will display with their original light background. Some emails may be designed for light backgrounds and look better with this disabled.", + "sidebar_credits": "Credits", + "credits_title": "Credits", + "credits_description": "Acknowledgments and attributions for EMLy.", + "credits_about_title": "About EMLy", + "credits_about_description": "\"A slick app that somehow still works, with a badass UI that makes reading emails almost enjoyable.\"", + "credits_about_description_2": " -Someone who clearly hasn't seen the codebase", + "credits_app_tagline": "EML & MSG Viewer for Windows", + "credits_app_description": "EMLy is a lightweight, modern desktop application designed to view .eml and .msg email files. Built with performance and usability in mind, it provides a clean interface for reading emails, viewing attachments, and handling Italian PEC certified emails.", + "credits_team_title": "Development Team", + "credits_team_description": "The people behind EMLy.", + "credits_role_lead_developer": "Lead Developer", + "credits_role_senior_developer": "Senior Developer", + "credits_foisx_desc": "Creator and maintainer of EMLy. Responsible for architecture, development, and design.", + "credits_laky64_desc": "Implemented the custom MSG file parser in Go for native Outlook message support.", + "credits_special_thanks_title": "Special Thanks", + "credits_special_thanks_description": "Contributors who helped make EMLy better in a significant way.", + "credits_made_with": "Made with", + "credits_at_3git": "at 3gIT", + "credits_tech_title": "Built With", + "credits_tech_description": "Core technologies powering EMLy.", + "credits_tech_wails": "Desktop application framework for Go", + "credits_tech_go": "Backend programming language", + "credits_tech_sveltekit": "Frontend application framework", + "credits_tech_svelte": "Reactive UI framework", + "credits_tech_typescript": "Type-safe JavaScript", + "credits_tech_tailwind": "Utility-first CSS framework", + "credits_libraries_title": "Libraries & Packages", + "credits_libraries_description": "Open source packages that make EMLy possible.", + "credits_lib_shadcn": "Beautiful UI components for Svelte", + "credits_lib_lucide": "Beautiful & consistent icon set", + "credits_lib_paraglide": "Type-safe internationalization", + "credits_lib_sonner": "Toast notifications for Svelte", + "credits_lib_pdfjs": "PDF rendering library by Mozilla", + "credits_lib_dompurify": "XSS sanitizer for HTML content", + "credits_license_title": "License & Source", + "credits_license_text": "EMLy is proprietary software developed by 3gIT. All rights reserved. The application uses various open source libraries, each governed by their respective licenses.", + "credits_copyright": "All rights reserved." } diff --git a/frontend/messages/it.json b/frontend/messages/it.json index d97c67c..abd5811 100644 --- a/frontend/messages/it.json +++ b/frontend/messages/it.json @@ -123,5 +123,42 @@ "settings_import_invalid": "File impostazioni non valido.", "settings_email_dark_viewer_label": "Tema scuro per contenuto email", "settings_email_dark_viewer_hint": "Visualizza il corpo dell'email con uno sfondo scuro che corrisponde al tema dell'app.", - "settings_email_dark_viewer_info": "Info: Quando disabilitato, le email verranno visualizzate con lo sfondo chiaro originale. Alcune email potrebbero essere progettate per sfondi chiari e apparire meglio con questa opzione disabilitata." + "settings_email_dark_viewer_info": "Info: Quando disabilitato, le email verranno visualizzate con lo sfondo chiaro originale. Alcune email potrebbero essere progettate per sfondi chiari e apparire meglio con questa opzione disabilitata.", + "sidebar_credits": "Crediti", + "credits_title": "Crediti", + "credits_description": "Riconoscimenti e attribuzioni per EMLy.", + "credits_about_title": "Informazioni su EMLy", + "credits_about_description": "\"Un'app che in qualche modo funziona ancora, con un'interfaccia da paura che rende quasi piacevole leggere le email.\"", + "credits_about_description_2": " -Qualcuno che chiaramente non ha visto il codice sorgente", + "credits_app_tagline": "Visualizzatore EML e MSG per Windows", + "credits_app_description": "EMLy è un'applicazione desktop leggera e moderna progettata per visualizzare file email .eml e .msg. Costruita con prestazioni e usabilità in mente, fornisce un'interfaccia pulita per leggere email, visualizzare allegati e gestire email PEC certificate italiane.", + "credits_team_title": "Team di Sviluppo", + "credits_team_description": "Le persone dietro EMLy.", + "credits_role_lead_developer": "Sviluppatore Principale", + "credits_role_senior_developer": "Sviluppatore Senior", + "credits_foisx_desc": "Creatore e manutentore di EMLy. Responsabile dell'architettura, sviluppo e design.", + "credits_laky64_desc": "Ha implementato il parser MSG personalizzato in Go per il supporto nativo dei messaggi Outlook.", + "credits_special_thanks_title": "Ringraziamenti Speciali", + "credits_special_thanks_description": "Contributori che hanno aiutato a migliorare EMLy in modo significativo.", + "credits_made_with": "Fatto con", + "credits_at_3git": "presso 3gIT", + "credits_tech_title": "Costruito Con", + "credits_tech_description": "Tecnologie principali che alimentano EMLy.", + "credits_tech_wails": "Framework per applicazioni desktop in Go", + "credits_tech_go": "Linguaggio di programmazione backend", + "credits_tech_sveltekit": "Framework per applicazioni frontend", + "credits_tech_svelte": "Framework UI reattivo", + "credits_tech_typescript": "JavaScript type-safe", + "credits_tech_tailwind": "Framework CSS utility-first", + "credits_libraries_title": "Librerie e Pacchetti", + "credits_libraries_description": "Pacchetti open source che rendono possibile EMLy.", + "credits_lib_shadcn": "Componenti UI per Svelte", + "credits_lib_lucide": "Set di icone belle e coerenti", + "credits_lib_paraglide": "Internazionalizzazione type-safe", + "credits_lib_sonner": "Notifiche toast per Svelte", + "credits_lib_pdfjs": "Libreria di rendering PDF di Mozilla", + "credits_lib_dompurify": "Sanitizzatore XSS per contenuti HTML", + "credits_license_title": "Licenza e Sorgente", + "credits_license_text": "EMLy è un software proprietario sviluppato da 3gIT. Tutti i diritti riservati. L'applicazione utilizza varie librerie open source, ciascuna governata dalle rispettive licenze.", + "credits_copyright": "Tutti i diritti riservati." } diff --git a/frontend/src/lib/components/SidebarApp.svelte b/frontend/src/lib/components/SidebarApp.svelte index 1b5da6d..460a709 100644 --- a/frontend/src/lib/components/SidebarApp.svelte +++ b/frontend/src/lib/components/SidebarApp.svelte @@ -3,7 +3,7 @@ import * as Sidebar from "$lib/components/ui/sidebar/index.js"; import { dangerZoneEnabled } from "$lib/stores/app"; import * as m from "$lib/paraglide/messages.js"; - import { Mail } from "@lucide/svelte/icons"; + import { Mail, Heart } from "@lucide/svelte/icons"; const CLICK_WINDOW_MS = 4000; const REQUIRED_CLICKS = 10; @@ -43,6 +43,13 @@ disabled: false, id: 2, }, + { + title: m.sidebar_credits(), + url: "/credits", + icon: Heart, + disabled: false, + id: 3, + }, ]; diff --git a/frontend/src/routes/(app)/+layout.svelte b/frontend/src/routes/(app)/+layout.svelte index ebfe475..8e949dc 100644 --- a/frontend/src/routes/(app)/+layout.svelte +++ b/frontend/src/routes/(app)/+layout.svelte @@ -23,6 +23,7 @@ FolderOpen, CheckCircle, Camera, + Heart, } from "@lucide/svelte"; import { Separator } from "$lib/components/ui/separator/index.js"; import { toast } from "svelte-sonner"; @@ -253,8 +254,10 @@ {#if dev} v{versionInfo?.EMLy.GUISemver}_{versionInfo?.EMLy.GUIReleaseChannel} (DEBUG BUILD) - {:else} + {:else if versionInfo?.EMLy.GUIReleaseChannel !== "stable"} v{versionInfo?.EMLy.GUISemver}_{versionInfo?.EMLy.GUIReleaseChannel} + {:else} + v{versionInfo?.EMLy.GUISemver} {/if} {#if versionInfo} @@ -355,6 +358,15 @@ style="cursor: pointer; opacity: 0.7;" class="hover:opacity-100 transition-opacity" /> + { + if (page.url.pathname !== "/credits" && page.url.pathname !== "/credits/") + goto("/credits"); + }} + style="cursor: pointer; opacity: 0.7;" + class="hover:opacity-100 transition-opacity" + /> + import { goto } from "$app/navigation"; + import { Button } from "$lib/components/ui/button"; + import * as Card from "$lib/components/ui/card"; + import { Separator } from "$lib/components/ui/separator"; + import { ChevronLeft, Heart, Code, Package, Globe, Github, Mail } from "@lucide/svelte"; + import * as m from "$lib/paraglide/messages"; + import { OpenURLInBrowser } from "$lib/wailsjs/go/main/App"; + + let { data } = $props(); + let config = $derived(data.config); + + // Open external URL in default browser + async function openUrl(url: string) { + try { + await OpenURLInBrowser(url); + } catch (e) { + console.error("Failed to open URL:", e); + } + } + + // Gravatar URL helper - uses MD5 hash of email + // Pre-computed hashes for known emails + const gravatarUrls: Record = { + "f.fois@3git.eu": "https://gravatar.com/avatar/6a2b6cfd8ab2c36ac3eace1faa871f79084b64ad08fb6e490f050e71ee1b599c", + "iraci.matteo@gmail.com": "https://gravatar.com/avatar/0c17334ae886eb44b670d226e7de32ac082b9c85925ce4ed4c12239d9d8351f2", + }; + + // Technology stack + const technologies = [ + { name: "Wails v2", description: m.credits_tech_wails(), url: "https://wails.io" }, + { name: "Go", description: m.credits_tech_go(), url: "https://go.dev" }, + { name: "SvelteKit", description: m.credits_tech_sveltekit(), url: "https://kit.svelte.dev" }, + { name: "Svelte 5", description: m.credits_tech_svelte(), url: "https://svelte.dev" }, + { name: "TypeScript", description: m.credits_tech_typescript(), url: "https://www.typescriptlang.org" }, + { name: "Tailwind CSS", description: m.credits_tech_tailwind(), url: "https://tailwindcss.com" }, + ]; + + // Libraries and packages + const libraries = [ + { name: "shadcn-svelte", description: m.credits_lib_shadcn(), url: "https://www.shadcn-svelte.com" }, + { name: "Lucide Icons", description: m.credits_lib_lucide(), url: "https://lucide.dev" }, + { name: "ParaglideJS", description: m.credits_lib_paraglide(), url: "https://inlang.com/m/gerre34r/library-inlang-paraglideJs" }, + { name: "svelte-sonner", description: m.credits_lib_sonner(), url: "https://svelte-sonner.vercel.app" }, + { name: "PDF.js", description: m.credits_lib_pdfjs(), url: "https://mozilla.github.io/pdf.js" }, + { name: "DOMPurify", description: m.credits_lib_dompurify(), url: "https://github.com/cure53/DOMPurify" }, + ]; + + // Team / Contributors + const team = [ + { + username: "FOISX", + name: "Flavio Fois", + role: m.credits_role_lead_developer(), + description: m.credits_foisx_desc(), + email: "f.fois@3git.eu", + }, + ]; + + // Special thanks + const specialThanks = [ + { + name: "Laky64", + contribution: m.credits_laky64_desc(), + email: "iraci.matteo@gmail.com", + }, + ]; + + +
+
+
+
+

+ {m.credits_title()} +

+

+ {m.credits_description()} +

+
+ +
+ + + + + + + {m.credits_about_title()} + + + {m.credits_about_description()} + {m.credits_about_description_2()} + + + +
+ EMLy Logo +
+

EMLy

+

{m.credits_app_tagline()}

+ {#if config} +

+ v{config.GUISemver} ({config.GUIReleaseChannel}) +

+ {/if} +
+
+

+ {m.credits_app_description()} +

+
+
+ + + + + + + {m.credits_team_title()} + + {m.credits_team_description()} + + + {#each team as member} +
+ {member.name} +
+
{member.username} ({member.name})
+
{member.role}
+
{member.description}
+ + + {member.email} + +
+
+ {/each} +
+ + {m.credits_made_with()} {m.credits_at_3git()} + +
+
+
+ + + + + + + {m.credits_special_thanks_title()} + + {m.credits_special_thanks_description()} + + +
+ {#each specialThanks as contributor} +
+ {contributor.name} +
+ {contributor.name} + - + {contributor.contribution} +
+
+ {/each} +
+
+
+ + + + + + + {m.credits_tech_title()} + + {m.credits_tech_description()} + + +
+ {#each technologies as tech} + + {/each} +
+
+
+ + + + + + + {m.credits_libraries_title()} + + {m.credits_libraries_description()} + + +
+ {#each libraries as lib} + + {/each} +
+
+
+ + + + + + + {m.credits_license_title()} + + + +

+ {m.credits_license_text()} +

+ +

+ © 2025-{new Date().getFullYear()} 3gIT. {m.credits_copyright()} +

+
+
+
+
diff --git a/frontend/src/routes/(app)/credits/+page.ts b/frontend/src/routes/(app)/credits/+page.ts new file mode 100644 index 0000000..daa08ae --- /dev/null +++ b/frontend/src/routes/(app)/credits/+page.ts @@ -0,0 +1,19 @@ +import type { PageLoad } from './$types'; +import { GetConfig } from "$lib/wailsjs/go/main/App"; +import { browser } from '$app/environment'; + +export const load = (async () => { + if (!browser) return { config: null }; + + try { + const configRoot = await GetConfig(); + return { + config: configRoot.EMLy + }; + } catch (e) { + console.error("Failed to load config for credits", e); + return { + config: null + }; + } +}) satisfies PageLoad; diff --git a/installer/installer.iss b/installer/installer.iss index da82a5a..a968f71 100644 --- a/installer/installer.iss +++ b/installer/installer.iss @@ -1,6 +1,6 @@ #define ApplicationName 'EMLy' #define ApplicationVersion GetVersionNumbersString('EMLy.exe') -#define ApplicationVersion '1.3.1_beta' +#define ApplicationVersion '1.4.0' [Setup] AppName={#ApplicationName}