Commit :emoji: on Tab even while a suggestion overlay is visible#458
Merged
Conversation
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
The inline
:emoji:picker would ignore the first Tab and only start working on later attempts. Root cause: the emoji commit fires from the listen-only observer pass (EmojiPickerController.observe()→machine.reduce(.commitKey)); the consuming accept tap only swallows the key afterward. ButInputMonitor.routeObserverKeyDownsuppressed the accept key (returned before callingonEvent) wheneverisAcceptTapOwningAcceptKeyswas true, which is the case whenever a ghost suggestion is concurrently visible. So if a late async suggestion became visible while the emoji panel was open (most likely on the slow first generation after focusing a field), Tab was classified as.acceptance, never reached the emoji controller, and was routed to the suggestion-accept path instead. Once the suggestion cleared, the emoji committed normally, which is why it "worked afterwards."Fix: exclude an open emoji capture from accept-key recognition at the observer, so the emoji picker keeps its intended "first look at every keystroke" even with a suggestion overlay showing:
The consuming tap's
emojiCaptureKeyDeciderthen swallows the Tab (the head-inserted observer runs before the tail-appended accept tap, sopendingDecisionis already set to consume). When no capture is open,captureInterceptionActiveis false and behavior is identical to before.Also adds the previously-missing
EmojiPickerControllerlogging (capture open / abort-on-nil-context / commit / focus-change cancel / replace) so this path is diagnosable fromcotabby.jsonl, and a regression test.Validation
The app-hosted unit-test run could not execute locally due to the known Team ID signing mismatch (test bundle signed "Sign to Run Locally" vs the host app's Apple Development identity →
dlopenrejects the bundle).build-for-testingconfirms the new test compiles; CI runs the suite with consistent signing.New test:
test_observerTapRoutesAcceptKeyToEmojiObserverWhileCapturingDespiteVisibleSuggestionasserts that with bothcaptureInterceptionActiveandisAcceptTapOwningAcceptKeysset, the accept key reachesonEventand is not classified as.acceptance.Linked issues
(none filed)
Risk / rollout notes
EmojiPickerControllerhad no logging until this PR. A second candidate cause exists: on the very first:after focusing a field, the AX focus context can still be nil, sobeginCaptureaborts and no panel opens. The added logging now distinguishes the two; if reports persist, checkcotabby.jsonlfor "emoji capture aborted at open".InputMonitor.captureInterceptionActivewidened fromprivateto internal (mirrorsisAcceptTapOwningAcceptKeys) so the test can stage state without installing real CGEvent taps. Production still mutates it only viasetCaptureInterceptionActive(_:).Greptile Summary
This PR fixes a race condition where pressing Tab to commit an inline
:emoji:would silently misfire on the first attempt whenever a ghost suggestion happened to be visible concurrently. The single-line logic fix inrouteObserverKeyDown(recognizesAcceptance = isAcceptTapOwningAcceptKeys && !captureInterceptionActive) ensures the accept key still flows through to the emoji controller's observer pass even when the suggestion overlay is also claiming the accept key.InputMonitor.swift: The core fix — excludes an active emoji capture from accept-key suppression at the observer, soEmojiPickerController.observe()always gets "first look" at the commit key.captureInterceptionActiveis widened tointernal(matching the existingisAcceptTapOwningAcceptKeyspattern) so tests can stage the concurrent-ownership state without installing real CGEvent taps.EmojiPickerController.swift: Addsimport Loggingand five debug log points (aborted open, capture opened, commit skipped/proceeding, focus-change cancel, replace operation) so the emoji commit path is now fully observable fromcotabby.jsonl.InputMonitorTests.swift: Adds a targeted regression test that stages both flags (captureInterceptionActive = true,isAcceptTapOwningAcceptKeys = true) and verifies Tab reachesonEventand is not classified as.acceptance.Confidence Score: 5/5
Safe to merge — the change is a minimal, well-scoped single-expression fix with a direct regression test, and no existing behavior is altered when capture is not active.
The fix is exactly one boolean expression change in
routeObserverKeyDown. All three combinations of the two flags produce correct routing. Logging is purely additive.No files require special attention.
Important Files Changed
&& !captureInterceptionActivetorecognizesAcceptanceso Tab is not silently dropped when both emoji capture and a ghost suggestion are active simultaneously.import Loggingand targeted debug-log lines at five previously-silent points. No logic changes.captureInterceptionActive = trueandisAcceptTapOwningAcceptKeys = true, verifying Tab reachesonEventand is not classified as.acceptance.Reviews (1): Last reviewed commit: "Commit :emoji: on Tab even while a sugge..." | Re-trigger Greptile