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;
|
let unlistenEvdev = null;
|
||||||
async function setupEvdevListener() {
|
async function setupEvdevListener() {
|
||||||
if (!tauriRuntimeAvailable) return;
|
if (!tauriRuntimeAvailable) return;
|
||||||
const { listen } = await import("@tauri-apps/api/event");
|
const { listen } = await import("@tauri-apps/api/event");
|
||||||
unlistenEvdev = await listen("kon:hotkey-pressed", () => {
|
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";
|
if (page.current !== "dictation") page.current = "dictation";
|
||||||
requestAnimationFrame(() => {
|
requestAnimationFrame(() => {
|
||||||
window.dispatchEvent(new CustomEvent("kon:toggle-recording"));
|
window.dispatchEvent(new CustomEvent("kon:toggle-recording"));
|
||||||
|
|||||||
Reference in New Issue
Block a user