Add Setting to Copy to Clipboard (#220)
* update transcribe-rs * add copy to clipboard feat
This commit is contained in:
parent
93215dc097
commit
db00ff240f
10 changed files with 102 additions and 7 deletions
4
src-tauri/Cargo.lock
generated
4
src-tauri/Cargo.lock
generated
|
|
@ -6924,9 +6924,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "transcribe-rs"
|
||||
version = "0.1.3"
|
||||
version = "0.1.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "77dbb8e1048585b7f0bc216d87025953e2f7b04a3e4f4489948f18ab847486a9"
|
||||
checksum = "856646076d3d9739998ba693ebfff3afe95c0cdb71d4fead32e761b11496094f"
|
||||
dependencies = [
|
||||
"async-openai",
|
||||
"async-trait",
|
||||
|
|
|
|||
|
|
@ -64,7 +64,7 @@ rusqlite = { version = "0.32", features = ["bundled"] }
|
|||
tauri-plugin-sql = { version = "2", features = ["sqlite"] }
|
||||
tar = "0.4.44"
|
||||
flate2 = "1.0"
|
||||
transcribe-rs = "0.1.3"
|
||||
transcribe-rs = "0.1.4"
|
||||
|
||||
[target.'cfg(not(any(target_os = "android", target_os = "ios")))'.dependencies]
|
||||
tauri-plugin-autostart = "2"
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
use crate::settings::{get_settings, PasteMethod};
|
||||
use crate::settings::{get_settings, ClipboardHandling, PasteMethod};
|
||||
use enigo::Enigo;
|
||||
use enigo::Key;
|
||||
use enigo::Keyboard;
|
||||
|
|
@ -83,8 +83,19 @@ pub fn paste(text: String, app_handle: AppHandle) -> Result<(), String> {
|
|||
|
||||
println!("Using paste method: {:?}", paste_method);
|
||||
|
||||
// Perform the paste operation
|
||||
match paste_method {
|
||||
PasteMethod::CtrlV => paste_via_clipboard(&text, &app_handle),
|
||||
PasteMethod::Direct => paste_via_direct_input(&text),
|
||||
PasteMethod::CtrlV => paste_via_clipboard(&text, &app_handle)?,
|
||||
PasteMethod::Direct => paste_via_direct_input(&text)?,
|
||||
}
|
||||
|
||||
// After pasting, optionally copy to clipboard based on settings
|
||||
if settings.clipboard_handling == ClipboardHandling::CopyToClipboard {
|
||||
let clipboard = app_handle.clipboard();
|
||||
clipboard
|
||||
.write_text(&text)
|
||||
.map_err(|e| format!("Failed to copy to clipboard: {}", e))?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
|
|
|||
|
|
@ -233,6 +233,7 @@ pub fn run() {
|
|||
shortcut::change_debug_mode_setting,
|
||||
shortcut::change_word_correction_threshold_setting,
|
||||
shortcut::change_paste_method_setting,
|
||||
shortcut::change_clipboard_handling_setting,
|
||||
shortcut::update_custom_words,
|
||||
shortcut::suspend_binding,
|
||||
shortcut::resume_binding,
|
||||
|
|
|
|||
|
|
@ -40,6 +40,13 @@ pub enum PasteMethod {
|
|||
Direct,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug, Clone, Copy, PartialEq, Eq)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
pub enum ClipboardHandling {
|
||||
DontModify,
|
||||
CopyToClipboard,
|
||||
}
|
||||
|
||||
impl Default for ModelUnloadTimeout {
|
||||
fn default() -> Self {
|
||||
ModelUnloadTimeout::Never
|
||||
|
|
@ -56,6 +63,12 @@ impl Default for PasteMethod {
|
|||
}
|
||||
}
|
||||
|
||||
impl Default for ClipboardHandling {
|
||||
fn default() -> Self {
|
||||
ClipboardHandling::DontModify
|
||||
}
|
||||
}
|
||||
|
||||
impl ModelUnloadTimeout {
|
||||
pub fn to_minutes(self) -> Option<u64> {
|
||||
match self {
|
||||
|
|
@ -146,6 +159,8 @@ pub struct AppSettings {
|
|||
pub history_limit: usize,
|
||||
#[serde(default)]
|
||||
pub paste_method: PasteMethod,
|
||||
#[serde(default)]
|
||||
pub clipboard_handling: ClipboardHandling,
|
||||
}
|
||||
|
||||
fn default_model() -> String {
|
||||
|
|
@ -244,6 +259,7 @@ pub fn get_default_settings() -> AppSettings {
|
|||
word_correction_threshold: default_word_correction_threshold(),
|
||||
history_limit: default_history_limit(),
|
||||
paste_method: PasteMethod::default(),
|
||||
clipboard_handling: ClipboardHandling::default(),
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ use tauri_plugin_global_shortcut::{GlobalShortcutExt, Shortcut, ShortcutState};
|
|||
|
||||
use crate::actions::ACTION_MAP;
|
||||
use crate::settings::ShortcutBinding;
|
||||
use crate::settings::{self, get_settings, OverlayPosition, PasteMethod, SoundTheme};
|
||||
use crate::settings::{self, get_settings, ClipboardHandling, OverlayPosition, PasteMethod, SoundTheme};
|
||||
use crate::ManagedToggleState;
|
||||
|
||||
pub fn init_shortcuts(app: &AppHandle) {
|
||||
|
|
@ -276,6 +276,22 @@ pub fn change_paste_method_setting(app: AppHandle, method: String) -> Result<(),
|
|||
Ok(())
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
pub fn change_clipboard_handling_setting(app: AppHandle, handling: String) -> Result<(), String> {
|
||||
let mut settings = settings::get_settings(&app);
|
||||
let parsed = match handling.as_str() {
|
||||
"dont_modify" => ClipboardHandling::DontModify,
|
||||
"copy_to_clipboard" => ClipboardHandling::CopyToClipboard,
|
||||
other => {
|
||||
eprintln!("Invalid clipboard handling '{}', defaulting to dont_modify", other);
|
||||
ClipboardHandling::DontModify
|
||||
}
|
||||
};
|
||||
settings.clipboard_handling = parsed;
|
||||
settings::write_settings(&app, settings);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Determine whether a shortcut string contains at least one non-modifier key.
|
||||
/// We allow single non-modifier keys (e.g. "f5" or "space") but disallow
|
||||
/// modifier-only combos (e.g. "ctrl" or "ctrl+shift").
|
||||
|
|
|
|||
43
src/components/settings/ClipboardHandling.tsx
Normal file
43
src/components/settings/ClipboardHandling.tsx
Normal file
|
|
@ -0,0 +1,43 @@
|
|||
import React from "react";
|
||||
import { Dropdown } from "../ui/Dropdown";
|
||||
import { SettingContainer } from "../ui/SettingContainer";
|
||||
import { useSettings } from "../../hooks/useSettings";
|
||||
import type { ClipboardHandling } from "../../lib/types";
|
||||
|
||||
interface ClipboardHandlingProps {
|
||||
descriptionMode?: "inline" | "tooltip";
|
||||
grouped?: boolean;
|
||||
}
|
||||
|
||||
const clipboardHandlingOptions = [
|
||||
{ value: "dont_modify", label: "Don't Modify Clipboard" },
|
||||
{ value: "copy_to_clipboard", label: "Copy to Clipboard" },
|
||||
];
|
||||
|
||||
export const ClipboardHandlingSetting: React.FC<ClipboardHandlingProps> = React.memo(({
|
||||
descriptionMode = "tooltip",
|
||||
grouped = false,
|
||||
}) => {
|
||||
const { getSetting, updateSetting, isUpdating } = useSettings();
|
||||
|
||||
const selectedHandling = (getSetting("clipboard_handling") ||
|
||||
"dont_modify") as ClipboardHandling;
|
||||
|
||||
return (
|
||||
<SettingContainer
|
||||
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."
|
||||
descriptionMode={descriptionMode}
|
||||
grouped={grouped}
|
||||
>
|
||||
<Dropdown
|
||||
options={clipboardHandlingOptions}
|
||||
selectedValue={selectedHandling}
|
||||
onSelect={(value) =>
|
||||
updateSetting("clipboard_handling", value as ClipboardHandling)
|
||||
}
|
||||
disabled={isUpdating("clipboard_handling")}
|
||||
/>
|
||||
</SettingContainer>
|
||||
);
|
||||
});
|
||||
|
|
@ -4,6 +4,7 @@ import { AppDataDirectory } from "./AppDataDirectory";
|
|||
import { SettingsGroup } from "../ui/SettingsGroup";
|
||||
import { HistoryLimit } from "./HistoryLimit";
|
||||
import { PasteMethodSetting } from "./PasteMethod";
|
||||
import { ClipboardHandlingSetting } from "./ClipboardHandling";
|
||||
import { AlwaysOnMicrophone } from "./AlwaysOnMicrophone";
|
||||
import { SoundPicker } from "./SoundPicker";
|
||||
|
||||
|
|
@ -12,6 +13,7 @@ export const DebugSettings: React.FC = () => {
|
|||
<div className="max-w-3xl w-full mx-auto space-y-6">
|
||||
<SettingsGroup title="Debug">
|
||||
<PasteMethodSetting descriptionMode="tooltip" grouped={true} />
|
||||
<ClipboardHandlingSetting descriptionMode="tooltip" grouped={true} />
|
||||
<SoundPicker
|
||||
label="Sound Theme"
|
||||
description="Choose a sound theme for recording start and stop feedback"
|
||||
|
|
|
|||
|
|
@ -37,6 +37,9 @@ export type ModelUnloadTimeout = z.infer<typeof ModelUnloadTimeoutSchema>;
|
|||
export const PasteMethodSchema = z.enum(["ctrl_v", "direct"]);
|
||||
export type PasteMethod = z.infer<typeof PasteMethodSchema>;
|
||||
|
||||
export const ClipboardHandlingSchema = z.enum(["dont_modify", "copy_to_clipboard"]);
|
||||
export type ClipboardHandling = z.infer<typeof ClipboardHandlingSchema>;
|
||||
|
||||
export const SettingsSchema = z.object({
|
||||
bindings: ShortcutBindingsMapSchema,
|
||||
push_to_talk: z.boolean(),
|
||||
|
|
@ -61,6 +64,7 @@ export const SettingsSchema = z.object({
|
|||
word_correction_threshold: z.number().optional().default(0.18),
|
||||
history_limit: z.number().optional().default(5),
|
||||
paste_method: PasteMethodSchema.optional().default("ctrl_v"),
|
||||
clipboard_handling: ClipboardHandlingSchema.optional().default("dont_modify"),
|
||||
});
|
||||
|
||||
export const BindingResponseSchema = z.object({
|
||||
|
|
|
|||
|
|
@ -98,6 +98,8 @@ const settingUpdaters: {
|
|||
invoke("change_word_correction_threshold_setting", { threshold: value }),
|
||||
paste_method: (value) =>
|
||||
invoke("change_paste_method_setting", { method: value }),
|
||||
clipboard_handling: (value) =>
|
||||
invoke("change_clipboard_handling_setting", { handling: value }),
|
||||
history_limit: (value) => invoke("update_history_limit", { limit: value }),
|
||||
};
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue