From 28d1d814b7351fcf3fb5041f3a5164a8fd1b429d Mon Sep 17 00:00:00 2001 From: CJ Pais Date: Mon, 19 Jan 2026 10:33:04 +0800 Subject: [PATCH] add experimental features to ui + reorg everything (#620) --- src-tauri/src/lib.rs | 1 + src-tauri/src/settings.rs | 3 ++ src-tauri/src/shortcut.rs | 9 +++++ src/bindings.ts | 10 +++++- .../settings/ExperimentalToggle.tsx | 30 ++++++++++++++++ .../settings/about/AboutSettings.tsx | 22 ++++++------ .../settings/advanced/AdvancedSettings.tsx | 35 +++++++++++++++++-- .../settings/debug/DebugSettings.tsx | 15 -------- .../settings/general/GeneralSettings.tsx | 2 ++ src/i18n/locales/cs/translation.json | 19 +++++++--- src/i18n/locales/de/translation.json | 19 +++++++--- src/i18n/locales/en/translation.json | 19 +++++++--- src/i18n/locales/es/translation.json | 19 +++++++--- src/i18n/locales/fr/translation.json | 19 +++++++--- src/i18n/locales/it/translation.json | 19 +++++++--- src/i18n/locales/ja/translation.json | 19 +++++++--- src/i18n/locales/pl/translation.json | 19 +++++++--- src/i18n/locales/pt/translation.json | 19 +++++++--- src/i18n/locales/ru/translation.json | 19 +++++++--- src/i18n/locales/tr/translation.json | 19 +++++++--- src/i18n/locales/uk/translation.json | 19 +++++++--- src/i18n/locales/vi/translation.json | 19 +++++++--- src/i18n/locales/zh/translation.json | 19 +++++++--- src/stores/settingsStore.ts | 2 ++ 24 files changed, 311 insertions(+), 84 deletions(-) create mode 100644 src/components/settings/ExperimentalToggle.tsx diff --git a/src-tauri/src/lib.rs b/src-tauri/src/lib.rs index 537b3e2..ffc630d 100644 --- a/src-tauri/src/lib.rs +++ b/src-tauri/src/lib.rs @@ -252,6 +252,7 @@ pub fn run() { shortcut::change_paste_method_setting, shortcut::change_clipboard_handling_setting, shortcut::change_post_process_enabled_setting, + shortcut::change_experimental_enabled_setting, shortcut::change_post_process_base_url_setting, shortcut::change_post_process_api_key_setting, shortcut::change_post_process_model_setting, diff --git a/src-tauri/src/settings.rs b/src-tauri/src/settings.rs index 1bd0d71..ffe2e31 100644 --- a/src-tauri/src/settings.rs +++ b/src-tauri/src/settings.rs @@ -293,6 +293,8 @@ pub struct AppSettings { pub append_trailing_space: bool, #[serde(default = "default_app_language")] pub app_language: String, + #[serde(default)] + pub experimental_enabled: bool, } fn default_model() -> String { @@ -581,6 +583,7 @@ pub fn get_default_settings() -> AppSettings { mute_while_recording: false, append_trailing_space: false, app_language: default_app_language(), + experimental_enabled: false, } } diff --git a/src-tauri/src/shortcut.rs b/src-tauri/src/shortcut.rs index a827ad8..d290943 100644 --- a/src-tauri/src/shortcut.rs +++ b/src-tauri/src/shortcut.rs @@ -373,6 +373,15 @@ pub fn change_post_process_enabled_setting(app: AppHandle, enabled: bool) -> Res Ok(()) } +#[tauri::command] +#[specta::specta] +pub fn change_experimental_enabled_setting(app: AppHandle, enabled: bool) -> Result<(), String> { + let mut settings = settings::get_settings(&app); + settings.experimental_enabled = enabled; + settings::write_settings(&app, settings); + Ok(()) +} + #[tauri::command] #[specta::specta] pub fn change_post_process_base_url_setting( diff --git a/src/bindings.ts b/src/bindings.ts index e7f1640..b7ea634 100644 --- a/src/bindings.ts +++ b/src/bindings.ts @@ -133,6 +133,14 @@ async changePostProcessEnabledSetting(enabled: boolean) : Promise> { + try { + return { status: "ok", data: await TAURI_INVOKE("change_experimental_enabled_setting", { enabled }) }; +} catch (e) { + if(e instanceof Error) throw e; + else return { status: "error", error: e as any }; +} +}, async changePostProcessBaseUrlSetting(providerId: string, baseUrl: string) : Promise> { try { return { status: "ok", data: await TAURI_INVOKE("change_post_process_base_url_setting", { providerId, baseUrl }) }; @@ -640,7 +648,7 @@ async isLaptop() : Promise> { /** user-defined types **/ -export type AppSettings = { bindings: Partial<{ [key in string]: ShortcutBinding }>; push_to_talk: boolean; audio_feedback: boolean; audio_feedback_volume?: number; sound_theme?: SoundTheme; start_hidden?: boolean; autostart_enabled?: boolean; update_checks_enabled?: boolean; selected_model?: string; 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; post_process_enabled?: boolean; post_process_provider_id?: string; post_process_providers?: PostProcessProvider[]; post_process_api_keys?: Partial<{ [key in string]: string }>; 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 } +export type AppSettings = { bindings: Partial<{ [key in string]: ShortcutBinding }>; push_to_talk: boolean; audio_feedback: boolean; audio_feedback_volume?: number; sound_theme?: SoundTheme; start_hidden?: boolean; autostart_enabled?: boolean; update_checks_enabled?: boolean; selected_model?: string; 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; post_process_enabled?: boolean; post_process_provider_id?: string; post_process_providers?: PostProcessProvider[]; post_process_api_keys?: Partial<{ [key in string]: string }>; 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 } export type AudioDevice = { index: string; name: string; is_default: boolean } export type BindingResponse = { success: boolean; binding: ShortcutBinding | null; error: string | null } export type ClipboardHandling = "dont_modify" | "copy_to_clipboard" diff --git a/src/components/settings/ExperimentalToggle.tsx b/src/components/settings/ExperimentalToggle.tsx new file mode 100644 index 0000000..ce1ee23 --- /dev/null +++ b/src/components/settings/ExperimentalToggle.tsx @@ -0,0 +1,30 @@ +import React from "react"; +import { useTranslation } from "react-i18next"; +import { ToggleSwitch } from "../ui/ToggleSwitch"; +import { useSettings } from "../../hooks/useSettings"; + +interface ExperimentalToggleProps { + descriptionMode?: "inline" | "tooltip"; + grouped?: boolean; +} + +export const ExperimentalToggle: React.FC = React.memo( + ({ descriptionMode = "tooltip", grouped = false }) => { + const { t } = useTranslation(); + const { getSetting, updateSetting, isUpdating } = useSettings(); + + const enabled = getSetting("experimental_enabled") || false; + + return ( + updateSetting("experimental_enabled", enabled)} + isUpdating={isUpdating("experimental_enabled")} + label={t("settings.advanced.experimentalToggle.label")} + description={t("settings.advanced.experimentalToggle.description")} + descriptionMode={descriptionMode} + grouped={grouped} + /> + ); + }, +); diff --git a/src/components/settings/about/AboutSettings.tsx b/src/components/settings/about/AboutSettings.tsx index 4246f98..7909617 100644 --- a/src/components/settings/about/AboutSettings.tsx +++ b/src/components/settings/about/AboutSettings.tsx @@ -7,6 +7,7 @@ import { SettingContainer } from "../../ui/SettingContainer"; import { Button } from "../../ui/Button"; import { AppDataDirectory } from "../AppDataDirectory"; import { AppLanguageSelector } from "../AppLanguageSelector"; +import { LogDirectory } from "../debug"; export const AboutSettings: React.FC = () => { const { t } = useTranslation(); @@ -47,7 +48,15 @@ export const AboutSettings: React.FC = () => { v{version} - + + + { - - - + + diff --git a/src/components/settings/advanced/AdvancedSettings.tsx b/src/components/settings/advanced/AdvancedSettings.tsx index 779bb97..755247d 100644 --- a/src/components/settings/advanced/AdvancedSettings.tsx +++ b/src/components/settings/advanced/AdvancedSettings.tsx @@ -10,27 +10,58 @@ import { AutostartToggle } from "../AutostartToggle"; import { PasteMethodSetting } from "../PasteMethod"; import { ClipboardHandlingSetting } from "../ClipboardHandling"; import { useModelStore } from "../../../stores/modelStore"; +import { PostProcessingToggle } from "../PostProcessingToggle"; +import { AppendTrailingSpace } from "../AppendTrailingSpace"; +import { HistoryLimit } from "../HistoryLimit"; +import { RecordingRetentionPeriodSelector } from "../RecordingRetentionPeriod"; +import { ExperimentalToggle } from "../ExperimentalToggle"; +import { useSettings } from "../../../hooks/useSettings"; export const AdvancedSettings: React.FC = () => { const { t } = useTranslation(); const { currentModel, getModelInfo } = useModelStore(); + const { getSetting } = useSettings(); const currentModelInfo = getModelInfo(currentModel); const showTranslateToEnglish = currentModelInfo?.engine_type === "Whisper" && currentModel !== "turbo"; + const experimentalEnabled = getSetting("experimental_enabled") || false; + return (
- + + + + + + + + + {showTranslateToEnglish && ( )} - + + + + + + + + {experimentalEnabled && ( + + + + )}
); }; diff --git a/src/components/settings/debug/DebugSettings.tsx b/src/components/settings/debug/DebugSettings.tsx index 4914010..630ef7f 100644 --- a/src/components/settings/debug/DebugSettings.tsx +++ b/src/components/settings/debug/DebugSettings.tsx @@ -2,16 +2,10 @@ import React from "react"; import { useTranslation } from "react-i18next"; import { type } from "@tauri-apps/plugin-os"; import { WordCorrectionThreshold } from "./WordCorrectionThreshold"; -import { LogDirectory } from "./LogDirectory"; import { LogLevelSelector } from "./LogLevelSelector"; import { SettingsGroup } from "../../ui/SettingsGroup"; -import { HistoryLimit } from "../HistoryLimit"; import { AlwaysOnMicrophone } from "../AlwaysOnMicrophone"; import { SoundPicker } from "../SoundPicker"; -import { PostProcessingToggle } from "../PostProcessingToggle"; -import { MuteWhileRecording } from "../MuteWhileRecording"; -import { AppendTrailingSpace } from "../AppendTrailingSpace"; -import { RecordingRetentionPeriodSelector } from "../RecordingRetentionPeriod"; import { ClamshellMicrophoneSelector } from "../ClamshellMicrophoneSelector"; import { HandyShortcut } from "../HandyShortcut"; import { UpdateChecksToggle } from "../UpdateChecksToggle"; @@ -26,7 +20,6 @@ export const DebugSettings: React.FC = () => { return (
- { description={t("settings.debug.soundTheme.description")} /> - - - - - {/* Cancel shortcut is disabled on Linux due to instability with dynamic shortcut registration */} {!isLinux && ( { const { t } = useTranslation(); @@ -28,6 +29,7 @@ export const GeneralSettings: React.FC = () => { + commands.setLogLevel(value as any), app_language: (value) => commands.changeAppLanguageSetting(value as string), + experimental_enabled: (value) => + commands.changeExperimentalEnabledSetting(value as boolean), }; export const useSettingsStore = create()(