Skip to content

Refactor: extract key-capture accept/translate rules into KeyCaptureRules#11

Merged
GamalAnwar merged 1 commit into
masterfrom
claude/solid-refactor-keycapture-rules
Jun 18, 2026
Merged

Refactor: extract key-capture accept/translate rules into KeyCaptureRules#11
GamalAnwar merged 1 commit into
masterfrom
claude/solid-refactor-keycapture-rules

Conversation

@GamalAnwar

Copy link
Copy Markdown
Contributor

Summary

Slice 2 of 2. Both single-flight key captures — ShortcutCapture (global hotkeys) and EditorKeyCapture (editor tool rebinding) — embedded their pure accept/translate decisions inside the NSEvent monitor closures, where they were untestable. This lifts the decisions out:

HotKeyCaptureRule.combination(keyCode:modifiers:)  // nil unless ≥1 device-independent modifier
EditorToolKey.binding(char:modifiers:)             // lowercase, validate charset, ⇧-prefix iff Shift-only

The monitor closures now read as control-flow only:

// ShortcutCapture
guard let combo = HotKeyCaptureRule.combination(keyCode: event.keyCode,
                                                modifiers: event.modifierFlags) else { return nil }
// EditorKeyCapture
guard let raw = event.charactersIgnoringModifiers,
      let key = EditorToolKey.binding(char: raw, modifiers: event.modifierFlags) else { return nil }

Scope / safety

  • Behavior-preserving. Same modifier-required rule, same charset, same mods == .shift exact-match for the ⇧ prefix, same .deviceIndependentFlagsMask intersection.
  • The single-flight mechanism (static active, cancelActive, monitor add/remove, Esc-cancel, flagsChanged ignore) is deliberately left in place — it's inherently NSEvent-coupled and not unit-testable. Only the pure rules moved.

Tests

KeyCaptureRulesTests (10 cases): plain letter, lowercasing, ⇧-prefix on Shift, no prefix for Command, Shift+other not treated as Shift, valid symbol/digit, empty/space rejected; and for hotkeys: modifier required, keyCode+modifiers preserved, non-device-independent bits stripped.

Note

This is independent of #10 (thumbnail geometry) — different files, both branched off master. They can merge in any order.

🤖 Generated with Claude Code


Generated by Claude Code

…ules

ShortcutCapture (global hotkeys) and EditorKeyCapture (editor tool rebinding)
embedded their pure accept/translate decisions inside NSEvent monitor closures,
where they couldn't be tested. Lift them out:

- HotKeyCaptureRule.combination(keyCode:modifiers:) — requires >=1 device-
  independent modifier, else nil.
- EditorToolKey.binding(char:modifiers:) — lowercases, validates the char set,
  prefixes the Shift glyph only when Shift alone is held.

The single-flight monitor mechanism (which is NSEvent-coupled) stays in place;
only the testable rules move. Behavior-preserving. Adds KeyCaptureRulesTests
(10 cases).
@GamalAnwar GamalAnwar merged commit 191f223 into master Jun 18, 2026
1 check passed
@GamalAnwar GamalAnwar deleted the claude/solid-refactor-keycapture-rules branch June 18, 2026 07:36
GamalAnwar added a commit that referenced this pull request Jun 18, 2026
…ules (#11)

Lift the pure accept/translate decisions out of ShortcutCapture and
EditorKeyCapture's NSEvent monitor closures into HotKeyCaptureRule.combination(
keyCode:modifiers:) and EditorToolKey.binding(char:modifiers:). The NSEvent-
coupled single-flight mechanism stays in place. Behavior-preserving. Adds
KeyCaptureRulesTests (10 cases). CI green.
GamalAnwar added a commit that referenced this pull request Jun 18, 2026
…ules (#11)

Lift the pure accept/translate decisions out of ShortcutCapture and
EditorKeyCapture's NSEvent monitor closures into HotKeyCaptureRule.combination(
keyCode:modifiers:) and EditorToolKey.binding(char:modifiers:). The NSEvent-
coupled single-flight mechanism stays in place. Behavior-preserving. Adds
KeyCaptureRulesTests (10 cases). CI green.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants