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
7 changes: 7 additions & 0 deletions src-tauri/src/clipboard.rs
Original file line number Diff line number Diff line change
Expand Up @@ -234,6 +234,13 @@ pub fn paste(text: String, app_handle: AppHandle) -> Result<(), String> {
let settings = get_settings(&app_handle);
let paste_method = settings.paste_method;

// Append trailing space if setting is enabled
let text = if settings.append_trailing_space {
format!("{} ", text)
} else {
text
};

info!("Using paste method: {:?}", paste_method);

// Perform the paste operation
Expand Down
1 change: 1 addition & 0 deletions src-tauri/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -253,6 +253,7 @@ pub fn run() {
shortcut::suspend_binding,
shortcut::resume_binding,
shortcut::change_mute_while_recording_setting,
shortcut::change_append_trailing_space_setting,
shortcut::change_update_checks_setting,
trigger_update_check,
commands::cancel_operation,
Expand Down
3 changes: 3 additions & 0 deletions src-tauri/src/settings.rs
Original file line number Diff line number Diff line change
Expand Up @@ -285,6 +285,8 @@ pub struct AppSettings {
pub post_process_selected_prompt_id: Option<String>,
#[serde(default)]
pub mute_while_recording: bool,
#[serde(default)]
pub append_trailing_space: bool,
}

fn default_model() -> String {
Expand Down Expand Up @@ -483,6 +485,7 @@ pub fn get_default_settings() -> AppSettings {
post_process_prompts: default_post_process_prompts(),
post_process_selected_prompt_id: None,
mute_while_recording: false,
append_trailing_space: false,
}
}

Expand Down
10 changes: 10 additions & 0 deletions src-tauri/src/shortcut.rs
Original file line number Diff line number Diff line change
Expand Up @@ -693,6 +693,16 @@ pub fn change_mute_while_recording_setting(app: AppHandle, enabled: bool) -> Res
Ok(())
}

#[tauri::command]
#[specta::specta]
pub fn change_append_trailing_space_setting(app: AppHandle, enabled: bool) -> Result<(), String> {
let mut settings = settings::get_settings(&app);
settings.append_trailing_space = enabled;
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").
Expand Down
10 changes: 9 additions & 1 deletion src/bindings.ts
Original file line number Diff line number Diff line change
Expand Up @@ -244,6 +244,14 @@ async changeMuteWhileRecordingSetting(enabled: boolean) : Promise<Result<null, s
else return { status: "error", error: e as any };
}
},
async changeAppendTrailingSpaceSetting(enabled: boolean) : Promise<Result<null, string>> {
try {
return { status: "ok", data: await TAURI_INVOKE("change_append_trailing_space_setting", { enabled }) };
} catch (e) {
if(e instanceof Error) throw e;
else return { status: "error", error: e as any };
}
},
async changeUpdateChecksSetting(enabled: boolean) : Promise<Result<null, string>> {
try {
return { status: "ok", data: await TAURI_INVOKE("change_update_checks_setting", { enabled }) };
Expand Down Expand Up @@ -602,7 +610,7 @@ async isLaptop() : Promise<Result<boolean, string>> {

/** user-defined types **/

export type AppSettings = { 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; selected_model?: string; always_on_microphone?: boolean; selected_microphone?: string | null; clamshell_microphone?: string | null; selected_output_device?: string | null; translate_to_english?: boolean; selected_language?: string; overlay_position?: OverlayPosition; debug_mode?: boolean; log_level?: LogLevel; custom_words?: string[]; model_unload_timeout?: ModelUnloadTimeout; word_correction_threshold?: number; history_limit?: string; recording_retention_period?: RecordingRetentionPeriod; paste_method?: PasteMethod; clipboard_handling?: ClipboardHandling; post_process_enabled?: boolean; post_process_provider_id?: string; post_process_providers?: PostProcessProvider[]; post_process_api_keys?: Partial<{ [key in string]: string }>; post_process_models?: Partial<{ [key in string]: string }>; post_process_prompts?: LLMPrompt[]; post_process_selected_prompt_id?: string | null; mute_while_recording?: boolean }
export type AppSettings = { 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; selected_model?: string; always_on_microphone?: boolean; selected_microphone?: string | null; clamshell_microphone?: string | null; selected_output_device?: string | null; translate_to_english?: boolean; selected_language?: string; overlay_position?: OverlayPosition; debug_mode?: boolean; log_level?: LogLevel; custom_words?: string[]; model_unload_timeout?: ModelUnloadTimeout; word_correction_threshold?: number; history_limit?: string; recording_retention_period?: RecordingRetentionPeriod; paste_method?: PasteMethod; clipboard_handling?: ClipboardHandling; post_process_enabled?: boolean; post_process_provider_id?: string; post_process_providers?: PostProcessProvider[]; post_process_api_keys?: Partial<{ [key in string]: string }>; post_process_models?: Partial<{ [key in string]: string }>; post_process_prompts?: LLMPrompt[]; post_process_selected_prompt_id?: string | null; mute_while_recording?: boolean; append_trailing_space?: boolean }
export type AudioDevice = { index: string; name: string; is_default: boolean }
export type BindingResponse = { success: boolean; binding: ShortcutBinding | null; error: string | null }
export type ClipboardHandling = "dont_modify" | "copy_to_clipboard"
Expand Down
27 changes: 27 additions & 0 deletions src/components/settings/AppendTrailingSpace.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import React from "react";
import { ToggleSwitch } from "../ui/ToggleSwitch";
import { useSettings } from "../../hooks/useSettings";

interface AppendTrailingSpaceProps {
descriptionMode?: "inline" | "tooltip";
grouped?: boolean;
}

export const AppendTrailingSpace: React.FC<AppendTrailingSpaceProps> =
React.memo(({ descriptionMode = "tooltip", grouped = false }) => {
const { getSetting, updateSetting, isUpdating } = useSettings();

const enabled = getSetting("append_trailing_space") ?? false;

return (
<ToggleSwitch
checked={enabled}
onChange={(enabled) => updateSetting("append_trailing_space", enabled)}
isUpdating={isUpdating("append_trailing_space")}
label="Append Trailing Space"
description="Automatically add a space at the end of transcribed text, making it easier to dictate multiple sentences in a row."
descriptionMode={descriptionMode}
grouped={grouped}
/>
);
});
Comment on lines +1 to +27
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🏁 Script executed:

# First, let's check the broader context of this setting and PR
# Look for append_trailing_space usage elsewhere in the codebase
rg "append_trailing_space" --type ts --type tsx -B 2 -A 2

Repository: dannysmith/Handy

Length of output: 87


🏁 Script executed:

# Check if there's a PR description or README that clarifies the behavior
git log --oneline -20

Repository: dannysmith/Handy

Length of output: 121


🏁 Script executed:

# Look for related documentation or comments about pasted vs transcribed text
rg "pasted|transcribed" --type ts --type tsx --type md -B 2 -A 2

Repository: dannysmith/Handy

Length of output: 87


🏁 Script executed:

# Examine the useSettings hook to understand the setting's purpose
fd "useSettings" --type ts --type tsx | head -5

Repository: dannysmith/Handy

Length of output: 229


🏁 Script executed:

# Check the ToggleSwitch component to see standard parameter naming patterns
fd "ToggleSwitch" --type ts --type tsx | head -5

Repository: dannysmith/Handy

Length of output: 229


🏁 Script executed:

# Search for append_trailing_space usage
rg "append_trailing_space" -B 2 -A 2

Repository: dannysmith/Handy

Length of output: 4938


🏁 Script executed:

# Look for related context about pasted or transcribed text
rg "pasted|transcribed" -B 2 -A 2

Repository: dannysmith/Handy

Length of output: 3502


🏁 Script executed:

# Find and examine useSettings hook
fd "useSettings"

Repository: dannysmith/Handy

Length of output: 84


🏁 Script executed:

# Find and examine ToggleSwitch component
fd "ToggleSwitch"

Repository: dannysmith/Handy

Length of output: 94


🏁 Script executed:

# Quick check of the ToggleSwitch component to see parameter naming patterns
head -50 src/components/ui/ToggleSwitch.tsx

Repository: dannysmith/Handy

Length of output: 1856


🏁 Script executed:

# Verify the useSettings hook implementation
head -50 src/hooks/useSettings.ts

Repository: dannysmith/Handy

Length of output: 1637


Fix description to match actual behavior

The description says "transcribed text" but the setting actually controls spacing during paste. The backend applies the trailing space when pasting to the clipboard (see src-tauri/src/clipboard.rs), not during transcription. Update the description to say "pasted text" instead of "transcribed text" for accuracy.

Avoid shadowing enabled in the onChange handler

The enabled variable from getSetting and the enabled parameter in the onChange callback share the same name, which reduces readability. Rename the callback parameter:

-      <ToggleSwitch
-        checked={enabled}
-        onChange={(enabled) => updateSetting("append_trailing_space", enabled)}
+      <ToggleSwitch
+        checked={enabled}
+        onChange={(nextEnabled) =>
+          updateSetting("append_trailing_space", nextEnabled)
+        }
         isUpdating={isUpdating("append_trailing_space")}
         label="Append Trailing Space"
         description="Automatically add a space at the end of pasted text, making it easier to dictate multiple sentences in a row."
         descriptionMode={descriptionMode}
         grouped={grouped}
       />
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
import React from "react";
import { ToggleSwitch } from "../ui/ToggleSwitch";
import { useSettings } from "../../hooks/useSettings";
interface AppendTrailingSpaceProps {
descriptionMode?: "inline" | "tooltip";
grouped?: boolean;
}
export const AppendTrailingSpace: React.FC<AppendTrailingSpaceProps> =
React.memo(({ descriptionMode = "tooltip", grouped = false }) => {
const { getSetting, updateSetting, isUpdating } = useSettings();
const enabled = getSetting("append_trailing_space") ?? false;
return (
<ToggleSwitch
checked={enabled}
onChange={(enabled) => updateSetting("append_trailing_space", enabled)}
isUpdating={isUpdating("append_trailing_space")}
label="Append Trailing Space"
description="Automatically add a space at the end of transcribed text, making it easier to dictate multiple sentences in a row."
descriptionMode={descriptionMode}
grouped={grouped}
/>
);
});
import React from "react";
import { ToggleSwitch } from "../ui/ToggleSwitch";
import { useSettings } from "../../hooks/useSettings";
interface AppendTrailingSpaceProps {
descriptionMode?: "inline" | "tooltip";
grouped?: boolean;
}
export const AppendTrailingSpace: React.FC<AppendTrailingSpaceProps> =
React.memo(({ descriptionMode = "tooltip", grouped = false }) => {
const { getSetting, updateSetting, isUpdating } = useSettings();
const enabled = getSetting("append_trailing_space") ?? false;
return (
<ToggleSwitch
checked={enabled}
onChange={(nextEnabled) =>
updateSetting("append_trailing_space", nextEnabled)
}
isUpdating={isUpdating("append_trailing_space")}
label="Append Trailing Space"
description="Automatically add a space at the end of pasted text, making it easier to dictate multiple sentences in a row."
descriptionMode={descriptionMode}
grouped={grouped}
/>
);
});
🤖 Prompt for AI Agents
In src/components/settings/AppendTrailingSpace.tsx around lines 1 to 27, the UI
description is inaccurate and the onChange callback shadows the outer enabled
variable; update the description string to reference "pasted text" (e.g.,
"Automatically add a space at the end of pasted text...") to match backend
behavior, and rename the onChange parameter from enabled to a different name
like newValue or checked to avoid shadowing the local enabled constant, then
pass that renamed parameter into updateSetting.

2 changes: 2 additions & 0 deletions src/components/settings/debug/DebugSettings.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import { AlwaysOnMicrophone } from "../AlwaysOnMicrophone";
import { SoundPicker } from "../SoundPicker";
import { PostProcessingToggle } from "../PostProcessingToggle";
import { MuteWhileRecording } from "../MuteWhileRecording";
import { AppendTrailingSpace } from "../AppendTrailingSpace";
import { RecordingRetentionPeriodSelector } from "../RecordingRetentionPeriod";
import { ClamshellMicrophoneSelector } from "../ClamshellMicrophoneSelector";
import { HandyShortcut } from "../HandyShortcut";
Expand Down Expand Up @@ -40,6 +41,7 @@ export const DebugSettings: React.FC = () => {
<ClamshellMicrophoneSelector descriptionMode="tooltip" grouped={true} />
<PostProcessingToggle descriptionMode="tooltip" grouped={true} />
<MuteWhileRecording descriptionMode="tooltip" grouped={true} />
<AppendTrailingSpace descriptionMode="tooltip" grouped={true} />
{/* Cancel shortcut is disabled on Linux due to instability with dynamic shortcut registration */}
{!isLinux && (
<HandyShortcut
Expand Down
2 changes: 2 additions & 0 deletions src/stores/settingsStore.ts
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,8 @@ const settingUpdaters: {
commands.setPostProcessSelectedPrompt(value as string),
mute_while_recording: (value) =>
commands.changeMuteWhileRecordingSetting(value as boolean),
append_trailing_space: (value) =>
commands.changeAppendTrailingSpaceSetting(value as boolean),
log_level: (value) => commands.setLogLevel(value as any),
};

Expand Down