From b02873dab5a93cad4babfc589be7fd5f949c6a87 Mon Sep 17 00:00:00 2001 From: Phuoc Thinh Vu <163667116+kakapt@users.noreply.github.com> Date: Wed, 11 Feb 2026 14:00:29 +0700 Subject: [PATCH] feat(linux): Add option to select typing tool (#760) * feat(linux): Add option to select typing tool - Added a Linux-only typing tool setting shown for direct paste, with a new dropdown in advanced settings. - Wired new typing tool setting through settings store, bindings, and Tauri commands. - Added typing tool enum/defaults and selection handling in Linux clipboard direct typing. - Added translations for the new typing tool setting across locales. * fix compilation on macos * format * only list available tools * format --------- Co-authored-by: Thinh Vu Co-authored-by: CJ Pais --- src-tauri/src/clipboard.rs | 78 +++++++++++++++++- src-tauri/src/lib.rs | 2 + src-tauri/src/settings.rs | 24 ++++++ src-tauri/src/shortcut/mod.rs | 38 ++++++++- src/bindings.ts | 14 +++- src/components/settings/TypingTool.tsx | 82 +++++++++++++++++++ .../settings/advanced/AdvancedSettings.tsx | 2 + src/i18n/locales/ar/translation.json | 7 ++ src/i18n/locales/cs/translation.json | 7 ++ src/i18n/locales/de/translation.json | 7 ++ src/i18n/locales/en/translation.json | 7 ++ src/i18n/locales/es/translation.json | 7 ++ src/i18n/locales/fr/translation.json | 7 ++ src/i18n/locales/it/translation.json | 7 ++ src/i18n/locales/ja/translation.json | 7 ++ src/i18n/locales/ko/translation.json | 7 ++ src/i18n/locales/pl/translation.json | 7 ++ src/i18n/locales/pt/translation.json | 7 ++ src/i18n/locales/ru/translation.json | 7 ++ src/i18n/locales/tr/translation.json | 7 ++ src/i18n/locales/uk/translation.json | 7 ++ src/i18n/locales/vi/translation.json | 7 ++ src/i18n/locales/zh/translation.json | 7 ++ src/stores/settingsStore.ts | 1 + 24 files changed, 346 insertions(+), 7 deletions(-) create mode 100644 src/components/settings/TypingTool.tsx diff --git a/src-tauri/src/clipboard.rs b/src-tauri/src/clipboard.rs index f9b799f..51c093f 100644 --- a/src-tauri/src/clipboard.rs +++ b/src-tauri/src/clipboard.rs @@ -1,4 +1,6 @@ use crate::input::{self, EnigoState}; +#[cfg(target_os = "linux")] +use crate::settings::TypingTool; use crate::settings::{get_settings, AutoSubmitKey, ClipboardHandling, PasteMethod}; use enigo::{Direction, Enigo, Key, Keyboard}; use log::info; @@ -119,7 +121,43 @@ fn try_send_key_combo_linux(paste_method: &PasteMethod) -> Result /// Attempts to type text directly using Linux-native tools. /// Returns `Ok(true)` if a native tool handled it, `Ok(false)` to fall back to enigo. #[cfg(target_os = "linux")] -fn try_direct_typing_linux(text: &str) -> Result { +fn try_direct_typing_linux(text: &str, preferred_tool: TypingTool) -> Result { + // If user specified a tool, try only that one + if preferred_tool != TypingTool::Auto { + return match preferred_tool { + TypingTool::Wtype if is_wtype_available() => { + info!("Using user-specified wtype"); + type_text_via_wtype(text)?; + Ok(true) + } + TypingTool::Kwtype if is_kwtype_available() => { + info!("Using user-specified kwtype"); + type_text_via_kwtype(text)?; + Ok(true) + } + TypingTool::Dotool if is_dotool_available() => { + info!("Using user-specified dotool"); + type_text_via_dotool(text)?; + Ok(true) + } + TypingTool::Ydotool if is_ydotool_available() => { + info!("Using user-specified ydotool"); + type_text_via_ydotool(text)?; + Ok(true) + } + TypingTool::Xdotool if is_xdotool_available() => { + info!("Using user-specified xdotool"); + type_text_via_xdotool(text)?; + Ok(true) + } + _ => Err(format!( + "Typing tool {:?} is not available on this system", + preferred_tool + )), + }; + } + + // Auto mode - existing fallback chain if is_wayland() { // KDE Wayland: prefer kwtype (uses KDE Fake Input protocol, supports umlauts) if is_kde_wayland() && is_kwtype_available() { @@ -161,6 +199,29 @@ fn try_direct_typing_linux(text: &str) -> Result { Ok(false) } +/// Returns the list of available typing tools on this system. +/// Always includes "auto" as the first entry. +#[cfg(target_os = "linux")] +pub fn get_available_typing_tools() -> Vec { + let mut tools = vec!["auto".to_string()]; + if is_wtype_available() { + tools.push("wtype".to_string()); + } + if is_kwtype_available() { + tools.push("kwtype".to_string()); + } + if is_dotool_available() { + tools.push("dotool".to_string()); + } + if is_ydotool_available() { + tools.push("ydotool".to_string()); + } + if is_xdotool_available() { + tools.push("xdotool".to_string()); + } + tools +} + /// Check if wtype is available (Wayland text input tool) #[cfg(target_os = "linux")] fn is_wtype_available() -> bool { @@ -434,10 +495,14 @@ fn send_key_combo_via_xdotool(paste_method: &PasteMethod) -> Result<(), String> } /// Types text directly by simulating individual key presses. -fn paste_direct(enigo: &mut Enigo, text: &str) -> Result<(), String> { +fn paste_direct( + enigo: &mut Enigo, + text: &str, + #[cfg(target_os = "linux")] typing_tool: TypingTool, +) -> Result<(), String> { #[cfg(target_os = "linux")] { - if try_direct_typing_linux(text)? { + if try_direct_typing_linux(text, typing_tool)? { return Ok(()); } info!("Falling back to enigo for direct text input"); @@ -525,7 +590,12 @@ pub fn paste(text: String, app_handle: AppHandle) -> Result<(), String> { info!("PasteMethod::None selected - skipping paste action"); } PasteMethod::Direct => { - paste_direct(&mut enigo, &text)?; + paste_direct( + &mut enigo, + &text, + #[cfg(target_os = "linux")] + settings.typing_tool, + )?; } PasteMethod::CtrlV | PasteMethod::CtrlShiftV | PasteMethod::ShiftInsert => { paste_via_clipboard( diff --git a/src-tauri/src/lib.rs b/src-tauri/src/lib.rs index deeefea..7d00fc9 100644 --- a/src-tauri/src/lib.rs +++ b/src-tauri/src/lib.rs @@ -274,6 +274,8 @@ pub fn run() { shortcut::change_debug_mode_setting, shortcut::change_word_correction_threshold_setting, shortcut::change_paste_method_setting, + shortcut::get_available_typing_tools, + shortcut::change_typing_tool_setting, shortcut::change_clipboard_handling_setting, shortcut::change_auto_submit_setting, shortcut::change_auto_submit_key_setting, diff --git a/src-tauri/src/settings.rs b/src-tauri/src/settings.rs index 4fe66c8..aac7e92 100644 --- a/src-tauri/src/settings.rs +++ b/src-tauri/src/settings.rs @@ -255,6 +255,23 @@ impl SoundTheme { } } +#[derive(Serialize, Deserialize, Debug, Clone, Copy, PartialEq, Eq, Type)] +#[serde(rename_all = "snake_case")] +pub enum TypingTool { + Auto, + Wtype, + Kwtype, + Dotool, + Ydotool, + Xdotool, +} + +impl Default for TypingTool { + fn default() -> Self { + TypingTool::Auto + } +} + /* still handy for composing the initial JSON in the store ------------- */ #[derive(Serialize, Deserialize, Debug, Clone, Type)] pub struct AppSettings { @@ -337,6 +354,8 @@ pub struct AppSettings { pub show_tray_icon: bool, #[serde(default = "default_paste_delay_ms")] pub paste_delay_ms: u64, + #[serde(default = "default_typing_tool")] + pub typing_tool: TypingTool, } fn default_model() -> String { @@ -528,6 +547,10 @@ fn default_post_process_prompts() -> Vec { }] } +fn default_typing_tool() -> TypingTool { + TypingTool::Auto +} + fn ensure_post_process_defaults(settings: &mut AppSettings) -> bool { let mut changed = false; for provider in default_post_process_providers() { @@ -663,6 +686,7 @@ pub fn get_default_settings() -> AppSettings { keyboard_implementation: KeyboardImplementation::default(), show_tray_icon: default_show_tray_icon(), paste_delay_ms: default_paste_delay_ms(), + typing_tool: default_typing_tool(), } } diff --git a/src-tauri/src/shortcut/mod.rs b/src-tauri/src/shortcut/mod.rs index 19056cf..757c1f0 100644 --- a/src-tauri/src/shortcut/mod.rs +++ b/src-tauri/src/shortcut/mod.rs @@ -21,8 +21,8 @@ use tauri_plugin_autostart::ManagerExt; use crate::settings::{ self, get_settings, AutoSubmitKey, ClipboardHandling, KeyboardImplementation, LLMPrompt, - OverlayPosition, PasteMethod, ShortcutBinding, SoundTheme, APPLE_INTELLIGENCE_DEFAULT_MODEL_ID, - APPLE_INTELLIGENCE_PROVIDER_ID, + OverlayPosition, PasteMethod, ShortcutBinding, SoundTheme, TypingTool, + APPLE_INTELLIGENCE_DEFAULT_MODEL_ID, APPLE_INTELLIGENCE_PROVIDER_ID, }; use crate::tray; @@ -678,6 +678,40 @@ pub fn change_paste_method_setting(app: AppHandle, method: String) -> Result<(), Ok(()) } +#[tauri::command] +#[specta::specta] +pub fn get_available_typing_tools() -> Vec { + #[cfg(target_os = "linux")] + { + crate::clipboard::get_available_typing_tools() + } + #[cfg(not(target_os = "linux"))] + { + vec!["auto".to_string()] + } +} + +#[tauri::command] +#[specta::specta] +pub fn change_typing_tool_setting(app: AppHandle, tool: String) -> Result<(), String> { + let mut settings = settings::get_settings(&app); + let parsed = match tool.as_str() { + "auto" => TypingTool::Auto, + "wtype" => TypingTool::Wtype, + "kwtype" => TypingTool::Kwtype, + "dotool" => TypingTool::Dotool, + "ydotool" => TypingTool::Ydotool, + "xdotool" => TypingTool::Xdotool, + other => { + warn!("Invalid typing tool '{}', defaulting to auto", other); + TypingTool::Auto + } + }; + settings.typing_tool = parsed; + settings::write_settings(&app, settings); + Ok(()) +} + #[tauri::command] #[specta::specta] pub fn change_clipboard_handling_setting(app: AppHandle, handling: String) -> Result<(), String> { diff --git a/src/bindings.ts b/src/bindings.ts index 1a6e731..80c9ca3 100644 --- a/src/bindings.ts +++ b/src/bindings.ts @@ -117,6 +117,17 @@ async changePasteMethodSetting(method: string) : Promise> { else return { status: "error", error: e as any }; } }, +async getAvailableTypingTools() : Promise { + return await TAURI_INVOKE("get_available_typing_tools"); +}, +async changeTypingToolSetting(tool: string) : Promise> { + try { + return { status: "ok", data: await TAURI_INVOKE("change_typing_tool_setting", { tool }) }; +} catch (e) { + if(e instanceof Error) throw e; + else return { status: "error", error: e as any }; +} +}, async changeClipboardHandlingSetting(handling: string) : Promise> { try { return { status: "ok", data: await TAURI_INVOKE("change_clipboard_handling_setting", { handling }) }; @@ -719,7 +730,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; auto_submit?: boolean; auto_submit_key?: AutoSubmitKey; 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; keyboard_implementation?: KeyboardImplementation; show_tray_icon?: boolean; paste_delay_ms?: number } +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; auto_submit?: boolean; auto_submit_key?: AutoSubmitKey; 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; keyboard_implementation?: KeyboardImplementation; show_tray_icon?: boolean; paste_delay_ms?: number; typing_tool?: TypingTool } export type AudioDevice = { index: string; name: string; is_default: boolean } export type AutoSubmitKey = "enter" | "ctrl_enter" | "cmd_enter" export type BindingResponse = { success: boolean; binding: ShortcutBinding | null; error: string | null } @@ -747,6 +758,7 @@ export type PostProcessProvider = { id: string; label: string; base_url: string; export type RecordingRetentionPeriod = "never" | "preserve_limit" | "days_3" | "weeks_2" | "months_3" export type ShortcutBinding = { id: string; name: string; description: string; default_binding: string; current_binding: string } export type SoundTheme = "marimba" | "pop" | "custom" +export type TypingTool = "auto" | "wtype" | "kwtype" | "dotool" | "ydotool" | "xdotool" /** tauri-specta globals **/ diff --git a/src/components/settings/TypingTool.tsx b/src/components/settings/TypingTool.tsx new file mode 100644 index 0000000..0e9e3ae --- /dev/null +++ b/src/components/settings/TypingTool.tsx @@ -0,0 +1,82 @@ +import React, { useEffect, useState } from "react"; +import { useTranslation } from "react-i18next"; +import { Dropdown } from "../ui/Dropdown"; +import { SettingContainer } from "../ui/SettingContainer"; +import { useSettings } from "../../hooks/useSettings"; +import { useOsType } from "../../hooks/useOsType"; +import { commands } from "@/bindings"; +import type { TypingTool } from "@/bindings"; + +interface TypingToolProps { + descriptionMode?: "inline" | "tooltip"; + grouped?: boolean; +} + +const allToolLabels: Record = { + wtype: "wtype", + kwtype: "kwtype", + dotool: "dotool", + ydotool: "ydotool", + xdotool: "xdotool", +}; + +export const TypingToolSetting: React.FC = React.memo( + ({ descriptionMode = "tooltip", grouped = false }) => { + const { t } = useTranslation(); + const { getSetting, updateSetting, isUpdating } = useSettings(); + const osType = useOsType(); + const [availableTools, setAvailableTools] = useState(null); + + useEffect(() => { + if (osType !== "linux") return; + commands + .getAvailableTypingTools() + .then(setAvailableTools) + .catch(() => { + setAvailableTools(["auto"]); + }); + }, [osType]); + + // Only show this setting on Linux + if (osType !== "linux") { + return null; + } + + // Only show if paste method is "direct" + const pasteMethod = getSetting("paste_method"); + if (pasteMethod !== "direct") { + return null; + } + + const tools = availableTools ?? ["auto"]; + const typingToolOptions = tools.map((tool) => + tool === "auto" + ? { + value: "auto", + label: t("settings.advanced.typingTool.options.auto"), + } + : { value: tool, label: allToolLabels[tool] ?? tool }, + ); + + const selectedTool = (getSetting("typing_tool") || "auto") as TypingTool; + + return ( + + + updateSetting("typing_tool", value as TypingTool) + } + disabled={isUpdating("typing_tool")} + /> + + ); + }, +); diff --git a/src/components/settings/advanced/AdvancedSettings.tsx b/src/components/settings/advanced/AdvancedSettings.tsx index e5c6185..6b7bc3a 100644 --- a/src/components/settings/advanced/AdvancedSettings.tsx +++ b/src/components/settings/advanced/AdvancedSettings.tsx @@ -8,6 +8,7 @@ import { StartHidden } from "../StartHidden"; import { AutostartToggle } from "../AutostartToggle"; import { ShowTrayIcon } from "../ShowTrayIcon"; import { PasteMethodSetting } from "../PasteMethod"; +import { TypingToolSetting } from "../TypingTool"; import { ClipboardHandlingSetting } from "../ClipboardHandling"; import { AutoSubmit } from "../AutoSubmit"; import { PostProcessingToggle } from "../PostProcessingToggle"; @@ -36,6 +37,7 @@ export const AdvancedSettings: React.FC = () => { + diff --git a/src/i18n/locales/ar/translation.json b/src/i18n/locales/ar/translation.json index 0a5aadd..c51c8cc 100644 --- a/src/i18n/locales/ar/translation.json +++ b/src/i18n/locales/ar/translation.json @@ -222,6 +222,13 @@ "none": "بلا" } }, + "typingTool": { + "title": "أداة الكتابة", + "description": ".اختر أداة الكتابة في Linux لاستخدامها مع طريقة اللصق المباشر. سيكتشف \"تلقائي\" تلقائياً أفضل أداة متاحة لنظامك ويستخدمها", + "options": { + "auto": "تلقائي (موصى به)" + } + }, "clipboardHandling": { "title": "التعامل مع الحافظة", "description": ".'عدم تعديل الحافظة' يحافظ على محتويات حافظتك الحالية بعد التفريغ. 'نسخ إلى الحافظة' يترك نتيجة التفريغ في حافظتك بعد اللصق", diff --git a/src/i18n/locales/cs/translation.json b/src/i18n/locales/cs/translation.json index 9fc99a4..56e3420 100644 --- a/src/i18n/locales/cs/translation.json +++ b/src/i18n/locales/cs/translation.json @@ -244,6 +244,13 @@ "none": "Žádné" } }, + "typingTool": { + "title": "Nástroj pro psaní", + "description": "Vyberte, který linuxový nástroj pro psaní použít pro metodu přímého vložení. Auto automaticky zjistí a použije nejlepší dostupný nástroj pro váš systém.", + "options": { + "auto": "Auto (Doporučeno)" + } + }, "clipboardHandling": { "title": "Práce se schránkou", "description": "'Neměnit schránku' zachová po přepisu aktuální obsah schránky. 'Kopírovat do schránky' ponechá po vložení výsledek přepisu ve schránce.", diff --git a/src/i18n/locales/de/translation.json b/src/i18n/locales/de/translation.json index 0a8e5b5..4526edf 100644 --- a/src/i18n/locales/de/translation.json +++ b/src/i18n/locales/de/translation.json @@ -244,6 +244,13 @@ "none": "Keine" } }, + "typingTool": { + "title": "Eingabetool", + "description": "Wählen Sie, welches Linux-Eingabetool für die Direkt-Einfügen-Methode verwendet werden soll. Auto erkennt und verwendet automatisch das beste verfügbare Tool für Ihr System.", + "options": { + "auto": "Auto (Empfohlen)" + } + }, "clipboardHandling": { "title": "Zwischenablage-Verhalten", "description": "Zwischenablage nicht ändern bewahrt den aktuellen Inhalt nach der Transkription. In Zwischenablage kopieren hinterlässt das Transkriptionsergebnis in der Zwischenablage.", diff --git a/src/i18n/locales/en/translation.json b/src/i18n/locales/en/translation.json index 85626fa..2baff18 100644 --- a/src/i18n/locales/en/translation.json +++ b/src/i18n/locales/en/translation.json @@ -244,6 +244,13 @@ "none": "None" } }, + "typingTool": { + "title": "Typing Tool", + "description": "Choose which Linux typing tool to use for Direct paste method. Auto will automatically detect and use the best available tool for your system.", + "options": { + "auto": "Auto (Recommended)" + } + }, "clipboardHandling": { "title": "Clipboard Handling", "description": "Don't Modify Clipboard preserves your current clipboard contents after transcription. Copy to Clipboard leaves the transcription result in your clipboard after pasting.", diff --git a/src/i18n/locales/es/translation.json b/src/i18n/locales/es/translation.json index 00bbe40..4cd055e 100644 --- a/src/i18n/locales/es/translation.json +++ b/src/i18n/locales/es/translation.json @@ -244,6 +244,13 @@ "none": "Ninguno" } }, + "typingTool": { + "title": "Herramienta de Escritura", + "description": "Elige qué herramienta de escritura de Linux usar para el método de pegado directo. Auto detectará y usará automáticamente la mejor herramienta disponible para tu sistema.", + "options": { + "auto": "Auto (Recomendado)" + } + }, "clipboardHandling": { "title": "Manejo del Portapapeles", "description": "No Modificar Portapapeles conserva el contenido actual de tu portapapeles después de la transcripción. Copiar al Portapapeles deja el resultado de la transcripción en tu portapapeles después de pegar.", diff --git a/src/i18n/locales/fr/translation.json b/src/i18n/locales/fr/translation.json index 5e11619..9ac29c4 100644 --- a/src/i18n/locales/fr/translation.json +++ b/src/i18n/locales/fr/translation.json @@ -244,6 +244,13 @@ "none": "Aucun" } }, + "typingTool": { + "title": "Outil de frappe", + "description": "Choisissez quel outil de frappe Linux utiliser pour la méthode de collage direct. Auto détectera et utilisera automatiquement le meilleur outil disponible pour votre système.", + "options": { + "auto": "Auto (Recommandé)" + } + }, "clipboardHandling": { "title": "Gestion du presse-papiers", "description": "Ne pas modifier le presse-papiers préserve le contenu actuel de votre presse-papiers après la transcription. Copier dans le presse-papiers laisse le résultat de la transcription dans votre presse-papiers après le collage.", diff --git a/src/i18n/locales/it/translation.json b/src/i18n/locales/it/translation.json index 1858b80..f85904b 100644 --- a/src/i18n/locales/it/translation.json +++ b/src/i18n/locales/it/translation.json @@ -244,6 +244,13 @@ "none": "Nessuno" } }, + "typingTool": { + "title": "Strumento di digitazione", + "description": "Scegli quale strumento di digitazione Linux usare per il metodo di incolla diretto. Auto rileverà e userà automaticamente lo strumento migliore disponibile per il tuo sistema.", + "options": { + "auto": "Auto (Consigliato)" + } + }, "clipboardHandling": { "title": "Gestione Appunti", "description": "Non Modificare gli Appunti mantiene il contenuto dei tuoi appunti dopo la trascrizione. Copia negli Appunti lascia il risultato della trascrizione negli appunti dopo aver incollato.", diff --git a/src/i18n/locales/ja/translation.json b/src/i18n/locales/ja/translation.json index 6f7e020..9f620b3 100644 --- a/src/i18n/locales/ja/translation.json +++ b/src/i18n/locales/ja/translation.json @@ -244,6 +244,13 @@ "none": "なし" } }, + "typingTool": { + "title": "タイピングツール", + "description": "直接貼り付け方式で使用する Linux のタイピングツールを選択します。Auto は自動的に最適なツールを検出して使用します。", + "options": { + "auto": "Auto(推奨)" + } + }, "clipboardHandling": { "title": "クリップボードの処理", "description": "クリップボードを変更しないを選択すると、文字起こし後も現在のクリップボード内容が保持されます。クリップボードにコピーを選択すると、貼り付け後も文字起こし結果がクリップボードに残ります。", diff --git a/src/i18n/locales/ko/translation.json b/src/i18n/locales/ko/translation.json index 866a430..7c166f7 100644 --- a/src/i18n/locales/ko/translation.json +++ b/src/i18n/locales/ko/translation.json @@ -244,6 +244,13 @@ "none": "없음" } }, + "typingTool": { + "title": "타이핑 도구", + "description": "직접 붙여넣기 방식에 사용할 Linux 타이핑 도구를 선택하세요. Auto는 시스템에서 사용 가능한 최적의 도구를 자동으로 감지해 사용합니다.", + "options": { + "auto": "Auto (권장)" + } + }, "clipboardHandling": { "title": "클립보드 처리", "description": "클립보드 수정 안 함은 전사 후 현재 클립보드 내용을 보존합니다. 클립보드에 복사는 붙여넣기 후 전사 결과를 클립보드에 남겨둡니다.", diff --git a/src/i18n/locales/pl/translation.json b/src/i18n/locales/pl/translation.json index 0c313a3..d907791 100644 --- a/src/i18n/locales/pl/translation.json +++ b/src/i18n/locales/pl/translation.json @@ -244,6 +244,13 @@ "none": "Brak" } }, + "typingTool": { + "title": "Narzędzie do wpisywania", + "description": "Wybierz, którego narzędzia do wpisywania w Linuxie użyć dla metody bezpośredniego wklejania. Auto automatycznie wykryje i użyje najlepszego dostępnego narzędzia dla Twojego systemu.", + "options": { + "auto": "Auto (Zalecane)" + } + }, "clipboardHandling": { "title": "Obsługa schowka", "description": "Nie modyfikuj schowka zachowuje jego zawartość. Kopiuj do schowka pozostawia wynik transkrypcji w schowku.", diff --git a/src/i18n/locales/pt/translation.json b/src/i18n/locales/pt/translation.json index feb0562..6c4cb60 100644 --- a/src/i18n/locales/pt/translation.json +++ b/src/i18n/locales/pt/translation.json @@ -244,6 +244,13 @@ "none": "Nenhum" } }, + "typingTool": { + "title": "Ferramenta de digitação", + "description": "Escolha qual ferramenta de digitação do Linux usar para o método de colagem direta. Auto detectará e usará automaticamente a melhor ferramenta disponível para o seu sistema.", + "options": { + "auto": "Auto (Recomendado)" + } + }, "clipboardHandling": { "title": "Manipulação da Área de Transferência", "description": "Não Modificar Área de Transferência preserva o conteúdo atual da área de transferência após a transcrição. Copiar para Área de Transferência deixa o resultado da transcrição na área de transferência após colar.", diff --git a/src/i18n/locales/ru/translation.json b/src/i18n/locales/ru/translation.json index bb67848..81fcfb5 100644 --- a/src/i18n/locales/ru/translation.json +++ b/src/i18n/locales/ru/translation.json @@ -244,6 +244,13 @@ "none": "Нет" } }, + "typingTool": { + "title": "Инструмент ввода", + "description": "Выберите, какой инструмент ввода в Linux использовать для метода прямой вставки. Auto автоматически определит и использует лучший доступный инструмент для вашей системы.", + "options": { + "auto": "Auto (Рекомендуется)" + } + }, "clipboardHandling": { "title": "Обработка буфера обмена", "description": "Функция «Не изменять буфер обмена» сохраняет текущее содержимое буфера обмена после транскрипции. Копировать в буфер обмена оставляет результат транскрипции в буфере обмена после вставки.", diff --git a/src/i18n/locales/tr/translation.json b/src/i18n/locales/tr/translation.json index 37baa4e..40a0e12 100644 --- a/src/i18n/locales/tr/translation.json +++ b/src/i18n/locales/tr/translation.json @@ -244,6 +244,13 @@ "none": "Yok" } }, + "typingTool": { + "title": "Yazma Aracı", + "description": "Doğrudan yapıştırma yöntemi için hangi Linux yazma aracının kullanılacağını seçin. Auto, sisteminiz için mevcut en iyi aracı otomatik olarak algılar ve kullanır.", + "options": { + "auto": "Auto (Önerilen)" + } + }, "clipboardHandling": { "title": "Pano Yönetimi", "description": "Panoyu Değiştirme, transkripsiyon sonrası mevcut pano içeriğini korur. Panoya Kopyala ise yapıştırma işleminden sonra transkripsiyon sonucunu panoda bırakır.", diff --git a/src/i18n/locales/uk/translation.json b/src/i18n/locales/uk/translation.json index 17638d6..a3c43d3 100644 --- a/src/i18n/locales/uk/translation.json +++ b/src/i18n/locales/uk/translation.json @@ -244,6 +244,13 @@ "none": "Немає" } }, + "typingTool": { + "title": "Інструмент введення", + "description": "Виберіть, який інструмент введення в Linux використовувати для методу прямого вставлення. Auto автоматично визначить і використає найкращий доступний інструмент для вашої системи.", + "options": { + "auto": "Auto (Рекомендовано)" + } + }, "clipboardHandling": { "title": "Робота з буфером обміну", "description": "Не змінювати буфер обміну зберігає поточний вміст буфера після транскрипції. Копіювати в буфер обміну залишає результат транскрипції в буфері після вставки.", diff --git a/src/i18n/locales/vi/translation.json b/src/i18n/locales/vi/translation.json index cd237b6..ec64103 100644 --- a/src/i18n/locales/vi/translation.json +++ b/src/i18n/locales/vi/translation.json @@ -244,6 +244,13 @@ "none": "Không có" } }, + "typingTool": { + "title": "Công cụ gõ", + "description": "Chọn công cụ gõ trên Linux cho phương thức dán trực tiếp. Auto sẽ tự động phát hiện và dùng công cụ tốt nhất có sẵn cho hệ thống của bạn.", + "options": { + "auto": "Auto (Khuyến nghị)" + } + }, "clipboardHandling": { "title": "Xử lý Clipboard", "description": "Không sửa đổi Clipboard giữ nguyên nội dung clipboard hiện tại sau khi chuyển đổi. Sao chép vào Clipboard để lại kết quả chuyển đổi trong clipboard sau khi dán.", diff --git a/src/i18n/locales/zh/translation.json b/src/i18n/locales/zh/translation.json index 2b9b155..af4ea20 100644 --- a/src/i18n/locales/zh/translation.json +++ b/src/i18n/locales/zh/translation.json @@ -244,6 +244,13 @@ "none": "无" } }, + "typingTool": { + "title": "输入工具", + "description": "选择在直接粘贴方式下使用的 Linux 输入工具。Auto 会自动检测并使用系统中可用的最佳工具。", + "options": { + "auto": "Auto(推荐)" + } + }, "clipboardHandling": { "title": "剪贴板处理", "description": "不修改剪贴板将在转录后保留当前剪贴板内容。复制到剪贴板将在粘贴后将转录结果留在剪贴板中。", diff --git a/src/stores/settingsStore.ts b/src/stores/settingsStore.ts index 5d5dfa8..46cab18 100644 --- a/src/stores/settingsStore.ts +++ b/src/stores/settingsStore.ts @@ -112,6 +112,7 @@ const settingUpdaters: { word_correction_threshold: (value) => commands.changeWordCorrectionThresholdSetting(value as number), paste_method: (value) => commands.changePasteMethodSetting(value as string), + typing_tool: (value) => commands.changeTypingToolSetting(value as string), clipboard_handling: (value) => commands.changeClipboardHandlingSetting(value as string), auto_submit: (value) => commands.changeAutoSubmitSetting(value as boolean),