Add Ctrl+Shift+M hotkey for speech-to-text toggle

Lifts useSTT hook from SttButton into TerminalView so both the hotkey
and the button share the same recording state. The hotkey keeps terminal
focus so after transcription the user just presses Enter. The button
also no longer steals focus via onMouseDown preventDefault.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-04-13 05:55:52 -07:00
parent caf3e26816
commit 49d09e4447
2 changed files with 23 additions and 12 deletions

View File

@@ -1,14 +1,15 @@
import { useCallback, useEffect, useRef, useState } from "react";
import { useSTT } from "../../hooks/useSTT";
import type { SttState } from "../../hooks/useSTT";
import * as commands from "../../lib/tauri-commands";
interface Props {
sessionId: string;
sendInput: (sessionId: string, data: string) => Promise<void>;
state: SttState;
error: string | null;
onToggle: () => Promise<void>;
onCancel: () => Promise<void>;
}
export default function SttButton({ sessionId, sendInput }: Props) {
const { state, error, toggle, cancelRecording } = useSTT(sessionId, sendInput);
export default function SttButton({ state, error, onToggle, onCancel }: Props) {
const [elapsed, setElapsed] = useState(0);
const timerRef = useRef<ReturnType<typeof setInterval> | null>(null);
@@ -40,17 +41,17 @@ export default function SttButton({ sessionId, sendInput }: Props) {
// Container start failed, toggle will still attempt transcription
}
}
await toggle();
}, [state, toggle]);
await onToggle();
}, [state, onToggle]);
const handleContextMenu = useCallback(
(e: React.MouseEvent) => {
e.preventDefault();
if (state === "recording") {
cancelRecording();
onCancel();
}
},
[state, cancelRecording],
[state, onCancel],
);
const formatTime = (seconds: number) => {
@@ -64,6 +65,7 @@ export default function SttButton({ sessionId, sendInput }: Props) {
<button
onClick={handleClick}
onContextMenu={handleContextMenu}
onMouseDown={(e) => e.preventDefault()} // prevent stealing focus from terminal
disabled={state === "transcribing"}
className={`w-8 h-8 rounded-full flex items-center justify-center transition-all cursor-pointer ${
state === "recording"
@@ -74,10 +76,10 @@ export default function SttButton({ sessionId, sendInput }: Props) {
}`}
title={
state === "recording"
? "Click to stop and transcribe (right-click to cancel)"
? "Click or Ctrl+Shift+M to stop and transcribe"
: state === "transcribing"
? "Transcribing..."
: "Speech to text"
: "Speech to text (Ctrl+Shift+M)"
}
>
{state === "transcribing" ? (