Skip to content
Open
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
1 change: 1 addition & 0 deletions src-tauri/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -605,6 +605,7 @@ pub fn run(cli_args: CliArgs) {
shortcut::change_binding,
shortcut::reset_binding,
shortcut::change_ptt_setting,
shortcut::change_double_tap_toggle_setting,
shortcut::change_audio_feedback_setting,
shortcut::change_audio_feedback_volume_setting,
shortcut::change_sound_theme_setting,
Expand Down
5 changes: 5 additions & 0 deletions src-tauri/src/settings.rs
Original file line number Diff line number Diff line change
Expand Up @@ -349,6 +349,10 @@ pub struct AppSettings {
pub bindings: HashMap<String, ShortcutBinding>,
#[serde(default = "default_push_to_talk")]
pub push_to_talk: bool,
/// Enables hold-or-double-tap behavior for the push-to-talk shortcut:
/// double-tap starts an ongoing session, a lone tap is discarded.
#[serde(default)]
pub double_tap_toggle: bool,
#[serde(default)]
pub audio_feedback: bool,
#[serde(default = "default_audio_feedback_volume")]
Expand Down Expand Up @@ -849,6 +853,7 @@ pub fn get_default_settings() -> AppSettings {
settings_schema_version: default_settings_schema_version(),
bindings,
push_to_talk: default_push_to_talk(),
double_tap_toggle: false,
audio_feedback: false,
audio_feedback_volume: default_audio_feedback_volume(),
sound_theme: default_sound_theme(),
Expand Down
11 changes: 9 additions & 2 deletions src-tauri/src/shortcut/handler.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ use tauri::{AppHandle, Manager};
use crate::actions::ACTION_MAP;
use crate::managers::audio::AudioRecordingManager;
use crate::settings::get_settings;
use crate::transcription_coordinator::is_transcribe_binding;
use crate::transcription_coordinator::{is_transcribe_binding, InputMode};
use crate::TranscriptionCoordinator;

/// Handle a shortcut event from either implementation.
Expand All @@ -37,7 +37,14 @@ pub fn handle_shortcut_event(
// Transcribe bindings are handled by the coordinator.
if is_transcribe_binding(binding_id) {
if let Some(coordinator) = app.try_state::<TranscriptionCoordinator>() {
coordinator.send_input(binding_id, hotkey_string, is_pressed, settings.push_to_talk);
let mode = if !settings.push_to_talk {
InputMode::Toggle
} else if settings.double_tap_toggle {
InputMode::HoldOrDoubleTap
} else {
InputMode::PushToTalk
};
coordinator.send_input(binding_id, hotkey_string, is_pressed, mode);
} else {
warn!("TranscriptionCoordinator is not initialized");
}
Expand Down
9 changes: 9 additions & 0 deletions src-tauri/src/shortcut/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -996,6 +996,15 @@ pub fn change_post_process_enabled_setting(app: AppHandle, enabled: bool) -> Res
Ok(())
}

#[tauri::command]
#[specta::specta]
pub fn change_double_tap_toggle_setting(app: AppHandle, enabled: bool) -> Result<(), String> {
let mut settings = settings::get_settings(&app);
settings.double_tap_toggle = enabled;
settings::write_settings(&app, settings);
Ok(())
}

#[tauri::command]
#[specta::specta]
pub fn change_experimental_enabled_setting(app: AppHandle, enabled: bool) -> Result<(), String> {
Expand Down
3 changes: 2 additions & 1 deletion src-tauri/src/signal_handle.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
use crate::transcription_coordinator::InputMode;
use crate::TranscriptionCoordinator;
#[cfg(unix)]
use log::debug;
Expand All @@ -17,7 +18,7 @@ use std::thread;
/// Used by signal handlers, CLI flags, and any other external trigger.
pub fn send_transcription_input(app: &AppHandle, binding_id: &str, source: &str) {
if let Some(c) = app.try_state::<TranscriptionCoordinator>() {
c.send_input(binding_id, source, true, false);
c.send_input(binding_id, source, true, InputMode::Toggle);
} else {
warn!("TranscriptionCoordinator not initialized");
}
Expand Down
509 changes: 451 additions & 58 deletions src-tauri/src/transcription_coordinator.rs

Large diffs are not rendered by default.

15 changes: 14 additions & 1 deletion src/bindings.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,14 @@ async changePttSetting(enabled: boolean) : Promise<Result<null, string>> {
else return { status: "error", error: e as any };
}
},
async changeDoubleTapToggleSetting(enabled: boolean) : Promise<Result<null, string>> {
try {
return { status: "ok", data: await TAURI_INVOKE("change_double_tap_toggle_setting", { enabled }) };
} catch (e) {
if(e instanceof Error) throw e;
else return { status: "error", error: e as any };
}
},
async changeAudioFeedbackSetting(enabled: boolean) : Promise<Result<null, string>> {
try {
return { status: "ok", data: await TAURI_INVOKE("change_audio_feedback_setting", { enabled }) };
Expand Down Expand Up @@ -933,7 +941,12 @@ settings_schema_version?: number;
* Defaults to empty on partial stores; the load path merges in the
* default bindings for any missing keys before the settings are used.
*/
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; show_whats_new_on_update?: boolean;
bindings?: Partial<{ [key in string]: ShortcutBinding }>; push_to_talk?: boolean;
/**
* Enables hold-or-double-tap behavior for the push-to-talk shortcut:
* double-tap starts an ongoing session, a lone tap is discarded.
*/
double_tap_toggle?: boolean; audio_feedback?: boolean; audio_feedback_volume?: number; sound_theme?: SoundTheme; start_hidden?: boolean; autostart_enabled?: boolean; update_checks_enabled?: boolean; show_whats_new_on_update?: boolean;
/**
* The app version whose What's New the user has already seen. Fresh installs
* default to the current version (nothing is "new" to them). Existing users
Expand Down
20 changes: 17 additions & 3 deletions src/components/settings/general/GeneralSettings.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,23 +8,37 @@ import { SettingsGroup } from "../../ui/SettingsGroup";
import { OutputDeviceSelector } from "../OutputDeviceSelector";
import { PushToTalk } from "../PushToTalk";
import { AudioFeedback } from "../AudioFeedback";
import { ToggleSwitch } from "../../ui/ToggleSwitch";
import { useSettings } from "../../../hooks/useSettings";
import { VolumeSlider } from "../VolumeSlider";
import { MuteWhileRecording } from "../MuteWhileRecording";
import { ModelSettingsCard } from "./ModelSettingsCard";

export const GeneralSettings: React.FC = () => {
const { t } = useTranslation();
const { audioFeedbackEnabled, getSetting } = useSettings();
const { audioFeedbackEnabled, getSetting, updateSetting, isUpdating } =
useSettings();
const pushToTalk = getSetting("push_to_talk");
const doubleTapToggle = getSetting("double_tap_toggle") || false;
const isLinux = type() === "linux";
return (
<div className="max-w-3xl w-full mx-auto space-y-6">
<SettingsGroup title={t("settings.general.title")}>
<ShortcutInput shortcutId="transcribe" grouped={true} />
<PushToTalk descriptionMode="tooltip" grouped={true} />
{/* Cancel shortcut is hidden with push-to-talk (release key cancels) and on Linux (dynamic shortcut instability) */}
{!isLinux && !pushToTalk && (
{pushToTalk && (
<ToggleSwitch
checked={doubleTapToggle}
onChange={(enabled) => updateSetting("double_tap_toggle", enabled)}
isUpdating={isUpdating("double_tap_toggle")}
label={t("settings.general.doubleTapToggle.label")}
description={t("settings.general.doubleTapToggle.description")}
descriptionMode="tooltip"
grouped={true}
/>
)}
{/* Cancel shortcut is hidden on Linux (dynamic shortcut instability) and when no shortcut can start a toggle-mode recording */}
{!isLinux && (!pushToTalk || doubleTapToggle) && (
<ShortcutInput shortcutId="cancel" grouped={true} />
)}
</SettingsGroup>
Expand Down
4 changes: 4 additions & 0 deletions src/i18n/locales/en/translation.json
Original file line number Diff line number Diff line change
Expand Up @@ -197,6 +197,10 @@
"pushToTalk": {
"label": "Push To Talk",
"description": "Hold to record, release to stop"
},
"doubleTapToggle": {
"label": "Double-Tap To Toggle",
"description": "Double-tap to start a hands-free session, press again to stop. Holding still records push-to-talk; a lone tap is ignored"
}
},
"models": {
Expand Down
2 changes: 2 additions & 0 deletions src/stores/settingsStore.ts
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,8 @@ const settingUpdaters: {
whats_new_last_seen_version: (value) =>
commands.changeWhatsNewLastSeenVersionSetting(value as string),
push_to_talk: (value) => commands.changePttSetting(value as boolean),
double_tap_toggle: (value) =>
commands.changeDoubleTapToggleSetting(value as boolean),
selected_microphone: (value) =>
commands.setSelectedMicrophone(
(value as string) === "Default" || value === null
Expand Down