[BUG] Auto (Hold or Toggle) incorrectly handles the first shortcut activation after startup
Before You Submit
I searched the existing issues for similar reports. Related PR: #1971
Bug Description
When using the Auto (Hold or Toggle) shortcut mode, the first shortcut activation after Handy starts behaves incorrectly.
The expected behavior is:
- Short press (< hold threshold): starts recording and keeps recording (toggle behavior).
- Long press (≥ hold threshold): records only while the shortcut is held (push-to-talk behavior).
However, after starting Handy, the first shortcut activation consistently behaves incorrectly, while subsequent activations work normally.
The issue appears to happen once after every Handy startup.
I initially tested this with the default 300 ms hold threshold, and the issue occurs there as well. I also tested with a longer hold threshold, and the same problem still occurs.
I also switched between Tauri Global Shortcuts and Tari Global Shortcuts, but the behavior is the same with both.
Steps to reproduce
- Start Handy.
- Set shortcut activation mode to Auto.
- Leave the hold threshold at the default 300 ms.
- Use the configured shortcut for the first time after startup.
- Make a short press.
- Observe that the first activation does not behave as expected.
- Use the shortcut again.
- Subsequent activations work correctly.
Expected behavior
The first shortcut activation should behave exactly like all subsequent activations and correctly distinguish between a short press and a long hold.
Actual behavior
The first activation after startup is incorrectly handled, while subsequent activations work normally.
The issue is reproducible and appears to reset after restarting Handy.
Additional observation
The logs show that the first shortcut activation triggers microphone initialization, which takes around 430 ms in the affected run.
Interestingly, the shortcut Released event arrives while this initialization is still in progress:
[handy_app_lib::shortcut::handy_keys][DEBUG] handy-keys event: binding=transcribe, hotkey=ctrl_right, state=Pressed
[handy_app_lib::actions][DEBUG] TranscribeAction::start called for binding: transcribe
[handy_app_lib::shortcut::handy_keys][DEBUG] handy-keys event: binding=transcribe, hotkey=ctrl_right, state=Released
[handy_app_lib::managers::audio][INFO] Microphone stream initialized in 430.5938ms
[handy_app_lib::managers::audio][DEBUG] Recording request accepted in 430.662ms; waiting for first microphone samples
[handy_app_lib::actions][DEBUG] TranscribeAction::start completed in 432.7184ms
[handy_app_lib::actions][DEBUG] TranscribeAction::stop called for binding: transcribeThe default Auto threshold is 300 ms, so the microphone initialization is longer than the threshold.
This may be related to the issue, although I am not sure whether the problem is in the shortcut handling, recording initialization, or the interaction between the two.
Another notable detail is that the affected first activation produces 0 audio samples.
Shortcut backend
I tested both:
- Tauri Global Shortcuts
- Tari Global Shortcuts
The issue occurs with both backends, so it does not appear to be specific to one of them.
I also tested a longer hold threshold instead of the default 300 ms, and the first-use issue still occurs.
System Information
App Version: 0.9.6
Operating System: Windows 11
CPU: AMD Ryzen 7
GPU: AMD Radeon 780M Graphics
Logs
Relevant excerpt from the affected session:
[handy_app_lib::shortcut::handy_keys][DEBUG] handy-keys event: binding=transcribe, hotkey=ctrl_right, state=Pressed
[handy_app_lib::actions][DEBUG] TranscribeAction::start called for binding: transcribe
[handy_app_lib::managers::transcription][INFO] transcribe.cpp accelerator preference: Auto (applied on next model load)
[handy_app_lib::managers::transcription][INFO] ORT accelerator set to: auto
[handy_app_lib::actions][DEBUG] start-path pre-recording steps completed
[handy_app_lib::managers::audio][DEBUG] device resolve: no mic configured -> system default
[handy_app_lib::shortcut::handy_keys][DEBUG] handy-keys event: binding=transcribe, hotkey=ctrl_right, state=Released
[handy_app_lib::managers::audio][INFO] Initialized Silero VAD backend
[handy_app_lib::managers::audio][INFO] Using device: [REDACTED]
[handy_app_lib::audio_toolkit::audio::recorder][DEBUG] mic worker init: fetch_config=9.3394ms (cached=false) build_stream=343.2038ms
[handy_app_lib::managers::audio][DEBUG] mic stream breakdown: device_resolve=92µs vad_ensure=75.5578ms open=354.7623ms
[handy_app_lib::managers::audio][INFO] Microphone stream initialized in 430.5938ms
[handy_app_lib::managers::audio][DEBUG] Recording requested for binding transcribe
[handy_app_lib::actions][DEBUG] Recording request accepted in 430.662ms; waiting for first microphone samples
[handy_app_lib::audio_toolkit::audio::recorder][DEBUG] Cmd::Start processed 211.7µs after send; capture begins with the next available samples
[handy_app_lib::actions][DEBUG] TranscribeAction::start completed in 432.7184ms
[handy_app_lib::audio_toolkit::audio::recorder][DEBUG] first audio samples arrived 16.1849ms after stream start
[handy_app_lib::actions][DEBUG] Microphone is receiving samples; recording is ready
[handy_app_lib::actions][DEBUG] TranscribeAction::stop called for binding: transcribe
[handy_app_lib::shortcut::handy_keys][DEBUG] Unregistered handy-keys shortcut: cancel
[handy_app_lib::actions][DEBUG] TranscribeAction::stop completed in 1.3085ms
[handy_app_lib::actions][DEBUG] Starting async transcription task for binding: transcribe
[handy_app_lib::managers::audio][DEBUG] Extra recording buffer: sleeping 500ms before stopping
[handy_app_lib::managers::audio][DEBUG] Microphone stream stopped
[handy_app_lib::actions][DEBUG] Recording stopped and samples retrieved; sample count: 0
[handy_app_lib::actions][DEBUG] Recording produced no audio samples; skipping persistenceSource: cjpais/Handy