Add single-tap toggle recording mode + fix hotkey character leak#167
Add single-tap toggle recording mode + fix hotkey character leak#167pchalasani wants to merge 1 commit intokitlangton:mainfrom
Conversation
Introduces a RecordingMode enum (.singleTap / .doubleTap) replacing the old useDoubleTapOnly boolean. Single-tap mode (new default) lets users tap the hotkey once to start recording, tap again to stop — no need for press-and-hold or double-tap. Double-tap mode preserves the existing hold-to-record + double-tap-lock behaviour. Also fixes a bug where the hotkey's printable character (e.g. semicolon in Control+Semicolon) leaked into the active app when stopping a recording in toggle/doubleTapLock mode, by intercepting the key event on .stopRecording when the key matches the hotkey. Settings UI updated: the "Use double-tap only" toggle (key+modifier only) is replaced with a "Single Tap / Double Tap" segmented picker shown for all hotkey types. Backwards-compatible: old JSON with useDoubleTapOnly=true decodes as .doubleTap. Includes 7 new HotKeyProcessor tests covering single-tap toggle for standard and modifier-only hotkeys, release persistence, and ESC cancellation.
📝 WalkthroughWalkthroughThis PR replaces a boolean Changes
Sequence Diagram(s)sequenceDiagram
actor User
participant UI as HotKeySectionView
participant Settings as HexSettings
participant Feature as TranscriptionFeature
participant Processor as HotKeyProcessor
User->>UI: Select Recording Mode (Single/Double Tap)
UI->>Settings: Update recordingMode property
Settings->>Feature: Notify settings change
Feature->>Processor: Initialize with recordingMode
Processor->>Processor: Configure state machine<br/>(singleTap → doubleTapLock path<br/>doubleTap → hold-to-record)
Processor->>Feature: Ready for hotkey detection
User->>Processor: Press hotkey
alt Single Tap Mode
Processor->>Processor: Transition to doubleTapLock
Processor->>Feature: Emit startRecording
User->>Processor: Release hotkey (matching key)
Processor->>Feature: Emit stopRecording
else Double Tap Mode
Processor->>Processor: Start timing
Processor->>Feature: Emit startRecording (on hold)
User->>Processor: Release hotkey
Processor->>Feature: Emit stopRecording
end
Estimated code review effort🎯 4 (Complex) | ⏱️ ~60 minutes Possibly related PRs
Poem
🚥 Pre-merge checks | ✅ 2 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing touches
🧪 Generate unit tests (beta)
No actionable comments were generated in the recent review. 🎉 🧹 Recent nitpick comments
Tip Issue Planner is now in beta. Read the docs and try it out! Share your feedback on Discord. Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: f540401fe4
ℹ️ 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".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| if recordingMode == .singleTap { | ||
| // Toggle mode: go straight to doubleTapLock | ||
| // (release will be ignored; next press stops) | ||
| state = .doubleTapLock |
There was a problem hiding this comment.
Preserve transcription cancel path before single-tap start
In single-tap mode, the first matching hotkey press now transitions directly to .doubleTapLock and returns .startRecording, which causes TranscriptionFeature.startHotKeyMonitoringEffect to dispatch .startRecording directly instead of .hotKeyPressed; that bypasses handleHotKeyPressed’s isTranscribing cancellation branch, so pressing the hotkey while a previous transcription is still running can leave the old transcription in flight while a new recording starts, leading to overlapping workflows and stale transcript output.
Useful? React with 👍 / 👎.
| state = .doubleTapLock | ||
| return .startRecording |
There was a problem hiding this comment.
Retain accidental click discard for modifier-only single-tap
Setting single-tap starts recording by immediately entering .doubleTapLock, but processMouseClick() only performs the modifier-only accidental-trigger discard while in .pressAndHold and always ignores clicks in .doubleTapLock; this means common Option+click interactions now keep recording running instead of being discarded within the safety window, regressing the existing protection against accidental modifier-only activations.
Useful? React with 👍 / 👎.
Summary
.singleTap/.doubleTap) replacing theuseDoubleTapOnlyboolean^;), the character (semicolon) no longer leaks into the active appuseDoubleTapOnly=truedecodes as.doubleTapFiles changed
HexCore/.../HexSettings.swiftRecordingModeenum, replaceuseDoubleTapOnlyproperty, backwards-compat decodeHexCore/.../HotKeyProcessor.swiftuseDoubleTapOnlywithrecordingMode, update state machine for toggle modeHex/.../TranscriptionFeature.swiftrecordingMode, intercept key on.stopRecordingto prevent character leakHex/.../HotKeySectionView.swiftHexCore/.../HotKeyProcessorTests.swiftrecordingModeparamHexCore/.../HexSettingsMigrationTests.swiftTest plan
cd HexCore && swift test— all 67 tests pass (7 new + 60 existing)^;, verify single-tap starts/stops recordingSummary by CodeRabbit
New Features
Bug Fixes