Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
54 changes: 54 additions & 0 deletions src-tauri/src/actions.rs
Original file line number Diff line number Diff line change
Expand Up @@ -436,6 +436,56 @@ impl ShortcutAction for CancelAction {
}
}

struct RetypeLastAction;

impl ShortcutAction for RetypeLastAction {
fn start(&self, app: &AppHandle, _binding_id: &str, _shortcut_str: &str) {
debug!("RetypeLastAction::start called");

let app_handle = app.clone();
tauri::async_runtime::spawn(async move {
let hm = app_handle.state::<Arc<HistoryManager>>();

match hm.get_latest_entry().await {
Ok(Some(entry)) => {
let text = entry
.post_processed_text
.filter(|t| !t.is_empty())
.unwrap_or(entry.transcription_text);

if text.is_empty() {
debug!("RetypeLastAction: Latest entry has empty text");
return;
}

debug!("RetypeLastAction: Retyping text: '{}'", text);

let app_for_paste = app_handle.clone();
app_handle
.run_on_main_thread(move || {
if let Err(e) = utils::paste(text, app_for_paste) {
error!("RetypeLastAction: Failed to paste text: {}", e);
}
})
.unwrap_or_else(|e| {
error!("RetypeLastAction: Failed to run on main thread: {:?}", e);
});
}
Ok(None) => {
debug!("RetypeLastAction: No history entries found");
}
Err(e) => {
error!("RetypeLastAction: Failed to get latest entry: {}", e);
}
}
});
}

fn stop(&self, _app: &AppHandle, _binding_id: &str, _shortcut_str: &str) {
// Nothing to do on stop for retype
}
}

// Test Action
struct TestAction;

Expand Down Expand Up @@ -470,6 +520,10 @@ pub static ACTION_MAP: Lazy<HashMap<String, Arc<dyn ShortcutAction>>> = Lazy::ne
"cancel".to_string(),
Arc::new(CancelAction) as Arc<dyn ShortcutAction>,
);
map.insert(
"retype_last".to_string(),
Arc::new(RetypeLastAction) as Arc<dyn ShortcutAction>,
);
map.insert(
"test".to_string(),
Arc::new(TestAction) as Arc<dyn ShortcutAction>,
Expand Down
26 changes: 26 additions & 0 deletions src-tauri/src/managers/history.rs
Original file line number Diff line number Diff line change
Expand Up @@ -379,6 +379,32 @@ impl HistoryManager {
Ok(entries)
}

pub async fn get_latest_entry(&self) -> Result<Option<HistoryEntry>> {
let conn = self.get_connection()?;
let mut stmt = conn.prepare(
"SELECT id, file_name, timestamp, saved, title, transcription_text, post_processed_text, post_process_prompt FROM transcription_history ORDER BY timestamp DESC LIMIT 1"
)?;

let result = stmt.query_row([], |row| {
Ok(HistoryEntry {
id: row.get("id")?,
file_name: row.get("file_name")?,
timestamp: row.get("timestamp")?,
saved: row.get("saved")?,
title: row.get("title")?,
transcription_text: row.get("transcription_text")?,
post_processed_text: row.get("post_processed_text")?,
post_process_prompt: row.get("post_process_prompt")?,
})
});

match result {
Ok(entry) => Ok(Some(entry)),
Err(rusqlite::Error::QueryReturnedNoRows) => Ok(None),
Err(e) => Err(e.into()),
}
}

pub async fn toggle_saved_status(&self, id: i64) -> Result<()> {
let conn = self.get_connection()?;

Expand Down
10 changes: 10 additions & 0 deletions src-tauri/src/settings.rs
Original file line number Diff line number Diff line change
Expand Up @@ -544,6 +544,16 @@ pub fn get_default_settings() -> AppSettings {
current_binding: "escape".to_string(),
},
);
bindings.insert(
"retype_last".to_string(),
ShortcutBinding {
id: "retype_last".to_string(),
name: "Retype Last".to_string(),
description: "Retypes the last transcription.".to_string(),
default_binding: "".to_string(),
current_binding: "".to_string(),
},
);

AppSettings {
bindings,
Expand Down
19 changes: 19 additions & 0 deletions src-tauri/src/shortcut.rs
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,25 @@ pub fn change_binding(
}
}

// If the new binding is empty, unregister the existing one and save
if binding.trim().is_empty() {
if !binding_to_modify.current_binding.trim().is_empty() {
if let Err(e) = unregister_shortcut(&app, binding_to_modify.clone()) {
warn!("Failed to unregister shortcut when clearing: {}", e);
}
}
if let Some(mut b) = settings.bindings.get(&id).cloned() {
b.current_binding = binding;
settings.bindings.insert(id.clone(), b.clone());
settings::write_settings(&app, settings);
return Ok(BindingResponse {
success: true,
binding: Some(b.clone()),
error: None,
});
}
}

// Unregister the existing binding
if let Err(e) = unregister_shortcut(&app, binding_to_modify.clone()) {
let error_msg = format!("Failed to unregister shortcut: {}", e);
Expand Down
8 changes: 6 additions & 2 deletions src/components/settings/HandyShortcut.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -331,10 +331,14 @@ export const HandyShortcut: React.FC<HandyShortcutProps> = ({
</div>
) : (
<div
className="px-2 py-1 text-sm font-semibold bg-mid-gray/10 border border-mid-gray/80 hover:bg-logo-primary/10 rounded cursor-pointer hover:border-logo-primary"
className={`px-2 py-1 text-sm bg-mid-gray/10 border border-mid-gray/80 hover:bg-logo-primary/10 rounded cursor-pointer hover:border-logo-primary ${
binding.current_binding ? "font-semibold" : "text-mid-gray italic"
}`}
onClick={() => startRecording(shortcutId)}
>
{formatKeyCombination(binding.current_binding, osType)}
{binding.current_binding
? formatKeyCombination(binding.current_binding, osType)
: t("settings.general.shortcut.notSet", "Not set")}
</div>
)}
<ResetButton
Expand Down
1 change: 1 addition & 0 deletions src/components/settings/general/GeneralSettings.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ export const GeneralSettings: React.FC = () => {
<div className="max-w-3xl w-full mx-auto space-y-6">
<SettingsGroup title={t("settings.general.title")}>
<HandyShortcut shortcutId="transcribe" grouped={true} />
<HandyShortcut shortcutId="retype_last" grouped={true} />
<LanguageSelector descriptionMode="tooltip" grouped={true} />
<PushToTalk descriptionMode="tooltip" grouped={true} />
</SettingsGroup>
Expand Down
5 changes: 5 additions & 0 deletions src/i18n/locales/en/translation.json
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,7 @@
"loading": "Loading shortcuts...",
"none": "No shortcuts configured",
"notFound": "Shortcut not found",
"notSet": "Not set",
"pressKeys": "Press keys...",
"bindings": {
"transcribe": {
Expand All @@ -98,6 +99,10 @@
"cancel": {
"name": "Cancel",
"description": "Cancels the current recording."
},
"retype_last": {
"name": "Retype Last",
"description": "Retypes the last transcription."
}
},
"errors": {
Expand Down