From 8eec44d80918bf72b35869f233cf59ea3647ecd0 Mon Sep 17 00:00:00 2001 From: Patrick Buckley Date: Mon, 11 May 2026 18:35:56 -0700 Subject: [PATCH] fix(ui): attach settings menu keydown synchronously MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Caught by Copilot on PR #514. openSettingsMenu sets _settingsMenu synchronously, but the menu's keydown handler was registered inside setTimeout(0). The previous-commit guard in the global keydown handler returns early when _settingsMenu is set (so dashboard isn't hidden by Escape over the menu), which created a window where Escape had no handler at all — the global skipped, the menu's own listener wasn't ready yet, and the menu got stuck open until the next interaction. Attach keydown synchronously; keep mousedown + initial focus in setTimeout (mousedown to avoid the opening click triggering its own outside-click close, focus because the menu DOM needs a tick to settle layout). --- turnstone/ui/static/app.js | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/turnstone/ui/static/app.js b/turnstone/ui/static/app.js index 70c5c9cc..bd82daab 100644 --- a/turnstone/ui/static/app.js +++ b/turnstone/ui/static/app.js @@ -6090,14 +6090,22 @@ function openSettingsMenu(triggerEl) { } }; - // Defer listener wiring + focus so the click that opened the menu - // doesn't immediately trigger the mousedown-close path. + // Attach the keydown listener synchronously so an Escape press + // queued behind the opening click isn't silently dropped: the + // global keydown handler at the bottom of this file returns early + // when _settingsMenu is set (the dashboard-Escape-wipes-composer + // guard), so without a synchronous menu-side listener there's a + // brief window where Escape has no handler at all. Mousedown + + // initial focus stay deferred — mousedown to avoid the click that + // opened the menu firing its own outside-click close, initial + // focus because the menu DOM needs a tick to settle layout before + // we call focus() on its first item. + document.addEventListener("keydown", _settingsMenuCloseHandler); var activeMenu = menu; var closeHandler = _settingsMenuCloseHandler; setTimeout(function () { if (_settingsMenu !== activeMenu || !closeHandler) return; document.addEventListener("mousedown", closeHandler); - document.addEventListener("keydown", closeHandler); var first = activeMenu.querySelector(".ws-tab-dropdown-item"); if (first) first.focus(); }, 0);