diff --git a/desktop/src/app/AppShell.tsx b/desktop/src/app/AppShell.tsx
index 5d06c7227..087897c6f 100644
--- a/desktop/src/app/AppShell.tsx
+++ b/desktop/src/app/AppShell.tsx
@@ -597,7 +597,7 @@ export function AppShell() {
if (key === "d" && !event.shiftKey) {
event.preventDefault();
- window.dispatchEvent(new CustomEvent("buzz:toggle-dictation"));
+ window.dispatchEvent(new CustomEvent("buzz:dictation-key-down"));
return;
}
@@ -608,9 +608,17 @@ export function AppShell() {
}
}
+ function handleKeyUp(event: KeyboardEvent) {
+ if (event.key.toLowerCase() === "d") {
+ window.dispatchEvent(new CustomEvent("buzz:dictation-key-up"));
+ }
+ }
+
window.addEventListener("keydown", handleKeyDown);
+ window.addEventListener("keyup", handleKeyUp);
return () => {
window.removeEventListener("keydown", handleKeyDown);
+ window.removeEventListener("keyup", handleKeyUp);
};
}, [
handleOpenBrowseChannels,
diff --git a/desktop/src/features/dictation/hooks/useComposerDictation.ts b/desktop/src/features/dictation/hooks/useComposerDictation.ts
index 46e252d15..e230bcae2 100644
--- a/desktop/src/features/dictation/hooks/useComposerDictation.ts
+++ b/desktop/src/features/dictation/hooks/useComposerDictation.ts
@@ -71,16 +71,31 @@ export function useComposerDictation({
}
}, [disabled, dictation.isRecording, dictation.cancelRecording]);
- // ⌘D global shortcut — dispatched from AppShell's keydown handler.
+ // ⌘D push-to-talk — hold to record, release to stop.
+ // Dispatched from AppShell's keydown/keyup handlers.
useEffect(() => {
- function handleToggle() {
- dictation.toggleRecording();
+ function handleKeyDown() {
+ if (!dictation.isRecording && !dictation.isStarting) {
+ dictation.startRecording();
+ }
}
- window.addEventListener("buzz:toggle-dictation", handleToggle);
+ function handleKeyUp() {
+ if (dictation.isRecording || dictation.isStarting) {
+ dictation.stopRecording();
+ }
+ }
+ window.addEventListener("buzz:dictation-key-down", handleKeyDown);
+ window.addEventListener("buzz:dictation-key-up", handleKeyUp);
return () => {
- window.removeEventListener("buzz:toggle-dictation", handleToggle);
+ window.removeEventListener("buzz:dictation-key-down", handleKeyDown);
+ window.removeEventListener("buzz:dictation-key-up", handleKeyUp);
};
- }, [dictation.toggleRecording]);
+ }, [
+ dictation.isRecording,
+ dictation.isStarting,
+ dictation.startRecording,
+ dictation.stopRecording,
+ ]);
return dictation;
}
diff --git a/desktop/src/features/dictation/ui/DictationButton.tsx b/desktop/src/features/dictation/ui/DictationButton.tsx
index 8f23d904b..f284957e1 100644
--- a/desktop/src/features/dictation/ui/DictationButton.tsx
+++ b/desktop/src/features/dictation/ui/DictationButton.tsx
@@ -1,4 +1,4 @@
-import { Mic } from "lucide-react";
+import { Mic, Square } from "lucide-react";
import { Button } from "@/shared/ui/button";
import { Tooltip, TooltipContent, TooltipTrigger } from "@/shared/ui/tooltip";
import { cn } from "@/shared/lib/cn";
@@ -51,7 +51,11 @@ export function DictationButton({
type="button"
variant={dictation.isRecording ? "default" : "ghost"}
>
-
+ {dictation.isRecording ? (
+
+ ) : (
+
+ )}
diff --git a/desktop/src/shared/lib/keyboard-shortcuts.ts b/desktop/src/shared/lib/keyboard-shortcuts.ts
index 8887ec4e1..fa9dd442f 100644
--- a/desktop/src/shared/lib/keyboard-shortcuts.ts
+++ b/desktop/src/shared/lib/keyboard-shortcuts.ts
@@ -184,7 +184,7 @@ export const KEYBOARD_SHORTCUTS: KeyboardShortcut[] = [
{
id: "voice-dictation",
label: "Voice Dictation",
- description: "Start or stop voice dictation in the composer",
+ description: "Hold to dictate, release to stop (push-to-talk)",
keys: "⌘D",
keysWindows: "Ctrl+D",
category: "Messages",