Fix VoiceOver: hide recording overlays and pass through system hotkeys#590
Fix VoiceOver: hide recording overlays and pass through system hotkeys#590Alshekhi wants to merge 3 commits into
Conversation
- GlobalHotkeyManager: fast-path pass-through for Cmd+Tab/Space/` in the event tap so VoiceOver isn't delayed on the dictation hotkey. - BottomOverlayView: setAccessibilityElement(false)/role .unknown on all overlay panels and .accessibilityHidden(true) on the body so VoiceOver ignores the overlays. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
|
The PR Policy check is blocking this PR because required template information is missing. Please update the PR description with:
Visual files detected:
Screenshots or video are required for UI, UX, settings, onboarding, overlay, menu bar, or visual behavior changes. If this PR has no visual changes, check the no-visual-change box in the template. If this remains incomplete for 48 hours after opening, the PR may be closed. |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 7ea11f2858
ℹ️ About Codex in GitHub
Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".
| if flags.contains(.maskCommand) { | ||
| let kc = UInt16(event.getIntegerValueField(.keyboardEventKeycode)) | ||
| // Tab(48), Space(49), Backtick(50 — Cmd+` for window cycling) | ||
| if kc == 48 || kc == 49 || kc == 50 { | ||
| return Unmanaged.passUnretained(event) |
There was a problem hiding this comment.
Mark Cmd system shortcuts as modifier-only interruptions
When the configured Fluid shortcut is a modifier-only Command key, this early return runs before the .keyDown branch can call markOtherInputDuringModifierOnly(). Pressing Cmd+Tab, Cmd+Space, or Cmd+while holding Command therefore leavesotherKeyPressedDuringModifierfalse; on the later CommandflagsChangedrelease,handleModifierOnlyShortcutFlagsChanged` treats the sequence as a clean tap and can toggle/start dictation instead of ignoring the combo. Please record the interruption before passing these keys through, or move the pass-through after that bookkeeping.
Useful? React with 👍 / 👎.
Greptile SummaryThis PR fixes two VoiceOver accessibility regressions: recording overlays being discoverable by VoiceOver, and Cmd+Tab / Cmd+Space gaining latency from the CGEvent tap while a dictation shortcut is active.
|
Addresses automated review feedback on the system-shortcut fast-path: - Skip the fast-path when the chord matches a user-configured Fluid shortcut (cancel, paste-last, prompt, command, rewrite, primary), so real bindings on Cmd+Tab/Space are never silently swallowed. - Call markOtherInputDuringModifierOnly() before passing the event through, so a system chord pressed during a modifier-only shortcut isn't mistaken for a clean tap on release. - Drop keycode 50 (Cmd+`): it is ANSI-only and maps to a different physical key on ISO layouts. Keep only Tab(48)/Space(49), which are layout-stable. - Extract flag->modifier mapping into modifierFlags(from:) so the tap and matcher share one source of truth. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 66b909a60c
ℹ️ About Codex in GitHub
Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".
| if self.commandModeShortcutEnabled, | ||
| let commandModeShortcut = self.commandModeShortcut, | ||
| commandModeShortcut.matches(keyCode: keyCode, modifiers: modifiers) | ||
| { |
There was a problem hiding this comment.
Include prompt-mode shortcuts in fast-path guard
When the Secondary Dictation/Prompt Mode shortcut is assigned to Cmd+Space or Cmd+Tab, this helper still returns false because it checks prompt assignments and then jumps straight to command mode without testing promptModeShortcut. The new fast path in handleKeyEvent then returns before handlePromptModeKeyDown can run, so that configured Fluid binding is ignored and the macOS system shortcut fires instead. Please include the enabled prompt-mode shortcut in this guard like the other configured shortcuts.
Useful? React with 👍 / 👎.
Codex review noted eventMatchesAnyConfiguredShortcut checked prompt assignments and command mode but skipped the prompt-mode shortcut (promptModeShortcut / handlePromptModeKeyDown). A prompt-mode binding on Cmd+Tab/Space would still be swallowed. Add it so the guard covers every configured shortcut the keyDown path matches. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
|
Thanks for the automated reviews — I've addressed every point:
Scope: this PR addresses root causes 1 and 3 of #401 (bottom-overlay accessibility + event-tap latency). Root cause 2 (DynamicNotchKit's Verified locally on Apple Silicon, macOS 26.5.2, with VoiceOver enabled: Cmd+Tab/Cmd+Space are no longer sluggish and the recording overlays are no longer announced by or focusable in VoiceOver. |
Description
Two accessibility fixes for VoiceOver users:
GlobalHotkeyManager— a fast-path in the CGEvent tap that passes the macOS system shortcuts Cmd+Tab and Cmd+Space straight through, so the tap doesn't add latency to app/space switching while the dictation hotkey is active. This is especially noticeable with VoiceOver, whose own event handling is latency-sensitive. The fast-path is skipped when the user has bound one of Fluid's own shortcuts to the same chord, and it preserves the modifier-only bookkeeping so a system chord pressed mid-hold isn't mistaken for a clean tap.BottomOverlayView—setAccessibilityElement(false)+setAccessibilityRole(.unknown)on all four recording-overlay panels, and.accessibilityHidden(true)on the SwiftUI body, so VoiceOver doesn't announce the overlays or shift focus to them.Both match behavior the repo's
CLAUDE.mdalready documents as intended (overlay panels must hide from VoiceOver; the event tap must pass system shortcuts through immediately).Type of Change
Related Issue or Discussion
Addresses #401 (VoiceOver: dictation overlay steals focus and Cmd+Tab becomes sluggish).
This PR fixes the two root causes in this repo:
BottomOverlayViewpanels being discoverable by VoiceOver (overlay focus stealing).The third root cause noted in #401 —
DynamicNotchKit'sDynamicNotchPaneloverridingcanBecomeKey/canBecomeMain— lives in the separate DynamicNotchKit dependency and is out of scope here; it only affects the non-default notch overlay and would need a fix in that repo. This PR covers the default bottom overlay path, so #401 is intentionally addressed, not auto-closed.Testing
swiftlint --strict --config .swiftlint.yml Sourcesswiftformat --config .swiftformat SourcesManually verified: pressing the dictation hotkey with VoiceOver active no longer delays Cmd+Tab / Cmd+Space; the recording overlays are no longer announced by or focusable in VoiceOver.
Screenshots / Video
The overlay change only sets accessibility attributes (
setAccessibilityElement,setAccessibilityRole,.accessibilityHidden) — it does not alter rendering in any way.Notes
Tested locally against the current
main. The hotkey fast-path is limited to Cmd+Tab and Cmd+Space (both layout-stable hardware keycodes) to avoid the ANSI/ISO ambiguity of the backtick key.