From f22df24f58b09b56d99aa77a04cb5b1f7e6123c7 Mon Sep 17 00:00:00 2001 From: Erwann Mest Date: Sat, 4 Jul 2026 19:42:19 +0100 Subject: [PATCH 1/6] =?UTF-8?q?=E2=9C=A8=20feat(theme):=20add=20light/dark?= =?UTF-8?q?/system=20appearance=20setting?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Introduce a Theme enum (System/Light/Dark) in settings.rs with a default of System, wiring it through the full stack: - Backend: Theme enum, default_theme(), AppSettings.theme field, change_theme_setting Tauri command - Frontend: theme.ts utility (applyTheme, getStoredTheme, syncThemeFromSettings), ThemeSelector component, CSS overrides via data-theme attribute - Bootstrapped synchronously in main.tsx to avoid palette flash on load - i18n keys added for title, description, and all three options --- src-tauri/src/lib.rs | 1 + src-tauri/src/settings.rs | 17 +++++ src-tauri/src/shortcut/mod.rs | 20 +++++- src/App.css | 23 +++++++ src/bindings.ts | 13 +++- src/components/settings/ThemeSelector.tsx | 49 +++++++++++++++ .../settings/about/AboutSettings.tsx | 2 + src/i18n/locales/en/translation.json | 9 +++ src/lib/utils/theme.ts | 63 +++++++++++++++++++ src/main.tsx | 10 +++ src/stores/settingsStore.ts | 1 + 11 files changed, 205 insertions(+), 3 deletions(-) create mode 100644 src/components/settings/ThemeSelector.tsx create mode 100644 src/lib/utils/theme.ts diff --git a/src-tauri/src/lib.rs b/src-tauri/src/lib.rs index f24509dae5..c8a06d2b45 100644 --- a/src-tauri/src/lib.rs +++ b/src-tauri/src/lib.rs @@ -538,6 +538,7 @@ pub fn run(cli_args: CliArgs) { shortcut::change_audio_feedback_setting, shortcut::change_audio_feedback_volume_setting, shortcut::change_sound_theme_setting, + shortcut::change_theme_setting, shortcut::change_start_hidden_setting, shortcut::change_autostart_setting, shortcut::change_translate_to_english_setting, diff --git a/src-tauri/src/settings.rs b/src-tauri/src/settings.rs index 69d1be25bb..6ef1397335 100644 --- a/src-tauri/src/settings.rs +++ b/src-tauri/src/settings.rs @@ -258,6 +258,16 @@ impl SoundTheme { } } +/// UI appearance mode. `System` follows the OS `prefers-color-scheme`; `Light` +/// and `Dark` force one of the two palettes Handy already ships. +#[derive(Serialize, Deserialize, Debug, Clone, Copy, PartialEq, Eq, Type)] +#[serde(rename_all = "snake_case")] +pub enum Theme { + System, + Light, + Dark, +} + #[derive(Serialize, Deserialize, Debug, Clone, Copy, PartialEq, Eq, Type, Default)] #[serde(rename_all = "snake_case")] pub enum TypingTool { @@ -408,6 +418,8 @@ pub struct AppSettings { pub append_trailing_space: bool, #[serde(default = "default_app_language")] pub app_language: String, + #[serde(default = "default_theme")] + pub theme: Theme, #[serde(default)] pub experimental_enabled: bool, #[serde(default)] @@ -537,6 +549,10 @@ fn default_sound_theme() -> SoundTheme { SoundTheme::Marimba } +fn default_theme() -> Theme { + Theme::System +} + fn default_post_process_enabled() -> bool { false } @@ -840,6 +856,7 @@ pub fn get_default_settings() -> AppSettings { mute_while_recording: false, append_trailing_space: false, app_language: default_app_language(), + theme: default_theme(), experimental_enabled: false, lazy_stream_close: false, keyboard_implementation: KeyboardImplementation::default(), diff --git a/src-tauri/src/shortcut/mod.rs b/src-tauri/src/shortcut/mod.rs index 3a33b3b7be..c1ed44a0cd 100644 --- a/src-tauri/src/shortcut/mod.rs +++ b/src-tauri/src/shortcut/mod.rs @@ -23,7 +23,7 @@ use tauri_plugin_autostart::ManagerExt; use crate::settings::APPLE_INTELLIGENCE_DEFAULT_MODEL_ID; use crate::settings::{ self, get_settings, AutoSubmitKey, ClipboardHandling, KeyboardImplementation, LLMPrompt, - OverlayPosition, OverlayStyle, PasteMethod, ShortcutBinding, SoundTheme, TypingTool, + OverlayPosition, OverlayStyle, PasteMethod, ShortcutBinding, SoundTheme, Theme, TypingTool, APPLE_INTELLIGENCE_PROVIDER_ID, }; use crate::tray; @@ -514,6 +514,24 @@ pub fn change_sound_theme_setting(app: AppHandle, theme: String) -> Result<(), S Ok(()) } +#[tauri::command] +#[specta::specta] +pub fn change_theme_setting(app: AppHandle, theme: String) -> Result<(), String> { + let mut settings = settings::get_settings(&app); + let parsed = match theme.as_str() { + "system" => Theme::System, + "light" => Theme::Light, + "dark" => Theme::Dark, + other => { + warn!("Invalid theme '{}', defaulting to system", other); + Theme::System + } + }; + settings.theme = parsed; + settings::write_settings(&app, settings); + Ok(()) +} + #[tauri::command] #[specta::specta] pub fn change_translate_to_english_setting(app: AppHandle, enabled: bool) -> Result<(), String> { diff --git a/src/App.css b/src/App.css index f0d791532b..ba529bf401 100644 --- a/src/App.css +++ b/src/App.css @@ -59,6 +59,29 @@ } } +/* + * Explicit theme override, driven by the `theme` setting via + * `document.documentElement.dataset.theme`. When set, the higher-specificity + * attribute selectors win over the `prefers-color-scheme` media query above, + * so a user can keep Handy light or dark regardless of the OS setting. + * `system` (or no attribute) leaves the OS in charge. + */ +:root[data-theme="light"] { + /* Colors - Light Theme */ + --color-text: #0f0f0f; + --color-background: #fbfbfb; + --color-logo-primary: #faa2ca; + --color-logo-stroke: #382731; +} + +:root[data-theme="dark"] { + /* Colors - Dark Theme */ + --color-text: #fbfbfb; + --color-background: #2c2b29; + --color-logo-primary: #f28cbb; + --color-logo-stroke: #fad1ed; +} + /* macOS - tint native overlay scrollbar thumb */ :root[data-platform="macos"] { scrollbar-color: var(--scrollbar-thumb) transparent; diff --git a/src/bindings.ts b/src/bindings.ts index fba9384a88..5a07b83439 100644 --- a/src/bindings.ts +++ b/src/bindings.ts @@ -53,6 +53,14 @@ async changeSoundThemeSetting(theme: string) : Promise> { else return { status: "error", error: e as any }; } }, +async changeThemeSetting(theme: string) : Promise> { + try { + return { status: "ok", data: await TAURI_INVOKE("change_theme_setting", { theme }) }; +} catch (e) { + if(e instanceof Error) throw e; + else return { status: "error", error: e as any }; +} +}, async changeStartHiddenSetting(enabled: boolean) : Promise> { try { return { status: "ok", data: await TAURI_INVOKE("change_start_hidden_setting", { enabled }) }; @@ -864,7 +872,7 @@ streamTextEvent: "stream-text-event" /** user-defined types **/ -export type AppSettings = { +export type AppSettings = { /** * Internal settings schema marker for one-time migrations. Fresh installs * start at the current version; existing stores missing this key are @@ -877,7 +885,7 @@ settings_schema_version?: number; bindings: Partial<{ [key in string]: ShortcutB * upgrading from before this key existed are blanked by the migration so they * see the current release's notes — see `apply_settings_migrations`. */ -whats_new_last_seen_version?: string; selected_model?: string; onboarding_completed?: boolean; always_on_microphone?: boolean; selected_microphone?: string | null; clamshell_microphone?: string | null; selected_output_device?: string | null; translate_to_english?: boolean; selected_language?: string; overlay_position?: OverlayPosition; debug_mode?: boolean; log_level?: LogLevel; custom_words?: string[]; model_unload_timeout?: ModelUnloadTimeout; word_correction_threshold?: number; history_limit?: number; recording_retention_period?: RecordingRetentionPeriod; paste_method?: PasteMethod; clipboard_handling?: ClipboardHandling; auto_submit?: boolean; auto_submit_key?: AutoSubmitKey; post_process_enabled?: boolean; post_process_provider_id?: string; post_process_providers?: PostProcessProvider[]; post_process_api_keys?: SecretMap; post_process_models?: Partial<{ [key in string]: string }>; post_process_prompts?: LLMPrompt[]; post_process_selected_prompt_id?: string | null; mute_while_recording?: boolean; append_trailing_space?: boolean; app_language?: string; experimental_enabled?: boolean; lazy_stream_close?: boolean; keyboard_implementation?: KeyboardImplementation; show_tray_icon?: boolean; paste_delay_ms?: number; typing_tool?: TypingTool; external_script_path: string | null; custom_filler_words?: string[] | null; transcribe_accelerator?: TranscribeAcceleratorSetting; ort_accelerator?: OrtAcceleratorSetting; transcribe_gpu_device?: number; extra_recording_buffer_ms?: number; vad_enabled?: boolean; +whats_new_last_seen_version?: string; selected_model?: string; onboarding_completed?: boolean; always_on_microphone?: boolean; selected_microphone?: string | null; clamshell_microphone?: string | null; selected_output_device?: string | null; translate_to_english?: boolean; selected_language?: string; overlay_position?: OverlayPosition; debug_mode?: boolean; log_level?: LogLevel; custom_words?: string[]; model_unload_timeout?: ModelUnloadTimeout; word_correction_threshold?: number; history_limit?: number; recording_retention_period?: RecordingRetentionPeriod; paste_method?: PasteMethod; clipboard_handling?: ClipboardHandling; auto_submit?: boolean; auto_submit_key?: AutoSubmitKey; post_process_enabled?: boolean; post_process_provider_id?: string; post_process_providers?: PostProcessProvider[]; post_process_api_keys?: SecretMap; post_process_models?: Partial<{ [key in string]: string }>; post_process_prompts?: LLMPrompt[]; post_process_selected_prompt_id?: string | null; mute_while_recording?: boolean; append_trailing_space?: boolean; app_language?: string; theme?: Theme; experimental_enabled?: boolean; lazy_stream_close?: boolean; keyboard_implementation?: KeyboardImplementation; show_tray_icon?: boolean; paste_delay_ms?: number; typing_tool?: TypingTool; external_script_path: string | null; custom_filler_words?: string[] | null; transcribe_accelerator?: TranscribeAcceleratorSetting; ort_accelerator?: OrtAcceleratorSetting; transcribe_gpu_device?: number; extra_recording_buffer_ms?: number; vad_enabled?: boolean; /** * Which recording overlay to show: None / Minimal / Live. Streaming mode is * not gated on this — that follows model capability. Migrated from the old @@ -988,6 +996,7 @@ export type StreamTextEvent = { committed: string; tentative: string } */ export type StreamWorkKind = "transcribing" | "polishing" export type TranscribeAcceleratorSetting = "auto" | "cpu" | "gpu" +export type Theme = "system" | "light" | "dark" export type TypingTool = "auto" | "wtype" | "kwtype" | "dotool" | "ydotool" | "xdotool" export type WindowsMicrophonePermissionStatus = { supported: boolean; overall_access: PermissionAccess; device_access: PermissionAccess; app_access: PermissionAccess; desktop_app_access: PermissionAccess } diff --git a/src/components/settings/ThemeSelector.tsx b/src/components/settings/ThemeSelector.tsx new file mode 100644 index 0000000000..3757fc5b7a --- /dev/null +++ b/src/components/settings/ThemeSelector.tsx @@ -0,0 +1,49 @@ +import React from "react"; +import { useTranslation } from "react-i18next"; +import { Dropdown } from "../ui/Dropdown"; +import { SettingContainer } from "../ui/SettingContainer"; +import { useSettings } from "@/hooks/useSettings"; +import { applyTheme, THEME_OPTIONS } from "@/lib/utils/theme"; +import type { Theme } from "@/bindings"; + +interface ThemeSelectorProps { + descriptionMode?: "inline" | "tooltip"; + grouped?: boolean; +} + +export const ThemeSelector: React.FC = React.memo( + ({ descriptionMode = "tooltip", grouped = false }) => { + const { t } = useTranslation(); + const { settings, updateSetting } = useSettings(); + + const currentTheme: Theme = settings?.theme ?? "system"; + + const themeOptions = THEME_OPTIONS.map((value) => ({ + value, + label: t(`theme.options.${value}`), + })); + + const handleThemeChange = (value: string) => { + const theme = value as Theme; + applyTheme(theme); + updateSetting("theme", theme); + }; + + return ( + + + + ); + }, +); + +ThemeSelector.displayName = "ThemeSelector"; diff --git a/src/components/settings/about/AboutSettings.tsx b/src/components/settings/about/AboutSettings.tsx index 8d31d33a6a..fdeb4df4a3 100644 --- a/src/components/settings/about/AboutSettings.tsx +++ b/src/components/settings/about/AboutSettings.tsx @@ -8,6 +8,7 @@ import { Button } from "../../ui/Button"; import { AppDataDirectory } from "../AppDataDirectory"; import { AppLanguageSelector } from "../AppLanguageSelector"; import { ShowWhatsNewOnUpdate } from "../ShowWhatsNewOnUpdate"; +import { ThemeSelector } from "../ThemeSelector"; import { LogDirectory } from "../debug"; export const AboutSettings: React.FC = () => { @@ -40,6 +41,7 @@ export const AboutSettings: React.FC = () => {
+ + value === "system" || value === "light" || value === "dark"; + +/** Apply a theme to the document root and remember it for the next launch. */ +export const applyTheme = (theme: Theme): void => { + const root = document.documentElement; + if (theme === "system") { + delete root.dataset.theme; + } else { + root.dataset.theme = theme; + } + try { + localStorage.setItem(THEME_STORAGE_KEY, theme); + } catch { + // localStorage may be unavailable (e.g. private mode); the setting still + // persists in AppSettings, so this only costs a one-frame flash on boot. + } +}; + +/** Read the last-applied theme for synchronous boot-time application. */ +export const getStoredTheme = (): Theme => { + try { + const stored = localStorage.getItem(THEME_STORAGE_KEY); + if (isTheme(stored)) return stored; + } catch { + // ignore + } + return "system"; +}; + +/** Apply the persisted theme from AppSettings (the source of truth). */ +export const syncThemeFromSettings = async (): Promise => { + try { + const result = await commands.getAppSettings(); + if (result.status === "ok") { + applyTheme(result.data.theme ?? "system"); + } + } catch (e) { + console.warn("Failed to sync theme from settings:", e); + } +}; diff --git a/src/main.tsx b/src/main.tsx index 1e83ae7417..3cd2ed60a5 100644 --- a/src/main.tsx +++ b/src/main.tsx @@ -2,10 +2,20 @@ import React from "react"; import ReactDOM from "react-dom/client"; import { platform } from "@tauri-apps/plugin-os"; import App from "./App"; +import { + applyTheme, + getStoredTheme, + syncThemeFromSettings, +} from "./lib/utils/theme"; // Set platform before render so CSS can scope per-platform (e.g. scrollbar styles) document.documentElement.dataset.platform = platform(); +// Apply the last-known theme synchronously before render to avoid a flash of +// the wrong palette, then reconcile with the persisted setting once it loads. +applyTheme(getStoredTheme()); +syncThemeFromSettings(); + // Initialize i18n import "./i18n"; diff --git a/src/stores/settingsStore.ts b/src/stores/settingsStore.ts index 02a8b670ca..c0d2944c9b 100644 --- a/src/stores/settingsStore.ts +++ b/src/stores/settingsStore.ts @@ -143,6 +143,7 @@ const settingUpdaters: { commands.changeAppendTrailingSpaceSetting(value as boolean), log_level: (value) => commands.setLogLevel(value as any), app_language: (value) => commands.changeAppLanguageSetting(value as string), + theme: (value) => commands.changeThemeSetting(value as string), experimental_enabled: (value) => commands.changeExperimentalEnabledSetting(value as boolean), lazy_stream_close: (value) => From 26d87402fcf4a008b630dba2361f9749903e2318 Mon Sep 17 00:00:00 2001 From: CJ Pais Date: Tue, 7 Jul 2026 18:56:34 +0800 Subject: [PATCH 2/6] update translations --- src/i18n/locales/ar/translation.json | 9 +++++++++ src/i18n/locales/bg/translation.json | 9 +++++++++ src/i18n/locales/cs/translation.json | 9 +++++++++ src/i18n/locales/de/translation.json | 9 +++++++++ src/i18n/locales/es/translation.json | 9 +++++++++ src/i18n/locales/fr/translation.json | 9 +++++++++ src/i18n/locales/he/translation.json | 9 +++++++++ src/i18n/locales/it/translation.json | 9 +++++++++ src/i18n/locales/ja/translation.json | 9 +++++++++ src/i18n/locales/ko/translation.json | 9 +++++++++ src/i18n/locales/nl/translation.json | 9 +++++++++ src/i18n/locales/pl/translation.json | 9 +++++++++ src/i18n/locales/pt/translation.json | 9 +++++++++ src/i18n/locales/ru/translation.json | 9 +++++++++ src/i18n/locales/sv/translation.json | 9 +++++++++ src/i18n/locales/tr/translation.json | 9 +++++++++ src/i18n/locales/uk/translation.json | 9 +++++++++ src/i18n/locales/vi/translation.json | 9 +++++++++ src/i18n/locales/zh-TW/translation.json | 9 +++++++++ src/i18n/locales/zh/translation.json | 9 +++++++++ 20 files changed, 180 insertions(+) diff --git a/src/i18n/locales/ar/translation.json b/src/i18n/locales/ar/translation.json index 01038a1633..4dbad86538 100644 --- a/src/i18n/locales/ar/translation.json +++ b/src/i18n/locales/ar/translation.json @@ -614,6 +614,15 @@ "title": "لغة التطبيق", "description": "تغيير لغة واجهة Handy" }, + "theme": { + "title": "المظهر", + "description": "اختر ما إذا كان Handy يتبع مظهر النظام أو يبقى فاتحًا أو داكنًا", + "options": { + "system": "النظام", + "light": "فاتح", + "dark": "داكن" + } + }, "overlay": { "transcribing": "...جاري التفريغ", "processing": "...جاري المعالجة" diff --git a/src/i18n/locales/bg/translation.json b/src/i18n/locales/bg/translation.json index 6f95f15d95..dcd4c68398 100644 --- a/src/i18n/locales/bg/translation.json +++ b/src/i18n/locales/bg/translation.json @@ -614,6 +614,15 @@ "title": "Език на приложението", "description": "Промяна на езика на интерфейса на Handy" }, + "theme": { + "title": "Външен вид", + "description": "Изберете дали Handy да следва системната тема, или да остане светла или тъмна", + "options": { + "system": "Системна", + "light": "Светла", + "dark": "Тъмна" + } + }, "overlay": { "transcribing": "Транскрибиране...", "processing": "Обработка..." diff --git a/src/i18n/locales/cs/translation.json b/src/i18n/locales/cs/translation.json index 405730ae1f..58790ef98d 100644 --- a/src/i18n/locales/cs/translation.json +++ b/src/i18n/locales/cs/translation.json @@ -614,6 +614,15 @@ "title": "Jazyk aplikace", "description": "Změňte jazyk rozhraní Handy" }, + "theme": { + "title": "Vzhled", + "description": "Zvolte, zda má Handy sledovat motiv systému, nebo zůstat světlý či tmavý", + "options": { + "system": "Systém", + "light": "Světlý", + "dark": "Tmavý" + } + }, "overlay": { "transcribing": "Přepisuji...", "processing": "Zpracovávám..." diff --git a/src/i18n/locales/de/translation.json b/src/i18n/locales/de/translation.json index 89b42a4e75..358411793a 100644 --- a/src/i18n/locales/de/translation.json +++ b/src/i18n/locales/de/translation.json @@ -614,6 +614,15 @@ "title": "Anwendungssprache", "description": "Sprache der Handy-Oberfläche ändern" }, + "theme": { + "title": "Erscheinungsbild", + "description": "Wählen Sie, ob Handy dem Systemthema folgt oder hell bzw. dunkel bleibt", + "options": { + "system": "System", + "light": "Hell", + "dark": "Dunkel" + } + }, "overlay": { "transcribing": "Transkribiere...", "processing": "Verarbeite..." diff --git a/src/i18n/locales/es/translation.json b/src/i18n/locales/es/translation.json index 3ab98aa5b7..184823c38c 100644 --- a/src/i18n/locales/es/translation.json +++ b/src/i18n/locales/es/translation.json @@ -614,6 +614,15 @@ "title": "Idioma de la aplicación", "description": "Cambia el idioma de la interfaz de Handy" }, + "theme": { + "title": "Apariencia", + "description": "Elige si Handy sigue el tema del sistema o permanece claro u oscuro", + "options": { + "system": "Sistema", + "light": "Claro", + "dark": "Oscuro" + } + }, "overlay": { "transcribing": "Transcribiendo...", "processing": "Procesando..." diff --git a/src/i18n/locales/fr/translation.json b/src/i18n/locales/fr/translation.json index da1612eb24..d669296d88 100644 --- a/src/i18n/locales/fr/translation.json +++ b/src/i18n/locales/fr/translation.json @@ -614,6 +614,15 @@ "title": "Langue de l'application", "description": "Changer la langue de l'interface de Handy" }, + "theme": { + "title": "Apparence", + "description": "Choisissez si Handy suit le thème du système ou reste clair ou sombre", + "options": { + "system": "Système", + "light": "Clair", + "dark": "Sombre" + } + }, "overlay": { "transcribing": "Transcription...", "processing": "Traitement..." diff --git a/src/i18n/locales/he/translation.json b/src/i18n/locales/he/translation.json index ec8da91d45..c7f6692fb5 100644 --- a/src/i18n/locales/he/translation.json +++ b/src/i18n/locales/he/translation.json @@ -614,6 +614,15 @@ "title": "שפת האפליקציה", "description": "שנה את שפת הממשק של Handy" }, + "theme": { + "title": "מראה", + "description": "בחר אם Handy יעקוב אחר ערכת הנושא של המערכת או יישאר בהיר או כהה", + "options": { + "system": "מערכת", + "light": "בהיר", + "dark": "כהה" + } + }, "overlay": { "transcribing": "מתמלל...", "processing": "מעבד..." diff --git a/src/i18n/locales/it/translation.json b/src/i18n/locales/it/translation.json index bc451e8dd9..96541bda1a 100644 --- a/src/i18n/locales/it/translation.json +++ b/src/i18n/locales/it/translation.json @@ -614,6 +614,15 @@ "title": "Lingua Applicazione", "description": "Cambia la lingua dell'interfaccia di Handy" }, + "theme": { + "title": "Aspetto", + "description": "Scegli se Handy segue il tema di sistema o resta chiaro o scuro", + "options": { + "system": "Sistema", + "light": "Chiaro", + "dark": "Scuro" + } + }, "overlay": { "transcribing": "Trascrizione...", "processing": "Elaborazione..." diff --git a/src/i18n/locales/ja/translation.json b/src/i18n/locales/ja/translation.json index eae732df69..42ed86d8ac 100644 --- a/src/i18n/locales/ja/translation.json +++ b/src/i18n/locales/ja/translation.json @@ -614,6 +614,15 @@ "title": "アプリの言語", "description": "Handy インターフェースの言語を変更" }, + "theme": { + "title": "外観", + "description": "Handy をシステムのテーマに合わせるか、ライトまたはダークで固定するかを選択します", + "options": { + "system": "システム", + "light": "ライト", + "dark": "ダーク" + } + }, "overlay": { "transcribing": "文字起こし中…", "processing": "処理中…" diff --git a/src/i18n/locales/ko/translation.json b/src/i18n/locales/ko/translation.json index 3eec584173..f87d1bebb0 100644 --- a/src/i18n/locales/ko/translation.json +++ b/src/i18n/locales/ko/translation.json @@ -614,6 +614,15 @@ "title": "애플리케이션 언어", "description": "Handy 인터페이스의 언어를 변경하세요" }, + "theme": { + "title": "화면 모드", + "description": "Handy가 시스템 테마를 따를지, 밝게 또는 어둡게 유지할지 선택하세요", + "options": { + "system": "시스템", + "light": "밝게", + "dark": "어둡게" + } + }, "overlay": { "transcribing": "텍스트로 변환 중...", "processing": "처리 중..." diff --git a/src/i18n/locales/nl/translation.json b/src/i18n/locales/nl/translation.json index d7a0eb993b..1c58010062 100644 --- a/src/i18n/locales/nl/translation.json +++ b/src/i18n/locales/nl/translation.json @@ -617,6 +617,15 @@ "title": "Interfacetaal", "description": "Wijzig de taal van de Handy-interface" }, + "theme": { + "title": "Weergave", + "description": "Kies of Handy het systeemthema volgt of licht of donker blijft", + "options": { + "system": "Systeem", + "light": "Licht", + "dark": "Donker" + } + }, "overlay": { "transcribing": "Transcriberen...", "processing": "Verwerken..." diff --git a/src/i18n/locales/pl/translation.json b/src/i18n/locales/pl/translation.json index 2a25069f97..f02bab4742 100644 --- a/src/i18n/locales/pl/translation.json +++ b/src/i18n/locales/pl/translation.json @@ -614,6 +614,15 @@ "title": "Język aplikacji", "description": "Zmień język interfejsu Handy" }, + "theme": { + "title": "Wygląd", + "description": "Wybierz, czy Handy ma podążać za motywem systemu, czy pozostać jasny lub ciemny", + "options": { + "system": "System", + "light": "Jasny", + "dark": "Ciemny" + } + }, "overlay": { "transcribing": "Transkrypcja...", "processing": "Przetwarzanie..." diff --git a/src/i18n/locales/pt/translation.json b/src/i18n/locales/pt/translation.json index 57a398e325..0d80bedb0f 100644 --- a/src/i18n/locales/pt/translation.json +++ b/src/i18n/locales/pt/translation.json @@ -614,6 +614,15 @@ "title": "Idioma da Aplicação", "description": "Alterar o idioma da interface do Handy" }, + "theme": { + "title": "Aparência", + "description": "Escolha se o Handy segue o tema do sistema ou permanece claro ou escuro", + "options": { + "system": "Sistema", + "light": "Claro", + "dark": "Escuro" + } + }, "overlay": { "transcribing": "Transcrevendo...", "processing": "Processando..." diff --git a/src/i18n/locales/ru/translation.json b/src/i18n/locales/ru/translation.json index 2f442115d5..c0addbab8e 100644 --- a/src/i18n/locales/ru/translation.json +++ b/src/i18n/locales/ru/translation.json @@ -614,6 +614,15 @@ "title": "Язык приложения", "description": "Изменить язык интерфейса Handy" }, + "theme": { + "title": "Внешний вид", + "description": "Выберите, следует ли Handy системной теме или остаётся светлым либо тёмным", + "options": { + "system": "Системная", + "light": "Светлая", + "dark": "Тёмная" + } + }, "overlay": { "transcribing": "Расшифровка...", "processing": "Обработка..." diff --git a/src/i18n/locales/sv/translation.json b/src/i18n/locales/sv/translation.json index dd839bbc66..6efe26a18c 100644 --- a/src/i18n/locales/sv/translation.json +++ b/src/i18n/locales/sv/translation.json @@ -614,6 +614,15 @@ "title": "Applikationsspråk", "description": "Ändra språket i Handy-gränssnittet" }, + "theme": { + "title": "Utseende", + "description": "Välj om Handy ska följa systemets tema eller förbli ljust eller mörkt", + "options": { + "system": "System", + "light": "Ljust", + "dark": "Mörkt" + } + }, "overlay": { "transcribing": "Transkriberar...", "processing": "Bearbetar..." diff --git a/src/i18n/locales/tr/translation.json b/src/i18n/locales/tr/translation.json index 1eb1f9f805..9ab6130bde 100644 --- a/src/i18n/locales/tr/translation.json +++ b/src/i18n/locales/tr/translation.json @@ -614,6 +614,15 @@ "title": "Uygulama Dili", "description": "Handy arayüzünün dilini değiştirin" }, + "theme": { + "title": "Görünüm", + "description": "Handy'nin sistem temasını izlemesini ya da açık veya koyu kalmasını seçin", + "options": { + "system": "Sistem", + "light": "Açık", + "dark": "Koyu" + } + }, "overlay": { "transcribing": "Transkribe ediliyor...", "processing": "İşleniyor..." diff --git a/src/i18n/locales/uk/translation.json b/src/i18n/locales/uk/translation.json index 5819215e28..759716dfd3 100644 --- a/src/i18n/locales/uk/translation.json +++ b/src/i18n/locales/uk/translation.json @@ -614,6 +614,15 @@ "title": "Мова інтерфейсу", "description": "Змінити мову інтерфейсу Handy" }, + "theme": { + "title": "Зовнішній вигляд", + "description": "Виберіть, чи має Handy слідувати темі системи, чи залишатися світлою або темною", + "options": { + "system": "Системна", + "light": "Світла", + "dark": "Темна" + } + }, "overlay": { "transcribing": "Обробка...", "processing": "Постобробка..." diff --git a/src/i18n/locales/vi/translation.json b/src/i18n/locales/vi/translation.json index 0f5860366d..4a5bd8e4a7 100644 --- a/src/i18n/locales/vi/translation.json +++ b/src/i18n/locales/vi/translation.json @@ -614,6 +614,15 @@ "title": "Ngôn ngữ ứng dụng", "description": "Thay đổi ngôn ngữ giao diện của Handy" }, + "theme": { + "title": "Giao diện", + "description": "Chọn xem Handy theo chủ đề hệ thống hay giữ ở chế độ sáng hoặc tối", + "options": { + "system": "Hệ thống", + "light": "Sáng", + "dark": "Tối" + } + }, "overlay": { "transcribing": "Đang chuyển đổi...", "processing": "Đang xử lý..." diff --git a/src/i18n/locales/zh-TW/translation.json b/src/i18n/locales/zh-TW/translation.json index 616377203f..b053582507 100644 --- a/src/i18n/locales/zh-TW/translation.json +++ b/src/i18n/locales/zh-TW/translation.json @@ -614,6 +614,15 @@ "title": "應用程式語言", "description": "變更 Handy 介面的語言" }, + "theme": { + "title": "外觀", + "description": "選擇 Handy 跟隨系統主題,或維持淺色或深色", + "options": { + "system": "跟隨系統", + "light": "淺色", + "dark": "深色" + } + }, "overlay": { "transcribing": "正在轉錄...", "processing": "處理中..." diff --git a/src/i18n/locales/zh/translation.json b/src/i18n/locales/zh/translation.json index 998a96b603..91276345a4 100644 --- a/src/i18n/locales/zh/translation.json +++ b/src/i18n/locales/zh/translation.json @@ -614,6 +614,15 @@ "title": "应用语言", "description": "更改 Handy 界面的语言" }, + "theme": { + "title": "外观", + "description": "选择 Handy 跟随系统主题,还是保持浅色或深色", + "options": { + "system": "跟随系统", + "light": "浅色", + "dark": "深色" + } + }, "overlay": { "transcribing": "正在转录...", "processing": "处理中..." From 2e27f9c81f63ea9cf69bb06c28c67f9702ff8e9f Mon Sep 17 00:00:00 2001 From: Erwann Mest Date: Fri, 10 Jul 2026 11:15:26 +0100 Subject: [PATCH 3/6] =?UTF-8?q?=F0=9F=8C=90=20fix(i18n):=20add=20missing?= =?UTF-8?q?=20theme=20keys=20to=20Nepali=20(ne)=20locale?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/i18n/locales/ne/translation.json | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/i18n/locales/ne/translation.json b/src/i18n/locales/ne/translation.json index 51c03f1493..bc12ec36c6 100644 --- a/src/i18n/locales/ne/translation.json +++ b/src/i18n/locales/ne/translation.json @@ -624,5 +624,14 @@ "overlay": { "transcribing": "ट्रान्सक्राइब गरिँदैछ...", "processing": "प्रशोधन गरिँदैछ..." + }, + "theme": { + "title": "रूप", + "description": "Handy ले तपाईंको प्रणालीको थिम अनुसरण गर्ने वा उज्यालो वा अँध्यारो रहने छनौट गर्नुहोस्।", + "options": { + "system": "प्रणाली", + "light": "उज्यालो", + "dark": "अँध्यारो" + } } } From 2785208d3be4de44fae46e3e7a8b3cea35b9c290 Mon Sep 17 00:00:00 2001 From: CJ Pais Date: Fri, 10 Jul 2026 18:15:56 +0800 Subject: [PATCH 4/6] translations and remove duplicated theme --- src/App.css | 18 ++++++-------- src/i18n/locales/ar/translation.json | 2 +- src/i18n/locales/bg/translation.json | 2 +- src/i18n/locales/cs/translation.json | 2 +- src/i18n/locales/de/translation.json | 2 +- src/i18n/locales/en/translation.json | 2 +- src/i18n/locales/es/translation.json | 2 +- src/i18n/locales/fr/translation.json | 2 +- src/i18n/locales/he/translation.json | 2 +- src/i18n/locales/it/translation.json | 2 +- src/i18n/locales/ja/translation.json | 2 +- src/i18n/locales/ko/translation.json | 2 +- src/i18n/locales/ne/translation.json | 9 +++++++ src/i18n/locales/nl/translation.json | 2 +- src/i18n/locales/pl/translation.json | 2 +- src/i18n/locales/pt/translation.json | 2 +- src/i18n/locales/ru/translation.json | 2 +- src/i18n/locales/sv/translation.json | 2 +- src/i18n/locales/tr/translation.json | 2 +- src/i18n/locales/uk/translation.json | 2 +- src/i18n/locales/vi/translation.json | 2 +- src/i18n/locales/zh-TW/translation.json | 2 +- src/i18n/locales/zh/translation.json | 2 +- src/styles/theme.css | 33 +++++++++++++++++++------ 24 files changed, 63 insertions(+), 39 deletions(-) diff --git a/src/App.css b/src/App.css index ba529bf401..650b809bd9 100644 --- a/src/App.css +++ b/src/App.css @@ -67,19 +67,17 @@ * `system` (or no attribute) leaves the OS in charge. */ :root[data-theme="light"] { - /* Colors - Light Theme */ - --color-text: #0f0f0f; - --color-background: #fbfbfb; - --color-logo-primary: #faa2ca; - --color-logo-stroke: #382731; + --color-text: var(--light-color-text); + --color-background: var(--light-color-background); + --color-logo-primary: var(--light-color-logo-primary); + --color-logo-stroke: var(--light-color-logo-stroke); } :root[data-theme="dark"] { - /* Colors - Dark Theme */ - --color-text: #fbfbfb; - --color-background: #2c2b29; - --color-logo-primary: #f28cbb; - --color-logo-stroke: #fad1ed; + --color-text: var(--dark-color-text); + --color-background: var(--dark-color-background); + --color-logo-primary: var(--dark-color-logo-primary); + --color-logo-stroke: var(--dark-color-logo-stroke); } /* macOS - tint native overlay scrollbar thumb */ diff --git a/src/i18n/locales/ar/translation.json b/src/i18n/locales/ar/translation.json index a3573bea5d..8062a9f869 100644 --- a/src/i18n/locales/ar/translation.json +++ b/src/i18n/locales/ar/translation.json @@ -619,7 +619,7 @@ "description": "تغيير لغة واجهة Handy" }, "theme": { - "title": "المظهر", + "title": "سمة التطبيق", "description": "اختر ما إذا كان Handy يتبع مظهر النظام أو يبقى فاتحًا أو داكنًا", "options": { "system": "النظام", diff --git a/src/i18n/locales/bg/translation.json b/src/i18n/locales/bg/translation.json index f44e7dea2a..302e5c1245 100644 --- a/src/i18n/locales/bg/translation.json +++ b/src/i18n/locales/bg/translation.json @@ -619,7 +619,7 @@ "description": "Промяна на езика на интерфейса на Handy" }, "theme": { - "title": "Външен вид", + "title": "Тема на приложението", "description": "Изберете дали Handy да следва системната тема, или да остане светла или тъмна", "options": { "system": "Системна", diff --git a/src/i18n/locales/cs/translation.json b/src/i18n/locales/cs/translation.json index 2fe8cf859f..cbdc75612a 100644 --- a/src/i18n/locales/cs/translation.json +++ b/src/i18n/locales/cs/translation.json @@ -619,7 +619,7 @@ "description": "Změňte jazyk rozhraní Handy" }, "theme": { - "title": "Vzhled", + "title": "Motiv aplikace", "description": "Zvolte, zda má Handy sledovat motiv systému, nebo zůstat světlý či tmavý", "options": { "system": "Systém", diff --git a/src/i18n/locales/de/translation.json b/src/i18n/locales/de/translation.json index 8e15914349..dcf04594c8 100644 --- a/src/i18n/locales/de/translation.json +++ b/src/i18n/locales/de/translation.json @@ -619,7 +619,7 @@ "description": "Sprache der Handy-Oberfläche ändern" }, "theme": { - "title": "Erscheinungsbild", + "title": "Anwendungsdesign", "description": "Wählen Sie, ob Handy dem Systemthema folgt oder hell bzw. dunkel bleibt", "options": { "system": "System", diff --git a/src/i18n/locales/en/translation.json b/src/i18n/locales/en/translation.json index 866e3c7c37..b1a8cc19b3 100644 --- a/src/i18n/locales/en/translation.json +++ b/src/i18n/locales/en/translation.json @@ -622,7 +622,7 @@ "description": "Change the language of the Handy interface" }, "theme": { - "title": "Appearance", + "title": "Application Theme", "description": "Choose whether Handy follows your system theme or stays light or dark", "options": { "system": "System", diff --git a/src/i18n/locales/es/translation.json b/src/i18n/locales/es/translation.json index 9e1787e749..ee34f55217 100644 --- a/src/i18n/locales/es/translation.json +++ b/src/i18n/locales/es/translation.json @@ -619,7 +619,7 @@ "description": "Cambia el idioma de la interfaz de Handy" }, "theme": { - "title": "Apariencia", + "title": "Tema de la aplicación", "description": "Elige si Handy sigue el tema del sistema o permanece claro u oscuro", "options": { "system": "Sistema", diff --git a/src/i18n/locales/fr/translation.json b/src/i18n/locales/fr/translation.json index e4aab00d5c..b4bb98a7ad 100644 --- a/src/i18n/locales/fr/translation.json +++ b/src/i18n/locales/fr/translation.json @@ -619,7 +619,7 @@ "description": "Changer la langue de l'interface de Handy" }, "theme": { - "title": "Apparence", + "title": "Thème de l'application", "description": "Choisissez si Handy suit le thème du système ou reste clair ou sombre", "options": { "system": "Système", diff --git a/src/i18n/locales/he/translation.json b/src/i18n/locales/he/translation.json index 31d572ea0e..dc3dcc8052 100644 --- a/src/i18n/locales/he/translation.json +++ b/src/i18n/locales/he/translation.json @@ -619,7 +619,7 @@ "description": "שנה את שפת הממשק של Handy" }, "theme": { - "title": "מראה", + "title": "ערכת נושא של האפליקציה", "description": "בחר אם Handy יעקוב אחר ערכת הנושא של המערכת או יישאר בהיר או כהה", "options": { "system": "מערכת", diff --git a/src/i18n/locales/it/translation.json b/src/i18n/locales/it/translation.json index 36e6cce88b..91df17b0ae 100644 --- a/src/i18n/locales/it/translation.json +++ b/src/i18n/locales/it/translation.json @@ -619,7 +619,7 @@ "description": "Cambia la lingua dell'interfaccia di Handy" }, "theme": { - "title": "Aspetto", + "title": "Tema dell'applicazione", "description": "Scegli se Handy segue il tema di sistema o resta chiaro o scuro", "options": { "system": "Sistema", diff --git a/src/i18n/locales/ja/translation.json b/src/i18n/locales/ja/translation.json index bc224eb465..560e798c0b 100644 --- a/src/i18n/locales/ja/translation.json +++ b/src/i18n/locales/ja/translation.json @@ -619,7 +619,7 @@ "description": "Handy インターフェースの言語を変更" }, "theme": { - "title": "外観", + "title": "アプリのテーマ", "description": "Handy をシステムのテーマに合わせるか、ライトまたはダークで固定するかを選択します", "options": { "system": "システム", diff --git a/src/i18n/locales/ko/translation.json b/src/i18n/locales/ko/translation.json index ba960048c4..8392596e37 100644 --- a/src/i18n/locales/ko/translation.json +++ b/src/i18n/locales/ko/translation.json @@ -619,7 +619,7 @@ "description": "Handy 인터페이스의 언어를 변경하세요" }, "theme": { - "title": "화면 모드", + "title": "앱 테마", "description": "Handy가 시스템 테마를 따를지, 밝게 또는 어둡게 유지할지 선택하세요", "options": { "system": "시스템", diff --git a/src/i18n/locales/ne/translation.json b/src/i18n/locales/ne/translation.json index 51c03f1493..6a4a7b2000 100644 --- a/src/i18n/locales/ne/translation.json +++ b/src/i18n/locales/ne/translation.json @@ -621,6 +621,15 @@ "title": "एपको भाषा", "description": "Handy इन्टरफेसको भाषा परिवर्तन गर्नुहोस्" }, + "theme": { + "title": "अनुप्रयोगको थिम", + "description": "Handy ले तपाईंको सिस्टम थिम अनुसरण गर्ने वा उज्यालो वा अँध्यारो रहने छान्नुहोस्", + "options": { + "system": "सिस्टम", + "light": "उज्यालो", + "dark": "अँध्यारो" + } + }, "overlay": { "transcribing": "ट्रान्सक्राइब गरिँदैछ...", "processing": "प्रशोधन गरिँदैछ..." diff --git a/src/i18n/locales/nl/translation.json b/src/i18n/locales/nl/translation.json index 0a84b744b2..9ebbfe5d70 100644 --- a/src/i18n/locales/nl/translation.json +++ b/src/i18n/locales/nl/translation.json @@ -622,7 +622,7 @@ "description": "Wijzig de taal van de Handy-interface" }, "theme": { - "title": "Weergave", + "title": "App-thema", "description": "Kies of Handy het systeemthema volgt of licht of donker blijft", "options": { "system": "Systeem", diff --git a/src/i18n/locales/pl/translation.json b/src/i18n/locales/pl/translation.json index c749043187..eab79049fb 100644 --- a/src/i18n/locales/pl/translation.json +++ b/src/i18n/locales/pl/translation.json @@ -619,7 +619,7 @@ "description": "Zmień język interfejsu Handy" }, "theme": { - "title": "Wygląd", + "title": "Motyw aplikacji", "description": "Wybierz, czy Handy ma podążać za motywem systemu, czy pozostać jasny lub ciemny", "options": { "system": "System", diff --git a/src/i18n/locales/pt/translation.json b/src/i18n/locales/pt/translation.json index 0d7cd1af42..6156a7a064 100644 --- a/src/i18n/locales/pt/translation.json +++ b/src/i18n/locales/pt/translation.json @@ -619,7 +619,7 @@ "description": "Alterar o idioma da interface do Handy" }, "theme": { - "title": "Aparência", + "title": "Tema do aplicativo", "description": "Escolha se o Handy segue o tema do sistema ou permanece claro ou escuro", "options": { "system": "Sistema", diff --git a/src/i18n/locales/ru/translation.json b/src/i18n/locales/ru/translation.json index 971820997f..06ef999b29 100644 --- a/src/i18n/locales/ru/translation.json +++ b/src/i18n/locales/ru/translation.json @@ -619,7 +619,7 @@ "description": "Изменить язык интерфейса Handy" }, "theme": { - "title": "Внешний вид", + "title": "Тема приложения", "description": "Выберите, следует ли Handy системной теме или остаётся светлым либо тёмным", "options": { "system": "Системная", diff --git a/src/i18n/locales/sv/translation.json b/src/i18n/locales/sv/translation.json index 9a6350b6c3..a86aadb18f 100644 --- a/src/i18n/locales/sv/translation.json +++ b/src/i18n/locales/sv/translation.json @@ -619,7 +619,7 @@ "description": "Ändra språket i Handy-gränssnittet" }, "theme": { - "title": "Utseende", + "title": "Apptema", "description": "Välj om Handy ska följa systemets tema eller förbli ljust eller mörkt", "options": { "system": "System", diff --git a/src/i18n/locales/tr/translation.json b/src/i18n/locales/tr/translation.json index 1e7840ce01..22d9e0b1ab 100644 --- a/src/i18n/locales/tr/translation.json +++ b/src/i18n/locales/tr/translation.json @@ -619,7 +619,7 @@ "description": "Handy arayüzünün dilini değiştirin" }, "theme": { - "title": "Görünüm", + "title": "Uygulama teması", "description": "Handy'nin sistem temasını izlemesini ya da açık veya koyu kalmasını seçin", "options": { "system": "Sistem", diff --git a/src/i18n/locales/uk/translation.json b/src/i18n/locales/uk/translation.json index dedbf72c97..5c0d913dea 100644 --- a/src/i18n/locales/uk/translation.json +++ b/src/i18n/locales/uk/translation.json @@ -619,7 +619,7 @@ "description": "Змінити мову інтерфейсу Handy" }, "theme": { - "title": "Зовнішній вигляд", + "title": "Тема застосунку", "description": "Виберіть, чи має Handy слідувати темі системи, чи залишатися світлою або темною", "options": { "system": "Системна", diff --git a/src/i18n/locales/vi/translation.json b/src/i18n/locales/vi/translation.json index 57c2c769ea..332a20b613 100644 --- a/src/i18n/locales/vi/translation.json +++ b/src/i18n/locales/vi/translation.json @@ -619,7 +619,7 @@ "description": "Thay đổi ngôn ngữ giao diện của Handy" }, "theme": { - "title": "Giao diện", + "title": "Giao diện ứng dụng", "description": "Chọn xem Handy theo chủ đề hệ thống hay giữ ở chế độ sáng hoặc tối", "options": { "system": "Hệ thống", diff --git a/src/i18n/locales/zh-TW/translation.json b/src/i18n/locales/zh-TW/translation.json index 7f5c61571b..c08e8504d3 100644 --- a/src/i18n/locales/zh-TW/translation.json +++ b/src/i18n/locales/zh-TW/translation.json @@ -619,7 +619,7 @@ "description": "變更 Handy 介面的語言" }, "theme": { - "title": "外觀", + "title": "應用程式主題", "description": "選擇 Handy 跟隨系統主題,或維持淺色或深色", "options": { "system": "跟隨系統", diff --git a/src/i18n/locales/zh/translation.json b/src/i18n/locales/zh/translation.json index 5368594318..a239dca7a4 100644 --- a/src/i18n/locales/zh/translation.json +++ b/src/i18n/locales/zh/translation.json @@ -619,7 +619,7 @@ "description": "更改 Handy 界面的语言" }, "theme": { - "title": "外观", + "title": "应用主题", "description": "选择 Handy 跟随系统主题,还是保持浅色或深色", "options": { "system": "跟随系统", diff --git a/src/styles/theme.css b/src/styles/theme.css index 401e00db29..2494b5b024 100644 --- a/src/styles/theme.css +++ b/src/styles/theme.css @@ -1,20 +1,37 @@ /* Handy Colors */ :root { - --color-text: #0f0f0f; - --color-background: #fbfbfb; + /* Palette source of truth. Each themed color is defined once as a + light/dark pair; the active tokens below and the `data-theme` overrides + in App.css reference these, so a hex value lives in exactly one place. */ + --light-color-text: #0f0f0f; + --light-color-background: #fbfbfb; + --light-color-logo-primary: #faa2ca; + --light-color-logo-stroke: #382731; + + --dark-color-text: #fbfbfb; + --dark-color-background: #2c2b29; + --dark-color-logo-primary: #f28cbb; + --dark-color-logo-stroke: #fad1ed; + + /* Tokens that do not change between light and dark */ --color-background-ui: #da5893; - --color-logo-primary: #faa2ca; - --color-logo-stroke: #382731; --color-text-stroke: #f6f6f6; --color-mid-gray: #808080; + + /* Active palette — defaults to light, follows the OS via the media query + below, or is forced by `data-theme` (see App.css). */ + --color-text: var(--light-color-text); + --color-background: var(--light-color-background); + --color-logo-primary: var(--light-color-logo-primary); + --color-logo-stroke: var(--light-color-logo-stroke); } @media (prefers-color-scheme: dark) { :root { - --color-text: #fbfbfb; - --color-background: #2c2b29; - --color-logo-primary: #f28cbb; - --color-logo-stroke: #fad1ed; + --color-text: var(--dark-color-text); + --color-background: var(--dark-color-background); + --color-logo-primary: var(--dark-color-logo-primary); + --color-logo-stroke: var(--dark-color-logo-stroke); } } From 5b1c7dbee43c1ec8a23365fe0a9b6204391dc806 Mon Sep 17 00:00:00 2001 From: CJ Pais Date: Fri, 10 Jul 2026 18:40:33 +0800 Subject: [PATCH 5/6] set tauri theme as well --- src-tauri/src/lib.rs | 5 +++++ src-tauri/src/shortcut/mod.rs | 18 ++++++++++++++++++ src/i18n/locales/ne/translation.json | 9 --------- 3 files changed, 23 insertions(+), 9 deletions(-) diff --git a/src-tauri/src/lib.rs b/src-tauri/src/lib.rs index b379dc3eca..83aa2b6c71 100644 --- a/src-tauri/src/lib.rs +++ b/src-tauri/src/lib.rs @@ -817,6 +817,11 @@ pub fn run(cli_args: CliArgs) { let mut settings = get_settings(app.handle()); + // Apply the persisted appearance theme to the window chrome (e.g. the + // Windows title bar) before it is shown, so it matches the in-app + // palette without a flash of the wrong theme. + shortcut::apply_window_theme(app.handle(), settings.theme); + // CLI --debug flag overrides debug_mode and log level (runtime-only, not persisted) if cli_args.debug { settings.debug_mode = true; diff --git a/src-tauri/src/shortcut/mod.rs b/src-tauri/src/shortcut/mod.rs index e655046f13..02e4ab4192 100644 --- a/src-tauri/src/shortcut/mod.rs +++ b/src-tauri/src/shortcut/mod.rs @@ -529,9 +529,27 @@ pub fn change_theme_setting(app: AppHandle, theme: String) -> Result<(), String> }; settings.theme = parsed; settings::write_settings(&app, settings); + apply_window_theme(&app, parsed); Ok(()) } +/// Applies the appearance setting to the OS window chrome (e.g. the Windows +/// title bar), which CSS `data-theme` cannot reach. `System` clears the override +/// so the window follows the OS. Call this on startup and whenever the setting +/// changes to keep the title bar in sync with the in-app palette. +pub fn apply_window_theme(app: &AppHandle, theme: Theme) { + let window_theme = match theme { + Theme::System => None, + Theme::Light => Some(tauri::Theme::Light), + Theme::Dark => Some(tauri::Theme::Dark), + }; + if let Some(window) = app.get_webview_window("main") { + if let Err(e) = window.set_theme(window_theme) { + warn!("Failed to apply window theme: {}", e); + } + } +} + #[tauri::command] #[specta::specta] pub fn change_translate_to_english_setting(app: AppHandle, enabled: bool) -> Result<(), String> { diff --git a/src/i18n/locales/ne/translation.json b/src/i18n/locales/ne/translation.json index 0384973d45..6a4a7b2000 100644 --- a/src/i18n/locales/ne/translation.json +++ b/src/i18n/locales/ne/translation.json @@ -633,14 +633,5 @@ "overlay": { "transcribing": "ट्रान्सक्राइब गरिँदैछ...", "processing": "प्रशोधन गरिँदैछ..." - }, - "theme": { - "title": "रूप", - "description": "Handy ले तपाईंको प्रणालीको थिम अनुसरण गर्ने वा उज्यालो वा अँध्यारो रहने छनौट गर्नुहोस्।", - "options": { - "system": "प्रणाली", - "light": "उज्यालो", - "dark": "अँध्यारो" - } } } From 474045667665d573d710a4880d2695762a435986 Mon Sep 17 00:00:00 2001 From: CJ Pais Date: Fri, 10 Jul 2026 18:58:01 +0800 Subject: [PATCH 6/6] scope tauri sys theme to windows only --- src-tauri/src/lib.rs | 8 +++++--- src-tauri/src/shortcut/mod.rs | 10 ++++++---- 2 files changed, 11 insertions(+), 7 deletions(-) diff --git a/src-tauri/src/lib.rs b/src-tauri/src/lib.rs index 83aa2b6c71..31c0cae77c 100644 --- a/src-tauri/src/lib.rs +++ b/src-tauri/src/lib.rs @@ -817,9 +817,11 @@ pub fn run(cli_args: CliArgs) { let mut settings = get_settings(app.handle()); - // Apply the persisted appearance theme to the window chrome (e.g. the - // Windows title bar) before it is shown, so it matches the in-app - // palette without a flash of the wrong theme. + // Apply the persisted appearance theme to the Windows title bar before + // the window is shown, so it matches the in-app palette without a flash + // of the wrong theme. On macOS/Linux, Tauri themes are app-wide and + // would also affect windows that intentionally keep the system theme. + #[cfg(target_os = "windows")] shortcut::apply_window_theme(app.handle(), settings.theme); // CLI --debug flag overrides debug_mode and log level (runtime-only, not persisted) diff --git a/src-tauri/src/shortcut/mod.rs b/src-tauri/src/shortcut/mod.rs index 02e4ab4192..e20edfc26f 100644 --- a/src-tauri/src/shortcut/mod.rs +++ b/src-tauri/src/shortcut/mod.rs @@ -529,14 +529,16 @@ pub fn change_theme_setting(app: AppHandle, theme: String) -> Result<(), String> }; settings.theme = parsed; settings::write_settings(&app, settings); + #[cfg(target_os = "windows")] apply_window_theme(&app, parsed); Ok(()) } -/// Applies the appearance setting to the OS window chrome (e.g. the Windows -/// title bar), which CSS `data-theme` cannot reach. `System` clears the override -/// so the window follows the OS. Call this on startup and whenever the setting -/// changes to keep the title bar in sync with the in-app palette. +/// Applies the appearance setting to the Windows title bar, which CSS +/// `data-theme` cannot reach. `System` clears the override so the window follows +/// Windows. Call this on startup and whenever the setting changes to keep the +/// title bar in sync with the in-app palette. +#[cfg(target_os = "windows")] pub fn apply_window_theme(app: &AppHandle, theme: Theme) { let window_theme = match theme { Theme::System => None,