Skip to content

Fix: prevent hotkey twitches from cancelling in-progress transcriptions#186

Open
uuuser-name wants to merge 1 commit intokitlangton:mainfrom
uuuser-name:fix/prevent-twitch-cancellation
Open

Fix: prevent hotkey twitches from cancelling in-progress transcriptions#186
uuuser-name wants to merge 1 commit intokitlangton:mainfrom
uuuser-name:fix/prevent-twitch-cancellation

Conversation

@uuuser-name
Copy link
Copy Markdown

@uuuser-name uuuser-name commented Mar 7, 2026

Problem

When using hold-to-record mode (e.g. Cmd+Option), releasing the modifier keys stops recording and begins transcription. However, involuntary finger twitches — re-pressing one or both modifier keys within milliseconds of release — trigger handleHotKeyPressed while isTranscribing is already true.

The previous implementation responded by merging Effect.send(.cancel) with Effect.send(.startRecording), which killed the active transcription and started a new (empty) recording. This caused users to lose their entire dictation with no warning.

Root Cause

In TranscriptionFeature.swift, handleHotKeyPressed always started a new recording, and additionally cancelled any in-progress transcription first:

// Before
func handleHotKeyPressed(isTranscribing: Bool) -> Effect<Action> {
    let maybeCancel = isTranscribing ? Effect.send(Action.cancel) : .none
    let startRecording = Effect.send(Action.startRecording)
    return .merge(maybeCancel, startRecording)
}

The .merge(.cancel, .startRecording) path was reachable any time a hotkey press arrived during transcription — including accidental re-presses immediately after releasing the hold-to-record key.

Fix

When isTranscribing is true, the hotkey press is now a no-op. The transcription completes uninterrupted regardless of any subsequent modifier key activity. Users can still cancel explicitly via ESC.

// After
func handleHotKeyPressed(isTranscribing: Bool) -> Effect<Action> {
    if isTranscribing { return .none }
    return .send(.startRecording)
}

Also updates the inline comment at the .hotKeyPressed case to accurately describe the new behavior.

Impact

  • Eliminates lost transcriptions caused by accidental hotkey re-presses
  • No change to intentional workflows — ESC still cancels, and new recordings start normally when no transcription is active
  • Minimal, self-contained change (one function + one comment)

Summary by CodeRabbit

  • Bug Fixes
    • Improved hotkey handling to prevent accidental transcription cancellations when the hotkey is pressed during an active transcription session. Hotkey now only starts recording when not currently transcribing, and stops recording when released.

When using hold-to-record mode (Cmd+Option), releasing the modifier
keys stops recording and begins transcription. However, involuntary
finger twitches — re-pressing one or both modifier keys within
milliseconds of release — would trigger handleHotKeyPressed while
isTranscribing was true. The previous implementation responded by
merging Effect.send(.cancel) with Effect.send(.startRecording),
which killed the active transcription and started a new (empty)
recording. This caused users to lose their entire dictation.

The fix changes handleHotKeyPressed to return .none when
isTranscribing is true, making the hotkey press a no-op during
active transcription. The transcription completes uninterrupted
regardless of any subsequent modifier key activity.

Users can still cancel explicitly via ESC if needed — this change
only prevents the accidental cancel-and-restart path that the
original merge(.cancel, .startRecording) produced.

Before:
  func handleHotKeyPressed(isTranscribing: Bool) -> Effect<Action> {
    let maybeCancel = isTranscribing ? Effect.send(Action.cancel) : .none
    let startRecording = Effect.send(Action.startRecording)
    return .merge(maybeCancel, startRecording)
  }

After:
  func handleHotKeyPressed(isTranscribing: Bool) -> Effect<Action> {
    if isTranscribing { return .none }
    return .send(.startRecording)
  }

Also updates the comment at the .hotKeyPressed case (line 99) to
accurately describe the new behavior ("ignore this press, protects
against finger twitches") instead of the stale "send a cancel first".

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai bot commented Mar 7, 2026

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: b036280d-1e94-41ba-9acb-56724c39173f

📥 Commits

Reviewing files that changed from the base of the PR and between f29125d and 1b32051.

📒 Files selected for processing (1)
  • Hex/Features/Transcription/TranscriptionFeature.swift

📝 Walkthrough

Walkthrough

Hotkey handling in TranscriptionFeature.swift was adjusted to ignore presses when transcription is already active, preventing cancellation conflicts. The implementation now uses a conditional that returns none if transcribing or starts recording otherwise.

Changes

Cohort / File(s) Summary
Hotkey Handling Logic
Hex/Features/Transcription/TranscriptionFeature.swift
Simplified hotKeyPressed logic to prevent unintended cancellations during active transcription. Changed from merging optional cancel and startRecording operations to a conditional that blocks input when already transcribing. Updated comments to clarify behavior.

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~10 minutes

Possibly related PRs

Poem

🐰 A hotkey dance, refined with care,
No accidental cancels in the air!
When fingers twitch, the recording stays,
Clean logic flows through transcribed days. ✨

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title directly and accurately describes the main change: preventing hotkey twitches from cancelling active transcriptions, which is the core fix implemented in the hotKeyPressed handler.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

Tip

Try Coding Plans. Let us write the prompt for your AI agent so you can ship faster (with fewer bugs).
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.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@hhushhas
Copy link
Copy Markdown

@kitlangton I've had multiple instances where 20 minute dictations got lost just because of this issue. Would love if you could merge this through!

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