Skip to content
Merged
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
30 changes: 30 additions & 0 deletions src-tauri/src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,9 @@ fn config_mutex() -> &'static Mutex<()> {
/// Default endpointing (seconds) used when the field is absent.
pub const DEFAULT_ENDPOINTING: f64 = 0.1;

/// Default activation mode used when the field is absent.
pub const DEFAULT_ACTIVATION_MODE: &str = "push-to-talk";

fn default_hotkey() -> String {
if cfg!(target_os = "macos") {
"Fn".to_string()
Expand All @@ -33,6 +36,9 @@ struct Config {
/// Whether to leave the final transcription on the clipboard after a
/// dictation session (instead of restoring the user's original clipboard).
copy_to_clipboard: Option<bool>,
/// How the trigger key starts a dictation: "push-to-talk" (hold) or
/// "toggle" (press once to start, once to stop).
activation_mode: Option<String>,
custom_vocabulary: Option<Vec<CustomVocabEntry>>,
/// Endpointing duration (seconds of silence before an utterance is
/// considered final). Missing (older config files) defaults to 0.1 —
Expand Down Expand Up @@ -61,6 +67,7 @@ impl Default for Config {
languages: Some(vec!["en".to_string()]),
code_switching: Some(false),
copy_to_clipboard: Some(false),
activation_mode: Some(DEFAULT_ACTIVATION_MODE.to_string()),
custom_vocabulary: Some(default_custom_vocabulary()),
endpointing: Some(DEFAULT_ENDPOINTING),
audio_device_selection: Some(AudioDeviceSelection::Automatic),
Expand Down Expand Up @@ -88,6 +95,9 @@ impl Config {
if self.copy_to_clipboard.is_none() {
self.copy_to_clipboard = defaults.copy_to_clipboard;
}
if self.activation_mode.is_none() {
self.activation_mode = defaults.activation_mode;
}
if self.custom_vocabulary.is_none() {
self.custom_vocabulary = defaults.custom_vocabulary;
}
Expand Down Expand Up @@ -378,6 +388,26 @@ pub async fn get_copy_to_clipboard() -> Result<bool, String> {
Ok(copy_to_clipboard())
}

#[tauri::command]
pub async fn save_activation_mode(mode: String) -> Result<(), String> {
if mode != "toggle" && mode != DEFAULT_ACTIVATION_MODE {
return Err(format!("Unknown activation mode: {mode}"));
}
with_config(|config| {
config.activation_mode = Some(mode);
})
}

#[tauri::command]
pub async fn get_activation_mode() -> Result<String, String> {
Ok(read_config(|config| {
config
.activation_mode
.clone()
.unwrap_or_else(|| DEFAULT_ACTIVATION_MODE.to_string())
}))
}

#[cfg(test)]
mod tests {
use super::*;
Expand Down
2 changes: 2 additions & 0 deletions src-tauri/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1264,6 +1264,8 @@ fn main() {
config::get_endpointing,
config::save_copy_to_clipboard,
config::get_copy_to_clipboard,
config::save_activation_mode,
config::get_activation_mode,
config::save_audio_device_selection,
config::get_audio_device_selection,
vocabulary::save_custom_vocabulary,
Expand Down
14 changes: 14 additions & 0 deletions src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import type {
TranscriptionHistoryPage,
AudioDeviceInfo,
AudioDeviceSelection,
ActivationMode,
} from "./types";
import { invoke } from "@tauri-apps/api/core";
import { getVersion } from "@tauri-apps/api/app";
Expand Down Expand Up @@ -190,6 +191,7 @@ export default function App() {
const vocabularySettingsLoaded = useRef(false);
const endpointingLoaded = useRef(false);
const copyToClipboardLoaded = useRef(false);
const activationModeLoaded = useRef(false);
const audioDeviceSelectionLoaded = useRef(false);

const isRecordingRef = useRef(false);
Expand Down Expand Up @@ -288,6 +290,12 @@ export default function App() {
enabled: settings.copyToClipboard,
}).catch(console.error);
}, [settings.copyToClipboard]);
useEffect(() => {
if (!activationModeLoaded.current) return;
invoke("save_activation_mode", { mode: settings.activationMode }).catch(
console.error,
);
}, [settings.activationMode]);
useEffect(() => {
if (!audioDeviceSelectionLoaded.current) return;
invoke("save_audio_device_selection", {
Expand Down Expand Up @@ -463,6 +471,10 @@ export default function App() {
"get_copy_to_clipboard",
).catch(() => false);

const savedActivationMode = await invoke<ActivationMode>(
"get_activation_mode",
).catch(() => "push-to-talk" as ActivationMode);

const savedAudioDeviceSelection = await invoke<AudioDeviceSelection>(
"get_audio_device_selection",
).catch(() => ({ mode: "automatic" }) as AudioDeviceSelection);
Expand All @@ -478,13 +490,15 @@ export default function App() {
: {}),
endpointing: savedEndpointing,
copyToClipboard: savedCopyToClipboard,
activationMode: savedActivationMode,
audioDeviceSelection: savedAudioDeviceSelection,
customVocabulary: savedVocabulary,
}));
languageSettingsLoaded.current = true;
vocabularySettingsLoaded.current = true;
endpointingLoaded.current = true;
copyToClipboardLoaded.current = true;
activationModeLoaded.current = true;
audioDeviceSelectionLoaded.current = true;
setSettingsReady(true);
};
Expand Down