Skip to content

Sweep fix: Caps Lock no longer blocks the dictation key or chord shortcuts - #2113

Merged
r3dbars merged 3 commits into
mainfrom
claude/sweep-fix-capslock-dictation-key
Oct 8, 2026
Merged

r3dbars merged 3 commits into
mainfrom
claude/sweep-fix-capslock-dictation-key

Conversation

@r3dbars

@r3dbars r3dbars commented Oct 7, 2026 •

Copy link
Copy Markdown
Owner

Two bugs from the Haiku sweep, one PR because they're the same fix: Caps Lock was being treated as a held modifier.

Caps Lock kills the Right Option dictation key

With Caps Lock latched, every event-tap flag set carries the capsLock bit. isModifierBindingActive compared option|capsLock minus option (so just capsLock) against the default binding's modifiers (0), got false, and dictation never started. Recording a modifier-key binding with Caps Lock on also saved the bit.

  • Failing test: testPhysicalDictationTriggerCapsLock suites "The Right Option dictation key still presses and releases with Caps Lock on" and "A modifier chord recorded with Caps Lock on does not need Caps Lock to fire". Red before the fix (press didn't match, recorded binding had modifiers 32), green after.
  • Fix: the modifier-key matcher ignores capsLock on both sides, and bindingForFlagsChanged drops it when recording. Duplicate detection already did this.

Caps Lock kills chord shortcuts (Option-M etc.)

matchesKeyDown checked binding.modifiers == (modifiers & .all), and .all includes capsLock, so Option-M with Caps Lock on was 0x04 vs 0x24 and just did nothing. Recording a chord with Caps Lock on stored the bit, so that shortcut then only worked with Caps Lock on.

  • Failing test: suite "Option-M and other chord shortcuts still fire with Caps Lock on" in the same file. Red before (3 failures), green after.
  • Fix: matchesKeyDown compares with capsLock masked out of both sides, and bindingForKeyDown doesn't record it. A real extra modifier (Command, Shift) still blocks a match; the test checks that too.

The Caps Lock key's own binding path is unchanged.

Review follow-up: bindings saved by older builds

A key recorded with Caps Lock on before this fix still had the bit on disk. For something like Left Option + Caps, pressing Caps Lock while holding Option counted as the dictation key (could start dictation or swallow Caps Lock), and Settings labelled it "Caps". Stored bindings now load with the bit stripped (in safeBinding, which every loader goes through).

  • Failing test: suite "A key saved with the Caps Lock bit by an older build loads without it". Red before (4 failures: loaded binding kept modifiers 32, Caps Lock press matched, label had "Caps", Option-Caps-M stayed 36), green after.

Overlaps

#2033 (removes dead paste-last / Hands-Free code) touches PhysicalDictationTriggerPreferences.swift and its test file, but different hunks: it doesn't touch the matchers or bindingFor*. I put the new tests in their own file (Tests/PhysicalDictationTriggerCapsLockTests.swift) so whichever lands second merges clean.

Checks

  • bash run-tests.sh --filter for testPhysicalDictationTriggerCapsLock, testPhysicalDictationTriggerPreferences, testPhysicalShortcutMatcher, testContextCaptureEnginePolicy, testHotkeyPreferences: all green
  • bash check.sh on the combined branch with all sweep fixes: PASS (build, 42,163 fast tests, all 19 steps)
  • bash build.sh --no-open on this branch alone: pass, plus the filtered suites above
  • check-source-pins.py --changed-only: pass
  • Not checked on real hardware: an actual Caps Lock + Right Option press in the running app.

Independent review: approve with nits; review fixes pushed. Remaining nits: none open on this one.

Re-review of the review fixes (round 2): approve. The new test goes red on the previous head; nothing new found.

🤖 Generated with Claude Code

With Caps Lock latched, every event tap flag set carries the capsLock bit,
so the modifier-key matcher compared option|capsLock against a binding with
no modifiers and the default Right Option key never started dictation.
Caps Lock is a toggle, so the matcher and the recorder now ignore it for
modifier-key bindings, the same way duplicate detection already did.
matchesKeyDown compared the binding against event modifiers that still had
the Caps Lock bit, so Option-M (and any other chord) silently did nothing
while Caps Lock was latched. Recording a chord with Caps Lock on also saved
the bit, so that shortcut then only worked with Caps Lock on. Both now
ignore Caps Lock.
Review follow-up. A binding recorded with Caps Lock on before this fix
still carried the bit on disk, so pressing Caps Lock while holding Left
Option counted as that dictation key, and Settings labelled it "Caps".
Stored bindings now load with the bit stripped.
@r3dbars
r3dbars marked this pull request as ready for review October 8, 2026 10:22
@r3dbars r3dbars added the waiting-on-human Needs a human (Justin) to look before anything else happens label Oct 8, 2026
@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Oct 8, 2026 •

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review ✅ Completed 2026-10-08T10:25:01.187572Z 1de66a4 Draft marked ready
🔒 Security Review ✅ Completed 2026-10-08T10:29:05.192817Z 1de66a4 Draft marked ready
ℹ️ About Codex in GitHub

Your team has set up Codex to 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" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@r3dbars
r3dbars merged commit f988869 into main Oct 8, 2026
12 checks passed
@r3dbars
r3dbars deleted the claude/sweep-fix-capslock-dictation-key branch October 8, 2026 12:42
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

waiting-on-human Needs a human (Justin) to look before anything else happens

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant