windows paste shift+ins instead of ctrl+v (#236)
* windows paste shift+ins instead of ctrl+v Shift+Insert is older and more universal than Ctrl+V on windows. For example SSH terminal software often will not permit ctrl+v. * add option for shift+ins on windows/linux --------- Co-authored-by: CJ Pais <cj@cjpais.com>
This commit is contained in:
parent
7b99da122a
commit
09f16b1048
5 changed files with 96 additions and 13 deletions
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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)]
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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<PasteMethodProps> = React.memo(
|
||||
({ descriptionMode = "tooltip", grouped = false }) => {
|
||||
const { getSetting, updateSetting, isUpdating } = useSettings();
|
||||
const [osType, setOsType] = useState<string>("unknown");
|
||||
|
||||
useEffect(() => {
|
||||
setOsType(getOsType());
|
||||
}, []);
|
||||
|
||||
const selectedMethod = (getSetting("paste_method") ||
|
||||
"ctrl_v") as PasteMethod;
|
||||
|
||||
const pasteMethodOptions = getPasteMethodOptions(osType);
|
||||
|
||||
return (
|
||||
<SettingContainer
|
||||
title="Paste Method"
|
||||
description="Clipboard (Ctrl+V) simulates Ctrl/Cmd+V keystrokes to paste from your clipboard. Direct tries to use system input methods if possible, otherwise inputs keystrokes one by one into the text field."
|
||||
description="Clipboard (Ctrl+V) simulates Ctrl/Cmd+V keystrokes to paste from your clipboard. Direct tries to use system input methods if possible, otherwise inputs keystrokes one by one into the text field. Clipboard (Shift+Insert) uses the more universal Shift+Insert shortcut, ideal for terminal applications and SSH clients."
|
||||
descriptionMode={descriptionMode}
|
||||
grouped={grouped}
|
||||
tooltipPosition="bottom"
|
||||
|
|
|
|||
|
|
@ -34,7 +34,7 @@ export const ModelUnloadTimeoutSchema = z.enum([
|
|||
]);
|
||||
export type ModelUnloadTimeout = z.infer<typeof ModelUnloadTimeoutSchema>;
|
||||
|
||||
export const PasteMethodSchema = z.enum(["ctrl_v", "direct"]);
|
||||
export const PasteMethodSchema = z.enum(["ctrl_v", "direct", "shift_insert"]);
|
||||
export type PasteMethod = z.infer<typeof PasteMethodSchema>;
|
||||
|
||||
export const ClipboardHandlingSchema = z.enum(["dont_modify", "copy_to_clipboard"]);
|
||||
|
|
|
|||
Loading…
Reference in a new issue