Skip to content

fix(recip-selector): cleanup esc handling in recip selector - #5626

Merged
sedson merged 3 commits into
mainfrom
seamus/focus-in-message-draft
Aug 13, 2026
Merged

fix(recip-selector): cleanup esc handling in recip selector#5626
sedson merged 3 commits into
mainfrom
seamus/focus-in-message-draft

Conversation

@sedson

@sedson sedson commented Aug 13, 2026

Copy link
Copy Markdown
Contributor

No description provided.

@coderabbitai

coderabbitai Bot commented Aug 13, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Important

Review skipped

Auto incremental reviews are disabled on this repository.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: 0299302b-09fb-4c46-adbb-e17d10aa774f

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
📝 Walkthrough

Summary by CodeRabbit

  • New Features

    • Pressing Escape now closes recipient menus, clears searches, and dismisses subject fields.
    • Escape handling now works consistently across desktop and mobile compose experiences.
  • Bug Fixes

    • Prevented Escape from triggering unintended browser or parent-level actions while interacting with compose inputs.
    • Improved dismissal behavior for open menus and active input fields.

Walkthrough

Escape handling now closes recipient menus, clears recipient searches, and blurs recipient inputs. Channel compose passes a recipient selector debug name. Desktop and mobile email subject fields blur on Escape. The channel input caches inline-menu state and logs development diagnostics before returning that state.

🚥 Pre-merge checks | ✅ 3 | ❌ 1

❌ Failed checks (1 inconclusive)

Check name Status Explanation Resolution
Description check ❓ Inconclusive No pull request description was provided, so the changeset has no author-provided summary or context. Add a brief description that summarizes the Escape-handling changes and their intended behavior.
✅ Passed checks (3 passed)
Check name Status Explanation
Title check ✅ Passed The title uses conventional commits format, describes the Escape-handling fix, and is under 72 characters.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

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.

@github-actions

github-actions Bot commented Aug 13, 2026

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@apps/web/src/features/block-email/component/BaseInput.tsx`:
- Around line 1818-1822: Update both subject input handlers and the hotkey
dispatcher so Escape on a subject input cannot trigger the “Close reply” action;
prefer excluding subject inputs in the dispatcher’s capture-phase logic, while
preserving the existing preventDefault and blur behavior.
🪄 Autofix

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: de4e1d21-be53-4fbf-8414-e2eedca826ff

📥 Commits

Reviewing files that changed from the base of the PR and between 5c6b242 and fd38017.

📒 Files selected for processing (5)
  • apps/web/src/features/block-channel/component/Compose.tsx
  • apps/web/src/features/block-email/component/BaseInput.tsx
  • apps/web/src/features/block-email/component/compose/ComposeSubject.tsx
  • apps/web/src/features/channel/Input/ChannelInput.tsx
  • apps/web/src/lib/core/component/RecipientSelector.tsx

Comment on lines +1818 to +1822
onKeyDown={(e) => {
if (e.key !== 'Escape') return;
e.preventDefault();
e.currentTarget.blur();
}}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🗄️ Data Integrity & Integration | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail
rg -n -C 15 'registerHotkey|useHotkeyDOMScope|runWithInputFocused' apps/web --glob '*.{ts,tsx}'

Repository: macro-inc/macro

Length of output: 50372


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo '=== BaseInput symbols and relevant ranges ==='
rg -n -C 12 "runWithInputFocused|hotkey: ['\"]escape|Close reply|onKeyDown=|on:keydown" apps/web/src/features/block-email/component/BaseInput.tsx
echo '=== hotkey implementation files ==='
rg -l "function registerHotkey|const registerHotkey|export .*registerHotkey|addEventListener\\(['\"]keydown|onKeyDown" apps/web/src/lib apps/web/src/features --glob '*.{ts,tsx}' | rg 'hotkey|Hotkey|keyboard|Key'
echo '=== reply Escape registrations ==='
rg -n -C 15 "Close reply|close reply|bottomReplyOpen|replyingToMessageId|hotkey: ['\"]escape" apps/web/src/features/block-email apps/web/src/lib/core --glob '*.{ts,tsx}' | head -n 1200

Repository: macro-inc/macro

Length of output: 33308


🏁 Script executed:

#!/bin/bash
set -euo pipefail

file=apps/web/src/lib/core/hotkey/hotkeys.ts
echo '=== hotkeys outline ==='
ast-grep outline "$file"
echo '=== hotkeys key symbols ==='
rg -n -C 20 "runWithInputFocused|addEventListener|removeEventListener|keydown|stopPropagation|preventDefault|attachHotkey|useHotkeyDOMScope|registerHotkey|dispatch" "$file"
echo '=== BaseInput scope attachment and lifecycle ==='
sed -n '1,180p' apps/web/src/features/block-email/component/BaseInput.tsx
sed -n '1120,1290p' apps/web/src/features/block-email/component/BaseInput.tsx

Repository: macro-inc/macro

Length of output: 31161


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo '=== runCommand implementation and hotkey types ==='
rg -n -C 30 "export function runCommand|function runCommand|runCommand\\(" apps/web/src/lib/core/hotkey --glob '*.{ts,tsx}'
echo '=== compose scope declaration and attachment ==='
rg -n -C 25 "composeHotkeyScope|attachComposeHotkeys|useHotkeyDOMScope" apps/web/src/features/block-email/component/BaseInput.tsx
echo '=== all native keydown capture listeners near hotkey root ==='
rg -n -C 8 "addEventListener\\(['\"]keydown|capture: true|on:keydown" apps/web/src/lib/core apps/web/src/features/block-email --glob '*.{ts,tsx}'

Repository: macro-inc/macro

Length of output: 50371


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo '=== runCommand body ==='
sed -n '435,535p' apps/web/src/lib/core/hotkey/utils.ts
echo '=== BaseInput document capture listener ==='
sed -n '560,645p' apps/web/src/features/block-email/component/BaseInput.tsx
echo '=== hotkey event finalization ==='
sed -n '700,750p' apps/web/src/lib/core/hotkey/hotkeys.ts
echo '=== static behavioral verifier ==='
python3 - <<'PY'
from pathlib import Path

base = Path("apps/web/src/features/block-email/component/BaseInput.tsx").read_text()
hotkeys = Path("apps/web/src/lib/core/hotkey/hotkeys.ts").read_text()
utils = Path("apps/web/src/lib/core/hotkey/utils.ts").read_text()

checks = {
    "subject handlers lack stopPropagation": base.count("e.currentTarget.blur();") >= 2
        and base.count("e.stopPropagation();") == 0,
    "close reply runs with focused input": "description: 'Close reply'" in base
        and "runWithInputFocused: true" in base[base.index("description: 'Close reply'"):base.index("description: 'Close reply'") + 500],
    "global keydown listener uses capture": "document.addEventListener('keydown', handleKeyDown, { capture: true });" in hotkeys,
    "close reply returns true": "return true;" in base[base.index("description: 'Close reply'"):base.index("description: 'Close reply'") + 900],
    "dispatcher invokes preventDefault and stopPropagation": "e.preventDefault();" in hotkeys and "e.stopPropagation();" in hotkeys,
    "dispatcher runs commands when input focused": "command.runWithInputFocused || !isEditableFocused" in hotkeys,
}
for name, ok in checks.items():
    print(f"{name}: {'PASS' if ok else 'FAIL'}")
if not all(checks.values()):
    raise SystemExit(1)

print("Conclusion: a target/bubble subject handler cannot suppress a document-capture hotkey listener that already dispatched Close reply.")
PY

Repository: macro-inc/macro

Length of output: 8538


🏁 Script executed:

#!/bin/bash
set -euo pipefail

python3 - <<'PY'
from pathlib import Path
import re

base_path = Path("apps/web/src/features/block-email/component/BaseInput.tsx")
hotkey_path = Path("apps/web/src/lib/core/hotkey/hotkeys.ts")
utils_path = Path("apps/web/src/lib/core/hotkey/utils.ts")

base = base_path.read_text()
hotkeys = hotkey_path.read_text()
utils = utils_path.read_text()

subject_blocks = re.findall(
    r"onKeyDown=\{\(e\) => \{\n"
    r"\s+if \(e\.key !== 'Escape'\) return;\n"
    r"(?P<body>.*?)"
    r"\n\s+\}\}",
    base,
    re.S,
)

assert len(subject_blocks) == 2, len(subject_blocks)
for i, body in enumerate(subject_blocks, 1):
    assert "e.preventDefault();" in body, i
    assert "e.currentTarget.blur();" in body, i
    assert "e.stopPropagation();" not in body, i

close_start = base.index("description: 'Close reply'")
close_block = base[close_start:base.index("hotkeyToken:", close_start)]
assert "runWithInputFocused: true" in close_block
assert "return true;" in close_block
assert "deleteDraftAndReset();" in close_block

assert "document.addEventListener('keydown', handleKeyDown, { capture: true });" in hotkeys
run_start = utils.index("export function runCommand(")
run_block = utils[run_start:]
assert "command.keyDownHandler?.(e)" in run_block
assert "e?.stopPropagation();" in run_block

print("PASS: both subject handlers blur without stopping propagation.")
print("PASS: Close reply runs with editable inputs and returns true.")
print("PASS: the dispatcher runs in document capture phase before target handlers.")
print("PASS: a captured Close reply command stops the event after its handler runs.")
PY

Repository: macro-inc/macro

Length of output: 431


Fix Escape handling for subject inputs.

The hotkey dispatcher runs in document capture phase, before either subject handler. stopPropagation() in the current onKeyDown cannot prevent Close reply from running. Exclude subject inputs from Close reply, or add an earlier capture-phase guard in the dispatcher. Apply this to both subject inputs.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@apps/web/src/features/block-email/component/BaseInput.tsx` around lines 1818
- 1822, Update both subject input handlers and the hotkey dispatcher so Escape
on a subject input cannot trigger the “Close reply” action; prefer excluding
subject inputs in the dispatcher’s capture-phase logic, while preserving the
existing preventDefault and blur behavior.

@sedson
sedson merged commit be0dfe7 into main Aug 13, 2026
24 checks passed
@sedson
sedson deleted the seamus/focus-in-message-draft branch August 13, 2026 16:30
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant