diff --git a/src-tauri/src/clipboard.rs b/src-tauri/src/clipboard.rs index 07b5210..155b099 100644 --- a/src-tauri/src/clipboard.rs +++ b/src-tauri/src/clipboard.rs @@ -6,9 +6,9 @@ use enigo::Settings; use tauri::AppHandle; use tauri_plugin_clipboard_manager::ClipboardExt; -/// Sends a paste command (Cmd+V or Ctrl+V) using platform-specific virtual key codes. +/// Sends a Ctrl+V or Cmd+V paste command using platform-specific virtual key codes. /// This ensures the paste works regardless of keyboard layout (e.g., Russian, AZERTY, DVORAK). -fn send_paste() -> Result<(), String> { +fn send_paste_ctrl_v() -> Result<(), String> { // Platform-specific key definitions #[cfg(target_os = "macos")] let (modifier_key, v_key_code) = (Key::Meta, Key::Other(9)); @@ -37,6 +37,35 @@ fn send_paste() -> Result<(), String> { Ok(()) } +/// Sends a Shift+Insert paste command (Windows and Linux only). +/// This is more universal for terminal applications and legacy software. +#[cfg(not(target_os = "macos"))] +fn send_paste_shift_insert() -> Result<(), String> { + #[cfg(target_os = "windows")] + let insert_key_code = Key::Other(0x2D); // VK_INSERT + #[cfg(target_os = "linux")] + let insert_key_code = Key::Other(0x76); // XK_Insert (keycode 118 / 0x76) + + let mut enigo = Enigo::new(&Settings::default()) + .map_err(|e| format!("Failed to initialize Enigo: {}", e))?; + + // Press Shift + Insert + enigo + .key(Key::Shift, enigo::Direction::Press) + .map_err(|e| format!("Failed to press Shift key: {}", e))?; + enigo + .key(insert_key_code, enigo::Direction::Click) + .map_err(|e| format!("Failed to click Insert key: {}", e))?; + + std::thread::sleep(std::time::Duration::from_millis(100)); + + enigo + .key(Key::Shift, enigo::Direction::Release) + .map_err(|e| format!("Failed to release Shift key: {}", e))?; + + Ok(()) +} + /// Pastes text directly using the enigo text method. /// This tries to use system input methods if possible, otherwise simulates keystrokes one by one. fn paste_via_direct_input(text: &str) -> Result<(), String> { @@ -50,9 +79,9 @@ fn paste_via_direct_input(text: &str) -> Result<(), String> { Ok(()) } -/// Pastes text using the clipboard method (Ctrl+V/Cmd+V). +/// Pastes text using the clipboard method with Ctrl+V/Cmd+V. /// Saves the current clipboard, writes the text, sends paste command, then restores the clipboard. -fn paste_via_clipboard(text: &str, app_handle: &AppHandle) -> Result<(), String> { +fn paste_via_clipboard_ctrl_v(text: &str, app_handle: &AppHandle) -> Result<(), String> { let clipboard = app_handle.clipboard(); // get the current clipboard content @@ -65,7 +94,35 @@ fn paste_via_clipboard(text: &str, app_handle: &AppHandle) -> Result<(), String> // small delay to ensure the clipboard content has been written to std::thread::sleep(std::time::Duration::from_millis(50)); - send_paste()?; + send_paste_ctrl_v()?; + + std::thread::sleep(std::time::Duration::from_millis(50)); + + // restore the clipboard + clipboard + .write_text(&clipboard_content) + .map_err(|e| format!("Failed to restore clipboard: {}", e))?; + + Ok(()) +} + +/// Pastes text using the clipboard method with Shift+Insert (Windows/Linux only). +/// Saves the current clipboard, writes the text, sends paste command, then restores the clipboard. +#[cfg(not(target_os = "macos"))] +fn paste_via_clipboard_shift_insert(text: &str, app_handle: &AppHandle) -> Result<(), String> { + let clipboard = app_handle.clipboard(); + + // get the current clipboard content + let clipboard_content = clipboard.read_text().unwrap_or_default(); + + clipboard + .write_text(text) + .map_err(|e| format!("Failed to write to clipboard: {}", e))?; + + // small delay to ensure the clipboard content has been written to + std::thread::sleep(std::time::Duration::from_millis(50)); + + send_paste_shift_insert()?; std::thread::sleep(std::time::Duration::from_millis(50)); @@ -85,8 +142,10 @@ pub fn paste(text: String, app_handle: AppHandle) -> Result<(), String> { // Perform the paste operation match paste_method { - PasteMethod::CtrlV => paste_via_clipboard(&text, &app_handle)?, + PasteMethod::CtrlV => paste_via_clipboard_ctrl_v(&text, &app_handle)?, PasteMethod::Direct => paste_via_direct_input(&text)?, + #[cfg(not(target_os = "macos"))] + PasteMethod::ShiftInsert => paste_via_clipboard_shift_insert(&text, &app_handle)?, } // After pasting, optionally copy to clipboard based on settings diff --git a/src-tauri/src/settings.rs b/src-tauri/src/settings.rs index 8f86148..2ebeca8 100644 --- a/src-tauri/src/settings.rs +++ b/src-tauri/src/settings.rs @@ -38,6 +38,8 @@ pub enum ModelUnloadTimeout { pub enum PasteMethod { CtrlV, Direct, + #[cfg(not(target_os = "macos"))] + ShiftInsert, } #[derive(Serialize, Deserialize, Debug, Clone, Copy, PartialEq, Eq)] diff --git a/src-tauri/src/shortcut.rs b/src-tauri/src/shortcut.rs index 011c46f..1302803 100644 --- a/src-tauri/src/shortcut.rs +++ b/src-tauri/src/shortcut.rs @@ -266,6 +266,8 @@ pub fn change_paste_method_setting(app: AppHandle, method: String) -> Result<(), let parsed = match method.as_str() { "ctrl_v" => PasteMethod::CtrlV, "direct" => PasteMethod::Direct, + #[cfg(not(target_os = "macos"))] + "shift_insert" => PasteMethod::ShiftInsert, other => { eprintln!("Invalid paste method '{}', defaulting to ctrl_v", other); PasteMethod::CtrlV diff --git a/src/components/settings/PasteMethod.tsx b/src/components/settings/PasteMethod.tsx index 70b1b71..5e4822c 100644 --- a/src/components/settings/PasteMethod.tsx +++ b/src/components/settings/PasteMethod.tsx @@ -1,4 +1,5 @@ -import React from "react"; +import React, { useEffect, useState } from "react"; +import { type as getOsType } from "@tauri-apps/plugin-os"; import { Dropdown } from "../ui/Dropdown"; import { SettingContainer } from "../ui/SettingContainer"; import { useSettings } from "../../hooks/useSettings"; @@ -9,22 +10,41 @@ interface PasteMethodProps { grouped?: boolean; } -const pasteMethodOptions = [ - { value: "ctrl_v", label: "Clipboard (Ctrl+V)" }, - { value: "direct", label: "Direct" }, -]; +const getPasteMethodOptions = (osType: string) => { + const baseOptions = [ + { value: "ctrl_v", label: "Clipboard (Ctrl+V)" }, + { value: "direct", label: "Direct" }, + ]; + + // Add Shift+Insert option for Windows and Linux only + if (osType === "windows" || osType === "linux") { + baseOptions.push({ + value: "shift_insert", + label: "Clipboard (Shift+Insert)", + }); + } + + return baseOptions; +}; export const PasteMethodSetting: React.FC = React.memo( ({ descriptionMode = "tooltip", grouped = false }) => { const { getSetting, updateSetting, isUpdating } = useSettings(); + const [osType, setOsType] = useState("unknown"); + + useEffect(() => { + setOsType(getOsType()); + }, []); const selectedMethod = (getSetting("paste_method") || "ctrl_v") as PasteMethod; + const pasteMethodOptions = getPasteMethodOptions(osType); + return ( ; -export const PasteMethodSchema = z.enum(["ctrl_v", "direct"]); +export const PasteMethodSchema = z.enum(["ctrl_v", "direct", "shift_insert"]); export type PasteMethod = z.infer; export const ClipboardHandlingSchema = z.enum(["dont_modify", "copy_to_clipboard"]);