Skip to content

Stop regenerating suggestion on Enter so post-Enter actions aren't masked#359

Merged
FuJacob merged 1 commit into
mainfrom
fix/enter-key-dismissal
May 28, 2026
Merged

Stop regenerating suggestion on Enter so post-Enter actions aren't masked#359
FuJacob merged 1 commit into
mainfrom
fix/enter-key-dismissal

Conversation

@FuJacob

@FuJacob FuJacob commented May 28, 2026

Copy link
Copy Markdown
Owner

Summary

Return (keycode 36) and Keypad Enter (76) were classified as .textMutation alongside Backspace and forward-delete in InputMonitor.classify(event:). .textMutation both clears the visible suggestion and schedules a fresh prediction via SuggestionCoordinator+Input.swift. The result: pressing Enter while a suggestion was on screen would clear it, then ~50-500ms later the regenerated suggestion's overlay popped back up over whatever Enter actually did in the field (find-next in a Find Bar, submit, send), so it felt like Cotabby had swallowed the key. The keystroke itself wasn't being consumed — the accept tap only swallows the configured accept binding — but the racing overlay obscured the post-Enter result.

Reclassify Return and Keypad Enter as .dismissal, the same bucket Escape already lives in. Suggestion still clears on Enter; no fresh prediction fires until the user types an actual character. In multi-line fields that means a one-keystroke delay before the next-line prediction shows, which is a much smaller cost than masking the user's intended action everywhere else.

Validation

xcodebuild -project Cotabby.xcodeproj -scheme Cotabby -destination 'platform=macOS' build
# ** BUILD SUCCEEDED **

swiftlint lint --quiet Cotabby/Services/Input/InputMonitor.swift
# exit 0

Manual repro before fix: open a browser, ⌘F to bring up the Find Bar, type a query, wait for Cotabby's suggestion overlay, press Enter. The overlay regenerates and obscures the find-next result. After fix: Enter dismisses the overlay cleanly and the find-next is unobscured.

Risk / rollout notes

  • Single-file, classification-only change. No state-machine or coordinator edits.
  • Behavior change is intentional and observable only when a suggestion is currently visible. In all other states pressing Enter was already a no-op for Cotabby.
  • Trade-off: in a multi-line text area, hitting Enter for a newline no longer kicks an immediate fresh prediction for the empty new line. The next typed character does. This matches how Escape already behaves and is the lesser-evil compared to the current regression.
  • No new tests: InputMonitor.classify(...) is private and operates on a CGEvent, so unit-testing it cleanly would require a small refactor (extract a pure helper); deferred so this fix stays minimal.

Greptile Summary

This PR reclassifies Return (keyCode 36) and Keypad Enter (76) from .textMutation to .dismissal in InputMonitor.classify(event:). Previously, pressing Enter while a suggestion was visible would clear the overlay and immediately schedule a fresh prediction, causing the regenerated overlay to race with and obscure the app's native response to Enter (e.g., find-next in a Find Bar, form submit, chat send).

  • Moves keycodes 36 and 76 out of the textMutation branch and into the dismissal branch alongside Escape (53), so shouldSchedulePrediction returns false for Enter and the coordinator sets state to .idle instead of queuing another request.
  • The change correctly handles both the "active session" path (handleInputEvent(_:with:), which now routes Enter to case .navigation, .dismissal: → invalidate + idle) and the "no active session" path (clears suggestion, skips prediction scheduling).
  • One minor documentation drift: the private isDirectTextMutation comment in SuggestionSessionReconciler.swift reads "Control characters such as backspace or return require regeneration" — Return no longer triggers regeneration after this change, so that comment is slightly stale. Worth a follow-up update.

Confidence Score: 5/5

Single-classification change with no state-machine or coordinator edits; both code paths that consume the event kind behave correctly for .dismissal.

The change touches exactly one conditional in one file. The .dismissal kind is already handled by every downstream consumer: shouldClearSuggestion returns true (overlay clears), shouldSchedulePrediction returns false (no racing prediction), and the active-session switch arm case .navigation, .dismissal: already existed and sets state to .idle. Return's character was already excluded from optimistic session advancement by isDirectTextMutation, so there is no silent behaviour gap. The only loose end is a now-stale doc comment in an untouched file, which carries no runtime impact.

No files require special attention. The stale isDirectTextMutation comment in SuggestionSessionReconciler.swift is the only follow-up worth noting, but it does not affect runtime behaviour.

Important Files Changed

Filename Overview
Cotabby/Services/Input/InputMonitor.swift Two keycodes (36=Return, 76=Keypad Enter) moved from .textMutation to .dismissal; the rest of the classify function is untouched. The change correctly threads through both the active-session and no-session coordinator paths — suggestion clears, no fresh prediction fires, state returns to idle.

Reviews (1): Last reviewed commit: "Stop regenerating suggestion on Enter so..." | Re-trigger Greptile

…sked

Return (keycode 36) and Keypad Enter (76) were grouped with Backspace and
forward-delete as .textMutation, which both clears the visible suggestion
AND immediately schedules a fresh prediction. The fresh suggestion's
overlay then re-appears ~50-500ms later over whatever Enter actually did
in the focused field (find-next in a Find Bar, form submit, send-message
in a chat composer), making it feel like Enter was swallowed.

Reclassify Return and Keypad Enter as .dismissal alongside Escape. They
already clear the suggestion via .dismissal's shouldClearSuggestion path
but no longer fire a fresh prediction. In multi-line fields the next
typed character will schedule one anyway, so the only behavior change is
that an immediate post-Enter overlay no longer races the user's intended
action.
@FuJacob FuJacob merged commit bb906dc into main May 28, 2026
4 checks passed
@FuJacob FuJacob deleted the fix/enter-key-dismissal branch May 28, 2026 10:16
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.

1 participant