feat(B.1 #4 UX): debounce hotkey press events by 120ms
A rapid double-tap of the global hotkey, evdev autorepeat, or a sticky-key compositor quirk (KDE's 'slow keys') can all deliver the same press twice within ~100ms. Without a guard, the recording toggles into and out of the same frame and the capture is lost. Gates the evdev 'kon:hotkey-pressed' forwarder in +layout.svelte behind a 120ms debounce (Date.now()-based; no timers, so no tail latency for a legitimate single press). The debounce is intentionally shorter than a deliberate double-press cadence but longer than any autorepeat interval we've seen in the wild. The audio-stream-warming half of brief item #4 (Handy #1143) lives in Workstream A's Phase A.3 warm-up WAV; this covers the UX side. Co-authored-by: jars <jakejars@users.noreply.github.com>
This commit is contained in:
@@ -136,12 +136,25 @@
|
||||
}
|
||||
}
|
||||
|
||||
// Listen for evdev hotkey events from the Rust backend
|
||||
// Listen for evdev hotkey events from the Rust backend.
|
||||
//
|
||||
// Debounce window: evdev autorepeat, a sticky-key compositor quirk,
|
||||
// or a user's nervous double-tap can all deliver the same press
|
||||
// twice within ~100 ms — which, without debouncing, toggles the
|
||||
// recording into and out of the same frame and loses the capture.
|
||||
// Matches Handy #1143 ('first press records nothing, second works').
|
||||
// This is the UX side of brief item #4; the audio-stream warm-up
|
||||
// side is owned by Workstream A.
|
||||
const HOTKEY_DEBOUNCE_MS = 120;
|
||||
let lastHotkeyAtMs = 0;
|
||||
let unlistenEvdev = null;
|
||||
async function setupEvdevListener() {
|
||||
if (!tauriRuntimeAvailable) return;
|
||||
const { listen } = await import("@tauri-apps/api/event");
|
||||
unlistenEvdev = await listen("kon:hotkey-pressed", () => {
|
||||
const now = Date.now();
|
||||
if (now - lastHotkeyAtMs < HOTKEY_DEBOUNCE_MS) return;
|
||||
lastHotkeyAtMs = now;
|
||||
if (page.current !== "dictation") page.current = "dictation";
|
||||
requestAnimationFrame(() => {
|
||||
window.dispatchEvent(new CustomEvent("kon:toggle-recording"));
|
||||
|
||||
Reference in New Issue
Block a user