parent
90bfa73377
commit
e9a6108559
6 changed files with 65 additions and 4 deletions
|
|
@ -2,6 +2,7 @@ use crate::input::{self, EnigoState};
|
||||||
use crate::settings::{get_settings, ClipboardHandling, PasteMethod};
|
use crate::settings::{get_settings, ClipboardHandling, PasteMethod};
|
||||||
use enigo::Enigo;
|
use enigo::Enigo;
|
||||||
use log::info;
|
use log::info;
|
||||||
|
use std::time::Duration;
|
||||||
use tauri::{AppHandle, Manager};
|
use tauri::{AppHandle, Manager};
|
||||||
use tauri_plugin_clipboard_manager::ClipboardExt;
|
use tauri_plugin_clipboard_manager::ClipboardExt;
|
||||||
|
|
||||||
|
|
@ -16,6 +17,7 @@ fn paste_via_clipboard(
|
||||||
text: &str,
|
text: &str,
|
||||||
app_handle: &AppHandle,
|
app_handle: &AppHandle,
|
||||||
paste_method: &PasteMethod,
|
paste_method: &PasteMethod,
|
||||||
|
paste_delay_ms: u64,
|
||||||
) -> Result<(), String> {
|
) -> Result<(), String> {
|
||||||
let clipboard = app_handle.clipboard();
|
let clipboard = app_handle.clipboard();
|
||||||
let clipboard_content = clipboard.read_text().unwrap_or_default();
|
let clipboard_content = clipboard.read_text().unwrap_or_default();
|
||||||
|
|
@ -25,7 +27,7 @@ fn paste_via_clipboard(
|
||||||
.write_text(text)
|
.write_text(text)
|
||||||
.map_err(|e| format!("Failed to write to clipboard: {}", e))?;
|
.map_err(|e| format!("Failed to write to clipboard: {}", e))?;
|
||||||
|
|
||||||
std::thread::sleep(std::time::Duration::from_millis(50));
|
std::thread::sleep(Duration::from_millis(paste_delay_ms));
|
||||||
|
|
||||||
// Send paste key combo
|
// Send paste key combo
|
||||||
#[cfg(target_os = "linux")]
|
#[cfg(target_os = "linux")]
|
||||||
|
|
@ -364,6 +366,7 @@ fn paste_direct(enigo: &mut Enigo, text: &str) -> Result<(), String> {
|
||||||
pub fn paste(text: String, app_handle: AppHandle) -> Result<(), String> {
|
pub fn paste(text: String, app_handle: AppHandle) -> Result<(), String> {
|
||||||
let settings = get_settings(&app_handle);
|
let settings = get_settings(&app_handle);
|
||||||
let paste_method = settings.paste_method;
|
let paste_method = settings.paste_method;
|
||||||
|
let paste_delay_ms = settings.paste_delay_ms;
|
||||||
|
|
||||||
// Append trailing space if setting is enabled
|
// Append trailing space if setting is enabled
|
||||||
let text = if settings.append_trailing_space {
|
let text = if settings.append_trailing_space {
|
||||||
|
|
@ -372,7 +375,10 @@ pub fn paste(text: String, app_handle: AppHandle) -> Result<(), String> {
|
||||||
text
|
text
|
||||||
};
|
};
|
||||||
|
|
||||||
info!("Using paste method: {:?}", paste_method);
|
info!(
|
||||||
|
"Using paste method: {:?}, delay: {}ms",
|
||||||
|
paste_method, paste_delay_ms
|
||||||
|
);
|
||||||
|
|
||||||
// Get the managed Enigo instance
|
// Get the managed Enigo instance
|
||||||
let enigo_state = app_handle
|
let enigo_state = app_handle
|
||||||
|
|
@ -392,7 +398,13 @@ pub fn paste(text: String, app_handle: AppHandle) -> Result<(), String> {
|
||||||
paste_direct(&mut enigo, &text)?;
|
paste_direct(&mut enigo, &text)?;
|
||||||
}
|
}
|
||||||
PasteMethod::CtrlV | PasteMethod::CtrlShiftV | PasteMethod::ShiftInsert => {
|
PasteMethod::CtrlV | PasteMethod::CtrlShiftV | PasteMethod::ShiftInsert => {
|
||||||
paste_via_clipboard(&mut enigo, &text, &app_handle, &paste_method)?
|
paste_via_clipboard(
|
||||||
|
&mut enigo,
|
||||||
|
&text,
|
||||||
|
&app_handle,
|
||||||
|
&paste_method,
|
||||||
|
paste_delay_ms,
|
||||||
|
)?
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -315,6 +315,8 @@ pub struct AppSettings {
|
||||||
pub experimental_enabled: bool,
|
pub experimental_enabled: bool,
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
pub keyboard_implementation: KeyboardImplementation,
|
pub keyboard_implementation: KeyboardImplementation,
|
||||||
|
#[serde(default = "default_paste_delay_ms")]
|
||||||
|
pub paste_delay_ms: u64,
|
||||||
}
|
}
|
||||||
|
|
||||||
fn default_model() -> String {
|
fn default_model() -> String {
|
||||||
|
|
@ -364,6 +366,10 @@ fn default_word_correction_threshold() -> f64 {
|
||||||
0.18
|
0.18
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn default_paste_delay_ms() -> u64 {
|
||||||
|
60
|
||||||
|
}
|
||||||
|
|
||||||
fn default_history_limit() -> usize {
|
fn default_history_limit() -> usize {
|
||||||
5
|
5
|
||||||
}
|
}
|
||||||
|
|
@ -605,6 +611,7 @@ pub fn get_default_settings() -> AppSettings {
|
||||||
app_language: default_app_language(),
|
app_language: default_app_language(),
|
||||||
experimental_enabled: false,
|
experimental_enabled: false,
|
||||||
keyboard_implementation: KeyboardImplementation::default(),
|
keyboard_implementation: KeyboardImplementation::default(),
|
||||||
|
paste_delay_ms: default_paste_delay_ms(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -703,7 +703,7 @@ async isLaptop() : Promise<Result<boolean, string>> {
|
||||||
|
|
||||||
/** user-defined types **/
|
/** 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; experimental_enabled?: boolean; keyboard_implementation?: KeyboardImplementation }
|
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; keyboard_implementation?: KeyboardImplementation; paste_delay_ms?: number }
|
||||||
export type AudioDevice = { index: string; name: string; is_default: boolean }
|
export type AudioDevice = { index: string; name: string; is_default: boolean }
|
||||||
export type BindingResponse = { success: boolean; binding: ShortcutBinding | null; error: string | null }
|
export type BindingResponse = { success: boolean; binding: ShortcutBinding | null; error: string | null }
|
||||||
export type ClipboardHandling = "dont_modify" | "copy_to_clipboard"
|
export type ClipboardHandling = "dont_modify" | "copy_to_clipboard"
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,7 @@ import { useTranslation } from "react-i18next";
|
||||||
import { type } from "@tauri-apps/plugin-os";
|
import { type } from "@tauri-apps/plugin-os";
|
||||||
import { WordCorrectionThreshold } from "./WordCorrectionThreshold";
|
import { WordCorrectionThreshold } from "./WordCorrectionThreshold";
|
||||||
import { LogLevelSelector } from "./LogLevelSelector";
|
import { LogLevelSelector } from "./LogLevelSelector";
|
||||||
|
import { PasteDelay } from "./PasteDelay";
|
||||||
import { SettingsGroup } from "../../ui/SettingsGroup";
|
import { SettingsGroup } from "../../ui/SettingsGroup";
|
||||||
import { AlwaysOnMicrophone } from "../AlwaysOnMicrophone";
|
import { AlwaysOnMicrophone } from "../AlwaysOnMicrophone";
|
||||||
import { SoundPicker } from "../SoundPicker";
|
import { SoundPicker } from "../SoundPicker";
|
||||||
|
|
@ -27,6 +28,7 @@ export const DebugSettings: React.FC = () => {
|
||||||
description={t("settings.debug.soundTheme.description")}
|
description={t("settings.debug.soundTheme.description")}
|
||||||
/>
|
/>
|
||||||
<WordCorrectionThreshold descriptionMode="tooltip" grouped={true} />
|
<WordCorrectionThreshold descriptionMode="tooltip" grouped={true} />
|
||||||
|
<PasteDelay descriptionMode="tooltip" grouped={true} />
|
||||||
<AlwaysOnMicrophone descriptionMode="tooltip" grouped={true} />
|
<AlwaysOnMicrophone descriptionMode="tooltip" grouped={true} />
|
||||||
<ClamshellMicrophoneSelector descriptionMode="tooltip" grouped={true} />
|
<ClamshellMicrophoneSelector descriptionMode="tooltip" grouped={true} />
|
||||||
{/* Cancel shortcut is disabled on Linux due to instability with dynamic shortcut registration */}
|
{/* Cancel shortcut is disabled on Linux due to instability with dynamic shortcut registration */}
|
||||||
|
|
|
||||||
36
src/components/settings/debug/PasteDelay.tsx
Normal file
36
src/components/settings/debug/PasteDelay.tsx
Normal file
|
|
@ -0,0 +1,36 @@
|
||||||
|
import React from "react";
|
||||||
|
import { useTranslation } from "react-i18next";
|
||||||
|
import { Slider } from "../../ui/Slider";
|
||||||
|
import { useSettings } from "../../../hooks/useSettings";
|
||||||
|
|
||||||
|
interface PasteDelayProps {
|
||||||
|
descriptionMode?: "tooltip" | "inline";
|
||||||
|
grouped?: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const PasteDelay: React.FC<PasteDelayProps> = ({
|
||||||
|
descriptionMode = "tooltip",
|
||||||
|
grouped = false,
|
||||||
|
}) => {
|
||||||
|
const { t } = useTranslation();
|
||||||
|
const { settings, updateSetting } = useSettings();
|
||||||
|
|
||||||
|
const handleDelayChange = (value: number) => {
|
||||||
|
updateSetting("paste_delay_ms", value);
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Slider
|
||||||
|
value={settings?.paste_delay_ms ?? 60}
|
||||||
|
onChange={handleDelayChange}
|
||||||
|
min={10}
|
||||||
|
max={200}
|
||||||
|
step={10}
|
||||||
|
label={t("settings.debug.pasteDelay.title")}
|
||||||
|
description={t("settings.debug.pasteDelay.description")}
|
||||||
|
descriptionMode={descriptionMode}
|
||||||
|
grouped={grouped}
|
||||||
|
formatValue={(v) => `${v}ms`}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
@ -370,6 +370,10 @@
|
||||||
"description": "Choose the keyboard shortcut backend.",
|
"description": "Choose the keyboard shortcut backend.",
|
||||||
"bindingsReset": "Keyboard shortcuts were incompatible and reset to defaults"
|
"bindingsReset": "Keyboard shortcuts were incompatible and reset to defaults"
|
||||||
},
|
},
|
||||||
|
"pasteDelay": {
|
||||||
|
"title": "Paste Delay",
|
||||||
|
"description": "Delay before sending paste keystroke (in milliseconds). Increase if wrong text is being pasted."
|
||||||
|
},
|
||||||
"paths": {
|
"paths": {
|
||||||
"appData": "App Data:",
|
"appData": "App Data:",
|
||||||
"models": "Models:",
|
"models": "Models:",
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue