[BUG] Modifier-only stop press is dropped when the health check rebuilds the event tap
Maintainers usually reply within 1 day
Nobody has claimed this yet.
Assessment
- Difficulty
- 3/5
- Estimated time
- 1-2 days
- Newbie friendliness
- 40/100
Research direction
Start in GlobalHotkeyManager.swift at cleanupEventTap, which calls only clearPrimaryShortcutPressState, and compare it with handleTapDisableEvent, which calls resetModifierOnlyShortcutTracking. Read evaluate and flagsChanged to see how modifierOnlyKeyDown, activeModifierOnlyType and pressedModifierKeyCodes block the next press. Done when a tap rebuild mid-press leaves no stale modifier-only state and the next full press of the shortcut works. The issue proposes a quarantine rule that needs maintainer agreement before you implement it.
Written by the indexing model from the issue text.
Description
Describe the bug
When the 30s health check finds the keyboard tap disabled (GlobalHotkeyManager.swift:2513-2515), it calls setupGlobalHotkey() → cleanupEventTap() (:737-738, :799-805). cleanupEventTap only calls clearPrimaryShortcutPressState() (:1168-1185). That clears isKeyPressed and the .transcription automatic/hold tracking, but leaves the modifier-only state in place: modifierOnlyKeyDown, activeModifierOnlyType, activeModifierOnlyShortcut, pressedModifierKeyCodes, otherKeyPressedDuringModifier.
handleTapDisableEvent (:2728) does a full resetModifierOnlyShortcutTracking(reason: .tapDisabled) (:2740), but only when a tapDisabledBy* event is delivered. In the cases we observed, no such event arrived and the tap was rebuilt only by the health check.
Effect when a modifier-only stop press is in flight at the rebuild:
- If the key-up was missed while the tap was disabled,
activeModifierOnlyTypestays set andmodifierOnlyKeyDownstaystrue. Any typing or click in the meantime marks the stale press as interrupted (:1188-1191). On the next press,evaluatewill not start a new press becauseactiveModifierOnlyType != nil(:125), and it ignores the key-down because the key is already inpressedModifierKeyCodes(:149). The key-up then ends the stale press as not clean, and the stop is rejected with "Transcription modifier released but another key was pressed - ignoring". - If the key-up does reach the new tap, hold mode still does not stop:
isKeyPressedwas cleared (:1177), sofinishModifierOnlyPressskips the.holdbranch (:1926-1927). Automatic mode loses the start time andwasTargetActive(:500-501).
synchronizedPressedModifierKeyCodes does not recover this. It only trims key codes whose modifier flag is no longer active; it never clears activeModifierOnlyType or modifierOnlyKeyDown.
Actual behavior
The stop press is logged with targetActive=true. The health check then finds the tap disabled and rebuilds it mid-press, and the release produces no stop. Escape was the only way out.
Expected behavior
A tap rebuild discards any half-finished press as a whole, and the next complete press of the shortcut works normally.
Fix
cleanupEventTapresets all in-flight press state when the tap is replaced: the modifier-only fields above, all mode-pressed flags, and the automatic/hold tracking for every mode. It does not stop or cancel the recording.- If a modifier press was in flight,
flagsChangedevents are ignored until an event arrives with no relevant modifiers down. The orphaned release or a partial press then cannot start or stop anything, and the next complete press is evaluated from a clean state.
This makes the tap-rebuild path consistent with the tap-disabled path, without ending the recording. Cost: if the key-up was missed while the tap was down, the first full press after recovery only clears the quarantine, so one extra press is needed to stop.
App Version
1.6.12-beta.1 (2ca292d1)
- Dominant language
- Swift
- Stars
- 11.9k
- Forks
- 863
- Avg merge
- 1d 20h
- Merged PRs (30d)
- 36
Getting set up
This project ships no dev container, Dockerfile or contributing guide, so setting up is up to you: start from its README, and see our first-contribution guide for the general steps.
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
More from altic-dev/FluidVoice
-
bug
Difficulty 2/5 1-3 hours Newbie friendliness 62/100
altic-dev/FluidVoice#1284 ·
Maintainers usually reply within 1 day
-
stale
Difficulty 2/5 1-3 hours Newbie friendliness 78/100
altic-dev/FluidVoice#1191 · 3 comments ·
Maintainers usually reply within 1 day
-
enhancement
Difficulty 4/5 3-5 days Newbie friendliness 48/100
altic-dev/FluidVoice#1307 ·
Maintainers usually reply within 1 day
-
Difficulty 3/5 1-2 days Newbie friendliness 68/100
altic-dev/FluidVoice#1304 · 1 comment ·
Maintainers usually reply within 1 day
-
enhancement
Difficulty 5/5 Over a week Newbie friendliness 25/100
altic-dev/FluidVoice#1298 ·
Maintainers usually reply within 1 day
All issues in altic-dev/FluidVoice
Similar issues
-
Difficulty 2/5 1-3 hours Newbie friendliness 77/100
carp-dk/flutter-plugins#1312 ·
-
Difficulty 2/5 1-3 hours Newbie friendliness 76/100
newrelic/newrelic-flutter-agent#256 ·
Maintainers usually reply within 1 day
-
Difficulty 1/5 1-3 hours Newbie friendliness 83/100
Maintainers usually reply within 1 day
-
Difficulty 2/5 1-3 hours Newbie friendliness 74/100
jhomlala/betterplayer#1646 ·
Maintainers usually reply within 1 day
-
Feature
Difficulty 2/5 1-3 hours Newbie friendliness 72/100
amplitude/Amplitude-Flutter#337 · 1 comment ·
Maintainers usually reply within 1 day